Skip to content

Commit cc89c2a

Browse files
fab-10matthew1001
authored andcommitted
Extend error handling of plugin RPC methods (besu-eth#6759)
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
1 parent 9f41938 commit cc89c2a

File tree

17 files changed

+369
-65
lines changed

17 files changed

+369
-65
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- Introduce `TransactionSimulationService` [#6686](https://github.com/hyperledger/besu/pull/6686)
2424
- Transaction call object to accept both `input` and `data` field simultaneously if they are set to equal values [#6702](https://github.com/hyperledger/besu/pull/6702)
2525
- `eth_call` for blob tx allows for empty `maxFeePerBlobGas` [#6731](https://github.com/hyperledger/besu/pull/6731)
26+
- Extend error handling of plugin RPC methods [#6759](https://github.com/hyperledger/besu/pull/6759)
2627

2728
### Bug fixes
2829
- Fix txpool dump/restore race condition [#6665](https://github.com/hyperledger/besu/pull/6665)

besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
import org.hyperledger.besu.ethereum.chain.Blockchain;
2020
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
2121
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
22+
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
23+
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
2224
import org.hyperledger.besu.ethereum.transaction.CallParameter;
25+
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
2326
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
2427
import org.hyperledger.besu.evm.tracing.OperationTracer;
2528
import org.hyperledger.besu.plugin.Unstable;
@@ -62,14 +65,16 @@ public Optional<TransactionSimulationResult> simulate(
6265

6366
final CallParameter callParameter = CallParameter.fromTransaction(transaction);
6467

65-
final var blockHeader =
66-
blockchain
67-
.getBlockHeader(blockHash)
68-
.or(() -> blockchain.getBlockHeaderSafe(blockHash))
69-
.orElseThrow(
70-
() ->
71-
new IllegalStateException(
72-
"Block header not yet present for chain head hash: " + blockHash));
68+
final var maybeBlockHeader =
69+
blockchain.getBlockHeader(blockHash).or(() -> blockchain.getBlockHeaderSafe(blockHash));
70+
71+
if (maybeBlockHeader.isEmpty()) {
72+
return Optional.of(
73+
new TransactionSimulationResult(
74+
transaction,
75+
TransactionProcessingResult.invalid(
76+
ValidationResult.invalid(TransactionInvalidReason.BLOCK_NOT_FOUND))));
77+
}
7378

7479
return transactionSimulator
7580
.process(
@@ -78,7 +83,7 @@ public Optional<TransactionSimulationResult> simulate(
7883
? SIMULATOR_ALLOWING_EXCEEDING_BALANCE
7984
: TransactionValidationParams.transactionSimulator(),
8085
operationTracer,
81-
blockHeader)
86+
maybeBlockHeader.get())
8287
.map(res -> new TransactionSimulationResult(transaction, res.result()));
8388
}
8489
}

ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthEstimateGasIntegrationTest.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
2929
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
3030
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
31-
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
31+
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
32+
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
3233
import org.hyperledger.besu.testutil.BlockTestUtil;
3334

3435
import java.util.Map;
@@ -171,11 +172,11 @@ public void shouldNotIgnoreSenderBalanceAccountWhenStrictModeDisabledAndThrowErr
171172
null,
172173
null);
173174
final JsonRpcRequestContext request = requestWithParams(callParameter);
174-
175-
final RpcErrorType rpcErrorType = RpcErrorType.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE;
176-
final JsonRpcError rpcError = new JsonRpcError(rpcErrorType);
177-
rpcError.setReason(
178-
"transaction up-front cost 0x1cc31b3333167018 exceeds transaction sender account balance 0x140");
175+
final ValidationResult<TransactionInvalidReason> validationResult =
176+
ValidationResult.invalid(
177+
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE,
178+
"transaction up-front cost 0x1cc31b3333167018 exceeds transaction sender account balance 0x140");
179+
final JsonRpcError rpcError = JsonRpcError.from(validationResult);
179180
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, rpcError);
180181

181182
final JsonRpcResponse response = method.response(request);

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ public static RpcErrorType convertTransactionInvalidReason(
8383
return RpcErrorType.BLOB_GAS_PRICE_BELOW_CURRENT_BLOB_BASE_FEE;
8484
case EXECUTION_HALTED:
8585
return RpcErrorType.EXECUTION_HALTED;
86+
case BLOCK_NOT_FOUND:
87+
return RpcErrorType.BLOCK_NOT_FOUND;
8688
default:
8789
return RpcErrorType.INTERNAL_ERROR;
8890
}

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,7 @@ protected JsonRpcErrorResponse errorResponse(
111111
result.getValidationResult();
112112
if (validationResult != null && !validationResult.isValid()) {
113113
if (validationResult.getErrorMessage().length() > 0) {
114-
final RpcErrorType rpcErrorType =
115-
JsonRpcErrorConverter.convertTransactionInvalidReason(
116-
validationResult.getInvalidReason());
117-
final JsonRpcError rpcError = new JsonRpcError(rpcErrorType);
118-
rpcError.setReason(validationResult.getErrorMessage());
119-
return errorResponse(request, rpcError);
114+
return errorResponse(request, JsonRpcError.from(validationResult));
120115
}
121116
return errorResponse(
122117
request,

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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@
1414
*/
1515
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
1616

17-
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INTERNAL_ERROR;
18-
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.PLUGIN_INTERNAL_ERROR;
19-
2017
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
2118
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
2219
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
@@ -53,13 +50,9 @@ public JsonRpcResponse response(final JsonRpcRequestContext request) {
5350
final Object result = function.apply(() -> request.getRequest().getParams());
5451
return new JsonRpcSuccessResponse(request.getRequest().getId(), result);
5552
} catch (final PluginRpcEndpointException ex) {
56-
final JsonRpcError error = new JsonRpcError(PLUGIN_INTERNAL_ERROR, ex.getMessage());
53+
final JsonRpcError error = new JsonRpcError(ex.getRpcMethodError(), ex.getMessage());
5754
LOG.error("Error calling plugin JSON-RPC endpoint", ex);
5855
return new JsonRpcErrorResponse(request.getRequest().getId(), error);
59-
} catch (final Exception ex) {
60-
LOG.error("Error calling plugin JSON-RPC endpoint", ex);
61-
return new JsonRpcErrorResponse(
62-
request.getRequest().getId(), new JsonRpcError(INTERNAL_ERROR));
6356
}
6457
}
6558
}

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

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,18 @@
1414
*/
1515
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.response;
1616

17+
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter;
18+
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
19+
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
20+
import org.hyperledger.besu.plugin.services.rpc.RpcMethodError;
21+
1722
import java.util.Objects;
1823

1924
import com.fasterxml.jackson.annotation.JsonCreator;
2025
import com.fasterxml.jackson.annotation.JsonFormat;
2126
import com.fasterxml.jackson.annotation.JsonGetter;
2227
import com.fasterxml.jackson.annotation.JsonInclude;
2328
import com.fasterxml.jackson.annotation.JsonProperty;
24-
import org.apache.tuweni.bytes.Bytes;
2529

2630
@JsonInclude(value = JsonInclude.Include.NON_NULL)
2731
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@@ -41,23 +45,28 @@ public JsonRpcError(
4145
this.data = data;
4246
}
4347

44-
public JsonRpcError(final RpcErrorType errorType, final String data) {
48+
public JsonRpcError(final RpcMethodError errorType, final String data) {
4549
this(errorType.getCode(), errorType.getMessage(), data);
4650

47-
// For execution reverted errors decode the data (if present)
48-
if (errorType == RpcErrorType.REVERT_ERROR && data != null) {
49-
JsonRpcErrorResponse.decodeRevertReason(Bytes.fromHexString(data))
50-
.ifPresent(
51-
(decodedReason) -> {
52-
this.reason = decodedReason;
53-
});
51+
if (data != null) {
52+
errorType.decodeData(data).ifPresent(decodedData -> this.reason = decodedData);
5453
}
5554
}
5655

5756
public JsonRpcError(final RpcErrorType errorType) {
5857
this(errorType, null);
5958
}
6059

60+
public static JsonRpcError from(
61+
final ValidationResult<TransactionInvalidReason> validationResult) {
62+
final var jsonRpcError =
63+
new JsonRpcError(
64+
JsonRpcErrorConverter.convertTransactionInvalidReason(
65+
validationResult.getInvalidReason()));
66+
jsonRpcError.reason = validationResult.getErrorMessage();
67+
return jsonRpcError;
68+
}
69+
6170
@JsonGetter("code")
6271
public int getCode() {
6372
return code;
@@ -73,10 +82,6 @@ public String getData() {
7382
return data;
7483
}
7584

76-
public void setReason(final String reason) {
77-
this.reason = reason;
78-
}
79-
8085
@Override
8186
public boolean equals(final Object o) {
8287
if (this == o) {

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@
1414
*/
1515
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.response;
1616

17-
public enum RpcErrorType {
17+
import org.hyperledger.besu.plugin.services.rpc.RpcMethodError;
18+
19+
import java.util.Optional;
20+
import java.util.function.Function;
21+
22+
import org.apache.tuweni.bytes.Bytes;
23+
24+
public enum RpcErrorType implements RpcMethodError {
1825
// Standard errors
1926
PARSE_ERROR(-32700, "Parse error"),
2027
INVALID_REQUEST(-32600, "Invalid Request"),
@@ -67,7 +74,10 @@ public enum RpcErrorType {
6774
REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED(-32000, "ChainId not supported"),
6875
REPLAY_PROTECTED_SIGNATURE_REQUIRED(-32000, "ChainId is required"),
6976
TX_FEECAP_EXCEEDED(-32000, "Transaction fee cap exceeded"),
70-
REVERT_ERROR(-32000, "Execution reverted"),
77+
REVERT_ERROR(
78+
-32000,
79+
"Execution reverted",
80+
data -> JsonRpcErrorResponse.decodeRevertReason(Bytes.fromHexString(data))),
7181
TRANSACTION_NOT_FOUND(-32000, "Transaction not found"),
7282
MAX_PRIORITY_FEE_PER_GAS_EXCEEDS_MAX_FEE_PER_GAS(
7383
-32000, "Max priority fee per gas exceeds max fee per gas"),
@@ -222,17 +232,31 @@ public enum RpcErrorType {
222232

223233
private final int code;
224234
private final String message;
235+
private final Function<String, Optional<String>> dataDecoder;
225236

226237
RpcErrorType(final int code, final String message) {
238+
this(code, message, null);
239+
}
240+
241+
RpcErrorType(
242+
final int code, final String message, Function<String, Optional<String>> dataDecoder) {
227243
this.code = code;
228244
this.message = message;
245+
this.dataDecoder = dataDecoder;
229246
}
230247

248+
@Override
231249
public int getCode() {
232250
return code;
233251
}
234252

253+
@Override
235254
public String getMessage() {
236255
return message;
237256
}
257+
258+
@Override
259+
public Optional<String> decodeData(final String data) {
260+
return dataDecoder == null ? Optional.empty() : dataDecoder.apply(data);
261+
}
238262
}

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTestBase.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,7 @@ public static void initServerAndClient() throws Exception {
152152
baseUrl = service.url();
153153
}
154154

155-
protected static JsonRpcHttpService createJsonRpcHttpService(final JsonRpcConfiguration config)
156-
throws Exception {
155+
protected static JsonRpcHttpService createJsonRpcHttpService(final JsonRpcConfiguration config) {
157156
return new JsonRpcHttpService(
158157
vertx,
159158
folder,
@@ -165,7 +164,7 @@ protected static JsonRpcHttpService createJsonRpcHttpService(final JsonRpcConfig
165164
HealthService.ALWAYS_HEALTHY);
166165
}
167166

168-
protected static JsonRpcHttpService createJsonRpcHttpService() throws Exception {
167+
protected static JsonRpcHttpService createJsonRpcHttpService() {
169168
return new JsonRpcHttpService(
170169
vertx,
171170
folder,

0 commit comments

Comments
 (0)