Make RPC reason settable, pass execution failure reason in RPC error message (#6343)

* Make RPC reason settable, pass execution failure reason in RPC error message

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Update unit tests

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Update tests

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Update change log

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Update integration tests

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

---------

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
pull/6359/head
Matt Whitehead 11 months ago committed by GitHub
parent 2f1922851b
commit aea171cf64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 9
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthEstimateGasIntegrationTest.java
  3. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java
  4. 8
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractEstimateGas.java
  5. 4
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java
  6. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java
  7. 50
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java
  8. 4
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth/eth_estimateGas_invalid.json
  9. 7
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java
  10. 1
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java

@ -3,6 +3,7 @@
## 24.1.0-SNAPSHOT
### Breaking Changes
- New `EXECUTION_HALTED` error returned if there is an error executing or simulating a transaction, with the reason for execution being halted. Replaces the generic `INTERNAL_ERROR` return code in certain cases which some applications may be checking for [#6343](https://github.com/hyperledger/besu/pull/6343)
### Deprecations
- Forest pruning (`pruning-enabled` options) is deprecated and will be removed soon. To save disk space consider switching to Bonsai data storage format [#6230](https://github.com/hyperledger/besu/pull/6230)

@ -24,6 +24,7 @@ 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.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
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;
@ -161,8 +162,12 @@ public class EthEstimateGasIntegrationTest {
true,
null);
final JsonRpcRequestContext request = requestWithParams(callParameter);
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE);
final RpcErrorType rpcErrorType = RpcErrorType.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE;
final JsonRpcError rpcError = new JsonRpcError(rpcErrorType);
rpcError.setReason(
"transaction up-front cost 0x1cc31b3333167018 exceeds transaction sender account balance 0x140");
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, rpcError);
final JsonRpcResponse response = method.response(request);
assertThat(response).usingRecursiveComparison().isEqualTo(expectedResponse);

@ -79,6 +79,8 @@ public class JsonRpcErrorConverter {
return RpcErrorType.PLUGIN_TX_VALIDATOR;
case INVALID_BLOBS:
return RpcErrorType.INVALID_BLOBS;
case EXECUTION_HALTED:
return RpcErrorType.EXECUTION_HALTED;
default:
return RpcErrorType.INTERNAL_ERROR;
}

@ -101,6 +101,14 @@ public abstract class AbstractEstimateGas implements JsonRpcMethod {
final ValidationResult<TransactionInvalidReason> validationResult =
result.getValidationResult();
if (validationResult != null && !validationResult.isValid()) {
if (validationResult.getErrorMessage().length() > 0) {
final RpcErrorType rpcErrorType =
JsonRpcErrorConverter.convertTransactionInvalidReason(
validationResult.getInvalidReason());
final JsonRpcError rpcError = new JsonRpcError(rpcErrorType);
rpcError.setReason(validationResult.getErrorMessage());
return errorResponse(request, rpcError);
}
return errorResponse(
request,
JsonRpcErrorConverter.convertTransactionInvalidReason(

@ -73,6 +73,10 @@ public class JsonRpcError {
return data;
}
public void setReason(final String reason) {
this.reason = reason;
}
@Override
public boolean equals(final Object o) {
if (this == o) {

@ -75,6 +75,7 @@ public enum RpcErrorType {
-32000, "An invalid transaction with a lower nonce exists"),
TOTAL_BLOB_GAS_TOO_HIGH(-32000, "Total blob gas too high"),
PLUGIN_TX_VALIDATOR(-32000, "Plugin has marked the transaction as invalid"),
EXECUTION_HALTED(-32000, "Transaction processing could not be completed due to an exception"),
// Execution engine failures
UNKNOWN_PAYLOAD(-32001, "Payload does not exist / is not available"),

@ -209,10 +209,13 @@ public class EthEstimateGasTest {
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
mockTransientProcessorResultTxInvalidReason(
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE);
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE,
"transaction up-front cost 10 exceeds transaction sender account balance 5");
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE);
final RpcErrorType rpcErrorType = RpcErrorType.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE;
final JsonRpcError rpcError = new JsonRpcError(rpcErrorType);
rpcError.setReason("transaction up-front cost 10 exceeds transaction sender account balance 5");
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, rpcError);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
@ -223,10 +226,13 @@ public class EthEstimateGasTest {
public void shouldReturnErrorWhenEip1559TransactionProcessorReturnsTxInvalidReason() {
final JsonRpcRequestContext request = ethEstimateGasRequest(eip1559TransactionCallParameter());
mockTransientProcessorResultTxInvalidReason(
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE);
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE,
"transaction up-front cost 10 exceeds transaction sender account balance 5");
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE);
final RpcErrorType rpcErrorType = RpcErrorType.TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE;
final JsonRpcError rpcError = new JsonRpcError(rpcErrorType);
rpcError.setReason("transaction up-front cost 10 exceeds transaction sender account balance 5");
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, rpcError);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
@ -243,9 +249,9 @@ public class EthEstimateGasTest {
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(null, RpcErrorType.WORLD_STATE_UNAVAILABLE);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
JsonRpcResponse theResponse = method.response(request);
Assertions.assertThat(theResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
@ -364,10 +370,32 @@ public class EthEstimateGasTest {
eq(1L));
}
private void mockTransientProcessorResultTxInvalidReason(final TransactionInvalidReason reason) {
@Test
public void shouldIncludeHaltReasonWhenExecutionHalts() {
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
mockTransientProcessorResultTxInvalidReason(
TransactionInvalidReason.EXECUTION_HALTED, "INVALID_OPERATION");
final RpcErrorType rpcErrorType = RpcErrorType.EXECUTION_HALTED;
final JsonRpcError rpcError = new JsonRpcError(rpcErrorType);
rpcError.setReason("INVALID_OPERATION");
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, rpcError);
Assertions.assertThat(method.response(request))
.usingRecursiveComparison()
.isEqualTo(expectedResponse);
}
private void mockTransientProcessorResultTxInvalidReason(
final TransactionInvalidReason reason, final String validationFailedErrorMessage) {
final TransactionSimulatorResult mockTxSimResult =
getMockTransactionSimulatorResult(false, 0, Wei.ZERO, Optional.empty());
when(mockTxSimResult.getValidationResult()).thenReturn(ValidationResult.invalid(reason));
when(mockTxSimResult.getValidationResult())
.thenReturn(
validationFailedErrorMessage == null
? ValidationResult.invalid(reason)
: ValidationResult.invalid(reason, validationFailedErrorMessage));
}
private void mockTransientProcessorTxReverted(

@ -14,8 +14,8 @@
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32603,
"message": "Internal error"
"code": -32000,
"message": "Transaction processing could not be completed due to an exception: INVALID_OPERATION"
}
},
"statusCode": 200

@ -426,6 +426,13 @@ public class MainnetTransactionProcessor {
if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
worldUpdater.commit();
} else {
if (initialFrame.getExceptionalHaltReason().isPresent()) {
validationResult =
ValidationResult.invalid(
TransactionInvalidReason.EXECUTION_HALTED,
initialFrame.getExceptionalHaltReason().get().toString());
}
}
if (LOG.isTraceEnabled()) {

@ -49,6 +49,7 @@ public enum TransactionInvalidReason {
TX_POOL_DISABLED,
INVALID_BLOBS,
PLUGIN_TX_VALIDATOR,
EXECUTION_HALTED,
// Private Transaction Invalid Reasons
PRIVATE_TRANSACTION_INVALID,
PRIVATE_TRANSACTION_FAILED,

Loading…
Cancel
Save