Skip to content

Commit ced161d

Browse files
Do not convert v1 to v2, ensure partial results and add tests (#8924)
Signed-off-by: Gabriel-Trintinalia <[email protected]>
1 parent 0b625f3 commit ced161d

File tree

5 files changed

+193
-32
lines changed

5 files changed

+193
-32
lines changed

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
9292
if (versionedHashes.length > 128) {
9393
return new JsonRpcErrorResponse(
9494
requestContext.getRequest().getId(),
95-
RpcErrorType.INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST);
95+
RpcErrorType.INVALID_ENGINE_GET_BLOBS_TOO_LARGE_REQUEST);
9696
}
9797

9898
final List<BlobAndProofV1> result = getBlobV1Result(versionedHashes);

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV2.java

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,10 @@
2828
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
2929
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlobAndProofV2;
3030
import org.hyperledger.besu.ethereum.core.kzg.BlobProofBundle;
31-
import org.hyperledger.besu.ethereum.core.kzg.CKZG4844Helper;
3231
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
3332

34-
import java.util.Arrays;
3533
import java.util.List;
36-
import javax.annotation.Nonnull;
37-
import javax.annotation.Nullable;
34+
import java.util.stream.Stream;
3835

3936
import io.vertx.core.Vertx;
4037
import org.slf4j.Logger;
@@ -66,9 +63,10 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
6663
if (versionedHashes.length > REQUEST_MAX_VERSIONED_HASHES) {
6764
return new JsonRpcErrorResponse(
6865
requestContext.getRequest().getId(),
69-
RpcErrorType.INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST);
66+
RpcErrorType.INVALID_ENGINE_GET_BLOBS_TOO_LARGE_REQUEST);
7067
}
71-
final List<BlobAndProofV2> result = getBlobV2Result(versionedHashes);
68+
final List<BlobAndProofV2> result =
69+
Stream.of(versionedHashes).map(this::getBlobAndProofOrNull).toList();
7270
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result);
7371
}
7472

@@ -83,31 +81,17 @@ private VersionedHash[] extractVersionedHashes(final JsonRpcRequestContext reque
8381
}
8482
}
8583

86-
private @Nonnull List<BlobAndProofV2> getBlobV2Result(final VersionedHash[] versionedHashes) {
87-
return Arrays.stream(versionedHashes)
88-
.map(transactionPool::getBlobProofBundle)
89-
.map(this::getBlobAndProofV2)
90-
.toList();
91-
}
92-
93-
private @Nullable BlobAndProofV2 getBlobAndProofV2(final BlobProofBundle blobProofBundle) {
94-
if (blobProofBundle == null) {
84+
private BlobAndProofV2 getBlobAndProofOrNull(final VersionedHash versionedHash) {
85+
final BlobProofBundle bundle = transactionPool.getBlobProofBundle(versionedHash);
86+
if (bundle == null) {
87+
LOG.trace("No BlobProofBundle found for versioned hash: {}", versionedHash);
9588
return null;
9689
}
97-
BlobProofBundle proofBundle = processBundle(blobProofBundle);
98-
return createBlobAndProofV2(proofBundle);
99-
}
100-
101-
private BlobProofBundle processBundle(final BlobProofBundle blobProofBundle) {
102-
// This may occur during fork transitions when the pool contains outdated blob types.
103-
// It should not happen once the pool is refreshed with new transactions.
104-
if (blobProofBundle.getBlobType() == BlobType.KZG_PROOF) {
105-
LOG.warn(
106-
"BlobProofBundle {} with KZG_PROOF type found, converting to KZG_CELL_PROOFS type.",
107-
blobProofBundle.getVersionedHash());
108-
return CKZG4844Helper.unsafeConvertToVersion1(blobProofBundle);
90+
if (bundle.getBlobType() == BlobType.KZG_PROOF) {
91+
LOG.trace("Unsupported blob type KZG_PROOF for versioned hash: {}", versionedHash);
92+
return null;
10993
}
110-
return blobProofBundle;
94+
return createBlobAndProofV2(bundle);
11195
}
11296

11397
private BlobAndProofV2 createBlobAndProofV2(final BlobProofBundle blobProofBundle) {

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public enum RpcErrorType implements RpcMethodError {
5656
INVALID_ENGINE_NEW_PAYLOAD_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid engine payload parameter"),
5757
INVALID_ENGINE_PREPARE_PAYLOAD_PARAMS(
5858
INVALID_PARAMS_ERROR_CODE, "Invalid engine prepare payload parameter"),
59-
INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST(-38004, "Too large request"),
59+
INVALID_ENGINE_GET_BLOBS_TOO_LARGE_REQUEST(-38004, "Too large request"),
6060
INVALID_ENODE_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid enode params"),
6161
INVALID_EXCESS_BLOB_GAS_PARAMS(
6262
INVALID_PARAMS_ERROR_CODE, "Invalid excess blob gas params (missing or invalid)"),

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetBlobsV1Test.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,9 @@ public void shouldFailWhenRequestingMoreThan128() {
215215
final JsonRpcResponse jsonRpcResponse = resp(versionedHashes);
216216

217217
assertThat(fromErrorResp(jsonRpcResponse).getCode())
218-
.isEqualTo(RpcErrorType.INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST.getCode());
218+
.isEqualTo(RpcErrorType.INVALID_ENGINE_GET_BLOBS_TOO_LARGE_REQUEST.getCode());
219219
assertThat(fromErrorResp(jsonRpcResponse).getMessage())
220-
.isEqualTo(RpcErrorType.INVALID_ENGINE_GET_BLOBS_V1_TOO_LARGE_REQUEST.getMessage());
220+
.isEqualTo(RpcErrorType.INVALID_ENGINE_GET_BLOBS_TOO_LARGE_REQUEST.getMessage());
221221
}
222222

223223
Transaction createBlobTransaction() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright contributors to Besu.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.hyperledger.besu.datatypes.BlobType.KZG_CELL_PROOFS;
19+
import static org.hyperledger.besu.datatypes.BlobType.KZG_PROOF;
20+
import static org.mockito.ArgumentMatchers.eq;
21+
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.when;
23+
24+
import org.hyperledger.besu.datatypes.Hash;
25+
import org.hyperledger.besu.datatypes.VersionedHash;
26+
import org.hyperledger.besu.ethereum.ProtocolContext;
27+
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
28+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
29+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
30+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
31+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
32+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
33+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
34+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
35+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlobAndProofV2;
36+
import org.hyperledger.besu.ethereum.core.BlobTestFixture;
37+
import org.hyperledger.besu.ethereum.core.kzg.BlobProofBundle;
38+
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
39+
import org.hyperledger.besu.ethereum.util.TrustedSetupClassLoaderExtension;
40+
41+
import java.util.Arrays;
42+
import java.util.List;
43+
44+
import io.vertx.core.Vertx;
45+
import org.junit.jupiter.api.BeforeEach;
46+
import org.junit.jupiter.api.Test;
47+
48+
public class EngineGetBlobsV2Test extends TrustedSetupClassLoaderExtension {
49+
50+
private TransactionPool transactionPool;
51+
private EngineGetBlobsV2 method;
52+
53+
@BeforeEach
54+
public void setup() {
55+
transactionPool = mock(TransactionPool.class);
56+
ProtocolContext protocolContext = mock(ProtocolContext.class);
57+
method =
58+
new EngineGetBlobsV2(
59+
mock(Vertx.class), protocolContext, mock(EngineCallListener.class), transactionPool);
60+
}
61+
62+
@Test
63+
public void shouldReturnMethodName() {
64+
assertThat(method.getName()).isEqualTo(RpcMethod.ENGINE_GET_BLOBS_V2.getMethodName());
65+
}
66+
67+
@Test
68+
public void shouldReturnValidBlobs() {
69+
BlobProofBundle bundle = createBundleAndRegisterToPool();
70+
JsonRpcSuccessResponse response =
71+
getSuccessResponse(buildRequestContext(bundle.getVersionedHash()));
72+
assertSingleValidBlob(response, bundle);
73+
}
74+
75+
@Test
76+
public void shouldReturnNullForUnknownHash() {
77+
VersionedHash unknown = new VersionedHash((byte) 1, Hash.ZERO);
78+
JsonRpcSuccessResponse response = getSuccessResponse(buildRequestContext(unknown));
79+
List<BlobAndProofV2> result = extractResult(response);
80+
assertThat(result).hasSize(1);
81+
assertThat(result.getFirst()).isNull();
82+
}
83+
84+
@Test
85+
public void shouldReturnPartialResults() {
86+
BlobProofBundle bundle = createBundleAndRegisterToPool();
87+
VersionedHash known = bundle.getVersionedHash();
88+
VersionedHash unknown = new VersionedHash((byte) 1, Hash.ZERO);
89+
90+
JsonRpcSuccessResponse response =
91+
getSuccessResponse(buildRequestContext(known, unknown, known));
92+
List<BlobAndProofV2> result = extractResult(response);
93+
94+
assertThat(result).hasSize(3);
95+
assertThat(result.get(0)).isNotNull();
96+
assertThat(result.get(1)).isNull();
97+
assertThat(result.get(2)).isNotNull();
98+
}
99+
100+
@Test
101+
public void shouldReturnNullForBlobProofBundleWithV1BlobType() {
102+
BlobTestFixture blobFixture = new BlobTestFixture();
103+
BlobProofBundle v1Bundle = blobFixture.createBlobProofBundle(KZG_PROOF);
104+
VersionedHash versionedHash = v1Bundle.getVersionedHash();
105+
when(transactionPool.getBlobProofBundle(versionedHash)).thenReturn(v1Bundle);
106+
107+
JsonRpcRequestContext requestContext = buildRequestContext(versionedHash);
108+
JsonRpcSuccessResponse response = getSuccessResponse(requestContext);
109+
List<BlobAndProofV2> result = extractResult(response);
110+
assertThat(result).hasSize(1);
111+
assertThat(result.getFirst()).isNull();
112+
}
113+
114+
@Test
115+
public void shouldReturnEmptyListWhenNoHashesGiven() {
116+
JsonRpcSuccessResponse response = getSuccessResponse(buildRequestContext());
117+
List<BlobAndProofV2> result = extractResult(response);
118+
assertThat(result).isEmpty();
119+
}
120+
121+
@Test
122+
public void shouldReturnErrorWhenTooManyHashesGiven() {
123+
VersionedHash[] hashes = new VersionedHash[129];
124+
Arrays.fill(hashes, new VersionedHash((byte) 1, Hash.ZERO));
125+
JsonRpcRequestContext context = buildRequestContext(hashes);
126+
JsonRpcResponse response = method.syncResponse(context);
127+
128+
assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
129+
JsonRpcErrorResponse error = (JsonRpcErrorResponse) response;
130+
assertThat(error.getError().getCode())
131+
.isEqualTo(RpcErrorType.INVALID_ENGINE_GET_BLOBS_TOO_LARGE_REQUEST.getCode());
132+
}
133+
134+
private BlobProofBundle createBundleAndRegisterToPool() {
135+
BlobTestFixture blobFixture = new BlobTestFixture();
136+
BlobProofBundle bundle = blobFixture.createBlobProofBundle(KZG_CELL_PROOFS);
137+
when(transactionPool.getBlobProofBundle(bundle.getVersionedHash())).thenReturn(bundle);
138+
return bundle;
139+
}
140+
141+
private JsonRpcRequestContext buildRequestContext(final VersionedHash... hashes) {
142+
JsonRpcRequestContext context = mock(JsonRpcRequestContext.class);
143+
try {
144+
when(context.getRequiredParameter(eq(0), eq(VersionedHash[].class))).thenReturn(hashes);
145+
} catch (JsonRpcParameter.JsonRpcParameterException e) {
146+
throw new RuntimeException(e);
147+
}
148+
when(context.getRequest())
149+
.thenReturn(new JsonRpcRequest("2.0", "engine_getBlobsV2", new Object[] {}));
150+
return context;
151+
}
152+
153+
private JsonRpcSuccessResponse getSuccessResponse(final JsonRpcRequestContext context) {
154+
JsonRpcResponse response = method.syncResponse(context);
155+
assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class);
156+
return (JsonRpcSuccessResponse) response;
157+
}
158+
159+
@SuppressWarnings("unchecked")
160+
private List<BlobAndProofV2> extractResult(final JsonRpcSuccessResponse response) {
161+
return (List<BlobAndProofV2>) response.getResult();
162+
}
163+
164+
private void assertSingleValidBlob(
165+
final JsonRpcSuccessResponse response, final BlobProofBundle bundle) {
166+
List<BlobAndProofV2> result = extractResult(response);
167+
assertThat(result).hasSize(1);
168+
BlobAndProofV2 blob = result.getFirst();
169+
assertThat(blob).isNotNull();
170+
assertThat(blob.getBlob()).isEqualTo(bundle.getBlob().getData().toHexString());
171+
172+
List<String> expectedProofs =
173+
bundle.getKzgProof().stream().map(p -> p.getData().toHexString()).toList();
174+
175+
assertThat(blob.getProofs()).containsExactlyElementsOf(expectedProofs);
176+
}
177+
}

0 commit comments

Comments
 (0)