From cf592c48d1bae767ce5049b9197a4cda8e16ab22 Mon Sep 17 00:00:00 2001 From: Stefan Pingel <16143240+pinges@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:37:36 +1000 Subject: [PATCH] implement engine_getBlobsV1 (#7553) * implement engine_getBlobsV1 Signed-off-by: stefan.pingel@consensys.net Signed-off-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> Co-authored-by: Sally MacFarlane --- CHANGELOG.md | 3 +- .../blockcreation/CliqueBlockCreatorTest.java | 4 +- .../CliqueMinerExecutorTest.java | 4 +- .../ibft/support/TestContextBuilder.java | 4 +- .../blockcreation/BftBlockCreatorTest.java | 4 +- .../blockcreation/MergeCoordinatorTest.java | 4 +- .../qbft/support/TestContextBuilder.java | 4 +- .../EthGetFilterChangesIntegrationTest.java | 4 +- .../EthGetFilterChangesIntegrationTest.java | 4 +- .../besu/ethereum/api/jsonrpc/RpcMethod.java | 1 + .../methods/engine/EngineGetBlobsV1.java | 115 ++++++++ .../internal/response/RpcErrorType.java | 2 + .../internal/results/BlobAndProofV1.java | 42 +++ .../ExecutionEngineJsonRpcMethods.java | 11 +- .../methods/JsonRpcMethodsFactory.java | 3 +- .../methods/engine/EngineGetBlobsV1Test.java | 267 ++++++++++++++++++ .../AbstractBlockCreatorTest.java | 4 +- ...FeeMarketBlockTransactionSelectorTest.java | 4 +- ...FeeMarketBlockTransactionSelectorTest.java | 4 +- .../blockcreation/PoWBlockCreatorTest.java | 4 +- .../blockcreation/PoWMinerExecutorTest.java | 4 +- .../bonsai/AbstractIsolationTests.java | 3 +- .../ethereum/eth/transactions/BlobCache.java | 4 + .../eth/transactions/TransactionPool.java | 44 ++- .../transactions/TransactionPoolFactory.java | 3 +- .../AbstractTransactionPoolTest.java | 3 +- 26 files changed, 532 insertions(+), 21 deletions(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1.java create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlobAndProofV1.java create mode 100644 ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e38dc1a385..4e151138641 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Additions and Improvements - Update Java and Gradle dependecies [#7571](https://github.com/hyperledger/besu/pull/7571) - Layered txpool: new options `--tx-pool-min-score` to remove a tx from pool when its score is lower than the specified value [#7576](https://github.com/hyperledger/besu/pull/7576) +- Add `engine_getBlobsV1` method to the Engine API [#7553](https://github.com/hyperledger/besu/pull/7553) ### Bug fixes - Layered txpool: do not send notifications when moving tx between layers [#7539](https://github.com/hyperledger/besu/pull/7539) @@ -39,7 +40,7 @@ - Correctly drops messages that exceeds local message size limit [#5455](https://github.com/hyperledger/besu/pull/7507) - **DebugMetrics**: Fixed a `ClassCastException` occurring in `DebugMetrics` when handling nested metric structures. Previously, `Double` values within these structures were incorrectly cast to `Map` objects, leading to errors. This update allows for proper handling of both direct values and nested structures at the same level. Issue# [#7383](https://github.com/hyperledger/besu/pull/7383) - `evmtool` was not respecting the `--genesis` setting, resulting in unexpected trace results. [#7433](https://github.com/hyperledger/besu/pull/7433) -- The genesis config override `contractSizeLimit` was not wired into code size limits [#7557](https://github.com/hyperledger/besu/pull/7557) +- The genesis config override `contractSizeLimit`q was not wired into code size limits [#7557](https://github.com/hyperledger/besu/pull/7557) - Fix incorrect key filtering in LayeredKeyValueStorage stream [#7535](https://github.com/hyperledger/besu/pull/7557) ## 24.8.0 diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java index ec10630df9c..9f2d848d452 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java @@ -55,6 +55,7 @@ import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; @@ -245,7 +246,8 @@ private TransactionPool createTransactionPool() { mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(metricsSystem), - conf); + conf, + new BlobCache()); transactionPool.setEnabled(); return transactionPool; } diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java index 9502d00a192..1aa2d75ef3b 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java @@ -45,6 +45,7 @@ import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; @@ -233,7 +234,8 @@ private TransactionPool createTransactionPool() { mock(TransactionBroadcaster.class), cliqueEthContext, new TransactionPoolMetrics(metricsSystem), - conf); + conf, + new BlobCache()); transactionPool.setEnabled(); return transactionPool; diff --git a/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java index 2d620c56321..5d2b02b1a7c 100644 --- a/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java +++ b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java @@ -84,6 +84,7 @@ import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; @@ -371,7 +372,8 @@ private static ControllerAndState createControllerAndFinalState( mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(metricsSystem), - poolConf); + poolConf, + new BlobCache()); transactionPool.setEnabled(); diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java index 1b86896f36f..15844578cdd 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java @@ -47,6 +47,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; @@ -152,7 +153,8 @@ public BlockHeaderValidator.Builder createBlockHeaderRuleset( mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(metricsSystem), - poolConf); + poolConf, + new BlobCache()); transactionPool.setEnabled(); diff --git a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java index 6ce8bcd8d0a..a3dc6b6e0b7 100644 --- a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java +++ b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java @@ -63,6 +63,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BackwardSyncContext; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; @@ -214,7 +215,8 @@ public void setUp() { mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(metricsSystem), - poolConf); + poolConf, + new BlobCache()); this.transactionPool.setEnabled(); diff --git a/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java b/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java index 3467dce9fb2..8906f0de7f6 100644 --- a/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java +++ b/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java @@ -98,6 +98,7 @@ import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; @@ -480,7 +481,8 @@ private static ControllerAndState createControllerAndFinalState( mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(metricsSystem), - poolConf); + poolConf, + new BlobCache()); transactionPool.setEnabled(); diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java index 16a25d3500a..d4de405305b 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java @@ -50,6 +50,7 @@ import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeers; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; @@ -121,7 +122,8 @@ public void setUp() { batchAddedListener, ethContext, new TransactionPoolMetrics(metricsSystem), - TransactionPoolConfiguration.DEFAULT); + TransactionPoolConfiguration.DEFAULT, + new BlobCache()); transactionPool.setEnabled(); final BlockchainQueries blockchainQueries = new BlockchainQueries( diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java index e88d377f7f0..0c194ad22f2 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java @@ -50,6 +50,7 @@ import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeers; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; @@ -121,7 +122,8 @@ public void setUp() { batchAddedListener, ethContext, new TransactionPoolMetrics(metricsSystem), - TransactionPoolConfiguration.DEFAULT); + TransactionPoolConfiguration.DEFAULT, + new BlobCache()); transactionPool.setEnabled(); final BlockchainQueries blockchainQueries = new BlockchainQueries( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java index 79d33ffbacd..f8bc597ff7c 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java @@ -51,6 +51,7 @@ public enum RpcMethod { DEBUG_GET_RAW_BLOCK("debug_getRawBlock"), DEBUG_GET_RAW_RECEIPTS("debug_getRawReceipts"), DEBUG_GET_RAW_TRANSACTION("debug_getRawTransaction"), + ENGINE_GET_BLOBS_V1("engine_getBlobsV1"), ENGINE_GET_PAYLOAD_V1("engine_getPayloadV1"), ENGINE_GET_PAYLOAD_V2("engine_getPayloadV2"), ENGINE_GET_PAYLOAD_V3("engine_getPayloadV3"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1.java new file mode 100644 index 00000000000..cc9ba2dce2a --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1.java @@ -0,0 +1,115 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; + +import org.hyperledger.besu.datatypes.BlobsWithCommitments; +import org.hyperledger.besu.datatypes.VersionedHash; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlobAndProofV1; +import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; + +import java.util.Arrays; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import io.vertx.core.Vertx; + +/** + * #### Specification + * + *

1. Given an array of blob versioned hashes client software **MUST** respond with an array of + * `BlobAndProofV1` objects with matching versioned hashes, respecting the order of versioned hashes + * in the input array. + * + *

2. Client software **MUST** place responses in the order given in the request, using `null` + * for any missing blobs. For instance, if the request is `[A_versioned_hash, B_versioned_hash, + * C_versioned_hash]` and client software has data for blobs `A` and `C`, but doesn't have data for + * `B`, the response **MUST** be `[A, null, C]`. + * + *

3. Client software **MUST** support request sizes of at least 128 blob versioned hashes. The + * client **MUST** return `-38004: Too large request` error if the number of requested blobs is too + * large. + * + *

4. Client software **MAY** return an array of all `null` entries if syncing or otherwise + * unable to serve blob pool data. + * + *

5. Callers **MUST** consider that execution layer clients may prune old blobs from their pool, + * and will respond with `null` if a blob has been pruned. + */ +public class EngineGetBlobsV1 extends ExecutionEngineJsonRpcMethod { + + private final TransactionPool transactionPool; + + public EngineGetBlobsV1( + final Vertx vertx, + final ProtocolContext protocolContext, + final EngineCallListener engineCallListener, + final TransactionPool transactionPool) { + super(vertx, protocolContext, engineCallListener); + this.transactionPool = transactionPool; + } + + @Override + public String getName() { + return "engine_getBlobsV1"; + } + + @Override + public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext) { + final VersionedHash[] versionedHashes; + try { + versionedHashes = requestContext.getRequiredParameter(0, VersionedHash[].class); + } catch (JsonRpcParameter.JsonRpcParameterException e) { + throw new InvalidJsonRpcParameters( + "Invalid versioned hashes parameter (index 0)", + RpcErrorType.INVALID_VERSIONED_HASHES_PARAMS, + e); + } + + if (versionedHashes.length > 128) { + return new JsonRpcErrorResponse( + requestContext.getRequest().getId(), + RpcErrorType.INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST); + } + + final List result = getBlobV1Result(versionedHashes); + + return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result); + } + + private @Nonnull List getBlobV1Result(final VersionedHash[] versionedHashes) { + return Arrays.stream(versionedHashes) + .map(transactionPool::getBlobQuad) + .map(this::getBlobAndProofV1) + .toList(); + } + + private @Nullable BlobAndProofV1 getBlobAndProofV1(final BlobsWithCommitments.BlobQuad bq) { + if (bq == null) { + return null; + } + return new BlobAndProofV1( + bq.blob().getData().toHexString(), bq.kzgProof().getData().toHexString()); + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java index 009fca38fc0..875eab601ba 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java @@ -58,6 +58,7 @@ public enum RpcErrorType implements RpcMethodError { INVALID_ENGINE_NEW_PAYLOAD_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid engine payload parameter"), INVALID_ENGINE_PREPARE_PAYLOAD_PARAMS( INVALID_PARAMS_ERROR_CODE, "Invalid engine prepare payload parameter"), + INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST(-38004, "Too large request"), INVALID_ENODE_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid enode params"), INVALID_EXCESS_BLOB_GAS_PARAMS( INVALID_PARAMS_ERROR_CODE, "Invalid excess blob gas params (missing or invalid)"), @@ -109,6 +110,7 @@ public enum RpcErrorType implements RpcMethodError { INVALID_TRANSACTION_LIMIT_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid transaction limit params"), INVALID_TRANSACTION_TRACE_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid transaction trace params"), INVALID_VERSIONED_HASH_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid versioned hash params"), + INVALID_VERSIONED_HASHES_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid versioned hashes params"), INVALID_VOTE_TYPE_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid vote type params"), INVALID_WITHDRAWALS_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid withdrawals"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlobAndProofV1.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlobAndProofV1.java new file mode 100644 index 00000000000..c8978e00c11 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlobAndProofV1.java @@ -0,0 +1,42 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +/** + * The result of the eth_getBlobAndProofV1 JSON-RPC method contains an array of BlobAndProofV1. + * BlobAndProofV1 contains the blob data and the kzg proof for the blob. + */ +@JsonPropertyOrder({"blob", "proof"}) +public class BlobAndProofV1 { + + private final String blob; + + private final String proof; + + public BlobAndProofV1(final String blob, final String proof) { + this.blob = blob; + this.proof = proof; + } + + public String getProof() { + return proof; + } + + public String getBlob() { + return blob; + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java index 6dda09bce98..d8d04027f6a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV1; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV2; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdatedV3; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetBlobsV1; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetClientVersionV1; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadBodiesByHashV1; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadBodiesByRangeV1; @@ -39,6 +40,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; import org.hyperledger.besu.ethereum.eth.manager.EthPeers; +import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import java.util.ArrayList; @@ -60,6 +62,7 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods { private final Vertx consensusEngineServer; private final String clientVersion; private final String commit; + private final TransactionPool transactionPool; ExecutionEngineJsonRpcMethods( final MiningCoordinator miningCoordinator, @@ -68,7 +71,8 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods { final EthPeers ethPeers, final Vertx consensusEngineServer, final String clientVersion, - final String commit) { + final String commit, + final TransactionPool transactionPool) { this.mergeCoordinator = Optional.ofNullable(miningCoordinator) .filter(mc -> mc.isCompatibleWithEngineApi()) @@ -79,6 +83,7 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods { this.consensusEngineServer = consensusEngineServer; this.clientVersion = clientVersion; this.commit = commit; + this.transactionPool = transactionPool; } @Override @@ -156,7 +161,9 @@ protected Map create() { new EnginePreparePayloadDebug( consensusEngineServer, protocolContext, engineQosTimer, mergeCoordinator.get()), new EngineGetClientVersionV1( - consensusEngineServer, protocolContext, engineQosTimer, clientVersion, commit))); + consensusEngineServer, protocolContext, engineQosTimer, clientVersion, commit), + new EngineGetBlobsV1( + consensusEngineServer, protocolContext, engineQosTimer, transactionPool))); if (protocolSchedule.anyMatch(p -> p.spec().getName().equalsIgnoreCase("cancun"))) { executionEngineApisSupported.add( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java index 41227c2ca82..55d9ef60292 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java @@ -119,7 +119,8 @@ public Map methods( ethPeers, consensusEngineServer, clientVersion, - commit), + commit, + transactionPool), new EthJsonRpcMethods( blockchainQueries, synchronizer, diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java new file mode 100644 index 00000000000..29c7ae82ed5 --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java @@ -0,0 +1,267 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SECPPrivateKey; +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.BlobsWithCommitments; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.TransactionType; +import org.hyperledger.besu.datatypes.VersionedHash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlobAndProofV1; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.BlobTestFixture; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.TransactionTestFixture; +import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; +import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; +import io.vertx.core.Vertx; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class EngineGetBlobsV1Test { + + private static final Supplier SIGNATURE_ALGORITHM = + Suppliers.memoize(SignatureAlgorithmFactory::getInstance); + private static final SECPPrivateKey PRIVATE_KEY1 = + SIGNATURE_ALGORITHM + .get() + .createPrivateKey( + Bytes32.fromHexString( + "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63")); + private static final KeyPair KEYS1 = + new KeyPair(PRIVATE_KEY1, SIGNATURE_ALGORITHM.get().createPublicKey(PRIVATE_KEY1)); + public static final VersionedHash VERSIONED_HASH_ZERO = new VersionedHash((byte) 1, Hash.ZERO); + + @Mock private ProtocolContext protocolContext; + @Mock private EngineCallListener engineCallListener; + @Mock private MutableBlockchain blockchain; + @Mock private TransactionPool transactionPool; + + private EngineGetBlobsV1 method; + + private static final Vertx vertx = Vertx.vertx(); + + @BeforeEach + public void before() { + when(protocolContext.getBlockchain()).thenReturn(blockchain); + this.method = + spy(new EngineGetBlobsV1(vertx, protocolContext, engineCallListener, transactionPool)); + } + + @Test + public void shouldReturnExpectedMethodName() { + assertThat(method.getName()).isEqualTo("engine_getBlobsV1"); + } + + @Test + public void shouldReturnBlobsAndProofsForKnownVersionedHashesFromMap() { + final Transaction blobTransaction = createBlobTransaction(); + + final BlobsWithCommitments blobsWithCommitments = + blobTransaction.getBlobsWithCommitments().get(); + + mockTransactionPoolMethod(blobsWithCommitments); + + VersionedHash[] versionedHashes = + blobsWithCommitments.getVersionedHashes().toArray(new VersionedHash[0]); + + final JsonRpcResponse jsonRpcResponse = resp(versionedHashes); + + final List blobAndProofV1s = fromSuccessResp(jsonRpcResponse); + + assertThat(blobAndProofV1s.size()).isEqualTo(versionedHashes.length); + // for loop to check each blob and proof + for (int i = 0; i < versionedHashes.length; i++) { + assertThat(Bytes.fromHexString(blobAndProofV1s.get(i).getBlob())) + .isEqualTo(blobsWithCommitments.getBlobQuads().get(i).blob().getData()); + assertThat(Bytes.fromHexString(blobAndProofV1s.get(i).getProof())) + .isEqualTo(blobsWithCommitments.getBlobQuads().get(i).kzgProof().getData()); + } + } + + @Test + public void shouldReturnNullForBlobsAndProofsForUnknownVersionedHashes() { + final Transaction blobTransaction = createBlobTransaction(); + + final BlobsWithCommitments blobsWithCommitments = + blobTransaction.getBlobsWithCommitments().get(); + + mockTransactionPoolMethod(blobsWithCommitments); + + List versionedHashesList = + blobsWithCommitments.getVersionedHashes().stream().toList(); + + final VersionedHash[] hashes = versionedHashesList.toArray(new VersionedHash[0]); + hashes[1] = VERSIONED_HASH_ZERO; + + final JsonRpcResponse jsonRpcResponse = resp(hashes); + + final List blobAndProofV1s = fromSuccessResp(jsonRpcResponse); + + assertThat(blobAndProofV1s.size()).isEqualTo(versionedHashesList.size()); + // for loop to check each blob and proof + for (int i = 0; i < versionedHashesList.size(); i++) { + if (i != 1) { + assertThat(Bytes.fromHexString(blobAndProofV1s.get(i).getBlob())) + .isEqualTo(blobsWithCommitments.getBlobQuads().get(i).blob().getData()); + assertThat(Bytes.fromHexString(blobAndProofV1s.get(i).getProof())) + .isEqualTo(blobsWithCommitments.getBlobQuads().get(i).kzgProof().getData()); + } else { + assertThat(blobAndProofV1s.get(i)).isNull(); + } + } + } + + @Test + public void shouldReturnOnlyNullsForBlobsAndProofsIfAllVersionedHashesUnknown() { + final Transaction blobTransaction = createBlobTransaction(); + + final BlobsWithCommitments blobsWithCommitments = + blobTransaction.getBlobsWithCommitments().get(); + + mockTransactionPoolMethod(blobsWithCommitments); + + List versionedHashesList = + blobsWithCommitments.getVersionedHashes().stream().toList(); + + final VersionedHash[] versionedHashes = new VersionedHash[6]; + Arrays.fill(versionedHashes, VERSIONED_HASH_ZERO); + final JsonRpcResponse jsonRpcResponse = resp(versionedHashes); + + final List blobAndProofV1s = fromSuccessResp(jsonRpcResponse); + + assertThat(blobAndProofV1s.size()).isEqualTo(versionedHashesList.size()); + // for loop to check each blob and proof + for (int i = 0; i < versionedHashes.length; i++) { + assertThat(blobAndProofV1s.get(i)).isNull(); + } + } + + @Test + public void shouldReturnEmptyResponseForEmptyRequest() { + final VersionedHash[] versionedHashes = new VersionedHash[0]; + + final JsonRpcResponse jsonRpcResponse = resp(versionedHashes); + + assertThat(fromSuccessResp(jsonRpcResponse).size()).isEqualTo(0); + } + + @Test + public void shouldFailWhenRequestingMoreThan128() { + final VersionedHash[] versionedHashes = new VersionedHash[129]; + for (int i = 0; i < 129; i++) { + versionedHashes[i] = new VersionedHash((byte) 1, Hash.ZERO); + } + + final JsonRpcResponse jsonRpcResponse = resp(versionedHashes); + + assertThat(fromErrorResp(jsonRpcResponse).getCode()) + .isEqualTo(RpcErrorType.INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST.getCode()); + assertThat(fromErrorResp(jsonRpcResponse).getMessage()) + .isEqualTo(RpcErrorType.INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST.getMessage()); + } + + Transaction createBlobTransaction() { + BlobTestFixture blobTestFixture = new BlobTestFixture(); + BlobsWithCommitments bwc = blobTestFixture.createBlobsWithCommitments(6); + TransactionTestFixture ttf = new TransactionTestFixture(); + Transaction fullOfBlobs = + ttf.to(Optional.of(Address.ZERO)) + .type(TransactionType.BLOB) + .chainId(Optional.of(BigInteger.valueOf(42))) + .gasLimit(21000) + .maxFeePerGas(Optional.of(Wei.of(15))) + .maxFeePerBlobGas(Optional.of(Wei.of(128))) + .maxPriorityFeePerGas(Optional.of(Wei.of(1))) + .versionedHashes(Optional.of(bwc.getVersionedHashes())) + .blobsWithCommitments(Optional.of(bwc)) + .createTransaction(KEYS1); + return fullOfBlobs; + } + + private void mockTransactionPoolMethod(final BlobsWithCommitments blobsWithCommitments) { + blobsWithCommitments + .getBlobQuads() + .forEach( + blobQuad -> + when(transactionPool.getBlobQuad(blobQuad.versionedHash())).thenReturn(blobQuad)); + } + + private JsonRpcResponse resp(final VersionedHash[] versionedHashes) { + return method.response( + new JsonRpcRequestContext( + new JsonRpcRequest( + "2.0", + RpcMethod.ENGINE_GET_BLOBS_V1.getMethodName(), + new Object[] {versionedHashes}))); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private List fromSuccessResp(final JsonRpcResponse resp) { + assertThat(resp.getType()).isEqualTo(RpcResponseType.SUCCESS); + final List list = + Optional.of(resp) + .map(JsonRpcSuccessResponse.class::cast) + .map(JsonRpcSuccessResponse::getResult) + .map(List.class::cast) + .get(); + final ArrayList blobAndProofV1s = new ArrayList<>(); + list.forEach(obj -> blobAndProofV1s.add((BlobAndProofV1) obj)); + return blobAndProofV1s; + } + + private RpcErrorType fromErrorResp(final JsonRpcResponse resp) { + assertThat(resp.getType()).isEqualTo(RpcResponseType.ERROR); + return Optional.of(resp) + .map(JsonRpcErrorResponse.class::cast) + .map(JsonRpcErrorResponse::getErrorType) + .get(); + } +} diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java index 946d4ab09d3..88a39925b92 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java @@ -63,6 +63,7 @@ import org.hyperledger.besu.ethereum.core.Withdrawal; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; @@ -419,7 +420,8 @@ private AbstractBlockCreator createBlockCreator(final ProtocolSpecAdapters proto mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(new NoOpMetricsSystem()), - poolConf); + poolConf, + new BlobCache()); transactionPool.setEnabled(); final MiningParameters miningParameters = diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java index 416e5fd2349..940d076bbc3 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java @@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; @@ -94,7 +95,8 @@ protected TransactionPool createTransactionPool() { mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(metricsSystem), - poolConf); + poolConf, + new BlobCache()); transactionPool.setEnabled(); return transactionPool; } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java index a59841d4f4f..68d9a71de6f 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; @@ -101,7 +102,8 @@ protected TransactionPool createTransactionPool() { mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(metricsSystem), - poolConf); + poolConf, + new BlobCache()); transactionPool.setEnabled(); return transactionPool; } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java index 7e3c01200ce..e9e727d99cb 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java @@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyCalculators; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; @@ -371,7 +372,8 @@ private TransactionPool createTransactionPool( mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(metricsSystem), - poolConf); + poolConf, + new BlobCache()); transactionPool.setEnabled(); return transactionPool; diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java index 6859409d49f..8c1e217d759 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java @@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionBroadcaster; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; @@ -117,7 +118,8 @@ private TransactionPool createTransactionPool() { mock(TransactionBroadcaster.class), ethContext, new TransactionPoolMetrics(new NoOpMetricsSystem()), - poolConf); + poolConf, + new BlobCache()); transactionPool.setEnabled(); return transactionPool; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java index 388a112699a..939e3ec0cd6 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java @@ -181,7 +181,8 @@ public void createStorage() { mock(TransactionBroadcaster.class), ethContext, txPoolMetrics, - poolConfiguration); + poolConfiguration, + new BlobCache()); transactionPool.setEnabled(); } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/BlobCache.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/BlobCache.java index 50cc23977c4..3d3a435f1f8 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/BlobCache.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/BlobCache.java @@ -87,4 +87,8 @@ public Optional restoreBlob(final Transaction transaction) { return Optional.empty(); } } + + public BlobsWithCommitments.BlobQuad get(final VersionedHash vh) { + return cache.getIfPresent(vh); + } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java index 6bb2029960a..315f82921bb 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java @@ -20,8 +20,10 @@ import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.TRANSACTION_ALREADY_KNOWN; import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.BlobsWithCommitments; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.TransactionType; +import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.chain.BlockAddedEvent; @@ -55,6 +57,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; +import java.util.HashMap; import java.util.IntSummaryStatistics; import java.util.List; import java.util.Map; @@ -89,6 +92,7 @@ public class TransactionPool implements BlockAddedObserver { private static final Logger LOG = LoggerFactory.getLogger(TransactionPool.class); private static final Logger LOG_FOR_REPLAY = LoggerFactory.getLogger("LOG_FOR_REPLAY"); private final Supplier pendingTransactionsSupplier; + private final BlobCache cacheForBlobsOfTransactionsAddedToABlock; private volatile PendingTransactions pendingTransactions = new DisabledPendingTransactions(); private final ProtocolSchedule protocolSchedule; private final ProtocolContext protocolContext; @@ -103,6 +107,8 @@ public class TransactionPool implements BlockAddedObserver { private final SaveRestoreManager saveRestoreManager = new SaveRestoreManager(); private final Set

localSenders = ConcurrentHashMap.newKeySet(); private final EthScheduler.OrderedProcessor blockAddedEventOrderedProcessor; + private final Map mapOfBlobsInTransactionPool = + new HashMap<>(); public TransactionPool( final Supplier pendingTransactionsSupplier, @@ -111,7 +117,8 @@ public TransactionPool( final TransactionBroadcaster transactionBroadcaster, final EthContext ethContext, final TransactionPoolMetrics metrics, - final TransactionPoolConfiguration configuration) { + final TransactionPoolConfiguration configuration, + final BlobCache blobCache) { this.pendingTransactionsSupplier = pendingTransactionsSupplier; this.protocolSchedule = protocolSchedule; this.protocolContext = protocolContext; @@ -121,7 +128,10 @@ public TransactionPool( this.configuration = configuration; this.blockAddedEventOrderedProcessor = ethContext.getScheduler().createOrderedProcessor(this::processBlockAddedEvent); + this.cacheForBlobsOfTransactionsAddedToABlock = blobCache; initLogForReplay(); + subscribePendingTransactions(this::mapBlobsOnTransactionAdded); + subscribeDroppedTransactions(this::unmapBlobsOnTransactionDropped); } private void initLogForReplay() { @@ -640,6 +650,38 @@ public CompletableFuture setDisabled() { return CompletableFuture.completedFuture(null); } + private void mapBlobsOnTransactionAdded( + final org.hyperledger.besu.datatypes.Transaction transaction) { + final Optional maybeBlobsWithCommitments = + transaction.getBlobsWithCommitments(); + if (maybeBlobsWithCommitments.isEmpty()) { + return; + } + final List blobQuads = + maybeBlobsWithCommitments.get().getBlobQuads(); + blobQuads.forEach(bq -> mapOfBlobsInTransactionPool.put(bq.versionedHash(), bq)); + } + + private void unmapBlobsOnTransactionDropped( + final org.hyperledger.besu.datatypes.Transaction transaction) { + final Optional maybeBlobsWithCommitments = + transaction.getBlobsWithCommitments(); + if (maybeBlobsWithCommitments.isEmpty()) { + return; + } + final List blobQuads = + maybeBlobsWithCommitments.get().getBlobQuads(); + blobQuads.forEach(bq -> mapOfBlobsInTransactionPool.remove(bq.versionedHash())); + } + + public BlobsWithCommitments.BlobQuad getBlobQuad(final VersionedHash vh) { + BlobsWithCommitments.BlobQuad blobQuad = mapOfBlobsInTransactionPool.get(vh); + if (blobQuad == null) { + blobQuad = cacheForBlobsOfTransactionsAddedToABlock.get(vh); + } + return blobQuad; + } + public boolean isEnabled() { return isPoolEnabled.get(); } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java index d32b7bafe8e..79b1298d27d 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java @@ -118,7 +118,8 @@ static TransactionPool createTransactionPool( newPooledTransactionHashesMessageSender), ethContext, metrics, - transactionPoolConfiguration); + transactionPoolConfiguration, + blobCache); final TransactionsMessageHandler transactionsMessageHandler = new TransactionsMessageHandler( diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java index cd780c2dc99..e5ec06fd356 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java @@ -293,7 +293,8 @@ private TransactionPool createTransactionPool( transactionBroadcaster, ethContext, new TransactionPoolMetrics(metricsSystem), - poolConfig); + poolConfig, + new BlobCache()); txPool.setEnabled(); return txPool; }