implement engine_getBlobsV1 (#7553)

* implement engine_getBlobsV1

Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: Stefan Pingel <16143240+pinges@users.noreply.github.com>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/7586/head
Stefan Pingel 3 months ago committed by GitHub
parent f0d2a665d7
commit cf592c48d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      CHANGELOG.md
  2. 4
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java
  3. 4
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java
  4. 4
      consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java
  5. 4
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java
  6. 4
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java
  7. 4
      consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java
  8. 4
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java
  9. 4
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java
  10. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java
  11. 115
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1.java
  12. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java
  13. 42
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlobAndProofV1.java
  14. 11
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java
  15. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java
  16. 267
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java
  17. 4
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java
  18. 4
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java
  19. 4
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java
  20. 4
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java
  21. 4
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java
  22. 3
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java
  23. 4
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/BlobCache.java
  24. 44
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  25. 3
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java
  26. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java

@ -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

@ -55,6 +55,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
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 @@ public class CliqueBlockCreatorTest {
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(metricsSystem),
conf);
conf,
new BlobCache());
transactionPool.setEnabled();
return transactionPool;
}

@ -45,6 +45,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
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 @@ public class CliqueMinerExecutorTest {
mock(TransactionBroadcaster.class),
cliqueEthContext,
new TransactionPoolMetrics(metricsSystem),
conf);
conf,
new BlobCache());
transactionPool.setEnabled();
return transactionPool;

@ -84,6 +84,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
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 @@ public class TestContextBuilder {
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
new BlobCache());
transactionPool.setEnabled();

@ -47,6 +47,7 @@ import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitV
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 class BftBlockCreatorTest {
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
new BlobCache());
transactionPool.setEnabled();

@ -63,6 +63,7 @@ import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
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 class MergeCoordinatorTest implements MergeGenesisConfigHelper {
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
new BlobCache());
this.transactionPool.setEnabled();

@ -98,6 +98,7 @@ import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
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 @@ public class TestContextBuilder {
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
new BlobCache());
transactionPool.setEnabled();

@ -50,6 +50,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
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 class EthGetFilterChangesIntegrationTest {
batchAddedListener,
ethContext,
new TransactionPoolMetrics(metricsSystem),
TransactionPoolConfiguration.DEFAULT);
TransactionPoolConfiguration.DEFAULT,
new BlobCache());
transactionPool.setEnabled();
final BlockchainQueries blockchainQueries =
new BlockchainQueries(

@ -50,6 +50,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
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 class EthGetFilterChangesIntegrationTest {
batchAddedListener,
ethContext,
new TransactionPoolMetrics(metricsSystem),
TransactionPoolConfiguration.DEFAULT);
TransactionPoolConfiguration.DEFAULT,
new BlobCache());
transactionPool.setEnabled();
final BlockchainQueries blockchainQueries =
new BlockchainQueries(

@ -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"),

@ -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
*
* <p>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.
*
* <p>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]`.
*
* <p>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.
*
* <p>4. Client software **MAY** return an array of all `null` entries if syncing or otherwise
* unable to serve blob pool data.
*
* <p>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<BlobAndProofV1> result = getBlobV1Result(versionedHashes);
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result);
}
private @Nonnull List<BlobAndProofV1> 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());
}
}

@ -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"),

@ -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;
}
}

@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineE
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.methods.engine.EngineQ
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 @@ public class ExecutionEngineJsonRpcMethods extends ApiGroupJsonRpcMethods {
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(

@ -119,7 +119,8 @@ public class JsonRpcMethodsFactory {
ethPeers,
consensusEngineServer,
clientVersion,
commit),
commit,
transactionPool),
new EthJsonRpcMethods(
blockchainQueries,
synchronizer,

@ -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<SignatureAlgorithm> 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<BlobAndProofV1> 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<VersionedHash> 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<BlobAndProofV1> 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<VersionedHash> versionedHashesList =
blobsWithCommitments.getVersionedHashes().stream().toList();
final VersionedHash[] versionedHashes = new VersionedHash[6];
Arrays.fill(versionedHashes, VERSIONED_HASH_ZERO);
final JsonRpcResponse jsonRpcResponse = resp(versionedHashes);
final List<BlobAndProofV1> 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<BlobAndProofV1> 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<BlobAndProofV1> 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();
}
}

@ -63,6 +63,7 @@ import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
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 @@ abstract class AbstractBlockCreatorTest {
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(new NoOpMetricsSystem()),
poolConf);
poolConf,
new BlobCache());
transactionPool.setEnabled();
final MiningParameters miningParameters =

@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.chain.BadBlockManager;
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 @@ public class LegacyFeeMarketBlockTransactionSelectorTest
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
new BlobCache());
transactionPool.setEnabled();
return transactionPool;
}

@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters;
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 @@ public class LondonFeeMarketBlockTransactionSelectorTest
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
new BlobCache());
transactionPool.setEnabled();
return transactionPool;
}

@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters;
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 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
new BlobCache());
transactionPool.setEnabled();
return transactionPool;

@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
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 @@ public class PoWMinerExecutorTest {
mock(TransactionBroadcaster.class),
ethContext,
new TransactionPoolMetrics(new NoOpMetricsSystem()),
poolConf);
poolConf,
new BlobCache());
transactionPool.setEnabled();
return transactionPool;

@ -181,7 +181,8 @@ public abstract class AbstractIsolationTests {
mock(TransactionBroadcaster.class),
ethContext,
txPoolMetrics,
poolConfiguration);
poolConfiguration,
new BlobCache());
transactionPool.setEnabled();
}

@ -87,4 +87,8 @@ public class BlobCache {
return Optional.empty();
}
}
public BlobsWithCommitments.BlobQuad get(final VersionedHash vh) {
return cache.getIfPresent(vh);
}
}

@ -20,8 +20,10 @@ import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason
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.nio.charset.StandardCharsets;
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<PendingTransactions> 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<Address> localSenders = ConcurrentHashMap.newKeySet();
private final EthScheduler.OrderedProcessor<BlockAddedEvent> blockAddedEventOrderedProcessor;
private final Map<VersionedHash, BlobsWithCommitments.BlobQuad> mapOfBlobsInTransactionPool =
new HashMap<>();
public TransactionPool(
final Supplier<PendingTransactions> pendingTransactionsSupplier,
@ -111,7 +117,8 @@ public class TransactionPool implements BlockAddedObserver {
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 class TransactionPool implements BlockAddedObserver {
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 class TransactionPool implements BlockAddedObserver {
return CompletableFuture.completedFuture(null);
}
private void mapBlobsOnTransactionAdded(
final org.hyperledger.besu.datatypes.Transaction transaction) {
final Optional<BlobsWithCommitments> maybeBlobsWithCommitments =
transaction.getBlobsWithCommitments();
if (maybeBlobsWithCommitments.isEmpty()) {
return;
}
final List<BlobsWithCommitments.BlobQuad> blobQuads =
maybeBlobsWithCommitments.get().getBlobQuads();
blobQuads.forEach(bq -> mapOfBlobsInTransactionPool.put(bq.versionedHash(), bq));
}
private void unmapBlobsOnTransactionDropped(
final org.hyperledger.besu.datatypes.Transaction transaction) {
final Optional<BlobsWithCommitments> maybeBlobsWithCommitments =
transaction.getBlobsWithCommitments();
if (maybeBlobsWithCommitments.isEmpty()) {
return;
}
final List<BlobsWithCommitments.BlobQuad> 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();
}

@ -118,7 +118,8 @@ public class TransactionPoolFactory {
newPooledTransactionHashesMessageSender),
ethContext,
metrics,
transactionPoolConfiguration);
transactionPoolConfiguration,
blobCache);
final TransactionsMessageHandler transactionsMessageHandler =
new TransactionsMessageHandler(

@ -293,7 +293,8 @@ public abstract class AbstractTransactionPoolTest {
transactionBroadcaster,
ethContext,
new TransactionPoolMetrics(metricsSystem),
poolConfig);
poolConfig,
new BlobCache());
txPool.setEnabled();
return txPool;
}

Loading…
Cancel
Save