Add Revert Reason to eth_call (#1829)

When an eth_call results in a revert report the call as an error with
the revert reason in the response, like we do with eth_estimateGas.

Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>
pull/1835/head
Danno Ferrin 4 years ago committed by GitHub
parent d84cd7c01c
commit f750ca729d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 38
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java
  3. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java
  4. 24
      ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/eth_latest/eth_call_revert.json

@ -4,11 +4,13 @@
### 21.2 Breaking Changes
* `--skip-pow-validation-enabled` is now an error with `block import --format JSON`. This is because the JSON format doesn't include the nonce so the proof of work must be calculated.
* `eth_call` will not return a JSON-RPC result if the call fails, but will return an error instead. If it was for a revert the revert reason will be included.
### Additions and Improvements
* Removed unused flags in default genesis configs [\#1812](https://github.com/hyperledger/besu/pull/1812)
* `--skip-pow-validation-enabled` is now an error with `block import --format JSON`. This is because the JSON format doesn't include the nonce so the proof of work must be calculated. [\#1815](https://github.com/hyperledger/besu/pull/1815)
* Added a new CLI option `--Xlauncher` to start a mainnet launcher. It will help to configure Besu easily.
* Return the revert reason from `eth_call` JSON-RPC api calls when the contract causes a revert. [\#1829](https://github.com/hyperledger/besu/pull/1829)
### Bug Fixes
* Ethereum classic heights will no longer be reported in mainnet metrics. Issue [\#1751]((https://github.com/hyperledger/besu/pull/1751) Fix [\#1820](https://github.com/hyperledger/besu/pull/1820)

@ -20,12 +20,17 @@ 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.parameters.BlockParameterOrBlockHash;
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;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
private final TransactionSimulator transactionSimulator;
@ -59,8 +64,10 @@ public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
.getValidationResult()
.either(
(() ->
new JsonRpcSuccessResponse(
request.getRequest().getId(), result.getOutput().toString())),
result.isSuccessful()
? new JsonRpcSuccessResponse(
request.getRequest().getId(), result.getOutput().toString())
: errorResponse(request, result)),
reason ->
new JsonRpcErrorResponse(
request.getRequest().getId(),
@ -77,6 +84,33 @@ public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
return new JsonRpcSuccessResponse(request.getRequest().getId(), null);
}
private JsonRpcErrorResponse errorResponse(
final JsonRpcRequestContext request, final TransactionSimulatorResult result) {
final JsonRpcError jsonRpcError;
final ValidationResult<TransactionInvalidReason> validationResult =
result.getValidationResult();
if (validationResult != null && !validationResult.isValid()) {
jsonRpcError =
JsonRpcErrorConverter.convertTransactionInvalidReason(
validationResult.getInvalidReason());
} else {
final TransactionProcessingResult resultTrx = result.getResult();
if (resultTrx != null && resultTrx.getRevertReason().isPresent()) {
jsonRpcError = JsonRpcError.REVERT_ERROR;
jsonRpcError.setData(resultTrx.getRevertReason().get().toHexString());
} else {
jsonRpcError = JsonRpcError.INTERNAL_ERROR;
}
}
return errorResponse(request, jsonRpcError);
}
private JsonRpcErrorResponse errorResponse(
final JsonRpcRequestContext request, final JsonRpcError jsonRpcError) {
return new JsonRpcErrorResponse(request.getRequest().getId(), jsonRpcError);
}
private JsonCallParameter validateAndGetCallParams(final JsonRpcRequestContext request) {
final JsonCallParameter callParams = request.getRequiredParameter(0, JsonCallParameter.class);
if (callParams.getTo() == null) {

@ -206,6 +206,7 @@ public class EthCallTest {
private void mockTransactionProcessorSuccessResult(final Bytes output) {
final TransactionSimulatorResult result = mock(TransactionSimulatorResult.class);
when(result.isSuccessful()).thenReturn(true);
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.getOutput()).thenReturn(output);
when(transactionSimulator.process(any(), any())).thenReturn(Optional.of(result));

@ -0,0 +1,24 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"from": "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73",
"to": "0x0110000000000000000000000000000000000000"
},
"0x11"
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32000,
"message": "Execution reverted",
"data": "0x7d88c1856cc95352"
}
},
"statusCode": 200
}
Loading…
Cancel
Save