Skip to content

Commit 8b7ca20

Browse files
macfarlafab-10
andauthored
eth_call overrides (#7801)
* add state and account overrides Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net>
1 parent 2384c09 commit 8b7ca20

File tree

12 files changed

+647
-21
lines changed

12 files changed

+647
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- Update Java dependencies [#7786](https://github.com/hyperledger/besu/pull/7786)
1616
- Add a method to get all the transaction in the pool, to the `TransactionPoolService`, to easily access the transaction pool content from plugins [#7813](https://github.com/hyperledger/besu/pull/7813)
1717
- Add a method to check if a metric category is enabled to the plugin API [#7832](https://github.com/hyperledger/besu/pull/7832)
18+
- Add account and state overrides to `eth_call` and `eth_estimateGas` [#7801](https://github.com/hyperledger/besu/pull/7801)
1819

1920
### Bug fixes
2021
- Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825)

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
2424
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
2525
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters;
26+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException;
2627
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash;
2728
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
2829
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter.JsonRpcParameterException;
@@ -40,8 +41,13 @@
4041
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
4142
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
4243
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
44+
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;
4345
import org.hyperledger.besu.evm.tracing.OperationTracer;
4446

47+
import java.util.Optional;
48+
49+
import com.google.common.annotations.VisibleForTesting;
50+
4551
public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
4652
private final TransactionSimulator transactionSimulator;
4753

@@ -81,10 +87,13 @@ protected Object resultByBlockHash(final JsonRpcRequestContext request, final Ha
8187
protected Object resultByBlockHeader(
8288
final JsonRpcRequestContext request, final BlockHeader header) {
8389
JsonCallParameter callParams = JsonCallParameterUtil.validateAndGetCallParams(request);
90+
Optional<AccountOverrideMap> maybeStateOverrides = getAddressAccountOverrideMap(request);
91+
// TODO implement for block overrides
8492

8593
return transactionSimulator
8694
.process(
8795
callParams,
96+
maybeStateOverrides,
8897
buildTransactionValidationParams(header, callParams),
8998
OperationTracer.NO_TRACING,
9099
(mutableWorldState, transactionSimulatorResult) ->
@@ -108,6 +117,17 @@ protected Object resultByBlockHeader(
108117
.orElse(errorResponse(request, INTERNAL_ERROR));
109118
}
110119

120+
@VisibleForTesting
121+
protected Optional<AccountOverrideMap> getAddressAccountOverrideMap(
122+
final JsonRpcRequestContext request) {
123+
try {
124+
return request.getOptionalParameter(2, AccountOverrideMap.class);
125+
} catch (JsonRpcParameterException e) {
126+
throw new InvalidJsonRpcRequestException(
127+
"Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e);
128+
}
129+
}
130+
111131
@Override
112132
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
113133
return (JsonRpcResponse) handleParamTypes(requestContext);

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,12 @@ private JsonNode getSingleCallResult(
160160
new DebugOperationTracer(buildTraceOptions(traceTypes), false);
161161
final Optional<TransactionSimulatorResult> maybeSimulatorResult =
162162
transactionSimulator.processWithWorldUpdater(
163-
callParameter, buildTransactionValidationParams(), tracer, header, worldUpdater);
163+
callParameter,
164+
Optional.empty(),
165+
buildTransactionValidationParams(),
166+
tracer,
167+
header,
168+
worldUpdater);
164169

165170
LOG.trace("Executing {} call for transaction {}", traceTypeParameter, callParameter);
166171
if (maybeSimulatorResult.isEmpty()) {

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
import org.hyperledger.besu.ethereum.transaction.PreCloseStateHandler;
5252
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
5353
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
54+
import org.hyperledger.besu.ethereum.util.AccountOverride;
55+
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;
5456

5557
import java.util.Optional;
5658

@@ -92,14 +94,41 @@ public void shouldReturnCorrectMethodName() {
9294
assertThat(method.getName()).isEqualTo("eth_call");
9395
}
9496

97+
@Test
98+
public void noAccountOverrides() {
99+
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest");
100+
Optional<AccountOverrideMap> overrideMap = method.getAddressAccountOverrideMap(request);
101+
assertThat(overrideMap.isPresent()).isFalse();
102+
}
103+
104+
@Test
105+
public void someAccountOverrides() {
106+
AccountOverrideMap expectedOverrides = new AccountOverrideMap();
107+
AccountOverride override = new AccountOverride.Builder().withNonce(88L).build();
108+
final Address address = Address.fromHexString("0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3");
109+
expectedOverrides.put(address, override);
110+
111+
final JsonRpcRequestContext request =
112+
ethCallRequestWithStateOverrides(callParameter(), "latest", expectedOverrides);
113+
114+
Optional<AccountOverrideMap> maybeOverrideMap = method.getAddressAccountOverrideMap(request);
115+
assertThat(maybeOverrideMap.isPresent()).isTrue();
116+
AccountOverrideMap overrideMap = maybeOverrideMap.get();
117+
assertThat(overrideMap.keySet()).hasSize(1);
118+
assertThat(overrideMap.values()).hasSize(1);
119+
120+
assertThat(overrideMap).containsKey(address);
121+
assertThat(overrideMap).containsValue(override);
122+
}
123+
95124
@Test
96125
public void shouldReturnInternalErrorWhenProcessorReturnsEmpty() {
97126
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest");
98127
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, INTERNAL_ERROR);
99128

100129
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
101130
when(blockchain.getChainHead()).thenReturn(chainHead);
102-
when(transactionSimulator.process(any(), any(), any(), any(), any()))
131+
when(transactionSimulator.process(any(), any(), any(), any(), any(), any()))
103132
.thenReturn(Optional.empty());
104133

105134
final BlockHeader blockHeader = mock(BlockHeader.class);
@@ -109,7 +138,7 @@ public void shouldReturnInternalErrorWhenProcessorReturnsEmpty() {
109138
final JsonRpcResponse response = method.response(request);
110139

111140
assertThat(response).usingRecursiveComparison().isEqualTo(expectedResponse);
112-
verify(transactionSimulator).process(any(), any(), any(), any(), any());
141+
verify(transactionSimulator).process(any(), any(), any(), any(), any(), any());
113142
}
114143

115144
@Test
@@ -130,12 +159,13 @@ public void shouldAcceptRequestWhenMissingOptionalFields() {
130159
when(result.isSuccessful()).thenReturn(true);
131160
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
132161
when(result.getOutput()).thenReturn(Bytes.of());
133-
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
162+
verify(transactionSimulator)
163+
.process(
164+
eq(callParameter), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
134165
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
135166
.isEqualTo(Optional.of(expectedResponse));
136167

137168
assertThat(response).usingRecursiveComparison().isEqualTo(expectedResponse);
138-
verify(transactionSimulator).process(eq(callParameter), any(), any(), any(), any());
139169
}
140170

141171
@Test
@@ -158,7 +188,8 @@ public void shouldReturnExecutionResultWhenExecutionIsSuccessful() {
158188
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
159189
when(result.getOutput()).thenReturn(Bytes.of(1));
160190
verify(transactionSimulator)
161-
.process(eq(callParameter()), any(), any(), mapperCaptor.capture(), any());
191+
.process(
192+
eq(callParameter()), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
162193
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
163194
.isEqualTo(Optional.of(expectedResponse));
164195

@@ -196,7 +227,8 @@ public void shouldReturnBasicExecutionRevertErrorWithoutReason() {
196227
when(result.isSuccessful()).thenReturn(false);
197228
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
198229
when(result.result()).thenReturn(processingResult);
199-
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
230+
verify(transactionSimulator)
231+
.process(any(), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
200232
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
201233
.isEqualTo(Optional.of(expectedResponse));
202234

@@ -235,7 +267,8 @@ public void shouldReturnExecutionRevertErrorWithABIParseError() {
235267
when(result.isSuccessful()).thenReturn(false);
236268
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
237269
when(result.result()).thenReturn(processingResult);
238-
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
270+
verify(transactionSimulator)
271+
.process(any(), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
239272
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
240273
.isEqualTo(Optional.of(expectedResponse));
241274

@@ -277,7 +310,8 @@ public void shouldReturnExecutionRevertErrorWithParsedABI() {
277310
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
278311
when(result.result()).thenReturn(processingResult);
279312

280-
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
313+
verify(transactionSimulator)
314+
.process(any(), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
281315
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
282316
.isEqualTo(Optional.of(expectedResponse));
283317

@@ -291,7 +325,7 @@ public void shouldUseCorrectBlockNumberWhenLatest() {
291325
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest");
292326
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
293327
when(blockchain.getChainHead()).thenReturn(chainHead);
294-
when(transactionSimulator.process(any(), any(), any(), any(), any()))
328+
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
295329
.thenReturn(Optional.empty());
296330

297331
final BlockHeader blockHeader = mock(BlockHeader.class);
@@ -301,7 +335,7 @@ public void shouldUseCorrectBlockNumberWhenLatest() {
301335
method.response(request);
302336

303337
verify(blockchainQueries, atLeastOnce()).getBlockchain();
304-
verify(transactionSimulator).process(any(), any(), any(), any(), any());
338+
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
305339
}
306340

307341
@Test
@@ -315,35 +349,35 @@ public void shouldUseCorrectBlockNumberWhenEarliest() {
315349
method.response(request);
316350

317351
verify(blockchainQueries).getBlockHeaderByHash(eq(Hash.ZERO));
318-
verify(transactionSimulator).process(any(), any(), any(), any(), any());
352+
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
319353
}
320354

321355
@Test
322356
public void shouldUseCorrectBlockNumberWhenSafe() {
323357
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "safe");
324358
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
325359
when(blockchainQueries.safeBlockHeader()).thenReturn(Optional.of(blockHeader));
326-
when(transactionSimulator.process(any(), any(), any(), any(), any()))
360+
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
327361
.thenReturn(Optional.empty());
328362
method.response(request);
329363

330364
verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
331365
verify(blockchainQueries).safeBlockHeader();
332-
verify(transactionSimulator).process(any(), any(), any(), any(), any());
366+
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
333367
}
334368

335369
@Test
336370
public void shouldUseCorrectBlockNumberWhenFinalized() {
337371
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "finalized");
338372
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
339373
when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(blockHeader));
340-
when(transactionSimulator.process(any(), any(), any(), any(), any()))
374+
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
341375
.thenReturn(Optional.empty());
342376
method.response(request);
343377

344378
verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
345379
verify(blockchainQueries).finalizedBlockHeader();
346-
verify(transactionSimulator).process(any(), any(), any(), any(), any());
380+
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
347381
}
348382

349383
@Test
@@ -353,13 +387,13 @@ public void shouldUseCorrectBlockNumberWhenSpecified() {
353387
when(blockchainQueries.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(Hash.ZERO));
354388
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO))
355389
.thenReturn(Optional.of(mock(BlockHeader.class)));
356-
when(transactionSimulator.process(any(), any(), any(), any(), any()))
390+
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
357391
.thenReturn(Optional.empty());
358392

359393
method.response(request);
360394

361395
verify(blockchainQueries).getBlockHeaderByHash(eq(Hash.ZERO));
362-
verify(transactionSimulator).process(any(), any(), any(), any(), any());
396+
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
363397
}
364398

365399
@Test
@@ -431,7 +465,7 @@ private void internalAutoSelectIsAllowedExceedingBalance(
431465
.build();
432466

433467
verify(transactionSimulator)
434-
.process(any(), eq(transactionValidationParams), any(), any(), any());
468+
.process(any(), eq(Optional.empty()), eq(transactionValidationParams), any(), any(), any());
435469
}
436470

437471
private JsonCallParameter callParameter() {
@@ -458,8 +492,17 @@ private JsonRpcRequestContext ethCallRequest(
458492
new JsonRpcRequest("2.0", "eth_call", new Object[] {callParameter, blockNumberInHex}));
459493
}
460494

495+
private JsonRpcRequestContext ethCallRequestWithStateOverrides(
496+
final CallParameter callParameter,
497+
final String blockNumberInHex,
498+
final AccountOverrideMap overrides) {
499+
return new JsonRpcRequestContext(
500+
new JsonRpcRequest(
501+
"2.0", "eth_call", new Object[] {callParameter, blockNumberInHex, overrides}));
502+
}
503+
461504
private void mockTransactionProcessorSuccessResult(final JsonRpcResponse jsonRpcResponse) {
462-
when(transactionSimulator.process(any(), any(), any(), any(), any()))
505+
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
463506
.thenReturn(Optional.of(jsonRpcResponse));
464507
}
465508
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"request": {
3+
"id": 3,
4+
"jsonrpc": "2.0",
5+
"method": "eth_call",
6+
"params": [
7+
{
8+
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
9+
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
10+
"data": "0x12a7b914"
11+
},
12+
"latest",
13+
{
14+
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {}
15+
}
16+
]
17+
},
18+
"response": {
19+
"jsonrpc": "2.0",
20+
"id": 3,
21+
"result": "0x0000000000000000000000000000000000000000000000000000000000000001"
22+
},
23+
"statusCode": 200
24+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"request": {
3+
"id": 3,
4+
"jsonrpc": "2.0",
5+
"method": "eth_call",
6+
"params": [
7+
{
8+
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
9+
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
10+
"data": "0x12a7b914"
11+
},
12+
"latest",
13+
{
14+
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
15+
"balance": "0xde0b6b3a7640000",
16+
"nonce": 88
17+
},
18+
"0xb9741079a300Cb3B8f324CdDB847c0d1d273a05E": {
19+
"stateDiff": {
20+
"0x1cf7945003fc5b59d2f6736f0704557aa805c4f2844084ccd1173b8d56946962": "0x000000000000000000000000000000000000000000000000000000110ed03bf7"
21+
}
22+
}
23+
}
24+
]
25+
},
26+
"response": {
27+
"jsonrpc": "2.0",
28+
"id": 3,
29+
"result": "0x0000000000000000000000000000000000000000000000000000000000000001"
30+
},
31+
"statusCode": 200
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"request": {
3+
"id": 3,
4+
"jsonrpc": "2.0",
5+
"method": "eth_call",
6+
"params": [
7+
{
8+
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
9+
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
10+
"value": "0x000002"
11+
},
12+
"latest",
13+
{
14+
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
15+
"balance": "0x000001"
16+
},
17+
"0xb9741079a300Cb3B8f324CdDB847c0d1d273a05E": {
18+
"stateDiff": {
19+
"0x1cf7945003fc5b59d2f6736f0704557aa805c4f2844084ccd1173b8d56946962": "0x000000000000000000000000000000000000000000000000000000110ed03bf7"
20+
}
21+
}
22+
}
23+
]
24+
},
25+
"response": {
26+
"jsonrpc": "2.0",
27+
"id": 3,
28+
"error" : {
29+
"code" : -32004,
30+
"message" : "Upfront cost exceeds account balance"
31+
}
32+
},
33+
"statusCode": 200
34+
}

0 commit comments

Comments
 (0)