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.perm.PermissioningConditions;
import org.hyperledger.besu.tests.acceptance.dsl.condition.priv.PrivConditions;
import org.hyperledger.besu.tests.acceptance.dsl.condition.txpool.TxPoolConditions;
import org.hyperledger.besu.tests.acceptance.dsl.condition.web3.Web3Conditions;
import org.hyperledger.besu.tests.acceptance.dsl.contract.ContractVerifier;
import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster;
@ -39,6 +40,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerTransact
import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.NetTransactions;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningTransactions;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyTransactions;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.web3.Web3Transactions;
import java.io.File;
@ -83,6 +85,8 @@ public class AcceptanceTestBase {
protected final Web3Conditions web3;
protected final PrivConditions priv;
protected final PrivacyTransactions privacyTransactions;
protected final TxPoolConditions txPoolConditions;
protected final TxPoolTransactions txPoolTransactions;
protected AcceptanceTestBase() {
ethTransactions = new EthTransactions();
@ -108,6 +112,8 @@ public class AcceptanceTestBase {
admin = new AdminConditions(adminTransactions);
web3 = new Web3Conditions(new Web3Transactions());
besu = new BesuNodeFactory();
txPoolTransactions = new TxPoolTransactions();
txPoolConditions = new TxPoolConditions(txPoolTransactions);
contractVerifier = new ContractVerifier(accounts.getPrimaryBenefactor());
permissionedNodeBuilder = new PermissionedNodeBuilder();
}

@ -0,0 +1,51 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.dsl.condition.txpool;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.tests.acceptance.dsl.WaitUtils;
import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions;
import java.util.List;
import java.util.Map;
public class TxPoolConditions {
private final TxPoolTransactions txPoolTransactions;
public TxPoolConditions(final TxPoolTransactions txPoolTransactions) {
this.txPoolTransactions = txPoolTransactions;
}
public Condition inTransactionPool(final Hash txHash) {
return node ->
WaitUtils.waitFor(
() -> {
List<Map<String, String>> poolContents =
node.execute(txPoolTransactions.getTxPoolContents());
boolean found = false;
for (Map<String, String> txInfo : poolContents) {
if (Hash.fromHexString(txInfo.get("hash")).equals(txHash)) {
found = true;
break;
}
}
assertThat(found).isTrue();
});
}
}

@ -42,6 +42,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerRequestF
import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.CustomRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningJsonRpcRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolRequestFactory;
import java.io.File;
import java.io.FileInputStream;
@ -330,6 +331,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
new PrivacyRequestFactory(web3jService),
new CustomRequestFactory(web3jService),
new MinerRequestFactory(web3jService),
new TxPoolRequestFactory(web3jService),
websocketService,
loginRequestFactory());
}

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

@ -92,7 +92,12 @@ public class BesuNodeFactory {
public BesuNode createArchiveNode(final String name) throws IOException {
return create(
new BesuNodeConfigurationBuilder().name(name).jsonRpcEnabled().webSocketEnabled().build());
new BesuNodeConfigurationBuilder()
.name(name)
.jsonRpcEnabled()
.jsonRpcTxPool()
.webSocketEnabled()
.build());
}
public BesuNode createNode(

@ -22,6 +22,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerRequestF
import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.CustomRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningJsonRpcRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolRequestFactory;
import java.util.Optional;
@ -40,6 +41,7 @@ public class NodeRequests {
private final Optional<WebSocketService> websocketService;
private final LoginRequestFactory login;
private final MinerRequestFactory miner;
private final TxPoolRequestFactory txPool;
public NodeRequests(
final Web3j netEth,
@ -50,6 +52,7 @@ public class NodeRequests {
final PrivacyRequestFactory privacy,
final CustomRequestFactory custom,
final MinerRequestFactory miner,
final TxPoolRequestFactory txPool,
final Optional<WebSocketService> websocketService,
final LoginRequestFactory login) {
this.netEth = netEth;
@ -60,6 +63,7 @@ public class NodeRequests {
this.privacy = privacy;
this.custom = custom;
this.miner = miner;
this.txPool = txPool;
this.websocketService = websocketService;
this.login = login;
}
@ -104,6 +108,10 @@ public class NodeRequests {
return miner;
}
public TxPoolRequestFactory txPool() {
return txPool;
}
public void shutdown() {
netEth.shutdown();
websocketService.ifPresent(WebSocketService::close);

@ -0,0 +1,34 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class TxPoolBesuTransaction implements Transaction<List<Map<String, String>>> {
@Override
public List<Map<String, String>> execute(final NodeRequests node) {
try {
return node.txPool().besuTransactions().send().getResult();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,38 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool;
import java.util.List;
import java.util.Map;
import org.web3j.protocol.Web3jService;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.Response;
public class TxPoolRequestFactory {
private final Web3jService web3jService;
public TxPoolRequestFactory(final Web3jService web3jService) {
this.web3jService = web3jService;
}
Request<?, TransactionInfoResponse> besuTransactions() {
return new Request<>(
"txpool_besuTransactions", null, web3jService, TransactionInfoResponse.class);
}
static class TransactionInfoResponse extends Response<List<Map<String, String>>> {}
}

@ -0,0 +1,22 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool;
public class TxPoolTransactions {
public TxPoolBesuTransaction getTxPoolContents() {
return new TxPoolBesuTransaction();
}
}

@ -0,0 +1,48 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Amount;
import org.hyperledger.besu.tests.acceptance.dsl.node.Node;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
import org.junit.Before;
import org.junit.Test;
public class GossipTransactionAcceptanceTest extends AcceptanceTestBase {
private Node archiveNode1;
@Before
public void setUp() throws Exception {
archiveNode1 = besu.createArchiveNode("archiveNode1");
cluster.start(archiveNode1, besu.createArchiveNode("archiveNode2"));
}
@Test
public void shouldGossipATransaction() {
final Account account = accounts.createAccount("account-one");
final Amount balance = Amount.ether(20);
TransferTransaction tx = accountTransactions.createTransfer(account, balance);
Hash txHash = archiveNode1.execute(tx);
cluster.verify(txPoolConditions.inTransactionPool(txHash));
}
}

@ -787,6 +787,15 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
arity = "1")
private final Integer txPoolMaxSize = TransactionPoolConfiguration.MAX_PENDING_TRANSACTIONS;
@Option(
names = {"--tx-pool-hashes-max-size"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
description =
"Maximum number of pending transaction hashes that will be kept in the transaction pool (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer pooledTransactionHashesSize =
TransactionPoolConfiguration.MAX_PENDING_TRANSACTIONS_HASHES;
@Option(
names = {"--tx-pool-retention-hours"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
@ -1690,6 +1699,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return transactionPoolOptions
.toDomainObject()
.txPoolMaxSize(txPoolMaxSize)
.pooledTransactionHashesSize(pooledTransactionHashesSize)
.pendingTxRetentionPeriod(pendingTxRetentionPeriod)
.build();
}

@ -27,6 +27,7 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
private static final String MAX_GET_BODIES_FLAG = "--Xewp-max-get-bodies";
private static final String MAX_GET_RECEIPTS_FLAG = "--Xewp-max-get-receipts";
private static final String MAX_GET_NODE_DATA_FLAG = "--Xewp-max-get-node-data";
private static final String MAX_GET_POOLED_TRANSACTIONS = "--Xewp-max-get-pooled-transactions";
@CommandLine.Option(
hidden = true,
@ -64,6 +65,15 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
private PositiveNumber maxGetNodeData =
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_NODE_DATA);
@CommandLine.Option(
hidden = true,
names = {MAX_GET_POOLED_TRANSACTIONS},
paramLabel = "<INTEGER>",
description =
"Maximum request limit for Ethereum Wire Protocol GET_POOLED_TRANSACTIONS. (default: ${DEFAULT-VALUE})")
private PositiveNumber maxGetPooledTransactions =
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_POOLED_TRANSACTIONS);
private EthProtocolOptions() {}
public static EthProtocolOptions create() {
@ -76,6 +86,7 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
options.maxGetBlockBodies = PositiveNumber.fromInt(config.getMaxGetBlockBodies());
options.maxGetReceipts = PositiveNumber.fromInt(config.getMaxGetReceipts());
options.maxGetNodeData = PositiveNumber.fromInt(config.getMaxGetNodeData());
options.maxGetPooledTransactions = PositiveNumber.fromInt(config.getMaxGetPooledTransactions());
return options;
}
@ -86,6 +97,7 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
.maxGetBlockBodies(maxGetBlockBodies)
.maxGetReceipts(maxGetReceipts)
.maxGetNodeData(maxGetNodeData)
.maxGetPooledTransactions(maxGetPooledTransactions)
.build();
}
@ -99,6 +111,8 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
MAX_GET_RECEIPTS_FLAG,
OptionParser.format(maxGetReceipts.getValue()),
MAX_GET_NODE_DATA_FLAG,
OptionParser.format(maxGetNodeData.getValue()));
OptionParser.format(maxGetNodeData.getValue()),
MAX_GET_POOLED_TRANSACTIONS,
OptionParser.format(maxGetPooledTransactions.getValue()));
}
}

@ -31,7 +31,11 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.peervalidation.ClassicForkPeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.DaoForkPeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
@ -242,13 +246,40 @@ public abstract class BesuControllerBuilder<C> {
prunerConfiguration));
}
}
final EthPeers ethPeers = new EthPeers(getSupportedProtocol(), clock, metricsSystem);
final EthMessages ethMessages = new EthMessages();
final EthScheduler scheduler =
new EthScheduler(
syncConfig.getDownloaderParallelism(),
syncConfig.getTransactionsParallelism(),
syncConfig.getComputationParallelism(),
metricsSystem);
final EthContext ethContext = new EthContext(ethPeers, ethMessages, scheduler);
final SyncState syncState = new SyncState(blockchain, ethPeers);
final boolean fastSyncEnabled = syncConfig.getSyncMode().equals(SyncMode.FAST);
final TransactionPool transactionPool =
TransactionPoolFactory.createTransactionPool(
protocolSchedule,
protocolContext,
ethContext,
clock,
metricsSystem,
syncState,
miningParameters.getMinTransactionGasPrice(),
transactionPoolConfiguration);
final EthProtocolManager ethProtocolManager =
createEthProtocolManager(
protocolContext, fastSyncEnabled, createPeerValidators(protocolSchedule));
final SyncState syncState =
new SyncState(blockchain, ethProtocolManager.ethContext().getEthPeers());
protocolContext,
fastSyncEnabled,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethContext,
ethMessages,
scheduler,
createPeerValidators(protocolSchedule));
final Synchronizer synchronizer =
new DefaultSynchronizer<>(
syncConfig,
@ -263,17 +294,6 @@ public abstract class BesuControllerBuilder<C> {
clock,
metricsSystem);
final TransactionPool transactionPool =
TransactionPoolFactory.createTransactionPool(
protocolSchedule,
protocolContext,
ethProtocolManager.ethContext(),
clock,
metricsSystem,
syncState,
miningParameters.getMinTransactionGasPrice(),
transactionPoolConfiguration);
final MiningCoordinator miningCoordinator =
createMiningCoordinator(
protocolSchedule,
@ -343,22 +363,32 @@ public abstract class BesuControllerBuilder<C> {
protected abstract C createConsensusContext(
Blockchain blockchain, WorldStateArchive worldStateArchive);
protected String getSupportedProtocol() {
return EthProtocol.NAME;
}
protected EthProtocolManager createEthProtocolManager(
final ProtocolContext<C> protocolContext,
final boolean fastSyncEnabled,
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthContext ethContext,
final EthMessages ethMessages,
final EthScheduler scheduler,
final List<PeerValidator> peerValidators) {
return new EthProtocolManager(
protocolContext.getBlockchain(),
protocolContext.getWorldStateArchive(),
networkId,
protocolContext.getWorldStateArchive(),
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators,
fastSyncEnabled,
syncConfig.getDownloaderParallelism(),
syncConfig.getTransactionsParallelism(),
syncConfig.getComputationParallelism(),
clock,
metricsSystem,
ethereumWireProtocolConfiguration,
scheduler,
genesisConfig.getForks());
}

@ -31,7 +31,12 @@ import org.hyperledger.besu.ethereum.blockcreation.NoopMiningCoordinator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -106,23 +111,34 @@ public class IbftLegacyBesuControllerBuilder extends BesuControllerBuilder<IbftC
}
}
@Override
protected String getSupportedProtocol() {
return Istanbul64Protocol.get().getName();
}
@Override
protected EthProtocolManager createEthProtocolManager(
final ProtocolContext<IbftContext> protocolContext,
final boolean fastSyncEnabled,
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthContext ethContext,
final EthMessages ethMessages,
final EthScheduler scheduler,
final List<PeerValidator> peerValidators) {
LOG.info("Operating on IBFT-1.0 network.");
return new Istanbul64ProtocolManager(
protocolContext.getBlockchain(),
protocolContext.getWorldStateArchive(),
networkId,
protocolContext.getWorldStateArchive(),
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators,
fastSyncEnabled,
syncConfig.getDownloaderParallelism(),
syncConfig.getTransactionsParallelism(),
syncConfig.getComputationParallelism(),
clock,
metricsSystem,
ethereumWireProtocolConfiguration);
scheduler);
}
}

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

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

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

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

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

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

@ -87,6 +87,7 @@ public class IbftBlockCreatorTest {
new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1,
5,
TestClock.fixed(),
metricsSystem);

@ -16,6 +16,7 @@ package org.hyperledger.besu.consensus.ibftlegacy.protocol;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
@ -47,6 +48,9 @@ public class Istanbul64Protocol implements SubProtocol {
EthPV62.GET_BLOCK_BODIES,
EthPV62.BLOCK_BODIES,
EthPV62.NEW_BLOCK,
EthPV65.NEW_POOLED_TRANSACTION_HASHES,
EthPV65.GET_POOLED_TRANSACTIONS,
EthPV65.POOLED_TRANSACTIONS,
EthPV63.GET_NODE_DATA,
EthPV63.NODE_DATA,
EthPV63.GET_RECEIPTS,
@ -90,6 +94,12 @@ public class Istanbul64Protocol implements SubProtocol {
return "BlockBodies";
case EthPV62.NEW_BLOCK:
return "NewBlock";
case EthPV65.NEW_POOLED_TRANSACTION_HASHES:
return "NewPooledTransactionHashes";
case EthPV65.GET_POOLED_TRANSACTIONS:
return "GetPooledTransactions";
case EthPV65.POOLED_TRANSACTIONS:
return "PooledTransactions";
case EthPV63.GET_NODE_DATA:
return "GetNodeData";
case EthPV63.NODE_DATA:

@ -18,14 +18,17 @@ import static java.util.Collections.singletonList;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.math.BigInteger;
import java.time.Clock;
import java.util.List;
/** This allows for interoperability with Quorum, but shouldn't be used otherwise. */
@ -33,28 +36,28 @@ public class Istanbul64ProtocolManager extends EthProtocolManager {
public Istanbul64ProtocolManager(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final BigInteger networkId,
final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthMessages ethMessages,
final EthContext ethContext,
final List<PeerValidator> peerValidators,
final boolean fastSyncEnabled,
final int syncWorkers,
final int txWorkers,
final int computationWorkers,
final Clock clock,
final MetricsSystem metricsSystem,
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
final EthScheduler scheduler) {
super(
blockchain,
worldStateArchive,
networkId,
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators,
fastSyncEnabled,
syncWorkers,
txWorkers,
computationWorkers,
clock,
metricsSystem,
ethereumWireProtocolConfiguration);
scheduler);
}
@Override

@ -101,6 +101,7 @@ public class IbftBlockCreatorTest {
new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1,
5,
TestClock.fixed(),
metricsSystem),
protContext,

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

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

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

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

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.core;
import static org.assertj.core.util.Preconditions.checkArgument;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.ethereum.ProtocolContext;
@ -24,6 +25,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -52,6 +54,7 @@ public class BlockchainSetupUtil<C> {
private final ProtocolContext<C> protocolContext;
private final ProtocolSchedule<C> protocolSchedule;
private final WorldStateArchive worldArchive;
private final TransactionPool transactionPool;
private final List<Block> blocks;
private final EthScheduler scheduler;
private long maxBlockNumber;
@ -62,6 +65,7 @@ public class BlockchainSetupUtil<C> {
final ProtocolContext<C> protocolContext,
final ProtocolSchedule<C> protocolSchedule,
final WorldStateArchive worldArchive,
final TransactionPool transactionPool,
final List<Block> blocks,
final EthScheduler scheduler) {
this.genesisState = genesisState;
@ -69,6 +73,7 @@ public class BlockchainSetupUtil<C> {
this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule;
this.worldArchive = worldArchive;
this.transactionPool = transactionPool;
this.blocks = blocks;
this.scheduler = scheduler;
}
@ -150,6 +155,7 @@ public class BlockchainSetupUtil<C> {
final GenesisState genesisState = GenesisState.fromJson(genesisJson, protocolSchedule);
final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock());
final WorldStateArchive worldArchive = createInMemoryWorldStateArchive();
final TransactionPool transactionPool = mock(TransactionPool.class);
genesisState.writeStateTo(worldArchive.getMutable());
final ProtocolContext<T> protocolContext =
@ -172,6 +178,7 @@ public class BlockchainSetupUtil<C> {
protocolContext,
protocolSchedule,
worldArchive,
transactionPool,
blocks,
scheduler);
} catch (final IOException | URISyntaxException ex) {
@ -209,6 +216,10 @@ public class BlockchainSetupUtil<C> {
return scheduler;
}
public TransactionPool getTransactionPool() {
return transactionPool;
}
private void importBlocks(final List<Block> blocks) {
for (final Block block : blocks) {
if (block.getHeader().getNumber() == BlockHeader.GENESIS_BLOCK_NUMBER) {

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
@ -32,6 +33,8 @@ public class EthProtocol implements SubProtocol {
public static final Capability ETH62 = Capability.create(NAME, EthVersion.V62);
public static final Capability ETH63 = Capability.create(NAME, EthVersion.V63);
public static final Capability ETH64 = Capability.create(NAME, EthVersion.V64);
public static final Capability ETH65 = Capability.create(NAME, EthVersion.V65);
private static final EthProtocol INSTANCE = new EthProtocol();
private static final List<Integer> eth62Messages =
@ -53,6 +56,16 @@ public class EthProtocol implements SubProtocol {
EthPV63.GET_NODE_DATA, EthPV63.NODE_DATA, EthPV63.GET_RECEIPTS, EthPV63.RECEIPTS));
}
private static final List<Integer> eth65Messages = new ArrayList<>(eth63Messages);
static {
eth65Messages.addAll(
Arrays.asList(
EthPV65.NEW_POOLED_TRANSACTION_HASHES,
EthPV65.GET_POOLED_TRANSACTIONS,
EthPV65.POOLED_TRANSACTIONS));
}
@Override
public String getName() {
return NAME;
@ -65,6 +78,9 @@ public class EthProtocol implements SubProtocol {
return 8;
case EthVersion.V63:
case EthVersion.V64:
case EthVersion
.V65: // same number of messages in the range, eth65 defines messages in the middle of the
// range defined by eth63.
return 17;
default:
return 0;
@ -79,6 +95,8 @@ public class EthProtocol implements SubProtocol {
case EthVersion.V63:
case EthVersion.V64:
return eth63Messages.contains(code);
case EthVersion.V65:
return eth65Messages.contains(code);
default:
return false;
}
@ -103,6 +121,12 @@ public class EthProtocol implements SubProtocol {
return "BlockBodies";
case EthPV62.NEW_BLOCK:
return "NewBlock";
case EthPV65.NEW_POOLED_TRANSACTION_HASHES:
return "NewPooledTransactionHashes";
case EthPV65.GET_POOLED_TRANSACTIONS:
return "GetPooledTransactions";
case EthPV65.POOLED_TRANSACTIONS:
return "PooledTransactions";
case EthPV63.GET_NODE_DATA:
return "GetNodeData";
case EthPV63.NODE_DATA:
@ -124,5 +148,6 @@ public class EthProtocol implements SubProtocol {
public static final int V62 = 62;
public static final int V63 = 63;
public static final int V64 = 64;
public static final int V65 = 65;
}
}

@ -26,21 +26,25 @@ public class EthProtocolConfiguration {
public static final int DEFAULT_MAX_GET_BLOCK_BODIES = 128;
public static final int DEFAULT_MAX_GET_RECEIPTS = 256;
public static final int DEFAULT_MAX_GET_NODE_DATA = 384;
public static final int DEFAULT_MAX_GET_POOLED_TRANSACTIONS = 256;
private final int maxGetBlockHeaders;
private final int maxGetBlockBodies;
private final int maxGetReceipts;
private final int maxGetNodeData;
private final int maxGetPooledTransactions;
public EthProtocolConfiguration(
final int maxGetBlockHeaders,
final int maxGetBlockBodies,
final int maxGetReceipts,
final int maxGetNodeData) {
final int maxGetNodeData,
final int maxGetPooledTransactions) {
this.maxGetBlockHeaders = maxGetBlockHeaders;
this.maxGetBlockBodies = maxGetBlockBodies;
this.maxGetReceipts = maxGetReceipts;
this.maxGetNodeData = maxGetNodeData;
this.maxGetPooledTransactions = maxGetPooledTransactions;
}
public static EthProtocolConfiguration defaultConfig() {
@ -48,7 +52,8 @@ public class EthProtocolConfiguration {
DEFAULT_MAX_GET_BLOCK_HEADERS,
DEFAULT_MAX_GET_BLOCK_BODIES,
DEFAULT_MAX_GET_RECEIPTS,
DEFAULT_MAX_GET_NODE_DATA);
DEFAULT_MAX_GET_NODE_DATA,
DEFAULT_MAX_GET_POOLED_TRANSACTIONS);
}
public static Builder builder() {
@ -71,6 +76,10 @@ public class EthProtocolConfiguration {
return maxGetNodeData;
}
public int getMaxGetPooledTransactions() {
return maxGetPooledTransactions;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
@ -83,7 +92,8 @@ public class EthProtocolConfiguration {
return maxGetBlockHeaders == that.maxGetBlockHeaders
&& maxGetBlockBodies == that.maxGetBlockBodies
&& maxGetReceipts == that.maxGetReceipts
&& maxGetNodeData == that.maxGetNodeData;
&& maxGetNodeData == that.maxGetNodeData
&& maxGetPooledTransactions == that.maxGetPooledTransactions;
}
@Override
@ -98,6 +108,7 @@ public class EthProtocolConfiguration {
.add("maxGetBlockBodies", maxGetBlockBodies)
.add("maxGetReceipts", maxGetReceipts)
.add("maxGetNodeData", maxGetNodeData)
.add("maxGetPooledTransactions", maxGetPooledTransactions)
.toString();
}
@ -114,6 +125,9 @@ public class EthProtocolConfiguration {
private PositiveNumber maxGetNodeData =
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_NODE_DATA);
private PositiveNumber maxGetPooledTransactions =
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_POOLED_TRANSACTIONS);
public Builder maxGetBlockHeaders(final PositiveNumber maxGetBlockHeaders) {
this.maxGetBlockHeaders = maxGetBlockHeaders;
return this;
@ -134,12 +148,18 @@ public class EthProtocolConfiguration {
return this;
}
public Builder maxGetPooledTransactions(final PositiveNumber maxGetPooledTransactions) {
this.maxGetPooledTransactions = maxGetPooledTransactions;
return this;
}
public EthProtocolConfiguration build() {
return new EthProtocolConfiguration(
maxGetBlockHeaders.getValue(),
maxGetBlockBodies.getValue(),
maxGetReceipts.getValue(),
maxGetNodeData.getValue());
maxGetNodeData.getValue(),
maxGetPooledTransactions.getValue());
}
}
}

@ -20,9 +20,11 @@ import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockBodiesMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetPooledTransactionsMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
@ -67,6 +69,7 @@ public class EthPeer {
private final RequestManager bodiesRequestManager = new RequestManager(this);
private final RequestManager receiptsRequestManager = new RequestManager(this);
private final RequestManager nodeDataRequestManager = new RequestManager(this);
private final RequestManager pooledTransactionsRequestManager = new RequestManager(this);
private final AtomicReference<Consumer<EthPeer>> onStatusesExchanged = new AtomicReference<>();
private final PeerReputation reputation = new PeerReputation();
@ -154,6 +157,8 @@ public class EthPeer {
return sendRequest(receiptsRequestManager, messageData);
case EthPV63.GET_NODE_DATA:
return sendRequest(nodeDataRequestManager, messageData);
case EthPV65.GET_POOLED_TRANSACTIONS:
return sendRequest(pooledTransactionsRequestManager, messageData);
default:
connection.sendForProtocol(protocolName, messageData);
return null;
@ -201,6 +206,12 @@ public class EthPeer {
return sendRequest(nodeDataRequestManager, message);
}
public RequestManager.ResponseStream getPooledTransactions(final List<Hash> hashes)
throws PeerNotConnected {
final GetPooledTransactionsMessage message = GetPooledTransactionsMessage.create(hashes);
return sendRequest(pooledTransactionsRequestManager, message);
}
boolean validateReceivedMessage(final EthMessage message) {
checkArgument(message.getPeer().equals(this), "Mismatched message sent to peer for dispatch");
switch (message.getData().getCode()) {
@ -228,6 +239,12 @@ public class EthPeer {
return false;
}
break;
case EthPV65.POOLED_TRANSACTIONS:
if (pooledTransactionsRequestManager.outstandingRequests() == 0) {
LOG.warn("Unsolicited pooling transactions received.");
return false;
}
break;
default:
// Nothing to do
}
@ -258,6 +275,10 @@ public class EthPeer {
reputation.resetTimeoutCount(EthPV63.GET_NODE_DATA);
nodeDataRequestManager.dispatchResponse(message);
break;
case EthPV65.POOLED_TRANSACTIONS:
reputation.resetTimeoutCount(EthPV65.GET_POOLED_TRANSACTIONS);
pooledTransactionsRequestManager.dispatchResponse(message);
break;
default:
// Nothing to do
}
@ -272,6 +293,7 @@ public class EthPeer {
bodiesRequestManager.close();
receiptsRequestManager.close();
nodeDataRequestManager.close();
pooledTransactionsRequestManager.close();
}
public void registerKnownBlock(final Hash hash) {
@ -344,7 +366,8 @@ public class EthPeer {
return headersRequestManager.outstandingRequests()
+ bodiesRequestManager.outstandingRequests()
+ receiptsRequestManager.outstandingRequests()
+ nodeDataRequestManager.outstandingRequests();
+ nodeDataRequestManager.outstandingRequests()
+ pooledTransactionsRequestManager.outstandingRequests();
}
public long getLastRequestTimestamp() {

@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.eth.messages.StatusMessage;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidatorRunner;
import org.hyperledger.besu.ethereum.eth.sync.BlockBroadcaster;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected;
@ -38,10 +39,8 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.math.BigInteger;
import java.time.Clock;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@ -54,9 +53,9 @@ import org.apache.logging.log4j.Logger;
public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
private static final Logger LOG = LogManager.getLogger();
private static final List<Capability> FAST_SYNC_CAPS =
List.of(EthProtocol.ETH63, EthProtocol.ETH64);
List.of(EthProtocol.ETH63, EthProtocol.ETH64, EthProtocol.ETH65);
private static final List<Capability> FULL_SYNC_CAPS =
List.of(EthProtocol.ETH62, EthProtocol.ETH63, EthProtocol.ETH64);
List.of(EthProtocol.ETH62, EthProtocol.ETH63, EthProtocol.ETH64, EthProtocol.ETH65);
private final EthScheduler scheduler;
private final CountDownLatch shutdown;
@ -76,14 +75,16 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
public EthProtocolManager(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final BigInteger networkId,
final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthMessages ethMessages,
final EthContext ethContext,
final List<PeerValidator> peerValidators,
final boolean fastSyncEnabled,
final EthScheduler scheduler,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final Clock clock,
final MetricsSystem metricsSystem,
final ForkIdManager forkIdManager) {
this.networkId = networkId;
this.peerValidators = peerValidators;
@ -96,9 +97,9 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
this.forkIdManager = forkIdManager;
ethPeers = new EthPeers(getSupportedProtocol(), clock, metricsSystem);
ethMessages = new EthMessages();
ethContext = new EthContext(ethPeers, ethMessages, scheduler);
this.ethPeers = ethPeers;
this.ethMessages = ethMessages;
this.ethContext = ethContext;
this.blockBroadcaster = new BlockBroadcaster(ethContext);
@ -108,58 +109,67 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
}
// Set up request handlers
new EthServer(blockchain, worldStateArchive, ethMessages, ethereumWireProtocolConfiguration);
new EthServer(
blockchain,
worldStateArchive,
transactionPool,
ethMessages,
ethereumWireProtocolConfiguration);
}
@VisibleForTesting
public EthProtocolManager(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final BigInteger networkId,
final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthMessages ethMessages,
final EthContext ethContext,
final List<PeerValidator> peerValidators,
final boolean fastSyncEnabled,
final int syncWorkers,
final int txWorkers,
final int computationWorkers,
final Clock clock,
final MetricsSystem metricsSystem,
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
final EthScheduler scheduler) {
this(
blockchain,
worldStateArchive,
networkId,
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators,
fastSyncEnabled,
new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem),
ethereumWireProtocolConfiguration,
clock,
metricsSystem,
scheduler,
new ForkIdManager(blockchain, Collections.emptyList()));
}
public EthProtocolManager(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final BigInteger networkId,
final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthMessages ethMessages,
final EthContext ethContext,
final List<PeerValidator> peerValidators,
final boolean fastSyncEnabled,
final int syncWorkers,
final int txWorkers,
final int computationWorkers,
final Clock clock,
final MetricsSystem metricsSystem,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthScheduler scheduler,
final List<Long> forks) {
this(
blockchain,
worldStateArchive,
networkId,
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators,
fastSyncEnabled,
new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem),
ethereumWireProtocolConfiguration,
clock,
metricsSystem,
scheduler,
new ForkIdManager(blockchain, forks));
}

@ -54,7 +54,7 @@ public class EthScheduler {
protected final ExecutorService servicesExecutor;
protected final ExecutorService computationExecutor;
private final Collection<CompletableFuture<?>> serviceFutures = new ConcurrentLinkedDeque<>();
private final Collection<CompletableFuture<?>> pendingFutures = new ConcurrentLinkedDeque<>();
public EthScheduler(
final int syncWorkerCount,
@ -120,21 +120,28 @@ public class EthScheduler {
syncWorkerExecutor.execute(command);
}
public <T> CompletableFuture<T> scheduleSyncWorkerTask(final EthTask<T> task) {
final CompletableFuture<T> syncFuture = task.runAsync(syncWorkerExecutor);
pendingFutures.add(syncFuture);
syncFuture.whenComplete((r, t) -> pendingFutures.remove(syncFuture));
return syncFuture;
}
public void scheduleTxWorkerTask(final Runnable command) {
txWorkerExecutor.execute(command);
}
public <T> CompletableFuture<T> scheduleServiceTask(final EthTask<T> task) {
final CompletableFuture<T> serviceFuture = task.runAsync(servicesExecutor);
serviceFutures.add(serviceFuture);
serviceFuture.whenComplete((r, t) -> serviceFutures.remove(serviceFuture));
pendingFutures.add(serviceFuture);
serviceFuture.whenComplete((r, t) -> pendingFutures.remove(serviceFuture));
return serviceFuture;
}
public CompletableFuture<Void> startPipeline(final Pipeline<?> pipeline) {
final CompletableFuture<Void> pipelineFuture = pipeline.start(servicesExecutor);
serviceFutures.add(pipelineFuture);
pipelineFuture.whenComplete((r, t) -> serviceFutures.remove(pipelineFuture));
pendingFutures.add(pipelineFuture);
pipelineFuture.whenComplete((r, t) -> pendingFutures.remove(pipelineFuture));
return pipelineFuture;
}
@ -226,7 +233,7 @@ public class EthScheduler {
public void awaitStop() throws InterruptedException {
shutdown.await();
serviceFutures.forEach(future -> future.cancel(true));
pendingFutures.forEach(future -> future.cancel(true));
if (!syncWorkerExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
LOG.error("{} worker executor did not shutdown cleanly.", this.getClass().getSimpleName());
}

@ -18,18 +18,23 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage;
import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockBodiesMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetPooledTransactionsMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.messages.NodeDataMessage;
import org.hyperledger.besu.ethereum.eth.messages.PooledTransactionsMessage;
import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
@ -52,16 +57,19 @@ class EthServer {
private final Blockchain blockchain;
private final WorldStateArchive worldStateArchive;
private final TransactionPool transactionPool;
private final EthMessages ethMessages;
private final EthProtocolConfiguration ethereumWireProtocolConfiguration;
EthServer(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final EthMessages ethMessages,
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
this.blockchain = blockchain;
this.worldStateArchive = worldStateArchive;
this.transactionPool = transactionPool;
this.ethMessages = ethMessages;
this.ethereumWireProtocolConfiguration = ethereumWireProtocolConfiguration;
this.setupListeners();
@ -72,6 +80,7 @@ class EthServer {
ethMessages.subscribe(EthPV62.GET_BLOCK_BODIES, this::handleGetBlockBodies);
ethMessages.subscribe(EthPV63.GET_RECEIPTS, this::handleGetReceipts);
ethMessages.subscribe(EthPV63.GET_NODE_DATA, this::handleGetNodeData);
ethMessages.subscribe(EthPV65.GET_POOLED_TRANSACTIONS, this::handleGetPooledTransactions);
}
private void handleGetBlockHeaders(final EthMessage message) {
@ -143,6 +152,26 @@ class EthServer {
}
}
private void handleGetPooledTransactions(final EthMessage message) {
LOG.trace("Responding to GET_POOLED_TRANSACTIONS request");
try {
final MessageData response =
constructGetPooledTransactionsResponse(
transactionPool,
message.getData(),
ethereumWireProtocolConfiguration.getMaxGetPooledTransactions());
message.getPeer().send(response);
} catch (final RLPException e) {
LOG.debug(
"Received malformed GET_POOLED_TRANSACTIONS message, disconnecting: {}",
message.getPeer(),
e);
message.getPeer().disconnect(DisconnectReason.BREACH_OF_PROTOCOL);
} catch (final PeerNotConnected peerNotConnected) {
// Peer disconnected before we could respond - nothing to do
}
}
static MessageData constructGetHeadersResponse(
final Blockchain blockchain, final MessageData message, final int requestLimit) {
final GetBlockHeadersMessage getHeaders = GetBlockHeadersMessage.readFrom(message);
@ -222,6 +251,28 @@ class EthServer {
return ReceiptsMessage.create(receipts);
}
static MessageData constructGetPooledTransactionsResponse(
final TransactionPool transactionPool, final MessageData message, final int requestLimit) {
final GetPooledTransactionsMessage getPooledTransactions =
GetPooledTransactionsMessage.readFrom(message);
final Iterable<Hash> hashes = getPooledTransactions.pooledTransactions();
final List<Transaction> tx = new ArrayList<>();
int count = 0;
for (final Hash hash : hashes) {
if (count >= requestLimit) {
break;
}
count++;
final Optional<Transaction> maybeTx = transactionPool.getTransactionByHash(hash);
if (maybeTx.isEmpty()) {
continue;
}
tx.add(maybeTx.get());
}
return PooledTransactionsMessage.create(tx);
}
static MessageData constructGetNodeDataResponse(
final WorldStateArchive worldStateArchive,
final MessageData message,

@ -0,0 +1,92 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.manager.task;
import static java.util.Collections.emptyList;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.PendingPeerRequest;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.eth.messages.PooledTransactionsMessage;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class GetPooledTransactionsFromPeerTask extends AbstractPeerRequestTask<List<Transaction>> {
private static final Logger LOG = LogManager.getLogger();
private final List<Hash> hashes;
private GetPooledTransactionsFromPeerTask(
final EthContext ethContext, final List<Hash> hashes, final MetricsSystem metricsSystem) {
super(ethContext, EthPV65.GET_POOLED_TRANSACTIONS, metricsSystem);
this.hashes = new ArrayList<>(hashes);
}
public static GetPooledTransactionsFromPeerTask forHashes(
final EthContext ethContext, final List<Hash> hashes, final MetricsSystem metricsSystem) {
return new GetPooledTransactionsFromPeerTask(ethContext, hashes, metricsSystem);
}
@Override
protected PendingPeerRequest sendRequest() {
return sendRequestToPeer(
peer -> {
LOG.debug("Requesting {} transaction pool entries from peer {}.", hashes.size(), peer);
return peer.getPooledTransactions(hashes);
},
0);
}
@Override
protected Optional<List<Transaction>> processResponse(
final boolean streamClosed, final MessageData message, final EthPeer peer) {
if (streamClosed) {
// We don't record this as a useless response because it's impossible to know if a peer has
// the data we're requesting.
return Optional.of(emptyList());
}
final PooledTransactionsMessage pooledTransactionsMessage =
PooledTransactionsMessage.readFrom(message);
final List<Transaction> tx = pooledTransactionsMessage.transactions();
if (tx.size() > hashes.size()) {
// Can't be the response to our request
return Optional.empty();
}
return mapNodeDataByHash(tx);
}
private Optional<List<Transaction>> mapNodeDataByHash(final List<Transaction> transactions) {
final List<Transaction> result = new ArrayList<>();
for (final Transaction tx : transactions) {
final Hash hash = tx.getHash();
if (!hashes.contains(hash)) {
return Optional.empty();
}
result.add(tx);
}
return Optional.of(result);
}
}

@ -0,0 +1,28 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.messages;
public final class EthPV65 {
public static final int NEW_POOLED_TRANSACTION_HASHES = 0x08;
public static final int GET_POOLED_TRANSACTIONS = 0x09;
public static final int POOLED_TRANSACTIONS = 0x0A;
private EthPV65() {
// Holder for constants only
}
}

@ -0,0 +1,69 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.messages;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
public final class GetPooledTransactionsMessage extends AbstractMessageData {
private static final int MESSAGE_CODE = EthPV65.GET_POOLED_TRANSACTIONS;
private List<Hash> pooledTransactions;
private GetPooledTransactionsMessage(final Bytes rlp) {
super(rlp);
}
@Override
public int getCode() {
return MESSAGE_CODE;
}
public static GetPooledTransactionsMessage create(final List<Hash> pooledTransactions) {
List<Hash> tx = pooledTransactions;
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeList(tx, (h, w) -> w.writeBytes(h));
return new GetPooledTransactionsMessage(out.encoded());
}
public static GetPooledTransactionsMessage readFrom(final MessageData message) {
if (message instanceof GetPooledTransactionsMessage) {
return (GetPooledTransactionsMessage) message;
}
final int code = message.getCode();
if (code != MESSAGE_CODE) {
throw new IllegalArgumentException(
String.format(
"Message has code %d and thus is not a GetPooledTransactionsMessage.", code));
}
return new GetPooledTransactionsMessage(message.getData());
}
public List<Hash> pooledTransactions() {
if (pooledTransactions == null) {
final BytesValueRLPInput in = new BytesValueRLPInput(getData(), false);
pooledTransactions = in.readList(rlp -> Hash.wrap(rlp.readBytes32()));
}
return pooledTransactions;
}
}

@ -0,0 +1,70 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.messages;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.util.ArrayList;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
public final class LimitedNewPooledTransactionHashesMessages {
static final int MAX_COUNT = 4096;
private final NewPooledTransactionHashesMessage transactionsMessage;
private final List<Hash> includedTransactions;
public LimitedNewPooledTransactionHashesMessages(
final NewPooledTransactionHashesMessage transactionsMessage,
final List<Hash> includedTransactions) {
this.transactionsMessage = transactionsMessage;
this.includedTransactions = includedTransactions;
}
public static LimitedNewPooledTransactionHashesMessages createLimited(
final Iterable<Hash> hashes) {
final List<Hash> includedTransactions = new ArrayList<>();
final BytesValueRLPOutput message = new BytesValueRLPOutput();
int count = 0;
message.startList();
for (final Hash txHash : hashes) {
final BytesValueRLPOutput encodedHashes = new BytesValueRLPOutput();
encodedHashes.writeBytes(txHash);
Bytes encodedBytes = encodedHashes.encoded();
message.writeRLPUnsafe(encodedBytes);
includedTransactions.add(txHash);
// Check if last transaction to add to the message
count++;
if (count >= MAX_COUNT) {
break;
}
}
message.endList();
return new LimitedNewPooledTransactionHashesMessages(
new NewPooledTransactionHashesMessage(message.encoded()), includedTransactions);
}
public final NewPooledTransactionHashesMessage getTransactionsMessage() {
return transactionsMessage;
}
public final List<Hash> getIncludedTransactions() {
return includedTransactions;
}
}

@ -0,0 +1,68 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.messages;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
public final class NewPooledTransactionHashesMessage extends AbstractMessageData {
private static final int MESSAGE_CODE = EthPV65.NEW_POOLED_TRANSACTION_HASHES;
private List<Hash> pendingTransactions;
NewPooledTransactionHashesMessage(final Bytes rlp) {
super(rlp);
}
@Override
public int getCode() {
return MESSAGE_CODE;
}
public static NewPooledTransactionHashesMessage create(final List<Hash> pendingTransactions) {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeList(pendingTransactions, (h, w) -> w.writeBytes(h));
return new NewPooledTransactionHashesMessage(out.encoded());
}
public static NewPooledTransactionHashesMessage readFrom(final MessageData message) {
if (message instanceof NewPooledTransactionHashesMessage) {
return (NewPooledTransactionHashesMessage) message;
}
final int code = message.getCode();
if (code != MESSAGE_CODE) {
throw new IllegalArgumentException(
String.format(
"Message has code %d and thus is not a NewPooledTransactionHashesMessage.", code));
}
return new NewPooledTransactionHashesMessage(message.getData());
}
public List<Hash> pendingTransactions() {
if (pendingTransactions == null) {
final BytesValueRLPInput in = new BytesValueRLPInput(getData(), false);
pendingTransactions = in.readList(rlp -> Hash.wrap(rlp.readBytes32()));
}
return pendingTransactions;
}
}

@ -0,0 +1,68 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.messages;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
public final class PooledTransactionsMessage extends AbstractMessageData {
private static final int MESSAGE_CODE = EthPV65.POOLED_TRANSACTIONS;
private List<Transaction> pooledTransactions;
private PooledTransactionsMessage(final Bytes rlp) {
super(rlp);
}
@Override
public int getCode() {
return MESSAGE_CODE;
}
public static PooledTransactionsMessage create(final List<Transaction> transactions) {
List<Transaction> tx = transactions;
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeList(tx, Transaction::writeTo);
return new PooledTransactionsMessage(out.encoded());
}
public static PooledTransactionsMessage readFrom(final MessageData message) {
if (message instanceof PooledTransactionsMessage) {
return (PooledTransactionsMessage) message;
}
final int code = message.getCode();
if (code != MESSAGE_CODE) {
throw new IllegalArgumentException(
String.format("Message has code %d and thus is not a PooledTransactionsMessage.", code));
}
return new PooledTransactionsMessage(message.getData());
}
public List<Transaction> transactions() {
if (pooledTransactions == null) {
final BytesValueRLPInput in = new BytesValueRLPInput(getData(), false);
pooledTransactions = in.readList(Transaction::readFrom);
}
return pooledTransactions;
}
}

@ -0,0 +1,94 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static java.util.Collections.emptySet;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class PeerPendingTransactionTracker implements EthPeer.DisconnectCallback {
private static final int MAX_TRACKED_SEEN_TRANSACTIONS = 10_000;
private final Map<EthPeer, Set<Hash>> seenTransactions = new ConcurrentHashMap<>();
private final Map<EthPeer, Set<Hash>> transactionsToSend = new ConcurrentHashMap<>();
private final PendingTransactions pendingTransactions;
public PeerPendingTransactionTracker(final PendingTransactions pendingTransactions) {
this.pendingTransactions = pendingTransactions;
}
public synchronized void markTransactionsHashesAsSeen(
final EthPeer peer, final Collection<Hash> transactions) {
final Set<Hash> seenTransactionsForPeer = getOrCreateSeenTransactionsForPeer(peer);
transactions.stream().forEach(seenTransactionsForPeer::add);
}
public synchronized void addToPeerSendQueue(final EthPeer peer, final Hash hash) {
if (!hasPeerSeenTransaction(peer, hash)) {
transactionsToSend.computeIfAbsent(peer, key -> createTransactionsSet()).add(hash);
}
}
public Iterable<EthPeer> getEthPeersWithUnsentTransactions() {
return transactionsToSend.keySet();
}
public synchronized Set<Hash> claimTransactionsToSendToPeer(final EthPeer peer) {
final Set<Hash> transactionsToSend = this.transactionsToSend.remove(peer);
if (transactionsToSend != null) {
markTransactionsHashesAsSeen(
peer,
transactionsToSend.stream()
.filter(h -> pendingTransactions.getTransactionByHash(h).isPresent())
.collect(Collectors.toSet()));
return transactionsToSend;
} else {
return emptySet();
}
}
private Set<Hash> getOrCreateSeenTransactionsForPeer(final EthPeer peer) {
return seenTransactions.computeIfAbsent(peer, key -> createTransactionsSet());
}
private boolean hasPeerSeenTransaction(final EthPeer peer, final Hash hash) {
final Set<Hash> seenTransactionsForPeer = seenTransactions.get(peer);
return seenTransactionsForPeer != null && seenTransactionsForPeer.contains(hash);
}
private <T> Set<T> createTransactionsSet() {
return Collections.newSetFromMap(
new LinkedHashMap<T, Boolean>(1 << 4, 0.75f, true) {
@Override
protected boolean removeEldestEntry(final Map.Entry<T, Boolean> eldest) {
return size() > MAX_TRACKED_SEEN_TRANSACTIONS;
}
});
}
@Override
public void onDisconnect(final EthPeer peer) {
seenTransactions.remove(peer);
transactionsToSend.remove(peer);
}
}

@ -0,0 +1,50 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool.TransactionBatchAddedListener;
class PendingTransactionSender implements TransactionBatchAddedListener {
private final PeerPendingTransactionTracker transactionTracker;
private final PendingTransactionsMessageSender transactionsMessageSender;
private final EthContext ethContext;
public PendingTransactionSender(
final PeerPendingTransactionTracker transactionTracker,
final PendingTransactionsMessageSender transactionsMessageSender,
final EthContext ethContext) {
this.transactionTracker = transactionTracker;
this.transactionsMessageSender = transactionsMessageSender;
this.ethContext = ethContext;
}
@Override
public void onTransactionsAdded(final Iterable<Transaction> transactions) {
ethContext
.getEthPeers()
.streamAvailablePeers()
.forEach(
peer ->
transactions.forEach(
transaction ->
transactionTracker.addToPeerSendQueue(peer, transaction.getHash())));
ethContext
.getScheduler()
.scheduleSyncWorkerTask(transactionsMessageSender::sendTransactionsToPeers);
}
}

@ -30,6 +30,7 @@ import java.time.Clock;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -37,6 +38,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@ -44,6 +46,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.EvictingQueue;
/**
* Holds the current set of pending transactions with the ability to iterate them based on priority
* for mining or look-up by hash.
@ -55,6 +60,7 @@ public class PendingTransactions {
private final int maxTransactionRetentionHours;
private final Clock clock;
private final Queue<Hash> newPooledHashes;
private final Map<Hash, TransactionInfo> pendingTransactions = new ConcurrentHashMap<>();
private final SortedSet<TransactionInfo> prioritizedTransactions =
new TreeSet<>(
@ -72,17 +78,20 @@ public class PendingTransactions {
private final LabelledMetric<Counter> transactionRemovedCounter;
private final Counter localTransactionAddedCounter;
private final Counter remoteTransactionAddedCounter;
private final Counter localTransactionHashesAddedCounter;
private final long maxPendingTransactions;
public PendingTransactions(
final int maxTransactionRetentionHours,
final int maxPendingTransactions,
final int maxPooledTransactionHashes,
final Clock clock,
final MetricsSystem metricsSystem) {
this.maxTransactionRetentionHours = maxTransactionRetentionHours;
this.maxPendingTransactions = maxPendingTransactions;
this.clock = clock;
this.newPooledHashes = EvictingQueue.create(maxPooledTransactionHashes);
final LabelledMetric<Counter> transactionAddedCounter =
metricsSystem.createLabelledCounter(
BesuMetricCategory.TRANSACTION_POOL,
@ -91,6 +100,7 @@ public class PendingTransactions {
"source");
localTransactionAddedCounter = transactionAddedCounter.labels("local");
remoteTransactionAddedCounter = transactionAddedCounter.labels("remote");
localTransactionHashesAddedCounter = transactionAddedCounter.labels("pool");
transactionRemovedCounter =
metricsSystem.createLabelledCounter(
@ -127,7 +137,19 @@ public class PendingTransactions {
return transactionAdded;
}
boolean addLocalTransaction(final Transaction transaction) {
boolean addTransactionHash(final Hash transactionHash) {
boolean hashAdded;
synchronized (newPooledHashes) {
hashAdded = newPooledHashes.add(transactionHash);
}
if (hashAdded) {
localTransactionHashesAddedCounter.inc();
}
return hashAdded;
}
@VisibleForTesting
public boolean addLocalTransaction(final Transaction transaction) {
final boolean transactionAdded =
addTransaction(new TransactionInfo(transaction, true, clock.instant()));
if (transactionAdded) {
@ -220,6 +242,7 @@ public class PendingTransactions {
}
prioritizedTransactions.add(transactionInfo);
pendingTransactions.put(transactionInfo.getHash(), transactionInfo);
tryEvictTransactionHash(transactionInfo.getHash());
if (pendingTransactions.size() > maxPendingTransactions) {
final TransactionInfo toRemove = prioritizedTransactions.last();
@ -340,6 +363,16 @@ public class PendingTransactions {
}
}
public void tryEvictTransactionHash(final Hash hash) {
synchronized (newPooledHashes) {
newPooledHashes.remove(hash);
}
}
public Collection<Hash> getNewPooledHashes() {
return newPooledHashes;
}
/**
* Tracks the additional metadata associated with transactions to enable prioritization for mining
* and deciding which transactions to drop when the transaction pool reaches its size limit.

@ -0,0 +1,52 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static java.time.Instant.now;
import org.hyperledger.besu.ethereum.eth.manager.EthMessage;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.messages.NewPooledTransactionHashesMessage;
import java.time.Duration;
import java.time.Instant;
class PendingTransactionsMessageHandler implements EthMessages.MessageCallback {
private final PendingTransactionsMessageProcessor transactionsMessageProcessor;
private final EthScheduler scheduler;
private final Duration txMsgKeepAlive;
public PendingTransactionsMessageHandler(
final EthScheduler scheduler,
final PendingTransactionsMessageProcessor transactionsMessageProcessor,
final int txMsgKeepAliveSeconds) {
this.scheduler = scheduler;
this.transactionsMessageProcessor = transactionsMessageProcessor;
this.txMsgKeepAlive = Duration.ofSeconds(txMsgKeepAliveSeconds);
}
@Override
public void exec(final EthMessage message) {
final NewPooledTransactionHashesMessage transactionsMessage =
NewPooledTransactionHashesMessage.readFrom(message.getData());
final Instant startedAt = now();
scheduler.scheduleTxWorkerTask(
() ->
transactionsMessageProcessor.processNewPooledTransactionHashesMessage(
message.getPeer(), transactionsMessage, startedAt, txMsgKeepAlive));
}
}

@ -0,0 +1,120 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static java.time.Instant.now;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.GetPooledTransactionsFromPeerTask;
import org.hyperledger.besu.ethereum.eth.messages.NewPooledTransactionHashesMessage;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.metrics.RunnableCounter;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.metrics.Counter;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.Logger;
class PendingTransactionsMessageProcessor {
private static final int MAX_HASHES = 256;
private static final int SKIPPED_MESSAGES_LOGGING_THRESHOLD = 1000;
private static final Logger LOG = getLogger();
private final PeerPendingTransactionTracker transactionTracker;
private final Counter totalSkippedTransactionsMessageCounter;
private final TransactionPool transactionPool;
private final EthContext ethContext;
private final MetricsSystem metricsSystem;
PendingTransactionsMessageProcessor(
final PeerPendingTransactionTracker transactionTracker,
final TransactionPool transactionPool,
final Counter metricsCounter,
final EthContext ethContext,
final MetricsSystem metricsSystem) {
this.transactionTracker = transactionTracker;
this.transactionPool = transactionPool;
this.ethContext = ethContext;
this.metricsSystem = metricsSystem;
this.totalSkippedTransactionsMessageCounter =
new RunnableCounter(
metricsCounter,
() ->
LOG.warn(
"{} expired transaction messages have been skipped.",
SKIPPED_MESSAGES_LOGGING_THRESHOLD),
SKIPPED_MESSAGES_LOGGING_THRESHOLD);
}
void processNewPooledTransactionHashesMessage(
final EthPeer peer,
final NewPooledTransactionHashesMessage transactionsMessage,
final Instant startedAt,
final Duration keepAlive) {
// Check if message not expired.
if (startedAt.plus(keepAlive).isAfter(now())) {
this.processNewPooledTransactionHashesMessage(peer, transactionsMessage);
} else {
totalSkippedTransactionsMessageCounter.inc();
}
}
private void processNewPooledTransactionHashesMessage(
final EthPeer peer, final NewPooledTransactionHashesMessage transactionsMessage) {
try {
LOG.trace("Received pooled transaction hashes message from {}", peer);
final List<Hash> pendingHashes = transactionsMessage.pendingTransactions();
transactionTracker.markTransactionsHashesAsSeen(peer, pendingHashes);
List<Hash> toRequest = new ArrayList<>();
for (Hash hash : pendingHashes) {
if (transactionPool.addTransactionHashes(hash)) {
toRequest.add(hash);
}
}
while (!toRequest.isEmpty()) {
List<Hash> messageHashes = toRequest.subList(0, Math.min(toRequest.size(), MAX_HASHES));
GetPooledTransactionsFromPeerTask task =
GetPooledTransactionsFromPeerTask.forHashes(ethContext, messageHashes, metricsSystem);
task.assignPeer(peer);
ethContext
.getScheduler()
.scheduleSyncWorkerTask(task)
.thenAccept(
result -> {
List<Transaction> txs = result.getResult();
transactionPool.addRemoteTransactions(txs);
});
toRequest.removeAll(messageHashes);
}
} catch (final RLPException ex) {
if (peer != null) {
LOG.debug(
"Malformed pooled transaction hashes message received, disconnecting: {}", peer, ex);
peer.disconnect(DisconnectReason.BREACH_OF_PROTOCOL);
}
}
}
}

@ -0,0 +1,52 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.messages.LimitedNewPooledTransactionHashesMessages;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected;
import java.util.Set;
import java.util.stream.StreamSupport;
class PendingTransactionsMessageSender {
private final PeerPendingTransactionTracker transactionTracker;
public PendingTransactionsMessageSender(final PeerPendingTransactionTracker transactionTracker) {
this.transactionTracker = transactionTracker;
}
public void sendTransactionsToPeers() {
StreamSupport.stream(transactionTracker.getEthPeersWithUnsentTransactions().spliterator(), true)
.parallel()
.forEach(this::sendTransactionsToPeer);
}
private void sendTransactionsToPeer(final EthPeer peer) {
final Set<Hash> allTxToSend = transactionTracker.claimTransactionsToSendToPeer(peer);
while (!allTxToSend.isEmpty()) {
final LimitedNewPooledTransactionHashesMessages limitedTransactionsMessages =
LimitedNewPooledTransactionHashesMessages.createLimited(allTxToSend);
allTxToSend.removeAll(limitedTransactionsMessages.getIncludedTransactions());
try {
peer.send(limitedTransactionsMessages.getTransactionsMessage());
} catch (final PeerNotConnected e) {
return;
}
}
}
}

@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Account;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
@ -43,6 +44,7 @@ import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.logging.log4j.Logger;
@ -65,27 +67,33 @@ public class TransactionPool implements BlockAddedObserver {
private final ProtocolSchedule<?> protocolSchedule;
private final ProtocolContext<?> protocolContext;
private final TransactionBatchAddedListener transactionBatchAddedListener;
private final TransactionBatchAddedListener pendingTransactionBatchAddedListener;
private final SyncState syncState;
private final Wei minTransactionGasPrice;
private final LabelledMetric<Counter> duplicateTransactionCounter;
private final PeerTransactionTracker peerTransactionTracker;
private final PeerPendingTransactionTracker peerPendingTransactionTracker;
public TransactionPool(
final PendingTransactions pendingTransactions,
final ProtocolSchedule<?> protocolSchedule,
final ProtocolContext<?> protocolContext,
final TransactionBatchAddedListener transactionBatchAddedListener,
final TransactionBatchAddedListener pendingTransactionBatchAddedListener,
final SyncState syncState,
final EthContext ethContext,
final PeerTransactionTracker peerTransactionTracker,
final PeerPendingTransactionTracker peerPendingTransactionTracker,
final Wei minTransactionGasPrice,
final MetricsSystem metricsSystem) {
this.pendingTransactions = pendingTransactions;
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
this.transactionBatchAddedListener = transactionBatchAddedListener;
this.pendingTransactionBatchAddedListener = pendingTransactionBatchAddedListener;
this.syncState = syncState;
this.peerTransactionTracker = peerTransactionTracker;
this.peerPendingTransactionTracker = peerPendingTransactionTracker;
this.minTransactionGasPrice = minTransactionGasPrice;
duplicateTransactionCounter =
@ -103,12 +111,24 @@ public class TransactionPool implements BlockAddedObserver {
for (final Transaction transaction : localTransactions) {
peerTransactionTracker.addToPeerSendQueue(peer, transaction);
}
final Collection<Hash> hashes = getNewPooledHashes();
for (final Hash hash : hashes) {
peerPendingTransactionTracker.addToPeerSendQueue(peer, hash);
}
}
public List<Transaction> getLocalTransactions() {
return pendingTransactions.getLocalTransactions();
}
public Collection<Hash> getNewPooledHashes() {
return pendingTransactions.getNewPooledHashes();
}
public boolean addTransactionHashes(final Hash transactionHash) {
return pendingTransactions.addTransactionHash(transactionHash);
}
public ValidationResult<TransactionInvalidReason> addLocalTransaction(
final Transaction transaction) {
if (transaction.getGasPrice().compareTo(minTransactionGasPrice) < 0) {
@ -121,7 +141,9 @@ public class TransactionPool implements BlockAddedObserver {
() -> {
final boolean added = pendingTransactions.addLocalTransaction(transaction);
if (added) {
transactionBatchAddedListener.onTransactionsAdded(singletonList(transaction));
Collection<Transaction> txs = singletonList(transaction);
transactionBatchAddedListener.onTransactionsAdded(txs);
pendingTransactionBatchAddedListener.onTransactionsAdded(txs);
} else {
duplicateTransactionCounter.labels(LOCAL).inc();
}
@ -135,6 +157,7 @@ public class TransactionPool implements BlockAddedObserver {
}
final Set<Transaction> addedTransactions = new HashSet<>();
for (final Transaction transaction : transactions) {
pendingTransactions.tryEvictTransactionHash(transaction.getHash());
if (pendingTransactions.containsTransaction(transaction.getHash())) {
// We already have this transaction, don't even validate it.
duplicateTransactionCounter.labels(REMOTE).inc();
@ -226,6 +249,10 @@ public class TransactionPool implements BlockAddedObserver {
.orElseGet(() -> ValidationResult.invalid(CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE));
}
public Optional<Transaction> getTransactionByHash(final Hash hash) {
return pendingTransactions.getTransactionByHash(hash);
}
private BlockHeader getChainHeadBlockHeader() {
final MutableBlockchain blockchain = protocolContext.getBlockchain();
return blockchain.getBlockHeader(blockchain.getChainHeadHash()).get();

@ -19,17 +19,21 @@ import java.util.Objects;
public class TransactionPoolConfiguration {
public static final int DEFAULT_TX_MSG_KEEP_ALIVE = 60;
public static final int MAX_PENDING_TRANSACTIONS = 4096;
public static final int MAX_PENDING_TRANSACTIONS_HASHES = 4096;
public static final int DEFAULT_TX_RETENTION_HOURS = 13;
private final int txPoolMaxSize;
private final int pooledTransactionHashesSize;
private final int pendingTxRetentionPeriod;
private final int txMessageKeepAliveSeconds;
public TransactionPoolConfiguration(
final int txPoolMaxSize,
final int pooledTransactionHashesSize,
final int pendingTxRetentionPeriod,
final int txMessageKeepAliveSeconds) {
this.txPoolMaxSize = txPoolMaxSize;
this.pooledTransactionHashesSize = pooledTransactionHashesSize;
this.pendingTxRetentionPeriod = pendingTxRetentionPeriod;
this.txMessageKeepAliveSeconds = txMessageKeepAliveSeconds;
}
@ -38,6 +42,10 @@ public class TransactionPoolConfiguration {
return txPoolMaxSize;
}
public int getPooledTransactionHashesSize() {
return pooledTransactionHashesSize;
}
public int getPendingTxRetentionPeriod() {
return pendingTxRetentionPeriod;
}
@ -85,12 +93,18 @@ public class TransactionPoolConfiguration {
private int txPoolMaxSize = MAX_PENDING_TRANSACTIONS;
private int pendingTxRetentionPeriod = DEFAULT_TX_RETENTION_HOURS;
private Integer txMessageKeepAliveSeconds = DEFAULT_TX_MSG_KEEP_ALIVE;
private int pooledTransactionHashesSize = MAX_PENDING_TRANSACTIONS_HASHES;
public Builder txPoolMaxSize(final int txPoolMaxSize) {
this.txPoolMaxSize = txPoolMaxSize;
return this;
}
public Builder pooledTransactionHashesSize(final int pooledTransactionHashesSize) {
this.pooledTransactionHashesSize = pooledTransactionHashesSize;
return this;
}
public Builder pendingTxRetentionPeriod(final int pendingTxRetentionPeriod) {
this.pendingTxRetentionPeriod = pendingTxRetentionPeriod;
return this;
@ -103,7 +117,10 @@ public class TransactionPoolConfiguration {
public TransactionPoolConfiguration build() {
return new TransactionPoolConfiguration(
txPoolMaxSize, pendingTxRetentionPeriod, txMessageKeepAliveSeconds);
txPoolMaxSize,
pooledTransactionHashesSize,
pendingTxRetentionPeriod,
txMessageKeepAliveSeconds);
}
}
}

@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.BesuMetricCategory;
@ -41,22 +42,30 @@ public class TransactionPoolFactory {
new PendingTransactions(
transactionPoolConfiguration.getPendingTxRetentionPeriod(),
transactionPoolConfiguration.getTxPoolMaxSize(),
transactionPoolConfiguration.getPooledTransactionHashesSize(),
clock,
metricsSystem);
final PeerTransactionTracker transactionTracker = new PeerTransactionTracker();
final TransactionsMessageSender transactionsMessageSender =
new TransactionsMessageSender(transactionTracker);
final PeerPendingTransactionTracker pendingTransactionTracker =
new PeerPendingTransactionTracker(pendingTransactions);
final PendingTransactionsMessageSender pendingTransactionsMessageSender =
new PendingTransactionsMessageSender(pendingTransactionTracker);
final TransactionPool transactionPool =
new TransactionPool(
pendingTransactions,
protocolSchedule,
protocolContext,
new TransactionSender(transactionTracker, transactionsMessageSender, ethContext),
new PendingTransactionSender(
pendingTransactionTracker, pendingTransactionsMessageSender, ethContext),
syncState,
ethContext,
transactionTracker,
pendingTransactionTracker,
minTransactionGasPrice,
metricsSystem);
@ -71,8 +80,23 @@ public class TransactionPoolFactory {
"transactions_messages_skipped_total",
"Total number of transactions messages skipped by the processor.")),
transactionPoolConfiguration.getTxMessageKeepAliveSeconds());
final PendingTransactionsMessageHandler pooledTransactionsMessageHandler =
new PendingTransactionsMessageHandler(
ethContext.getScheduler(),
new PendingTransactionsMessageProcessor(
pendingTransactionTracker,
transactionPool,
metricsSystem.createCounter(
BesuMetricCategory.TRANSACTION_POOL,
"pending_transactions_messages_skipped_total",
"Total number of pending transactions messages skipped by the processor."),
ethContext,
metricsSystem),
transactionPoolConfiguration.getTxMessageKeepAliveSeconds());
ethContext.getEthMessages().subscribe(EthPV62.TRANSACTIONS, transactionsMessageHandler);
ethContext
.getEthMessages()
.subscribe(EthPV65.NEW_POOLED_TRANSACTION_HASHES, pooledTransactionsMessageHandler);
protocolContext.getBlockchain().observeBlockAdded(transactionPool);
ethContext.getEthPeers().subscribeDisconnect(transactionTracker);
return transactionPool;

@ -53,6 +53,7 @@ import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.messages.StatusMessage;
import org.hyperledger.besu.ethereum.eth.messages.TransactionsMessage;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
@ -94,6 +95,7 @@ import org.mockito.ArgumentCaptor;
public final class EthProtocolManagerTest {
private static Blockchain blockchain;
private static TransactionPool transactionPool;
private static ProtocolSchedule<Void> protocolSchedule;
private static BlockDataGenerator gen;
private static ProtocolContext<Void> protocolContext;
@ -105,6 +107,7 @@ public final class EthProtocolManagerTest {
final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
blockchainSetupUtil.importAllBlocks();
blockchain = blockchainSetupUtil.getBlockchain();
transactionPool = blockchainSetupUtil.getTransactionPool();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext();
assert (blockchainSetupUtil.getMaxBlockNumber() >= 20L);
@ -113,17 +116,11 @@ public final class EthProtocolManagerTest {
@Test
public void disconnectOnUnsolicitedMessage() {
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData =
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
@ -136,17 +133,11 @@ public final class EthProtocolManagerTest {
@Test
public void disconnectOnFailureToSendStatusMessage() {
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData =
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
@ -160,17 +151,11 @@ public final class EthProtocolManagerTest {
@Test
public void disconnectOnWrongChainId() {
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData =
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
@ -195,17 +180,11 @@ public final class EthProtocolManagerTest {
@Test
public void disconnectOnWrongGenesisHash() {
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData =
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
@ -230,17 +209,11 @@ public final class EthProtocolManagerTest {
@Test(expected = ConditionTimeoutException.class)
public void doNotDisconnectOnValidMessage() {
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData =
GetBlockBodiesMessage.create(Collections.singletonList(gen.hash()));
@ -257,17 +230,11 @@ public final class EthProtocolManagerTest {
public void respondToGetHeaders() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final long startBlock = 5L;
final int blockCount = 5;
@ -300,18 +267,12 @@ public final class EthProtocolManagerTest {
final CompletableFuture<Void> done = new CompletableFuture<>();
final int limit = 5;
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
new EthProtocolConfiguration(limit, limit, limit, limit))) {
transactionPool,
new EthProtocolConfiguration(limit, limit, limit, limit, limit))) {
final long startBlock = 5L;
final int blockCount = 10;
final MessageData messageData =
@ -342,18 +303,13 @@ public final class EthProtocolManagerTest {
public void respondToGetHeadersReversed() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final long endBlock = 10L;
final int blockCount = 5;
final MessageData messageData = GetBlockHeadersMessage.create(endBlock, blockCount, 0, true);
@ -383,18 +339,13 @@ public final class EthProtocolManagerTest {
public void respondToGetHeadersWithSkip() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final long startBlock = 5L;
final int blockCount = 5;
final int skip = 1;
@ -427,18 +378,13 @@ public final class EthProtocolManagerTest {
throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final long endBlock = 10L;
final int blockCount = 5;
final int skip = 1;
@ -492,18 +438,13 @@ public final class EthProtocolManagerTest {
public void respondToGetHeadersPartial() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final long startBlock = blockchain.getChainHeadBlockNumber() - 1L;
final int blockCount = 5;
final MessageData messageData =
@ -534,18 +475,13 @@ public final class EthProtocolManagerTest {
public void respondToGetHeadersEmpty() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final long startBlock = blockchain.getChainHeadBlockNumber() + 1;
final int blockCount = 5;
final MessageData messageData =
@ -573,18 +509,13 @@ public final class EthProtocolManagerTest {
public void respondToGetBodies() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
// Setup blocks query
final long startBlock = blockchain.getChainHeadBlockNumber() - 5;
final int blockCount = 2;
@ -628,18 +559,12 @@ public final class EthProtocolManagerTest {
final CompletableFuture<Void> done = new CompletableFuture<>();
final int limit = 5;
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
new EthProtocolConfiguration(limit, limit, limit, limit))) {
transactionPool,
new EthProtocolConfiguration(limit, limit, limit, limit, limit))) {
// Setup blocks query
final int blockCount = 10;
final long startBlock = blockchain.getChainHeadBlockNumber() - blockCount;
@ -682,17 +607,11 @@ public final class EthProtocolManagerTest {
public void respondToGetBodiesPartial() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
// Setup blocks query
final long expectedBlockNumber = blockchain.getChainHeadBlockNumber() - 1;
@ -730,17 +649,11 @@ public final class EthProtocolManagerTest {
public void respondToGetReceipts() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
// Setup blocks query
final long startBlock = blockchain.getChainHeadBlockNumber() - 5;
@ -784,18 +697,12 @@ public final class EthProtocolManagerTest {
final CompletableFuture<Void> done = new CompletableFuture<>();
final int limit = 5;
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
new EthProtocolConfiguration(limit, limit, limit, limit))) {
transactionPool,
new EthProtocolConfiguration(limit, limit, limit, limit, limit))) {
// Setup blocks query
final int blockCount = 10;
final long startBlock = blockchain.getChainHeadBlockNumber() - blockCount;
@ -837,17 +744,11 @@ public final class EthProtocolManagerTest {
public void respondToGetReceiptsPartial() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
// Setup blocks query
final long blockNumber = blockchain.getChainHeadBlockNumber() - 5;
@ -887,17 +788,11 @@ public final class EthProtocolManagerTest {
final WorldStateArchive worldStateArchive = protocolContext.getWorldStateArchive();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
worldStateArchive,
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
() -> false,
protocolContext.getWorldStateArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
// Setup node data query
@ -939,63 +834,59 @@ public final class EthProtocolManagerTest {
@Test
public void newBlockMinedSendsNewBlockMessageToAllPeers() {
final EthProtocolManager ethManager =
new EthProtocolManager(
try (final EthProtocolManager ethManager =
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig());
// 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));
}
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
// Define handler to validate response
final PeerSendHandler onSend = mock(PeerSendHandler.class);
final List<PeerConnection> peers = Lists.newArrayList();
final Hash chainHeadHash = blockchain.getChainHeadHash();
final Block minedBlock =
new Block(
blockchain.getBlockHeader(chainHeadHash).get(),
blockchain.getBlockBody(chainHeadHash).get());
final int PEER_COUNT = 5;
for (int i = 0; i < PEER_COUNT; i++) {
peers.add(setupPeer(ethManager, onSend));
}
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 =
ArgumentCaptor.forClass(NewBlockMessage.class);
final ArgumentCaptor<PeerConnection> receivingPeerCaptor =
ArgumentCaptor.forClass(PeerConnection.class);
final ArgumentCaptor<Capability> capabilityCaptor = ArgumentCaptor.forClass(Capability.class);
ethManager.blockMined(minedBlock);
verify(onSend, times(PEER_COUNT))
.exec(
capabilityCaptor.capture(), messageSentCaptor.capture(), receivingPeerCaptor.capture());
final ArgumentCaptor<NewBlockMessage> messageSentCaptor =
ArgumentCaptor.forClass(NewBlockMessage.class);
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
assertThat(capabilityCaptor.getAllValues().stream().distinct().collect(Collectors.toList()))
.isEqualTo(Collections.singletonList(EthProtocol.ETH63));
verify(onSend, times(PEER_COUNT))
.exec(
capabilityCaptor.capture(),
messageSentCaptor.capture(),
receivingPeerCaptor.capture());
// 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);
}
// assert that all entries in capability param were Eth63
assertThat(capabilityCaptor.getAllValues().stream().distinct().collect(Collectors.toList()))
.isEqualTo(Collections.singletonList(EthProtocol.ETH63));
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
@ -1014,18 +905,13 @@ public final class EthProtocolManagerTest {
final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
final long startBlock = 1L;
final int requestedBlockCount = 13;
final int receivedBlockCount = 2;
@ -1084,18 +970,12 @@ public final class EthProtocolManagerTest {
final TransactionsMessage transactionMessage = TransactionsMessage.readFrom(raw);
try (final EthProtocolManager ethManager =
new EthProtocolManager(
EthProtocolManagerTestUtil.create(
blockchain,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
ethScheduler,
EthProtocolConfiguration.defaultConfig(),
TestClock.fixed(),
metricsSystem,
new ForkIdManager(blockchain, Collections.emptyList()))) {
protocolContext.getWorldStateArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig())) {
// Create a transaction pool. This has a side effect of registering a listener for the
// transactions message.
TransactionPoolFactory.createTransactionPool(

@ -16,17 +16,19 @@ package org.hyperledger.besu.ethereum.eth.manager;
import static com.google.common.base.Preconditions.checkArgument;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.ChainHead;
import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler.TimeoutPolicy;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage;
@ -43,32 +45,58 @@ public class EthProtocolManagerTestUtil {
public static EthProtocolManager create(
final Blockchain blockchain,
final TimeoutPolicy timeoutPolicy,
final WorldStateArchive worldStateArchive,
final TimeoutPolicy timeoutPolicy) {
return create(blockchain, worldStateArchive, new DeterministicEthScheduler(timeoutPolicy));
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
return create(
blockchain,
new DeterministicEthScheduler(timeoutPolicy),
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration);
}
public static EthProtocolManager create(
final Blockchain blockchain,
final EthScheduler ethScheduler,
final WorldStateArchive worldStateArchive,
final EthScheduler ethScheduler) {
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthMessages ethMessages,
final EthContext ethContext) {
final BigInteger networkId = BigInteger.ONE;
return new EthProtocolManager(
blockchain,
worldStateArchive,
networkId,
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
Collections.emptyList(),
false,
ethScheduler,
EthProtocolConfiguration.defaultConfig(),
TestClock.fixed(),
new NoOpMetricsSystem(),
new ForkIdManager(blockchain, Collections.emptyList()));
}
public static EthProtocolManager create(final Blockchain blockchain) {
return create(blockchain, new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT));
}
public static EthProtocolManager create(
final Blockchain blockchain, final WorldStateArchive worldStateArchive) {
return create(blockchain, worldStateArchive, TimeoutPolicy.NEVER_TIMEOUT);
final Blockchain blockchain,
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) {
@ -76,8 +104,43 @@ public class EthProtocolManagerTestUtil {
final GenesisConfigFile config = GenesisConfigFile.mainnet();
final GenesisState genesisState = GenesisState.fromConfig(config, protocolSchedule);
final Blockchain blockchain = createInMemoryBlockchain(genesisState.getBlock());
final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
return create(blockchain, worldStateArchive, ethScheduler);
return create(blockchain, ethScheduler);
}
public static EthProtocolManager create(
final Blockchain blockchain,
final EthScheduler ethScheduler,
final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final EthProtocolConfiguration configuration) {
EthPeers peers = new EthPeers(EthProtocol.NAME, TestClock.fixed(), new NoOpMetricsSystem());
EthMessages messages = new EthMessages();
return create(
blockchain,
ethScheduler,
worldStateArchive,
transactionPool,
configuration,
peers,
messages,
new EthContext(peers, messages, ethScheduler));
}
public static EthProtocolManager create(
final Blockchain blockchain, final EthScheduler ethScheduler) {
EthPeers peers = new EthPeers(EthProtocol.NAME, TestClock.fixed(), new NoOpMetricsSystem());
EthMessages messages = new EthMessages();
return create(
blockchain,
ethScheduler,
BlockchainSetupUtil.forTesting().getWorldArchive(),
mock(TransactionPool.class),
EthProtocolConfiguration.defaultConfig(),
peers,
messages,
new EthContext(peers, messages, ethScheduler));
}
public static EthProtocolManager create() {

@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
import org.hyperledger.besu.ethereum.eth.messages.NodeDataMessage;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.Optional;
@ -43,13 +44,18 @@ public class EthServerTest {
private static final Hash HASH3 = Hash.hash(VALUE3);
private final Blockchain blockchain = mock(Blockchain.class);
private final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class);
private final TransactionPool transactionPool = mock(TransactionPool.class);
private final EthPeer ethPeer = mock(EthPeer.class);
private final EthMessages ethMessages = new EthMessages();
@Before
public void setUp() {
new EthServer(
blockchain, worldStateArchive, ethMessages, new EthProtocolConfiguration(2, 2, 2, 2));
blockchain,
worldStateArchive,
transactionPool,
ethMessages,
new EthProtocolConfiguration(2, 2, 2, 2, 2));
}
@Test

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.eth.manager;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockBody;
@ -24,15 +25,19 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage;
import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.eth.messages.NodeDataMessage;
import org.hyperledger.besu.ethereum.eth.messages.PooledTransactionsMessage;
import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage;
@ -223,12 +228,24 @@ public class RespondingEthPeer {
};
}
public static Responder blockchainResponder(final Blockchain blockchain) {
return blockchainResponder(blockchain, createInMemoryWorldStateArchive());
private static TransactionPool createTransactionPool() {
return mock(TransactionPool.class);
}
public static Responder blockchainResponder(
final Blockchain blockchain, final WorldStateArchive worldStateArchive) {
return blockchainResponder(blockchain, worldStateArchive, createTransactionPool());
}
public static Responder blockchainResponder(final Blockchain blockchain) {
return blockchainResponder(
blockchain, createInMemoryWorldStateArchive(), createTransactionPool());
}
public static Responder blockchainResponder(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool) {
return (cap, msg) -> {
MessageData response = null;
switch (msg.getCode()) {
@ -244,6 +261,8 @@ public class RespondingEthPeer {
case EthPV63.GET_NODE_DATA:
response = EthServer.constructGetNodeDataResponse(worldStateArchive, msg, 200);
break;
case EthPV65.GET_POOLED_TRANSACTIONS:
response = EthServer.constructGetPooledTransactionsResponse(transactionPool, msg, 200);
}
return Optional.ofNullable(response);
};
@ -265,11 +284,13 @@ public class RespondingEthPeer {
public static <C> Responder partialResponder(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final ProtocolSchedule<C> protocolSchedule,
final float portion) {
checkArgument(portion >= 0.0 && portion <= 1.0, "Portion is in the range [0.0..1.0]");
final Responder fullResponder = blockchainResponder(blockchain, worldStateArchive);
final Responder fullResponder =
blockchainResponder(blockchain, worldStateArchive, transactionPool);
return (cap, msg) -> {
final Optional<MessageData> maybeResponse = fullResponder.respond(cap, msg);
if (!maybeResponse.isPresent()) {
@ -310,6 +331,15 @@ public class RespondingEthPeer {
originalNodeData.subList(0, (int) (originalNodeData.size() * portion));
partialResponse = NodeDataMessage.create(partialNodeData);
break;
case EthPV65.GET_POOLED_TRANSACTIONS:
final PooledTransactionsMessage pooledTransactionsMessage =
PooledTransactionsMessage.readFrom(originalResponse);
final List<Transaction> originalPooledTx =
Lists.newArrayList(pooledTransactionsMessage.transactions());
final List<Transaction> partialPooledTx =
originalPooledTx.subList(0, (int) (originalPooledTx.size() * portion));
partialResponse = PooledTransactionsMessage.create(partialPooledTx);
break;
}
return Optional.of(partialResponse);
};
@ -331,6 +361,9 @@ public class RespondingEthPeer {
case EthPV63.GET_NODE_DATA:
response = NodeDataMessage.create(Collections.emptyList());
break;
case EthPV65.GET_POOLED_TRANSACTIONS:
response = PooledTransactionsMessage.create(Collections.emptyList());
break;
}
return Optional.ofNullable(response);
};

@ -19,15 +19,27 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.TestClock;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
@ -49,6 +61,7 @@ public abstract class AbstractMessageTaskTest<T, R> {
protected static MetricsSystem metricsSystem = new NoOpMetricsSystem();
protected EthProtocolManager ethProtocolManager;
protected EthContext ethContext;
protected TransactionPool transactionPool;
protected AtomicBoolean peersDoTimeout;
protected AtomicInteger peerCountToTimeout;
@ -59,6 +72,7 @@ public abstract class AbstractMessageTaskTest<T, R> {
blockchain = blockchainSetupUtil.getBlockchain();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext();
assert (blockchainSetupUtil.getMaxBlockNumber() >= 20L);
}
@ -66,12 +80,33 @@ public abstract class AbstractMessageTaskTest<T, R> {
public void setupTest() {
peersDoTimeout = new AtomicBoolean(false);
peerCountToTimeout = new AtomicInteger(0);
final EthPeers ethPeers = new EthPeers(EthProtocol.NAME, TestClock.fixed(), metricsSystem);
final EthMessages ethMessages = new EthMessages();
final EthScheduler ethScheduler =
new DeterministicEthScheduler(
() -> peerCountToTimeout.getAndDecrement() > 0 || peersDoTimeout.get());
ethContext = new EthContext(ethPeers, ethMessages, ethScheduler);
final SyncState syncState = new SyncState(blockchain, ethContext.getEthPeers());
transactionPool =
TransactionPoolFactory.createTransactionPool(
protocolSchedule,
protocolContext,
ethContext,
TestClock.fixed(),
metricsSystem,
syncState,
Wei.of(1),
TransactionPoolConfiguration.builder().build());
ethProtocolManager =
EthProtocolManagerTestUtil.create(
blockchain,
ethScheduler,
protocolContext.getWorldStateArchive(),
() -> peerCountToTimeout.getAndDecrement() > 0 || peersDoTimeout.get());
ethContext = ethProtocolManager.ethContext();
transactionPool,
EthProtocolConfiguration.defaultConfig(),
ethPeers,
ethMessages,
ethContext);
}
protected abstract T generateDataToBeRequested();
@ -85,7 +120,8 @@ public abstract class AbstractMessageTaskTest<T, R> {
public void completesWhenPeersAreResponsive() {
// Setup a responsive peer
final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);

@ -43,7 +43,11 @@ public abstract class PeerMessageTaskTest<T>
// Setup a partially responsive peer
final RespondingEthPeer.Responder responder =
RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.5f);
blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.5f);
final RespondingEthPeer respondingEthPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);

@ -54,7 +54,11 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
// Setup a partially responsive peer and a non-responsive peer
final RespondingEthPeer.Responder partialResponder =
RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.5f);
blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.5f);
final RespondingEthPeer.Responder emptyResponder = RespondingEthPeer.emptyResponder();
final RespondingEthPeer respondingPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
@ -100,16 +104,32 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
// Respond with partial data up until complete.
respondingPeer.respond(
RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.25f));
blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.25f));
respondingPeer.respond(
RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.50f));
blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.50f));
respondingPeer.respond(
RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.75f));
blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.75f));
respondingPeer.respond(
RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 1.0f));
blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
1.0f));
assertThat(future.isDone()).isTrue();
assertResultMatchesExpectation(requestedData, future.get(), respondingPeer.getEthPeer());
@ -140,7 +160,8 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
// Setup a peer
final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
respondingPeer.respondWhile(responder, () -> !future.isDone());
@ -153,7 +174,8 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
throws ExecutionException, InterruptedException {
peerCountToTimeout.set(1);
final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
final T requestedData = generateDataToBeRequested();

@ -0,0 +1,87 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.manager.task;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.ethtaskutils.PeerMessageTaskTest;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.google.common.collect.Lists;
public class GetPooledTransactionsFromPeerTaskTest extends PeerMessageTaskTest<List<Transaction>> {
private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
@Override
protected List<Transaction> generateDataToBeRequested() {
final List<Transaction> requestedData = new ArrayList<>();
SECP256K1.KeyPair keyPair = SECP256K1.KeyPair.generate();
for (int i = 0; i < 3; i++) {
Transaction tx =
new TransactionTestFixture()
.nonce(i)
.gasLimit(100000)
.chainId(Optional.empty())
.createTransaction(keyPair);
assertThat(transactionPool.getPendingTransactions().addLocalTransaction(tx)).isTrue();
requestedData.add(tx);
}
return requestedData;
}
@Override
protected EthTask<AbstractPeerTask.PeerTaskResult<List<Transaction>>> createTask(
final List<Transaction> requestedData) {
final List<Hash> hashes =
Lists.newArrayList(requestedData).stream()
.map(Transaction::getHash)
.collect(Collectors.toList());
return GetPooledTransactionsFromPeerTask.forHashes(ethContext, hashes, metricsSystem);
}
@Override
protected void assertPartialResultMatchesExpectation(
final List<Transaction> requestedData, final List<Transaction> partialResponse) {
assertThat(partialResponse.size()).isLessThanOrEqualTo(requestedData.size());
assertThat(partialResponse.size()).isGreaterThan(0);
for (Transaction data : partialResponse) {
assertThat(requestedData).contains(data);
}
}
@Override
protected void assertResultMatchesExpectation(
final List<Transaction> requestedData,
final AbstractPeerTask.PeerTaskResult<List<Transaction>> response,
final EthPeer respondingPeer) {
assertThat(response.getResult().size()).isEqualTo(requestedData.size());
for (Transaction data : response.getResult()) {
assertThat(requestedData).contains(data);
}
}
}

@ -74,7 +74,11 @@ public class RetryingGetNodeDataFromPeerTaskTest extends RetryingMessageTaskTest
// Respond with partial data.
respondingPeer.respond(
RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.50f));
blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.50f));
assertThat(future.isDone()).isTrue();
// Check that it immediately returns the data we got in the response.

@ -0,0 +1,47 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.messages;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import java.util.Arrays;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.Test;
public class GetPooledTransactionsMessageTest {
@Test
public void roundTripGetPooledTransactionsMessage() {
List<Hash> hashes = Arrays.asList(Hash.wrap(Bytes32.random()));
final GetPooledTransactionsMessage msg = GetPooledTransactionsMessage.create(hashes);
assertThat(msg.getCode()).isEqualTo(EthPV65.GET_POOLED_TRANSACTIONS);
assertThat(msg.pooledTransactions()).isEqualTo(hashes);
}
@Test
public void readFromMessageWithWrongCodeThrows() {
final RawMessage rawMsg = new RawMessage(EthPV62.BLOCK_HEADERS, Bytes.of(0));
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> GetPooledTransactionsMessage.readFrom(rawMsg));
}
}

@ -0,0 +1,71 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.messages;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
public class LimitedNewPooledTransactionHashesMessagesTest {
private final BlockDataGenerator generator = new BlockDataGenerator();
private final List<Hash> sampleTxs =
generator.transactions(1).stream().map(Transaction::getHash).collect(Collectors.toList());
private final NewPooledTransactionHashesMessage sampleTransactionMessages =
NewPooledTransactionHashesMessage.create(sampleTxs);
private final LimitedNewPooledTransactionHashesMessages sampleLimitedTransactionsMessages =
new LimitedNewPooledTransactionHashesMessages(sampleTransactionMessages, sampleTxs);
@Test
public void createLimited() {
final List<Hash> txs =
generator.transactions(6000).stream()
.map(Transaction::getHash)
.collect(Collectors.toList());
final LimitedNewPooledTransactionHashesMessages firstMessage =
LimitedNewPooledTransactionHashesMessages.createLimited(txs);
assertThat(firstMessage.getIncludedTransactions().size()).isEqualTo(4096);
txs.removeAll(firstMessage.getIncludedTransactions());
assertThat(txs.size()).isEqualTo(6000 - 4096);
final LimitedNewPooledTransactionHashesMessages secondMessage =
LimitedNewPooledTransactionHashesMessages.createLimited(txs);
assertThat(secondMessage.getIncludedTransactions().size()).isEqualTo(6000 - 4096);
txs.removeAll(secondMessage.getIncludedTransactions());
assertThat(txs.size()).isEqualTo(0);
assertThat(
firstMessage.getTransactionsMessage().getSize()
+ secondMessage.getTransactionsMessage().getSize())
.isLessThan(2 * LimitedTransactionsMessages.LIMIT);
}
@Test
public void getTransactionsMessage() {
assertThat(sampleLimitedTransactionsMessages.getTransactionsMessage())
.isEqualTo(sampleTransactionMessages);
}
@Test
public void getIncludedTransactions() {
assertThat(sampleLimitedTransactionsMessages.getIncludedTransactions()).isEqualTo(sampleTxs);
}
}

@ -0,0 +1,47 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.messages;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import java.util.Arrays;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.Test;
public class NewPooledTransactionHashesMessageTest {
@Test
public void roundTripNewPooledTransactionHashesMessage() {
List<Hash> hashes = Arrays.asList(Hash.wrap(Bytes32.random()));
final NewPooledTransactionHashesMessage msg = NewPooledTransactionHashesMessage.create(hashes);
assertThat(msg.getCode()).isEqualTo(EthPV65.NEW_POOLED_TRANSACTION_HASHES);
assertThat(msg.pendingTransactions()).isEqualTo(hashes);
}
@Test
public void readFromMessageWithWrongCodeThrows() {
final RawMessage rawMsg = new RawMessage(EthPV62.BLOCK_HEADERS, Bytes.of(0));
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> NewPooledTransactionHashesMessage.readFrom(rawMsg));
}
}

@ -0,0 +1,56 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.messages;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
import java.util.Arrays;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
public class PooledTransactionsMessageTest {
@Test
public void roundTripPooledTransactionsMessage() {
List<Transaction> tx =
Arrays.asList(
Transaction.builder()
.nonce(42)
.gasLimit(654321)
.gasPrice(Wei.of(2))
.value(Wei.of(1337))
.payload(Bytes.EMPTY)
.signAndBuild(SECP256K1.KeyPair.generate()));
final PooledTransactionsMessage msg = PooledTransactionsMessage.create(tx);
assertThat(msg.getCode()).isEqualTo(EthPV65.POOLED_TRANSACTIONS);
assertThat(msg.transactions()).isEqualTo(tx);
}
@Test
public void readFromMessageWithWrongCodeThrows() {
final RawMessage rawMsg = new RawMessage(EthPV62.BLOCK_HEADERS, Bytes.of(0));
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> PooledTransactionsMessage.readFrom(rawMsg));
}
}

@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions;
import org.hyperledger.besu.ethereum.core.BlockImporter;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
@ -93,7 +94,11 @@ public class BlockPropagationManagerTest {
tempProtocolContext.getWorldStateArchive(),
tempProtocolContext.getConsensusState());
ethProtocolManager =
EthProtocolManagerTestUtil.create(blockchain, blockchainUtil.getWorldArchive());
EthProtocolManagerTestUtil.create(
blockchain,
blockchainUtil.getWorldArchive(),
blockchainUtil.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
syncConfig = SynchronizerConfiguration.builder().blockPropagationRange(-3, 5).build();
syncState = new SyncState(blockchain, ethProtocolManager.ethContext().getEthPeers());
blockBroadcaster = mock(BlockBroadcaster.class);

@ -38,7 +38,7 @@ public class ChainHeadTrackerTest {
private final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
private final MutableBlockchain blockchain = blockchainSetupUtil.getBlockchain();
private final EthProtocolManager ethProtocolManager =
EthProtocolManagerTestUtil.create(blockchain, blockchainSetupUtil.getWorldArchive());
EthProtocolManagerTestUtil.create(blockchain);
private final RespondingEthPeer respondingPeer =
RespondingEthPeer.builder()
.ethProtocolManager(ethProtocolManager)
@ -62,7 +62,9 @@ public class ChainHeadTrackerTest {
public void shouldRequestHeaderChainHeadWhenNewPeerConnects() {
final Responder responder =
RespondingEthPeer.blockchainResponder(
blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive());
blockchainSetupUtil.getBlockchain(),
blockchainSetupUtil.getWorldArchive(),
blockchainSetupUtil.getTransactionPool());
chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer());
Assertions.assertThat(chainHeadState().getEstimatedHeight()).isZero();
@ -77,7 +79,9 @@ public class ChainHeadTrackerTest {
public void shouldIgnoreHeadersIfChainHeadHasAlreadyBeenUpdatedWhileWaiting() {
final Responder responder =
RespondingEthPeer.blockchainResponder(
blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive());
blockchainSetupUtil.getBlockchain(),
blockchainSetupUtil.getWorldArchive(),
blockchainSetupUtil.getTransactionPool());
chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer());
// Change the hash of the current known head
@ -92,7 +96,9 @@ public class ChainHeadTrackerTest {
public void shouldCheckTrialingPeerLimits() {
final Responder responder =
RespondingEthPeer.blockchainResponder(
blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive());
blockchainSetupUtil.getBlockchain(),
blockchainSetupUtil.getWorldArchive(),
blockchainSetupUtil.getTransactionPool());
chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer());
Assertions.assertThat(chainHeadState().getEstimatedHeight()).isZero();

@ -23,11 +23,13 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
@ -50,6 +52,7 @@ public class CheckpointHeaderFetcherTest {
private static ProtocolSchedule<Void> protocolSchedule;
private static ProtocolContext<Void> protocolContext;
private static final MetricsSystem metricsSystem = new NoOpMetricsSystem();
private static TransactionPool transactionPool;
private EthProtocolManager ethProtocolManager;
private Responder responder;
private RespondingEthPeer respondingPeer;
@ -59,6 +62,7 @@ public class CheckpointHeaderFetcherTest {
final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
blockchainSetupUtil.importAllBlocks();
blockchain = blockchainSetupUtil.getBlockchain();
transactionPool = blockchainSetupUtil.getTransactionPool();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext();
}
@ -67,9 +71,14 @@ public class CheckpointHeaderFetcherTest {
public void setUpTest() {
ethProtocolManager =
EthProtocolManagerTestUtil.create(
blockchain, protocolContext.getWorldStateArchive(), () -> false);
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig());
responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
respondingPeer =
EthProtocolManagerTestUtil.createPeer(
ethProtocolManager, blockchain.getChainHeadBlockNumber());

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

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.sync.fastsync;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
@ -24,9 +25,11 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockWithReceipts;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import java.util.List;
@ -54,8 +57,14 @@ public class DownloadReceiptsStepTest {
@Before
public void setUp() {
TransactionPool transactionPool = mock(TransactionPool.class);
ethProtocolManager =
EthProtocolManagerTestUtil.create(blockchain, protocolContext.getWorldStateArchive());
EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig());
downloadReceiptsStep =
new DownloadReceiptsStep(ethProtocolManager.ethContext(), new NoOpMetricsSystem());
}

@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
@ -67,8 +68,10 @@ public class FastSyncActionsTest {
ethProtocolManager =
EthProtocolManagerTestUtil.create(
blockchain,
() -> timeoutCount.getAndDecrement() > 0,
blockchainSetupUtil.getWorldArchive(),
() -> timeoutCount.getAndDecrement() > 0);
blockchainSetupUtil.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
fastSyncActions = createFastSyncActions(syncConfig);
}

@ -70,9 +70,8 @@ public class FastSyncChainDownloaderTest {
protocolContext = localBlockchainSetup.getProtocolContext();
ethProtocolManager =
EthProtocolManagerTestUtil.create(
localBlockchain,
localBlockchainSetup.getWorldArchive(),
new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
localBlockchain, new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()));
ethContext = ethProtocolManager.ethContext();
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers());
}

@ -24,11 +24,13 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder;
import org.hyperledger.besu.ethereum.eth.sync.fastsync.PivotBlockConfirmer.ContestedPivotBlockException;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
@ -51,6 +53,7 @@ public class PivotBlockConfirmerTest {
private final AtomicBoolean timeout = new AtomicBoolean(false);
private EthProtocolManager ethProtocolManager;
private MutableBlockchain blockchain;
private TransactionPool transactionPool;
private PivotBlockConfirmer<Void> pivotBlockConfirmer;
private ProtocolSchedule<Void> protocolSchedule;
@ -59,11 +62,16 @@ public class PivotBlockConfirmerTest {
final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
blockchainSetupUtil.importAllBlocks();
blockchain = blockchainSetupUtil.getBlockchain();
transactionPool = blockchainSetupUtil.getTransactionPool();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext();
ethProtocolManager =
EthProtocolManagerTestUtil.create(
blockchain, blockchainSetupUtil.getWorldArchive(), timeout::get);
blockchain,
timeout::get,
blockchainSetupUtil.getWorldArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig());
pivotBlockConfirmer = createPivotBlockConfirmer(3, 1);
}
@ -85,7 +93,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -113,7 +122,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -145,7 +155,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -184,7 +195,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -223,7 +235,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -264,7 +277,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(3, 1);
final Responder responderA =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);

@ -26,11 +26,13 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
@ -54,6 +56,7 @@ public class PivotBlockRetrieverTest {
private final AtomicBoolean timeout = new AtomicBoolean(false);
private EthProtocolManager ethProtocolManager;
private MutableBlockchain blockchain;
private TransactionPool transactionPool;
private PivotBlockRetriever<Void> pivotBlockRetriever;
private ProtocolSchedule<Void> protocolSchedule;
@ -64,9 +67,15 @@ public class PivotBlockRetrieverTest {
blockchain = blockchainSetupUtil.getBlockchain();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext();
transactionPool = blockchainSetupUtil.getTransactionPool();
ethProtocolManager =
EthProtocolManagerTestUtil.create(
blockchain, blockchainSetupUtil.getWorldArchive(), timeout::get);
blockchain,
timeout::get,
blockchainSetupUtil.getWorldArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig());
pivotBlockRetriever = createPivotBlockRetriever(3, 1, 1);
}
@ -87,7 +96,8 @@ public class PivotBlockRetrieverTest {
@Test
public void shouldSucceedWhenAllPeersAgree() {
final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
final RespondingEthPeer respondingPeerB =
@ -111,7 +121,8 @@ public class PivotBlockRetrieverTest {
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager);
final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
final RespondingEthPeer badPeerA = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1);
@ -153,7 +164,8 @@ public class PivotBlockRetrieverTest {
public void shouldIgnorePeersThatAreNotFullyValidated() {
final PeerValidator peerValidator = mock(PeerValidator.class);
final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000, peerValidator);
final RespondingEthPeer badPeerA =
@ -212,7 +224,8 @@ public class PivotBlockRetrieverTest {
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager);
final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer peerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, Difficulty.of(1000), 1000);
@ -239,7 +252,8 @@ public class PivotBlockRetrieverTest {
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager);
final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final Responder emptyResponder = RespondingEthPeer.emptyResponder();
final RespondingEthPeer peerA =
@ -273,7 +287,8 @@ public class PivotBlockRetrieverTest {
pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1);
final Responder responderA =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -306,7 +321,8 @@ public class PivotBlockRetrieverTest {
pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1);
final Responder responderA =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -342,7 +358,8 @@ public class PivotBlockRetrieverTest {
pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1);
final Responder responderA =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);

@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -64,8 +65,10 @@ public class FullSyncChainDownloaderForkTest {
ethProtocolManager =
EthProtocolManagerTestUtil.create(
localBlockchain,
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
localBlockchainSetup.getWorldArchive(),
new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
localBlockchainSetup.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
ethContext = ethProtocolManager.ethContext();
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers());
}

@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -83,8 +84,10 @@ public class FullSyncChainDownloaderTest {
ethProtocolManager =
EthProtocolManagerTestUtil.create(
localBlockchain,
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
localBlockchainSetup.getWorldArchive(),
new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
localBlockchainSetup.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
ethContext = ethProtocolManager.ethContext();
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers());
}

@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -57,8 +58,10 @@ public class FullSyncDownloaderTest {
ethProtocolManager =
EthProtocolManagerTestUtil.create(
localBlockchain,
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
localBlockchainSetup.getWorldArchive(),
new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
localBlockchainSetup.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
ethContext = ethProtocolManager.ethContext();
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers());
}

@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -65,7 +66,11 @@ public class FullSyncTargetManagerTest {
new ProtocolContext<>(localBlockchain, localWorldState, null);
ethProtocolManager =
EthProtocolManagerTestUtil.create(
localBlockchain, localWorldState, new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
localBlockchain,
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
localWorldState,
localBlockchainSetup.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
final EthContext ethContext = ethProtocolManager.ethContext();
localBlockchainSetup.importFirstBlocks(5);
otherBlockchainSetup.importFirstBlocks(20);

@ -90,9 +90,7 @@ public class SyncStateTest {
@Before
public void setUp() {
ethProtocolManager =
EthProtocolManagerTestUtil.create(
blockchain, InMemoryStorageProvider.createInMemoryWorldStateArchive());
ethProtocolManager = EthProtocolManagerTestUtil.create(blockchain);
ethPeers = spy(ethProtocolManager.ethContext().getEthPeers());
syncTargetPeer = createPeer(TARGET_DIFFICULTY, TARGET_CHAIN_HEIGHT);
otherPeer = createPeer(Difficulty.ZERO, 0);

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.eth.sync.tasks;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
@ -26,11 +27,13 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -140,8 +143,11 @@ public class DetermineCommonAncestorTaskParameterizedTest {
final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
final EthProtocolManager ethProtocolManager =
EthProtocolManagerTestUtil.create(localBlockchain, worldStateArchive);
EthProtocolManagerTestUtil.create(
localBlockchain,
worldStateArchive,
mock(TransactionPool.class),
EthProtocolConfiguration.defaultConfig());
final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(remoteBlockchain);
final RespondingEthPeer respondingEthPeer =

@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ -33,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
@ -40,6 +42,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.exceptions.EthTaskException;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -74,7 +77,12 @@ public class DetermineCommonAncestorTaskTest {
localGenesisBlock = blockDataGenerator.genesisBlock();
localBlockchain = createInMemoryBlockchain(localGenesisBlock);
final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
ethProtocolManager = EthProtocolManagerTestUtil.create(localBlockchain, worldStateArchive);
ethProtocolManager =
EthProtocolManagerTestUtil.create(
localBlockchain,
worldStateArchive,
mock(TransactionPool.class),
EthProtocolConfiguration.defaultConfig());
ethContext = ethProtocolManager.ethContext();
protocolContext = new ProtocolContext<>(localBlockchain, worldStateArchive, null);
}

@ -45,6 +45,7 @@ import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.rlp.RLP;
@ -111,7 +112,7 @@ public class WorldStateDownloaderTest {
.build());
final EthProtocolManager ethProtocolManager =
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()));
@After
public void tearDown() throws Exception {
@ -576,7 +577,8 @@ public class WorldStateDownloaderTest {
// Respond to node data requests
final List<MessageData> sentMessages = new ArrayList<>();
final RespondingEthPeer.Responder blockChainResponder =
RespondingEthPeer.blockchainResponder(mock(Blockchain.class), remoteWorldStateArchive);
RespondingEthPeer.blockchainResponder(
mock(Blockchain.class), remoteWorldStateArchive, mock(TransactionPool.class));
final RespondingEthPeer.Responder responder =
RespondingEthPeer.wrapResponderWithCollector(blockChainResponder, sentMessages);
@ -606,7 +608,7 @@ public class WorldStateDownloaderTest {
@Test
public void stalledDownloader() {
final EthProtocolManager ethProtocolManager =
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()));
// Setup "remote" state
final WorldStateStorage remoteStorage =
@ -898,10 +900,15 @@ public class WorldStateDownloaderTest {
final WorldStateArchive remoteWorldStateArchive,
final CompletableFuture<?> downloaderFuture) {
final RespondingEthPeer.Responder fullResponder =
RespondingEthPeer.blockchainResponder(mock(Blockchain.class), remoteWorldStateArchive);
RespondingEthPeer.blockchainResponder(
mock(Blockchain.class), remoteWorldStateArchive, mock(TransactionPool.class));
final RespondingEthPeer.Responder partialResponder =
RespondingEthPeer.partialResponder(
mock(Blockchain.class), remoteWorldStateArchive, MainnetProtocolSchedule.create(), .5f);
mock(Blockchain.class),
remoteWorldStateArchive,
mock(TransactionPool.class),
MainnetProtocolSchedule.create(),
.5f);
final RespondingEthPeer.Responder emptyResponder = RespondingEthPeer.emptyResponder();
// Send a few partial responses

@ -0,0 +1,106 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import java.util.Optional;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
import org.junit.Test;
public class PeerPendingTransactionTrackerTest {
private final EthPeer ethPeer1 = mock(EthPeer.class);
private final EthPeer ethPeer2 = mock(EthPeer.class);
private final BlockDataGenerator generator = new BlockDataGenerator();
private final PendingTransactions pendingTransactions = mock(PendingTransactions.class);
private final PeerPendingTransactionTracker tracker =
new PeerPendingTransactionTracker(pendingTransactions);
private final Hash hash1 = generator.transaction().getHash();
private final Hash hash2 = generator.transaction().getHash();
private final Hash hash3 = generator.transaction().getHash();
@Before
public void setUp() {
Transaction tx = mock(Transaction.class);
when(pendingTransactions.getTransactionByHash(any())).thenReturn(Optional.of(tx));
}
@Test
public void shouldTrackTransactionsToSendToPeer() {
tracker.addToPeerSendQueue(ethPeer1, hash1);
tracker.addToPeerSendQueue(ethPeer1, hash2);
tracker.addToPeerSendQueue(ethPeer2, hash3);
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer1, ethPeer2);
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer1)).containsOnly(hash1, hash2);
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer2)).containsOnly(hash3);
}
@Test
public void shouldExcludeAlreadySeenTransactionsFromTransactionsToSend() {
tracker.markTransactionsHashesAsSeen(ethPeer1, ImmutableSet.of(hash2));
tracker.addToPeerSendQueue(ethPeer1, hash1);
tracker.addToPeerSendQueue(ethPeer1, hash2);
tracker.addToPeerSendQueue(ethPeer2, hash3);
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer1, ethPeer2);
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer1)).containsOnly(hash1);
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer2)).containsOnly(hash3);
}
@Test
public void shouldExcludeAlreadySeenTransactionsAsACollectionFromTransactionsToSend() {
tracker.markTransactionsHashesAsSeen(ethPeer1, ImmutableSet.of(hash1, hash2));
tracker.addToPeerSendQueue(ethPeer1, hash1);
tracker.addToPeerSendQueue(ethPeer1, hash2);
tracker.addToPeerSendQueue(ethPeer2, hash3);
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer2);
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer1)).isEmpty();
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer2)).containsOnly(hash3);
}
@Test
public void shouldClearDataWhenPeerDisconnects() {
tracker.markTransactionsHashesAsSeen(ethPeer1, ImmutableSet.of(hash3));
tracker.addToPeerSendQueue(ethPeer1, hash2);
tracker.addToPeerSendQueue(ethPeer2, hash3);
tracker.onDisconnect(ethPeer1);
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer2);
// Should have cleared data that ethPeer1 has already seen transaction1
tracker.addToPeerSendQueue(ethPeer1, hash1);
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer1, ethPeer2);
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer1)).containsOnly(hash1);
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer2)).containsOnly(hash3);
}
}

@ -0,0 +1,97 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static java.time.Instant.now;
import static java.util.Arrays.asList;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.messages.NewPooledTransactionHashesMessage;
import org.hyperledger.besu.plugin.services.metrics.Counter;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class PendingTransactionsMessageProcessorTest {
@Mock private TransactionPool transactionPool;
@Mock private PeerPendingTransactionTracker transactionTracker;
@Mock private Counter totalSkippedTransactionsMessageCounter;
@Mock private EthPeer peer1;
@InjectMocks private PendingTransactionsMessageProcessor messageHandler;
private final BlockDataGenerator generator = new BlockDataGenerator();
private final Hash hash1 = generator.transaction().getHash();
private final Hash hash2 = generator.transaction().getHash();
private final Hash hash3 = generator.transaction().getHash();
@Test
public void shouldMarkAllReceivedTransactionsAsSeen() {
messageHandler.processNewPooledTransactionHashesMessage(
peer1,
NewPooledTransactionHashesMessage.create(asList(hash1, hash2, hash3)),
now(),
ofMinutes(1));
verify(transactionTracker)
.markTransactionsHashesAsSeen(peer1, Arrays.asList(hash1, hash2, hash3));
}
@Test
public void shouldAddInitiatedRequestingTransactions() {
messageHandler.processNewPooledTransactionHashesMessage(
peer1,
NewPooledTransactionHashesMessage.create(asList(hash1, hash2, hash3)),
now(),
ofMinutes(1));
verify(transactionPool).addTransactionHashes(hash1);
verify(transactionPool).addTransactionHashes(hash2);
verify(transactionPool).addTransactionHashes(hash3);
}
@Test
public void shouldNotMarkReceivedExpiredTransactionsAsSeen() {
messageHandler.processNewPooledTransactionHashesMessage(
peer1,
NewPooledTransactionHashesMessage.create(asList(hash1, hash2, hash3)),
now().minus(ofMinutes(1)),
ofMillis(1));
verifyZeroInteractions(transactionTracker);
verify(totalSkippedTransactionsMessageCounter).inc(1);
}
@Test
public void shouldNotAddReceivedTransactionsToTransactionPoolIfExpired() {
messageHandler.processNewPooledTransactionHashesMessage(
peer1,
NewPooledTransactionHashesMessage.create(asList(hash1, hash2, hash3)),
now().minus(ofMinutes(1)),
ofMillis(1));
verifyZeroInteractions(transactionPool);
verify(totalSkippedTransactionsMessageCounter).inc(1);
}
}

@ -0,0 +1,127 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static com.google.common.collect.Sets.newHashSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.eth.messages.NewPooledTransactionHashesMessage;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Sets;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
public class PendingTransactionsMessageSenderTest {
private final EthPeer peer1 = mock(EthPeer.class);
private final EthPeer peer2 = mock(EthPeer.class);
private final BlockDataGenerator generator = new BlockDataGenerator();
private final Hash transaction1 = generator.transaction().getHash();
private final Hash transaction2 = generator.transaction().getHash();
private final Hash transaction3 = generator.transaction().getHash();
private final PendingTransactions pendingTransactions = mock(PendingTransactions.class);
private final PeerPendingTransactionTracker transactionTracker =
new PeerPendingTransactionTracker(pendingTransactions);
private final PendingTransactionsMessageSender messageSender =
new PendingTransactionsMessageSender(transactionTracker);
@Before
public void setUp() {
Transaction tx = mock(Transaction.class);
when(pendingTransactions.getTransactionByHash(any())).thenReturn(Optional.of(tx));
}
@Test
public void shouldSendPendingTransactionsToEachPeer() throws Exception {
transactionTracker.addToPeerSendQueue(peer1, transaction1);
transactionTracker.addToPeerSendQueue(peer1, transaction2);
transactionTracker.addToPeerSendQueue(peer2, transaction3);
messageSender.sendTransactionsToPeers();
verify(peer1).send(transactionsMessageContaining(transaction1, transaction2));
verify(peer2).send(transactionsMessageContaining(transaction3));
verifyNoMoreInteractions(peer1, peer2);
}
@Test
public void shouldSendTransactionsInBatchesWithLimit() throws Exception {
final Set<Hash> transactions =
generator.transactions(6000).stream().map(Transaction::getHash).collect(Collectors.toSet());
transactions.forEach(transaction -> transactionTracker.addToPeerSendQueue(peer1, transaction));
messageSender.sendTransactionsToPeers();
final ArgumentCaptor<MessageData> messageDataArgumentCaptor =
ArgumentCaptor.forClass(MessageData.class);
verify(peer1, times(2)).send(messageDataArgumentCaptor.capture());
final List<MessageData> sentMessages = messageDataArgumentCaptor.getAllValues();
assertThat(sentMessages).hasSize(2);
assertThat(sentMessages)
.allMatch(message -> message.getCode() == EthPV65.NEW_POOLED_TRANSACTION_HASHES);
final Set<Hash> firstBatch = getTransactionsFromMessage(sentMessages.get(0));
final Set<Hash> secondBatch = getTransactionsFromMessage(sentMessages.get(1));
final int expectedFirstBatchSize = 4096, expectedSecondBatchSize = 1904, toleranceDelta = 0;
assertThat(firstBatch)
.hasSizeBetween(
expectedFirstBatchSize - toleranceDelta, expectedFirstBatchSize + toleranceDelta);
assertThat(secondBatch)
.hasSizeBetween(
expectedSecondBatchSize - toleranceDelta, expectedSecondBatchSize + toleranceDelta);
assertThat(Sets.union(firstBatch, secondBatch)).isEqualTo(transactions);
}
private MessageData transactionsMessageContaining(final Hash... transactions) {
return argThat(
message -> {
final Set<Hash> actualSentTransactions = getTransactionsFromMessage(message);
final Set<Hash> expectedTransactions = newHashSet(transactions);
return message.getCode() == EthPV65.NEW_POOLED_TRANSACTION_HASHES
&& actualSentTransactions.equals(expectedTransactions);
});
}
private Set<Hash> getTransactionsFromMessage(final MessageData message) {
final NewPooledTransactionHashesMessage transactionsMessage =
NewPooledTransactionHashesMessage.readFrom(message);
return newHashSet(transactionsMessage.pendingTransactions());
}
}

@ -41,6 +41,7 @@ import org.junit.Test;
public class PendingTransactionsTest {
private static final int MAX_TRANSACTIONS = 5;
private static final int MAX_TRANSACTION_HASHES = 5;
private static final KeyPair KEYS1 = KeyPair.generate();
private static final KeyPair KEYS2 = KeyPair.generate();
private static final String ADDED_COUNTER = "transactions_added_total";
@ -55,6 +56,7 @@ public class PendingTransactionsTest {
new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
MAX_TRANSACTIONS,
MAX_TRANSACTION_HASHES,
TestClock.fixed(),
metricsSystem);
private final Transaction transaction1 = createTransaction(2);
@ -551,7 +553,11 @@ public class PendingTransactionsTest {
final int maxTransactionRetentionHours = 1;
final PendingTransactions transactions =
new PendingTransactions(
maxTransactionRetentionHours, MAX_TRANSACTIONS, clock, metricsSystem);
maxTransactionRetentionHours,
MAX_TRANSACTIONS,
MAX_TRANSACTION_HASHES,
clock,
metricsSystem);
transactions.addRemoteTransaction(transaction1);
assertThat(transactions.size()).isEqualTo(1);
@ -569,7 +575,11 @@ public class PendingTransactionsTest {
final int maxTransactionRetentionHours = 1;
final PendingTransactions transactions =
new PendingTransactions(
maxTransactionRetentionHours, MAX_TRANSACTIONS, clock, metricsSystem);
maxTransactionRetentionHours,
MAX_TRANSACTIONS,
MAX_TRANSACTION_HASHES,
clock,
metricsSystem);
transactions.addRemoteTransaction(transaction1);
assertThat(transactions.size()).isEqualTo(1);
clock.step(2L, ChronoUnit.HOURS);
@ -583,7 +593,11 @@ public class PendingTransactionsTest {
final int maxTransactionRetentionHours = 2;
final PendingTransactions transactions =
new PendingTransactions(
maxTransactionRetentionHours, MAX_TRANSACTIONS, clock, metricsSystem);
maxTransactionRetentionHours,
MAX_TRANSACTIONS,
MAX_TRANSACTION_HASHES,
clock,
metricsSystem);
transactions.addRemoteTransaction(transaction1);
assertThat(transactions.size()).isEqualTo(1);
clock.step(3L, ChronoUnit.HOURS);

@ -34,7 +34,10 @@ import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyProtocolSch
import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
@ -110,19 +113,41 @@ public class TestNode implements Closeable {
genesisState.writeStateTo(worldStateArchive.getMutable());
final ProtocolContext<Void> protocolContext =
new ProtocolContext<>(blockchain, worldStateArchive, null);
final SyncState syncState = mock(SyncState.class);
when(syncState.isInSync(anyLong())).thenReturn(true);
final EthMessages ethMessages = new EthMessages();
final EthPeers ethPeers = new EthPeers(EthProtocol.NAME, TestClock.fixed(), metricsSystem);
final EthScheduler scheduler = new EthScheduler(1, 1, 1, metricsSystem);
final EthContext ethContext = new EthContext(ethPeers, ethMessages, scheduler);
transactionPool =
TransactionPoolFactory.createTransactionPool(
protocolSchedule,
protocolContext,
ethContext,
TestClock.fixed(),
metricsSystem,
syncState,
Wei.ZERO,
TransactionPoolConfiguration.builder().build());
final EthProtocolManager ethProtocolManager =
new EthProtocolManager(
blockchain,
worldStateArchive,
BigInteger.ONE,
worldStateArchive,
transactionPool,
EthProtocolConfiguration.defaultConfig(),
ethPeers,
ethMessages,
ethContext,
Collections.emptyList(),
false,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig());
scheduler);
final NetworkRunner networkRunner =
NetworkRunner.builder()
@ -143,22 +168,6 @@ public class TestNode implements Closeable {
network.subscribeDisconnect(
(connection, reason, initiatedByPeer) -> disconnections.put(connection, reason));
final EthContext ethContext = ethProtocolManager.ethContext();
final SyncState syncState = mock(SyncState.class);
when(syncState.isInSync(anyLong())).thenReturn(true);
transactionPool =
TransactionPoolFactory.createTransactionPool(
protocolSchedule,
protocolContext,
ethContext,
TestClock.fixed(),
metricsSystem,
syncState,
Wei.ZERO,
TransactionPoolConfiguration.builder().build());
networkRunner.start();
selfPeer = DefaultPeer.fromEnodeURL(network.getLocalEnode().get());
}

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

@ -163,7 +163,7 @@ public class RetestethContext {
final EthPeers ethPeers = new EthPeers("reteseth", retestethClock, metricsSystem);
final SyncState syncState = new SyncState(blockchain, ethPeers);
ethScheduler = new EthScheduler(1, 1, 1, metricsSystem);
ethScheduler = new EthScheduler(1, 1, 1, 1, metricsSystem);
final EthContext ethContext = new EthContext(ethPeers, new EthMessages(), ethScheduler);
final TransactionPoolConfiguration transactionPoolConfiguration =

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

Loading…
Cancel
Save