From 5fa91389725a0ea669570075fffc10d8eb0f4d17 Mon Sep 17 00:00:00 2001 From: "stefan.pingel@consensys.net" Date: Mon, 2 Sep 2024 13:26:34 +1000 Subject: [PATCH 1/9] implement engine_getBlobsV1 Signed-off-by: stefan.pingel@consensys.net --- .../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 | 146 +++++++++ .../internal/response/RpcErrorType.java | 1 + .../internal/results/BlobAndProofV1.java | 43 +++ .../ExecutionEngineJsonRpcMethods.java | 11 +- .../methods/JsonRpcMethodsFactory.java | 3 +- .../methods/engine/EngineGetBlobsV1Test.java | 284 ++++++++++++++++++ .../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 | 9 +- .../transactions/TransactionPoolFactory.java | 3 +- .../AbstractTransactionPoolTest.java | 3 +- 25 files changed, 543 insertions(+), 20 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/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 63fa5b3ce55..47df98ade33 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..d8a545a6079 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1.java @@ -0,0 +1,146 @@ +/* + * 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.Transaction; +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.methods.ExecutionEngineJsonRpcMethod; +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.BlobCache; +import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +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 Map blobMap = new HashMap<>(); + private final BlobCache blobCache; + + public EngineGetBlobsV1( + final Vertx vertx, + final ProtocolContext protocolContext, + final EngineCallListener engineCallListener, + final TransactionPool transactionPool) { + super(vertx, protocolContext, engineCallListener); + this.blobCache = transactionPool.getBlobCache(); + transactionPool.subscribePendingTransactions(this::onTransactionAdded); + transactionPool.subscribeDroppedTransactions(this::onTransactionDropped); + } + + @Override + public String getName() { + return "engine_getBlobsV1"; + } + + @Override + public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext) { + final VersionedHash[] versionedHashes = + requestContext.getRequiredParameter(0, VersionedHash[].class); + + if (versionedHashes.length > 128) { + return new JsonRpcErrorResponse( + requestContext.getRequest().getId(), + RpcErrorType.INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST); + } + + final List result = getResult(versionedHashes); + + return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result); + } + + private @Nonnull List getResult(final VersionedHash[] versionedHashes) { + return Arrays.stream(versionedHashes) + .map(this::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()); + } + + private BlobsWithCommitments.BlobQuad getBlobQuad(final VersionedHash vh) { + + BlobsWithCommitments.BlobQuad blobQuad = blobMap.get(vh); + if (blobQuad == null) { + blobQuad = blobCache.get(vh); + } + + return blobQuad; + } + + public void onTransactionAdded(final Transaction transaction) { + final Optional maybeBlobsWithCommitments = + transaction.getBlobsWithCommitments(); + if (maybeBlobsWithCommitments.isEmpty()) { + return; + } + final List blobQuads = + maybeBlobsWithCommitments.get().getBlobQuads(); + blobQuads.forEach(bq -> blobMap.put(bq.versionedHash(), bq)); + } + + public void onTransactionDropped(final Transaction transaction) { + final Optional maybeBlobsWithCommitments = + transaction.getBlobsWithCommitments(); + if (maybeBlobsWithCommitments.isEmpty()) { + return; + } + final List blobQuads = + maybeBlobsWithCommitments.get().getBlobQuads(); + blobQuads.forEach(bq -> blobMap.remove(bq.versionedHash())); + } +} 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..d51f6be5e6d 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)"), 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..a7ffe740604 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlobAndProofV1.java @@ -0,0 +1,43 @@ +/* + * 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; + +/** + * BlobAndProofV1: title: Blob and proof object V1 type: object required: - blob - proof properties: + * blob: title: Blob $ref: '#/components/schemas/bytes' proof: title: proof $ref: + * '#/components/schemas/bytes48' + */ +@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 ca66926656e..d684346dc8e 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.EngineGetPayloadBodiesByHashV1; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadBodiesByRangeV1; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayloadV1; @@ -38,6 +39,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; @@ -57,13 +59,15 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods { private final ProtocolContext protocolContext; private final EthPeers ethPeers; private final Vertx consensusEngineServer; + private final TransactionPool transactionPool; ExecutionEngineJsonRpcMethods( final MiningCoordinator miningCoordinator, final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, final EthPeers ethPeers, - final Vertx consensusEngineServer) { + final Vertx consensusEngineServer, + final TransactionPool transactionPool) { this.mergeCoordinator = Optional.ofNullable(miningCoordinator) .filter(mc -> mc.isCompatibleWithEngineApi()) @@ -72,6 +76,7 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods { this.protocolContext = protocolContext; this.ethPeers = ethPeers; this.consensusEngineServer = consensusEngineServer; + this.transactionPool = transactionPool; } @Override @@ -147,7 +152,9 @@ protected Map create() { new EngineExchangeCapabilities( consensusEngineServer, protocolContext, engineQosTimer), new EnginePreparePayloadDebug( - consensusEngineServer, protocolContext, engineQosTimer, mergeCoordinator.get()))); + consensusEngineServer, protocolContext, engineQosTimer, mergeCoordinator.get()), + 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 924889ef47a..310c5d4bde8 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 @@ -115,7 +115,8 @@ public Map methods( protocolSchedule, protocolContext, ethPeers, - consensusEngineServer), + consensusEngineServer, + 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..cf84cabd96b --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java @@ -0,0 +1,284 @@ +/* + * 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.BlobCache; +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; + @Mock private BlobCache blobCache; + + 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(); + method.onTransactionAdded(blobTransaction); + + final BlobsWithCommitments blobsWithCommitments = + blobTransaction.getBlobsWithCommitments().get(); + List versionedHashesList = + blobsWithCommitments.getVersionedHashes().stream().toList(); + + final JsonRpcResponse jsonRpcResponse = resp(versionedHashesList.toArray(new VersionedHash[0])); + + 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++) { + 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 shouldReturnBlobsAndProofsForKnownVersionedHashesFromCache() { + final Transaction blobTransaction = createBlobTransaction(); + + final BlobsWithCommitments blobsWithCommitments = + blobTransaction.getBlobsWithCommitments().get(); + List versionedHashesList = + blobsWithCommitments.getVersionedHashes().stream().toList(); + + // set the blockCache up to respond with the right data + for (int i = 0; i < versionedHashesList.size(); i++) { + when(blobCache.get(blobTransaction.getVersionedHashes().get().get(i))) + .thenReturn(blobTransaction.getBlobsWithCommitments().get().getBlobQuads().get(i)); + } + + final JsonRpcResponse jsonRpcResponse = resp(versionedHashesList.toArray(new VersionedHash[0])); + + 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++) { + 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(); + method.onTransactionAdded(blobTransaction); + + final BlobsWithCommitments blobsWithCommitments = + blobTransaction.getBlobsWithCommitments().get(); + List versionedHashesList = + blobsWithCommitments.getVersionedHashes().stream().toList(); + + final VersionedHash[] hashesListArray = versionedHashesList.toArray(new VersionedHash[0]); + hashesListArray[1] = VERSIONED_HASH_ZERO; + + final JsonRpcResponse jsonRpcResponse = resp(hashesListArray); + + 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(); + method.onTransactionAdded(blobTransaction); + + final BlobsWithCommitments blobsWithCommitments = + blobTransaction.getBlobsWithCommitments().get(); + 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 shouldRetrunEmptyResponseForEmptyRequest() { + 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 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..86d78eb78cd 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 @@ -89,6 +89,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 blobCache; private volatile PendingTransactions pendingTransactions = new DisabledPendingTransactions(); private final ProtocolSchedule protocolSchedule; private final ProtocolContext protocolContext; @@ -111,7 +112,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,6 +123,7 @@ public TransactionPool( this.configuration = configuration; this.blockAddedEventOrderedProcessor = ethContext.getScheduler().createOrderedProcessor(this::processBlockAddedEvent); + this.blobCache = blobCache; initLogForReplay(); } @@ -167,6 +170,10 @@ void handleConnect(final EthPeer peer) { peer, pendingTransactions.getPendingTransactions()); } + public BlobCache getBlobCache() { + return blobCache; + } + public ValidationResult addTransactionViaApi( final Transaction transaction) { 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; } From d9c272b7e26f26b22b80bd800ed045945cf9465b Mon Sep 17 00:00:00 2001 From: "stefan.pingel@consensys.net" Date: Tue, 3 Sep 2024 13:20:40 +1000 Subject: [PATCH 2/9] fix java doc Signed-off-by: stefan.pingel@consensys.net --- .../api/jsonrpc/internal/results/BlobAndProofV1.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 index a7ffe740604..c8978e00c11 100644 --- 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 @@ -17,9 +17,8 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; /** - * BlobAndProofV1: title: Blob and proof object V1 type: object required: - blob - proof properties: - * blob: title: Blob $ref: '#/components/schemas/bytes' proof: title: proof $ref: - * '#/components/schemas/bytes48' + * 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 { From c4324c3034ac4de76478fcd06d63712dc044cce9 Mon Sep 17 00:00:00 2001 From: Stefan Pingel <16143240+pinges@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:58:29 +1000 Subject: [PATCH 3/9] Update ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java Co-authored-by: Sally MacFarlane Signed-off-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> --- .../jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index c83e6a70923..69b426c0da2 100644 --- 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 @@ -211,7 +211,7 @@ public void shouldReturnOnlyNullsForBlobsAndProofsIfAllVersionedHashesUnknown() } @Test - public void shouldRetrunEmptyResponseForEmptyRequest() { + public void shouldReturnEmptyResponseForEmptyRequest() { final VersionedHash[] versionedHashes = new VersionedHash[0]; final JsonRpcResponse jsonRpcResponse = resp(versionedHashes); From e94486832d9d0448587abbc7ab615987765b87f6 Mon Sep 17 00:00:00 2001 From: "stefan.pingel@consensys.net" Date: Wed, 4 Sep 2024 16:00:56 +1000 Subject: [PATCH 4/9] fix test Signed-off-by: stefan.pingel@consensys.net --- .../internal/methods/engine/EngineGetBlobsV1Test.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 index c83e6a70923..3f1e792cbd3 100644 --- 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 @@ -110,16 +110,16 @@ public void shouldReturnBlobsAndProofsForKnownVersionedHashesFromMap() { final BlobsWithCommitments blobsWithCommitments = blobTransaction.getBlobsWithCommitments().get(); - List versionedHashesList = - blobsWithCommitments.getVersionedHashes().stream().toList(); + VersionedHash[] versionedHashes = + blobsWithCommitments.getVersionedHashes().toArray(new VersionedHash[0]); - final JsonRpcResponse jsonRpcResponse = resp(versionedHashesList.toArray(new VersionedHash[0])); + final JsonRpcResponse jsonRpcResponse = resp(versionedHashes); final List blobAndProofV1s = fromSuccessResp(jsonRpcResponse); - assertThat(blobAndProofV1s.size()).isEqualTo(versionedHashesList.size()); + assertThat(blobAndProofV1s.size()).isEqualTo(versionedHashes.length); // for loop to check each blob and proof - for (int i = 0; i < versionedHashesList.size(); i++) { + 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())) From 300a035825f6f0b3125b38488cd3073c15d6dc89 Mon Sep 17 00:00:00 2001 From: "stefan.pingel@consensys.net" Date: Thu, 5 Sep 2024 13:51:08 +1000 Subject: [PATCH 5/9] move logic into transaction pool Signed-off-by: stefan.pingel@consensys.net --- .../methods/engine/EngineGetBlobsV1.java | 50 ++--------------- .../methods/engine/EngineGetBlobsV1Test.java | 53 +++++++------------ .../eth/transactions/TransactionPool.java | 38 +++++++++++++ 3 files changed, 62 insertions(+), 79 deletions(-) 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 index b4c070083f1..cc9ba2dce2a 100644 --- 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 @@ -15,7 +15,6 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; import org.hyperledger.besu.datatypes.BlobsWithCommitments; -import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; @@ -27,14 +26,10 @@ 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.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -64,8 +59,7 @@ */ public class EngineGetBlobsV1 extends ExecutionEngineJsonRpcMethod { - private final Map blobMap = new HashMap<>(); - private final BlobCache blobCache; + private final TransactionPool transactionPool; public EngineGetBlobsV1( final Vertx vertx, @@ -73,9 +67,7 @@ public EngineGetBlobsV1( final EngineCallListener engineCallListener, final TransactionPool transactionPool) { super(vertx, protocolContext, engineCallListener); - this.blobCache = transactionPool.getBlobCache(); - transactionPool.subscribePendingTransactions(this::onTransactionAdded); - transactionPool.subscribeDroppedTransactions(this::onTransactionDropped); + this.transactionPool = transactionPool; } @Override @@ -101,14 +93,14 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext) RpcErrorType.INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST); } - final List result = getResult(versionedHashes); + final List result = getBlobV1Result(versionedHashes); return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result); } - private @Nonnull List getResult(final VersionedHash[] versionedHashes) { + private @Nonnull List getBlobV1Result(final VersionedHash[] versionedHashes) { return Arrays.stream(versionedHashes) - .map(this::getBlobQuad) + .map(transactionPool::getBlobQuad) .map(this::getBlobAndProofV1) .toList(); } @@ -120,36 +112,4 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext) return new BlobAndProofV1( bq.blob().getData().toHexString(), bq.kzgProof().getData().toHexString()); } - - private BlobsWithCommitments.BlobQuad getBlobQuad(final VersionedHash vh) { - - BlobsWithCommitments.BlobQuad blobQuad = blobMap.get(vh); - if (blobQuad == null) { - blobQuad = blobCache.get(vh); - } - - return blobQuad; - } - - public void onTransactionAdded(final Transaction transaction) { - final Optional maybeBlobsWithCommitments = - transaction.getBlobsWithCommitments(); - if (maybeBlobsWithCommitments.isEmpty()) { - return; - } - final List blobQuads = - maybeBlobsWithCommitments.get().getBlobQuads(); - blobQuads.forEach(bq -> blobMap.put(bq.versionedHash(), bq)); - } - - public void onTransactionDropped(final Transaction transaction) { - final Optional maybeBlobsWithCommitments = - transaction.getBlobsWithCommitments(); - if (maybeBlobsWithCommitments.isEmpty()) { - return; - } - final List blobQuads = - maybeBlobsWithCommitments.get().getBlobQuads(); - blobQuads.forEach(bq -> blobMap.remove(bq.versionedHash())); - } } 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 index 89b04240ef6..bf892b32b58 100644 --- 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 @@ -106,10 +106,12 @@ public void shouldReturnExpectedMethodName() { @Test public void shouldReturnBlobsAndProofsForKnownVersionedHashesFromMap() { final Transaction blobTransaction = createBlobTransaction(); - method.onTransactionAdded(blobTransaction); final BlobsWithCommitments blobsWithCommitments = blobTransaction.getBlobsWithCommitments().get(); + + mockTransactionPoolMethod(blobsWithCommitments); + VersionedHash[] versionedHashes = blobsWithCommitments.getVersionedHashes().toArray(new VersionedHash[0]); @@ -128,48 +130,21 @@ public void shouldReturnBlobsAndProofsForKnownVersionedHashesFromMap() { } @Test - public void shouldReturnBlobsAndProofsForKnownVersionedHashesFromCache() { + public void shouldReturnNullForBlobsAndProofsForUnknownVersionedHashes() { final Transaction blobTransaction = createBlobTransaction(); final BlobsWithCommitments blobsWithCommitments = blobTransaction.getBlobsWithCommitments().get(); - List versionedHashesList = - blobsWithCommitments.getVersionedHashes().stream().toList(); - - // set the blockCache up to respond with the right data - for (int i = 0; i < versionedHashesList.size(); i++) { - when(blobCache.get(blobTransaction.getVersionedHashes().get().get(i))) - .thenReturn(blobTransaction.getBlobsWithCommitments().get().getBlobQuads().get(i)); - } - - final JsonRpcResponse jsonRpcResponse = resp(versionedHashesList.toArray(new VersionedHash[0])); - - 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++) { - 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(); - method.onTransactionAdded(blobTransaction); + mockTransactionPoolMethod(blobsWithCommitments); - final BlobsWithCommitments blobsWithCommitments = - blobTransaction.getBlobsWithCommitments().get(); List versionedHashesList = blobsWithCommitments.getVersionedHashes().stream().toList(); - final VersionedHash[] hashesListArray = versionedHashesList.toArray(new VersionedHash[0]); - hashesListArray[1] = VERSIONED_HASH_ZERO; + final VersionedHash[] hashes = versionedHashesList.toArray(new VersionedHash[0]); + hashes[1] = VERSIONED_HASH_ZERO; - final JsonRpcResponse jsonRpcResponse = resp(hashesListArray); + final JsonRpcResponse jsonRpcResponse = resp(hashes); final List blobAndProofV1s = fromSuccessResp(jsonRpcResponse); @@ -190,10 +165,12 @@ public void shouldReturnNullForBlobsAndProofsForUnknownVersionedHashes() { @Test public void shouldReturnOnlyNullsForBlobsAndProofsIfAllVersionedHashesUnknown() { final Transaction blobTransaction = createBlobTransaction(); - method.onTransactionAdded(blobTransaction); final BlobsWithCommitments blobsWithCommitments = blobTransaction.getBlobsWithCommitments().get(); + + mockTransactionPoolMethod(blobsWithCommitments); + List versionedHashesList = blobsWithCommitments.getVersionedHashes().stream().toList(); @@ -252,6 +229,14 @@ Transaction createBlobTransaction() { 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( 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 86d78eb78cd..1c880b50278 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; @@ -104,6 +107,7 @@ 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 blobMap = new HashMap<>(); public TransactionPool( final Supplier pendingTransactionsSupplier, @@ -125,6 +129,8 @@ public TransactionPool( ethContext.getScheduler().createOrderedProcessor(this::processBlockAddedEvent); this.blobCache = blobCache; initLogForReplay(); + subscribePendingTransactions(this::mapBlobsOnTransactionAdded); + subscribeDroppedTransactions(this::unmapBlobsOnTransactionDropped); } private void initLogForReplay() { @@ -647,6 +653,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 -> blobMap.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 -> blobMap.remove(bq.versionedHash())); + } + + public BlobsWithCommitments.BlobQuad getBlobQuad(final VersionedHash vh) { + BlobsWithCommitments.BlobQuad blobQuad = blobMap.get(vh); + if (blobQuad == null) { + blobQuad = blobCache.get(vh); + } + return blobQuad; + } + public boolean isEnabled() { return isPoolEnabled.get(); } From 110d6bd66d5be2d6d3ef5185b129deda21a9c383 Mon Sep 17 00:00:00 2001 From: "stefan.pingel@consensys.net" Date: Thu, 5 Sep 2024 19:54:22 +1000 Subject: [PATCH 6/9] changes after reviews Signed-off-by: stefan.pingel@consensys.net --- CHANGELOG.md | 1 + .../methods/engine/EngineGetBlobsV1Test.java | 9 ++++----- .../eth/transactions/TransactionPool.java | 19 ++++++++----------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 102a0c58177..f8953c84bfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - Performance optimzation for ECMUL (2 of 2) [#7543](https://github.com/hyperledger/besu/pull/7543) - Include current chain head block when computing `eth_maxPriorityFeePerGas` [#7485](https://github.com/hyperledger/besu/pull/7485) - Remove (old) documentation updates from the changelog [#7562](https://github.com/hyperledger/besu/pull/7562) +- Add `engine_getBlobsV1` method to the Engine API [#7553](https://github.com/hyperledger/besu/pull/7553) ### Bug fixes - Fix tracing in precompiled contracts when halting for out of gas [#7318](https://github.com/hyperledger/besu/issues/7318) 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 index bf892b32b58..3376e651c0f 100644 --- 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 @@ -93,7 +93,6 @@ public class EngineGetBlobsV1Test { @BeforeEach public void before() { when(protocolContext.getBlockchain()).thenReturn(blockchain); - when(transactionPool.getBlobCache()).thenReturn(blobCache); this.method = spy(new EngineGetBlobsV1(vertx, protocolContext, engineCallListener, transactionPool)); } @@ -231,10 +230,10 @@ Transaction createBlobTransaction() { private void mockTransactionPoolMethod(final BlobsWithCommitments blobsWithCommitments) { blobsWithCommitments - .getBlobQuads() - .forEach( - blobQuad -> - when(transactionPool.getBlobQuad(blobQuad.versionedHash())).thenReturn(blobQuad)); + .getBlobQuads() + .forEach( + blobQuad -> + when(transactionPool.getBlobQuad(blobQuad.versionedHash())).thenReturn(blobQuad)); } private JsonRpcResponse resp(final VersionedHash[] versionedHashes) { 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 1c880b50278..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 @@ -92,7 +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 blobCache; + private final BlobCache cacheForBlobsOfTransactionsAddedToABlock; private volatile PendingTransactions pendingTransactions = new DisabledPendingTransactions(); private final ProtocolSchedule protocolSchedule; private final ProtocolContext protocolContext; @@ -107,7 +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 blobMap = new HashMap<>(); + private final Map mapOfBlobsInTransactionPool = + new HashMap<>(); public TransactionPool( final Supplier pendingTransactionsSupplier, @@ -127,7 +128,7 @@ public TransactionPool( this.configuration = configuration; this.blockAddedEventOrderedProcessor = ethContext.getScheduler().createOrderedProcessor(this::processBlockAddedEvent); - this.blobCache = blobCache; + this.cacheForBlobsOfTransactionsAddedToABlock = blobCache; initLogForReplay(); subscribePendingTransactions(this::mapBlobsOnTransactionAdded); subscribeDroppedTransactions(this::unmapBlobsOnTransactionDropped); @@ -176,10 +177,6 @@ void handleConnect(final EthPeer peer) { peer, pendingTransactions.getPendingTransactions()); } - public BlobCache getBlobCache() { - return blobCache; - } - public ValidationResult addTransactionViaApi( final Transaction transaction) { @@ -662,7 +659,7 @@ private void mapBlobsOnTransactionAdded( } final List blobQuads = maybeBlobsWithCommitments.get().getBlobQuads(); - blobQuads.forEach(bq -> blobMap.put(bq.versionedHash(), bq)); + blobQuads.forEach(bq -> mapOfBlobsInTransactionPool.put(bq.versionedHash(), bq)); } private void unmapBlobsOnTransactionDropped( @@ -674,13 +671,13 @@ private void unmapBlobsOnTransactionDropped( } final List blobQuads = maybeBlobsWithCommitments.get().getBlobQuads(); - blobQuads.forEach(bq -> blobMap.remove(bq.versionedHash())); + blobQuads.forEach(bq -> mapOfBlobsInTransactionPool.remove(bq.versionedHash())); } public BlobsWithCommitments.BlobQuad getBlobQuad(final VersionedHash vh) { - BlobsWithCommitments.BlobQuad blobQuad = blobMap.get(vh); + BlobsWithCommitments.BlobQuad blobQuad = mapOfBlobsInTransactionPool.get(vh); if (blobQuad == null) { - blobQuad = blobCache.get(vh); + blobQuad = cacheForBlobsOfTransactionsAddedToABlock.get(vh); } return blobQuad; } From cdec0d19eb39073f98009c95366a67aef933c117 Mon Sep 17 00:00:00 2001 From: "stefan.pingel@consensys.net" Date: Thu, 5 Sep 2024 20:03:05 +1000 Subject: [PATCH 7/9] remove unused mock Signed-off-by: stefan.pingel@consensys.net --- .../jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java | 1 - 1 file changed, 1 deletion(-) 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 index 3376e651c0f..744f7a312ca 100644 --- 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 @@ -84,7 +84,6 @@ public class EngineGetBlobsV1Test { @Mock private EngineCallListener engineCallListener; @Mock private MutableBlockchain blockchain; @Mock private TransactionPool transactionPool; - @Mock private BlobCache blobCache; private EngineGetBlobsV1 method; From bdbd0cc3d94639933fc38780c6038cb2b8fd43c0 Mon Sep 17 00:00:00 2001 From: "stefan.pingel@consensys.net" Date: Thu, 5 Sep 2024 20:09:30 +1000 Subject: [PATCH 8/9] spotless Signed-off-by: stefan.pingel@consensys.net --- .../jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java | 1 - 1 file changed, 1 deletion(-) 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 index 744f7a312ca..29c7ae82ed5 100644 --- 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 @@ -41,7 +41,6 @@ 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.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; From a066ed78ca16e943be0a19dc45dfd9682ba9ed5e Mon Sep 17 00:00:00 2001 From: "stefan.pingel@consensys.net" Date: Fri, 6 Sep 2024 09:34:32 +1000 Subject: [PATCH 9/9] move chnagelog entry Signed-off-by: stefan.pingel@consensys.net --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8953c84bfc..20a201f2fb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Breaking Changes ### Additions and Improvements +- Add `engine_getBlobsV1` method to the Engine API [#7553](https://github.com/hyperledger/besu/pull/7553) ### Bug fixes @@ -26,7 +27,6 @@ - Performance optimzation for ECMUL (2 of 2) [#7543](https://github.com/hyperledger/besu/pull/7543) - Include current chain head block when computing `eth_maxPriorityFeePerGas` [#7485](https://github.com/hyperledger/besu/pull/7485) - Remove (old) documentation updates from the changelog [#7562](https://github.com/hyperledger/besu/pull/7562) -- Add `engine_getBlobsV1` method to the Engine API [#7553](https://github.com/hyperledger/besu/pull/7553) ### Bug fixes - Fix tracing in precompiled contracts when halting for out of gas [#7318](https://github.com/hyperledger/besu/issues/7318)