Skip to content

Commit 6a21ec5

Browse files
fix: return correct error code for eth_simulate (#9564)
* Map correct error code Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * Fix spotless Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * Fix error code Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * fix tests Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * add tests Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * fix javadoc Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * split exceptions Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * split exceptions Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * simplify Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * simplify Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> --------- Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> Co-authored-by: Justin Florentine <justin+github@florentine.us>
1 parent 4427012 commit 6a21ec5

File tree

11 files changed

+239
-89
lines changed

11 files changed

+239
-89
lines changed

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

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ protected Object resultByBlockHeader(
110110
}
111111
return process(header, simulateV1Parameter);
112112
} catch (final BlockStateCallException e) {
113-
return handleBlockSimulationException(request, e);
113+
JsonRpcError error = new JsonRpcError(e.getError().getCode(), e.getMessage(), null);
114+
return new JsonRpcErrorResponse(request.getRequest().getId(), error);
114115
} catch (final JsonRpcParameterException e) {
115116
return errorResponse(request, INVALID_PARAMS);
116117
} catch (Exception e) {
@@ -128,22 +129,6 @@ private Object process(final BlockHeader header, final SimulateV1Parameter simul
128129
.collect(Collectors.toList());
129130
}
130131

131-
private JsonRpcErrorResponse handleBlockSimulationException(
132-
final JsonRpcRequestContext request, final BlockStateCallException e) {
133-
JsonRpcError error =
134-
e.getResult()
135-
.map(
136-
r -> {
137-
BlockStateCallError blockStateCallError =
138-
BlockStateCallError.of(r.getValidationResult().getInvalidReason());
139-
String message = r.getInvalidReason().orElse(blockStateCallError.getMessage());
140-
return new JsonRpcError(blockStateCallError.getCode(), message, null);
141-
})
142-
.orElseGet(() -> new JsonRpcError(RpcErrorType.INTERNAL_ERROR));
143-
144-
return new JsonRpcErrorResponse(request.getRequest().getId(), error);
145-
}
146-
147132
private Set<Address> getValidPrecompileAddresses(final BlockHeader header) {
148133
return protocolSchedule
149134
.getByBlockHeader(header)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"request": {
3+
"jsonrpc": "2.0",
4+
"id": 1,
5+
"method": "eth_simulateV1",
6+
"params": [
7+
{
8+
"blockStateCalls": [
9+
{
10+
"blockOverrides": {
11+
"number": "0x91"
12+
},
13+
"stateOverrides": {
14+
"0xc100000000000000000000000000000000000000": {
15+
"code": "0x608060405234801561001057600080fd5b506000366060484641444543425a3a60014361002c919061009b565b406040516020016100469a99989796959493929190610138565b6040516020818303038152906040529050915050805190602001f35b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006100a682610062565b91506100b183610062565b92508282039050818111156100c9576100c861006c565b5b92915050565b6100d881610062565b82525050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610109826100de565b9050919050565b610119816100fe565b82525050565b6000819050919050565b6101328161011f565b82525050565b60006101408201905061014e600083018d6100cf565b61015b602083018c6100cf565b610168604083018b610110565b610175606083018a6100cf565b61018260808301896100cf565b61018f60a08301886100cf565b61019c60c08301876100cf565b6101a960e08301866100cf565b6101b76101008301856100cf565b6101c5610120830184610129565b9b9a505050505050505050505056fea26469706673582212205139ae3ba8d46d11c29815d001b725f9840c90e330884ed070958d5af4813d8764736f6c63430008120033"
16+
}
17+
},
18+
"calls": [
19+
{
20+
"from": "0xc000000000000000000000000000000000000000",
21+
"to": "0xc100000000000000000000000000000000000000",
22+
"input": "0x"
23+
}
24+
]
25+
},
26+
{
27+
"calls": [
28+
{
29+
"from": "0xc000000000000000000000000000000000000000",
30+
"to": "0xc100000000000000000000000000000000000000",
31+
"input": "0x"
32+
}
33+
]
34+
},
35+
{
36+
"blockOverrides": {
37+
"number": "0x92"
38+
},
39+
"calls": [
40+
{
41+
"from": "0xc000000000000000000000000000000000000000",
42+
"to": "0xc100000000000000000000000000000000000000",
43+
"input": "0x"
44+
}
45+
]
46+
}
47+
]
48+
},
49+
"latest"
50+
]
51+
},
52+
"response": {
53+
"jsonrpc": "2.0",
54+
"id": 1,
55+
"error": {
56+
"code": -38020,
57+
"message": "Block number is invalid. Trying to add a call at block number 146, while current block number is 146."
58+
}
59+
},
60+
"statusCode": 200
61+
}
62+
63+
64+
65+

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulationParameter.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import static com.google.common.base.Preconditions.checkNotNull;
1818
import static org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError.BLOCK_NUMBERS_NOT_ASCENDING;
19+
import static org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError.DUPLICATED_PRECOMPILE_TARGET;
1920
import static org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError.INVALID_NONCES;
2021
import static org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError.INVALID_PRECOMPILE_ADDRESS;
2122
import static org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError.TIMESTAMPS_NOT_ASCENDING;
@@ -28,6 +29,7 @@
2829

2930
import java.math.BigInteger;
3031
import java.util.HashMap;
32+
import java.util.HashSet;
3133
import java.util.List;
3234
import java.util.Map;
3335
import java.util.Optional;
@@ -185,14 +187,20 @@ private Optional<BlockStateCallError> validateNonces() {
185187

186188
private Optional<BlockStateCallError> validateStateOverrides(
187189
final Set<Address> validPrecompileAddresses) {
190+
Set<Address> targetAddresses = new HashSet<>();
188191
for (BlockStateCall call : blockStateCalls) {
189192
if (call.getStateOverrideMap().isPresent()) {
190193
var stateOverrideMap = call.getStateOverrideMap().get();
191194
for (Address stateOverride : stateOverrideMap.keySet()) {
192195
final StateOverride override = stateOverrideMap.get(stateOverride);
193-
if (override.getMovePrecompileToAddress().isPresent()
194-
&& !validPrecompileAddresses.contains(stateOverride)) {
195-
return Optional.of(INVALID_PRECOMPILE_ADDRESS);
196+
if (override.getMovePrecompileToAddress().isPresent()) {
197+
if (!validPrecompileAddresses.contains(stateOverride)) {
198+
return Optional.of(INVALID_PRECOMPILE_ADDRESS);
199+
}
200+
Address target = override.getMovePrecompileToAddress().get();
201+
if (!targetAddresses.add(target)) {
202+
return Optional.of(DUPLICATED_PRECOMPILE_TARGET);
203+
}
196204
}
197205
}
198206
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessingContext;
5555
import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessorCoordinator;
5656
import org.hyperledger.besu.ethereum.mainnet.systemcall.BlockProcessingContext;
57+
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError;
5758
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallException;
5859
import org.hyperledger.besu.ethereum.trie.pathbased.common.provider.PathBasedWorldStateProvider;
5960
import org.hyperledger.besu.ethereum.trie.pathbased.common.worldview.PathBasedWorldState;
@@ -359,11 +360,15 @@ protected BlockStateCallSimulationResult processTransactions(
359360

360361
TransactionSimulatorResult transactionSimulationResult =
361362
transactionSimulatorResult.orElseThrow(
362-
() -> new BlockStateCallException("Transaction simulator result is empty"));
363+
() ->
364+
new BlockStateCallException(
365+
"Transaction simulation returned no result",
366+
BlockStateCallError.EMPTY_SIMULATION_RESULT));
363367

364368
if (transactionSimulationResult.isInvalid()) {
365369
throw new BlockStateCallException(
366-
"Transaction simulator result is invalid", transactionSimulationResult);
370+
transactionSimulationResult.getValidationResult().getErrorMessage(),
371+
transactionSimulationResult.getValidationResult().getInvalidReason());
367372
}
368373

369374
transactionSimulationResult

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockStateCalls.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import org.hyperledger.besu.ethereum.core.BlockHeader;
1818
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
19+
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError;
20+
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallException;
1921
import org.hyperledger.besu.plugin.data.BlockOverrides;
2022

2123
import java.util.ArrayList;
@@ -43,13 +45,14 @@ public static List<BlockStateCall> fillBlockStateCalls(
4345
final BlockHeader header) {
4446
long lastPresentBlockNumber = findLastBlockNumber(blockStateCalls);
4547
if (lastPresentBlockNumber > header.getNumber() + MAX_BLOCK_CALL_SIZE) {
46-
throw new IllegalArgumentException(
48+
String errorMessage =
4749
String.format(
4850
"Block number %d exceeds the limit of %d (header: %d + MAX_BLOCK_CALL_SIZE: %d)",
4951
lastPresentBlockNumber,
5052
header.getNumber() + MAX_BLOCK_CALL_SIZE,
5153
header.getNumber(),
52-
MAX_BLOCK_CALL_SIZE));
54+
MAX_BLOCK_CALL_SIZE);
55+
throw new BlockStateCallException(errorMessage, BlockStateCallError.TOO_MANY_BLOCK_CALLS);
5356
}
5457
List<BlockStateCall> filledCalls = new ArrayList<>();
5558
long currentBlock = header.getNumber();
@@ -92,16 +95,18 @@ private static void add(
9295
long blockNumber = blockStateCall.getBlockOverrides().getBlockNumber().orElseThrow();
9396
long timestamp = blockStateCall.getBlockOverrides().getTimestamp().orElseThrow();
9497
if (blockNumber <= currentBlockNumber) {
95-
throw new IllegalArgumentException(
98+
throw new BlockStateCallException(
9699
String.format(
97100
"Block number is invalid. Trying to add a call at block number %s, while current block number is %s.",
98-
blockNumber, currentBlockNumber));
101+
blockNumber, currentBlockNumber),
102+
BlockStateCallError.BLOCK_NUMBERS_NOT_ASCENDING);
99103
}
100104
if (timestamp <= currentTimestamp) {
101-
throw new IllegalArgumentException(
105+
throw new BlockStateCallException(
102106
String.format(
103107
"Timestamp is invalid. Trying to add a call at timestamp %s, while current timestamp is %s.",
104-
timestamp, currentTimestamp));
108+
timestamp, currentTimestamp),
109+
BlockStateCallError.TIMESTAMPS_NOT_ASCENDING);
105110
}
106111
blockStateCalls.add(blockStateCall);
107112
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/SimulationTransactionProcessorFactory.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
2020
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
2121
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
22+
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError;
23+
import org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallException;
2224
import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry;
2325
import org.hyperledger.besu.evm.processor.SimulationMessageCallProcessor;
2426

@@ -106,10 +108,14 @@ public static PrecompileContractRegistry overridePrecompileAddresses(
106108
precompileOverrides.forEach(
107109
(oldAddress, newAddress) -> {
108110
if (!originalAddresses.contains(oldAddress)) {
109-
throw new IllegalArgumentException("Address " + oldAddress + " is not a precompile.");
111+
throw new BlockStateCallException(
112+
"Address " + oldAddress + " is not a precompile.",
113+
BlockStateCallError.INVALID_PRECOMPILE_ADDRESS);
110114
}
111115
if (newRegistry.getPrecompileAddresses().contains(newAddress)) {
112-
throw new IllegalArgumentException("Duplicate precompile address: " + newAddress);
116+
throw new BlockStateCallException(
117+
"Duplicate precompile address: " + newAddress,
118+
BlockStateCallError.DUPLICATED_PRECOMPILE_TARGET);
113119
}
114120
newRegistry.put(newAddress, originalRegistry.get(oldAddress));
115121
});
@@ -119,8 +125,9 @@ public static PrecompileContractRegistry overridePrecompileAddresses(
119125
.forEach(
120126
originalAddress -> {
121127
if (newRegistry.getPrecompileAddresses().contains(originalAddress)) {
122-
throw new IllegalArgumentException(
123-
"Duplicate precompile address: " + originalAddress);
128+
throw new BlockStateCallException(
129+
"Duplicate precompile address: " + originalAddress,
130+
BlockStateCallError.DUPLICATED_PRECOMPILE_TARGET);
124131
}
125132
newRegistry.put(originalAddress, originalRegistry.get(originalAddress));
126133
});

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/exceptions/BlockStateCallError.java

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
*/
1515
package org.hyperledger.besu.ethereum.transaction.exceptions;
1616

17-
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
18-
1917
/**
2018
* An enumeration of errors that can occur during a block state call.
2119
*
@@ -30,6 +28,8 @@ public enum BlockStateCallError {
3028
TIMESTAMPS_NOT_ASCENDING(-38021, "Timestamps are not ascending"),
3129
/** Invalid precompile address. */
3230
INVALID_PRECOMPILE_ADDRESS(-32000, "Invalid precompile address"),
31+
/** Duplicated move precompile target. */
32+
DUPLICATED_PRECOMPILE_TARGET(-38023, "Duplicated move precompile target"),
3333
/** The nonce is invalid. */
3434
INVALID_NONCES(-32602, "Invalid nonces"),
3535
/** Upfront cost exceeds balance. */
@@ -39,7 +39,9 @@ public enum BlockStateCallError {
3939
/** Intrinsic gas exceeds gas limit. */
4040
INTRINSIC_GAS_EXCEEDS_GAS_LIMIT(-38013, "Intrinsic gas exceeds gas limit"),
4141
/** Unknown error. */
42-
UNKNOWN(-32602, "Internal error");
42+
UNKNOWN(-32602, "Internal error"),
43+
/** Empty simulation result. */
44+
EMPTY_SIMULATION_RESULT(-32602, "Transaction simulation returned no result");
4345

4446
private final int code;
4547
private final String message;
@@ -64,21 +66,6 @@ public int getCode() {
6466
return code;
6567
}
6668

67-
/**
68-
* Returns the error message.
69-
*
70-
* @param transactionInvalidReason The transaction invalid reason.
71-
* @return The error message.
72-
*/
73-
public static BlockStateCallError of(final TransactionInvalidReason transactionInvalidReason) {
74-
return switch (transactionInvalidReason) {
75-
case UPFRONT_COST_EXCEEDS_BALANCE -> UPFRONT_COST_EXCEEDS_BALANCE;
76-
case GAS_PRICE_TOO_LOW -> GAS_PRICE_TOO_LOW;
77-
case INTRINSIC_GAS_EXCEEDS_GAS_LIMIT -> INTRINSIC_GAS_EXCEEDS_GAS_LIMIT;
78-
default -> UNKNOWN;
79-
};
80-
}
81-
8269
/**
8370
* Returns the error message.
8471
*

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/exceptions/BlockStateCallException.java

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,43 +14,64 @@
1414
*/
1515
package org.hyperledger.besu.ethereum.transaction.exceptions;
1616

17-
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
17+
import static org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError.GAS_PRICE_TOO_LOW;
18+
import static org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT;
19+
import static org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError.UNKNOWN;
20+
import static org.hyperledger.besu.ethereum.transaction.exceptions.BlockStateCallError.UPFRONT_COST_EXCEEDS_BALANCE;
1821

19-
import java.util.Optional;
22+
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
2023

21-
/** Exception thrown when a block state call fails. */
24+
/** Exception thrown when a block state call validation fails. */
2225
public class BlockStateCallException extends RuntimeException {
2326

24-
/** The result of the block state call. */
25-
private final TransactionSimulatorResult result;
27+
/** The block state call error. */
28+
private final BlockStateCallError error;
2629

2730
/**
28-
* Constructs a new BlockStateCallException with the given message.
31+
* Constructs an exception with a message and an error descriptor.
2932
*
3033
* @param message the message
34+
* @param error the block state call error
3135
*/
32-
public BlockStateCallException(final String message) {
36+
public BlockStateCallException(final String message, final BlockStateCallError error) {
3337
super(message);
34-
this.result = null;
38+
this.error = error;
3539
}
3640

3741
/**
38-
* Constructs a new BlockStateCallException with the given message and result.
42+
* Returns the block state call error, if present.
43+
*
44+
* @return optional error descriptor
45+
*/
46+
public BlockStateCallError getError() {
47+
return error;
48+
}
49+
50+
/**
51+
* Constructs an exception with a message and an error descriptor.
3952
*
4053
* @param message the message
41-
* @param result the result
54+
* @param invalidReason the invalidReason
4255
*/
43-
public BlockStateCallException(final String message, final TransactionSimulatorResult result) {
56+
public BlockStateCallException(
57+
final String message, final TransactionInvalidReason invalidReason) {
4458
super(message);
45-
this.result = result;
59+
this.error = fromTransactionInvalidReason(invalidReason);
4660
}
4761

4862
/**
49-
* Gets the result of the block state call.
63+
* Returns the error message.
5064
*
51-
* @return the result
65+
* @param transactionInvalidReason The transaction invalid reason.
66+
* @return The error message.
5267
*/
53-
public Optional<TransactionSimulatorResult> getResult() {
54-
return Optional.ofNullable(result);
68+
private static BlockStateCallError fromTransactionInvalidReason(
69+
final TransactionInvalidReason transactionInvalidReason) {
70+
return switch (transactionInvalidReason) {
71+
case UPFRONT_COST_EXCEEDS_BALANCE -> UPFRONT_COST_EXCEEDS_BALANCE;
72+
case GAS_PRICE_TOO_LOW -> GAS_PRICE_TOO_LOW;
73+
case INTRINSIC_GAS_EXCEEDS_GAS_LIMIT -> INTRINSIC_GAS_EXCEEDS_GAS_LIMIT;
74+
default -> UNKNOWN;
75+
};
5576
}
5677
}

0 commit comments

Comments
 (0)