Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standardise the Engine JSON-RPC error codes #8695

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8ef3a38
Standardise the Engine JSON-RPC error codes
Malaydewangan09 Oct 9, 2024
9969d19
Merge branch 'master' into standardise-json-rpc-error-codes
Malaydewangan09 Oct 10, 2024
6612848
Merge branch 'Consensys:master' into standardise-json-rpc-error-codes
Malaydewangan09 Oct 11, 2024
7514da8
Refactor JsonRpcErrorCodes to enum for improved efficiency and structure
lucassaldanha Oct 11, 2024
abfaed5
Merge branch 'standardise-json-rpc-error-codes' of github.com:Malayde…
Malaydewangan09 Oct 11, 2024
4b8711e
Make method parameters final in JsonRpcErrorCodes
Malaydewangan09 Oct 11, 2024
7e468d5
Optimize JsonRpcErrorCodes lookup using Int2ObjectOpenHashMap
Malaydewangan09 Oct 11, 2024
a9f1d80
Standardise the Engine JSON-RPC error codes
Malaydewangan09 Oct 9, 2024
3bad1b8
Refactor JsonRpcErrorCodes to enum for improved efficiency and structure
lucassaldanha Oct 11, 2024
2ddbb0f
Make method parameters final in JsonRpcErrorCodes
Malaydewangan09 Oct 11, 2024
dc68ccc
Optimize JsonRpcErrorCodes lookup using Int2ObjectOpenHashMap
Malaydewangan09 Oct 11, 2024
d4dd83c
Merge branch 'standardise-json-rpc-error-codes' of github.com:Malayde…
Malaydewangan09 Oct 13, 2024
08a2366
change JsonRpcErrorCodes to package private
Malaydewangan09 Oct 13, 2024
4504c04
Merge branch 'master' into standardise-json-rpc-error-codes
Malaydewangan09 Oct 14, 2024
6aa34bc
change method name for getter
Malaydewangan09 Oct 14, 2024
33ba572
Merge branch 'master' into standardise-json-rpc-error-codes
Malaydewangan09 Oct 14, 2024
ed9d033
Improve JSON-RPC error message formatting
Malaydewangan09 Oct 14, 2024
754f99a
Merge branch 'master' into standardise-json-rpc-error-codes
Malaydewangan09 Oct 16, 2024
598cd71
Improve JSON-RPC error handling test coverage
Malaydewangan09 Oct 16, 2024
ad9ece0
Merge branch 'standardise-json-rpc-error-codes' of github.com:Malayde…
Malaydewangan09 Oct 16, 2024
7eb382a
Merge branch 'master' into standardise-json-rpc-error-codes
tbenr Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/

package tech.pegasys.teku.ethereum.executionclient.web3j;

import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;

enum JsonRpcErrorCodes {
PARSE_ERROR(-32700, "Parse error"),
INVALID_REQUEST(-32600, "Invalid Request"),
METHOD_NOT_FOUND(-32601, "Method not found"),
INVALID_PARAMS(-32602, "Invalid params"),
INTERNAL_ERROR(-32603, "Internal error"),
SERVER_ERROR(-32000, "Server error");

private final int errorCode;
private final String description;
private static final Int2ObjectOpenHashMap<JsonRpcErrorCodes> CODE_TO_ERROR_MAP;

static {
CODE_TO_ERROR_MAP = new Int2ObjectOpenHashMap<>();
for (final JsonRpcErrorCodes error : values()) {
CODE_TO_ERROR_MAP.put(error.getErrorCode(), error);
}
}

JsonRpcErrorCodes(final int errorCode, final String description) {
this.errorCode = errorCode;
this.description = description;
}

public int getErrorCode() {
return errorCode;
}

public String getDescription() {
return description;
}

public static String getDescription(final int errorCode) {
return fromCode(errorCode).getDescription();
}

public static JsonRpcErrorCodes fromCode(final int errorCode) {
final JsonRpcErrorCodes error = CODE_TO_ERROR_MAP.get(errorCode);
if (error != null) {
return error;
}
return errorCode >= -32099 && errorCode <= -32000 ? SERVER_ERROR : INTERNAL_ERROR;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import static tech.pegasys.teku.infrastructure.exceptions.ExceptionUtil.getMessageOrSimpleName;

import java.io.IOException;
import java.net.ConnectException;
import java.time.Duration;
import java.util.Collection;
Expand Down Expand Up @@ -86,21 +85,41 @@ public <T> SafeFuture<Response<T>> doRequest(
(response, exception) -> {
final boolean isCriticalRequest = isCriticalRequest(web3jRequest);
if (exception != null) {
final boolean couldBeAuthError = isAuthenticationException(exception);
handleError(isCriticalRequest, exception, couldBeAuthError);
return Response.withErrorMessage(getMessageOrSimpleName(exception));
return handleException(exception, isCriticalRequest);
} else if (response.hasError()) {
final String errorMessage =
response.getError().getCode() + ": " + response.getError().getMessage();
handleError(isCriticalRequest, new IOException(errorMessage), false);
return Response.withErrorMessage(errorMessage);
return handleJsonRpcError(response.getError(), isCriticalRequest);
} else {
handleSuccess(isCriticalRequest);
return new Response<>(response.getResult());
}
});
}

private <T> Response<T> handleException(
final Throwable exception, final boolean isCriticalRequest) {
final boolean couldBeAuthError = isAuthenticationException(exception);
handleError(isCriticalRequest, exception, couldBeAuthError);
return Response.withErrorMessage(getMessageOrSimpleName(exception));
}

private <T> Response<T> handleJsonRpcError(
final org.web3j.protocol.core.Response.Error error, final boolean isCriticalRequest) {
final int errorCode = error.getCode();
final String errorType = JsonRpcErrorCodes.getDescription(errorCode);
final String formattedError =
String.format("JSON-RPC error: %s (%d): %s", errorType, errorCode, error.getMessage());

if (isCriticalRequest) {
logError(formattedError);
}

return Response.withErrorMessage(formattedError);
}

private void logError(final String errorMessage) {
eventLog.executionClientRequestFailed(new Exception(errorMessage), false);
}

private boolean isCriticalRequest(final Request<?, ?> request) {
return !nonCriticalMethods.contains(request.getMethod());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package tech.pegasys.teku.ethereum.executionclient.web3j;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
Expand Down Expand Up @@ -272,6 +273,40 @@ void shouldNotUpdateAvailabilityWhenNonCriticalMethodFailsWithErrorResponse(
verifyNoInteractions(executionClientEventsPublisher);
}

@ParameterizedTest
@MethodSource("getClientInstances")
void shouldDecodeJsonRpcErrorCodesCorrectly(final ClientFactory clientFactory) throws Exception {
final Web3JClient client = clientFactory.create(eventLog, executionClientEventsPublisher);
Request<Void, VoidResponse> request = createRequest(client);

// Create a response with a specific JSON-RPC error
VoidResponse errorResponse = new VoidResponse();
Error rpcError =
new Error(
JsonRpcErrorCodes.INVALID_PARAMS.getErrorCode(),
"engine_newPayload method has been called with invalid parameters");
errorResponse.setError(rpcError);

when(client.getWeb3jService().sendAsync(request, VoidResponse.class))
.thenReturn(SafeFuture.completedFuture(errorResponse));

final SafeFuture<Response<Void>> result = client.doRequest(request, DEFAULT_TIMEOUT);
Waiter.waitFor(result);

SafeFutureAssert.assertThatSafeFuture(result).isCompleted();
final Response<Void> response = SafeFutureAssert.safeJoin(result);

assertThat(response.getErrorMessage())
.isEqualTo(
String.format(
"JSON-RPC error: %s (%d): %s",
JsonRpcErrorCodes.INVALID_PARAMS.getDescription(),
JsonRpcErrorCodes.INVALID_PARAMS.getErrorCode(),
"engine_newPayload method has been called with invalid parameters"));

verify(eventLog).executionClientRequestFailed(any(Exception.class), eq(false));
}

private static Request<Void, VoidResponse> createRequest(final Web3JClient client) {
return new Request<>("test", new ArrayList<>(), client.getWeb3jService(), VoidResponse.class);
}
Expand Down