Skip to content
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Breaking Changes
- ETC Classic support in Besu is removed. This includes all ETC related hardforks including Mordor and Spiral. [#9671](https://github.com/hyperledger/besu/pull/9671)
- eth_simulateV1 returns BLOCK_NOT_FOUND instead of null success response when called with a future block [#9837](https://github.com/hyperledger/besu/pull/9837)
- eth_simulateV1: map UPFRONT_COST_EXCEEDS_BALANCE (-38014) error code to -32602 [#9837](https://github.com/hyperledger/besu/pull/9837)
- Forest db subcommands `x-backup-state` and `x-restore-state` are removed [#9821](https://github.com/hyperledger/besu/pull/9821)
- **Chain pruning CLI options have been redesigned with new behavior:** [#9637](https://github.com/hyperledger/besu/pull/9637)
- `--Xchain-pruning-enabled` now accepts three strategy values instead of boolean:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.SimulateV1Parameter;
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.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockStateCallResult;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
Expand Down Expand Up @@ -96,8 +98,13 @@ protected Object resultByBlockHash(final JsonRpcRequestContext request, final Ha
@Override
protected Object resultByBlockHeader(
final JsonRpcRequestContext request, final BlockHeader header) {
SimulateV1Parameter simulateV1Parameter;
try {
simulateV1Parameter = getBlockStateCalls(request);
} catch (final JsonRpcParameterException e) {
return errorResponse(request, INVALID_PARAMS);
}
try {
SimulateV1Parameter simulateV1Parameter = getBlockStateCalls(request);
Optional<BlockStateCallError> maybeValidationError =
simulateV1Parameter.validate(getValidPrecompileAddresses(header));
if (maybeValidationError.isPresent()) {
Expand All @@ -110,10 +117,15 @@ protected Object resultByBlockHeader(
}
return process(header, simulateV1Parameter);
} catch (final BlockStateCallException e) {
JsonRpcError error = new JsonRpcError(e.getError().getCode(), e.getMessage(), null);
int errorCode = e.getError().getCode();
// When validation is enabled, map simulation-specific error codes to -32602
// (invalid params) per the execution-apis spec.
if (simulateV1Parameter.isValidation()
&& e.getError() == BlockStateCallError.UPFRONT_COST_EXCEEDS_BALANCE) {
errorCode = INVALID_PARAMS.getCode();
}
JsonRpcError error = new JsonRpcError(errorCode, e.getMessage(), null);
return new JsonRpcErrorResponse(request.getRequest().getId(), error);
} catch (final JsonRpcParameterException e) {
return errorResponse(request, INVALID_PARAMS);
} catch (Exception e) {
throw new RuntimeException(e);
}
Expand Down Expand Up @@ -141,6 +153,25 @@ private SimulateV1Parameter getBlockStateCalls(final JsonRpcRequestContext reque
return request.getRequiredParameter(0, SimulateV1Parameter.class);
}

@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
Object response = handleParamTypes(requestContext);

if (response instanceof JsonRpcResponse) {
// Convert null success responses to BLOCK_NOT_FOUND errors for eth_simulateV1.
// The base class returns null for future/unknown blocks, but eth_simulateV1
// should return an error per the execution-apis spec.
if (response instanceof JsonRpcSuccessResponse successResponse
&& successResponse.getResult() == null) {
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), new JsonRpcError(BLOCK_NOT_FOUND));
}
return (JsonRpcResponse) response;
}

return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), response);
}

private JsonRpcErrorResponse errorResponse(
final JsonRpcRequestContext request, final RpcErrorType rpcErrorType) {
return new JsonRpcErrorResponse(request.getRequest().getId(), new JsonRpcError(rpcErrorType));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
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.parameters.SimulateV1Parameter;
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.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
Expand Down Expand Up @@ -76,15 +77,16 @@ public void shouldReturnCorrectMethodName() {
}

@Test
public void shouldReturnNullWhenInvalidBlockNumberSpecified() {
public void shouldReturnBlockNotFoundErrorWhenFutureBlockNumberSpecified() {
final JsonRpcRequestContext request =
ethSimulateV1Request(simulateParameter(), Quantity.create(33L));
when(blockchainQueries.headBlockNumber()).thenReturn(14L);

final JsonRpcResponse response = method.response(request);

Assertions.assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class);
Assertions.assertThat(((JsonRpcSuccessResponse) response).getResult()).isNull();
Assertions.assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
Assertions.assertThat(((JsonRpcErrorResponse) response).getError().getCode())
.isEqualTo(RpcErrorType.BLOCK_NOT_FOUND.getCode());

verify(blockchainQueries).headBlockNumber();
}
Expand Down
Loading