Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f2f0b87
draft
macfarla Oct 22, 2024
36ee2ff
draft
macfarla Oct 23, 2024
ee630da
check types
macfarla Oct 23, 2024
c8dcb83
tinkering with serialization
macfarla Oct 23, 2024
9fa398c
add comments to test to explain failures
macfarla Oct 23, 2024
7e794ec
Merge branch 'main' of github.com:hyperledger/besu into ethcall-override
macfarla Oct 23, 2024
a2deffe
foramtting
macfarla Oct 23, 2024
2ad8bf6
deserialization one step closer
macfarla Oct 24, 2024
a640f1d
parameter serialization successful
macfarla Oct 24, 2024
38ba498
Merge branch 'main' of github.com:hyperledger/besu into ethcall-override
macfarla Oct 24, 2024
68fdc24
tidy up test
macfarla Oct 25, 2024
f63bea6
added extra tests
macfarla Oct 25, 2024
1b7cd4b
Merge branch 'main' of github.com:hyperledger/besu into ethcall-override
macfarla Oct 25, 2024
99c3846
fix ethcall tests
macfarla Oct 25, 2024
3436c85
fix ethcall tests
macfarla Oct 25, 2024
56c98fc
Merge branch 'main' of github.com:hyperledger/besu into ethcall-override
macfarla Oct 28, 2024
f83f1f4
test cases
macfarla Oct 28, 2024
fec7581
apply storage overrides
macfarla Oct 29, 2024
bb96d76
Merge branch 'main' of github.com:hyperledger/besu into ethcall-override
macfarla Oct 29, 2024
fcad92d
Merge branch 'main' of github.com:hyperledger/besu into ethcall-override
macfarla Nov 19, 2024
9a64862
removed TODO that is implemented
macfarla Nov 19, 2024
a021d90
Update ethereum/core/src/main/java/org/hyperledger/besu/ethereum/tran…
macfarla Nov 19, 2024
c1a0b31
Update ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util…
macfarla Nov 19, 2024
521132a
Update ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/j…
macfarla Nov 19, 2024
aa199af
unreachable statement
macfarla Nov 19, 2024
3915cc4
Merge branch 'main' of github.com:hyperledger/besu into ethcall-override
macfarla Nov 19, 2024
2eff15a
add state and account overrides
macfarla Nov 19, 2024
7043303
Merge branch 'main' into ethcall-override
macfarla Nov 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Update Java dependencies [#7786](https://github.com/hyperledger/besu/pull/7786)
- 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)
- Add a method to check if a metric category is enabled to the plugin API [#7832](https://github.com/hyperledger/besu/pull/7832)
- Add account and state overrides to `eth_call` and `eth_estimateGas` [#7801](https://github.com/hyperledger/besu/pull/7801)

### Bug fixes
- Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
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.exception.InvalidJsonRpcRequestException;
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.parameters.JsonRpcParameter.JsonRpcParameterException;
Expand All @@ -40,8 +41,13 @@
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;
import org.hyperledger.besu.evm.tracing.OperationTracer;

import java.util.Optional;

import com.google.common.annotations.VisibleForTesting;

public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
private final TransactionSimulator transactionSimulator;

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

return transactionSimulator
.process(
callParams,
maybeStateOverrides,
buildTransactionValidationParams(header, callParams),
OperationTracer.NO_TRACING,
(mutableWorldState, transactionSimulatorResult) ->
Expand All @@ -108,6 +117,17 @@ protected Object resultByBlockHeader(
.orElse(errorResponse(request, INTERNAL_ERROR));
}

@VisibleForTesting
protected Optional<AccountOverrideMap> getAddressAccountOverrideMap(
final JsonRpcRequestContext request) {
try {
return request.getOptionalParameter(2, AccountOverrideMap.class);
} catch (JsonRpcParameterException e) {
throw new InvalidJsonRpcRequestException(
"Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e);
}
}

@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
return (JsonRpcResponse) handleParamTypes(requestContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ private JsonNode getSingleCallResult(
new DebugOperationTracer(buildTraceOptions(traceTypes), false);
final Optional<TransactionSimulatorResult> maybeSimulatorResult =
transactionSimulator.processWithWorldUpdater(
callParameter, buildTransactionValidationParams(), tracer, header, worldUpdater);
callParameter,
Optional.empty(),
buildTransactionValidationParams(),
tracer,
header,
worldUpdater);

LOG.trace("Executing {} call for transaction {}", traceTypeParameter, callParameter);
if (maybeSimulatorResult.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
import org.hyperledger.besu.ethereum.transaction.PreCloseStateHandler;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.ethereum.util.AccountOverride;
import org.hyperledger.besu.ethereum.util.AccountOverrideMap;

import java.util.Optional;

Expand Down Expand Up @@ -92,14 +94,41 @@ public void shouldReturnCorrectMethodName() {
assertThat(method.getName()).isEqualTo("eth_call");
}

@Test
public void noAccountOverrides() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest");
Optional<AccountOverrideMap> overrideMap = method.getAddressAccountOverrideMap(request);
assertThat(overrideMap.isPresent()).isFalse();
}

@Test
public void someAccountOverrides() {
AccountOverrideMap expectedOverrides = new AccountOverrideMap();
AccountOverride override = new AccountOverride.Builder().withNonce(88L).build();
final Address address = Address.fromHexString("0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3");
expectedOverrides.put(address, override);

final JsonRpcRequestContext request =
ethCallRequestWithStateOverrides(callParameter(), "latest", expectedOverrides);

Optional<AccountOverrideMap> maybeOverrideMap = method.getAddressAccountOverrideMap(request);
assertThat(maybeOverrideMap.isPresent()).isTrue();
AccountOverrideMap overrideMap = maybeOverrideMap.get();
assertThat(overrideMap.keySet()).hasSize(1);
assertThat(overrideMap.values()).hasSize(1);

assertThat(overrideMap).containsKey(address);
assertThat(overrideMap).containsValue(override);
}

@Test
public void shouldReturnInternalErrorWhenProcessorReturnsEmpty() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest");
final JsonRpcResponse expectedResponse = new JsonRpcErrorResponse(null, INTERNAL_ERROR);

when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchain.getChainHead()).thenReturn(chainHead);
when(transactionSimulator.process(any(), any(), any(), any(), any()))
when(transactionSimulator.process(any(), any(), any(), any(), any(), any()))
.thenReturn(Optional.empty());

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

assertThat(response).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionSimulator).process(any(), any(), any(), any(), any());
verify(transactionSimulator).process(any(), any(), any(), any(), any(), any());
}

@Test
Expand All @@ -130,12 +159,13 @@ public void shouldAcceptRequestWhenMissingOptionalFields() {
when(result.isSuccessful()).thenReturn(true);
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.getOutput()).thenReturn(Bytes.of());
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
verify(transactionSimulator)
.process(
eq(callParameter), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse));

assertThat(response).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(transactionSimulator).process(eq(callParameter), any(), any(), any(), any());
}

@Test
Expand All @@ -158,7 +188,8 @@ public void shouldReturnExecutionResultWhenExecutionIsSuccessful() {
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.getOutput()).thenReturn(Bytes.of(1));
verify(transactionSimulator)
.process(eq(callParameter()), any(), any(), mapperCaptor.capture(), any());
.process(
eq(callParameter()), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse));

Expand Down Expand Up @@ -196,7 +227,8 @@ public void shouldReturnBasicExecutionRevertErrorWithoutReason() {
when(result.isSuccessful()).thenReturn(false);
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.result()).thenReturn(processingResult);
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
verify(transactionSimulator)
.process(any(), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse));

Expand Down Expand Up @@ -235,7 +267,8 @@ public void shouldReturnExecutionRevertErrorWithABIParseError() {
when(result.isSuccessful()).thenReturn(false);
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.result()).thenReturn(processingResult);
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
verify(transactionSimulator)
.process(any(), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse));

Expand Down Expand Up @@ -277,7 +310,8 @@ public void shouldReturnExecutionRevertErrorWithParsedABI() {
when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.result()).thenReturn(processingResult);

verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
verify(transactionSimulator)
.process(any(), eq(Optional.empty()), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse));

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

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

verify(blockchainQueries, atLeastOnce()).getBlockchain();
verify(transactionSimulator).process(any(), any(), any(), any(), any());
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
}

@Test
Expand All @@ -315,35 +349,35 @@ public void shouldUseCorrectBlockNumberWhenEarliest() {
method.response(request);

verify(blockchainQueries).getBlockHeaderByHash(eq(Hash.ZERO));
verify(transactionSimulator).process(any(), any(), any(), any(), any());
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
}

@Test
public void shouldUseCorrectBlockNumberWhenSafe() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "safe");
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
when(blockchainQueries.safeBlockHeader()).thenReturn(Optional.of(blockHeader));
when(transactionSimulator.process(any(), any(), any(), any(), any()))
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
.thenReturn(Optional.empty());
method.response(request);

verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
verify(blockchainQueries).safeBlockHeader();
verify(transactionSimulator).process(any(), any(), any(), any(), any());
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
}

@Test
public void shouldUseCorrectBlockNumberWhenFinalized() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "finalized");
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(blockHeader));
when(transactionSimulator.process(any(), any(), any(), any(), any()))
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
.thenReturn(Optional.empty());
method.response(request);

verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
verify(blockchainQueries).finalizedBlockHeader();
verify(transactionSimulator).process(any(), any(), any(), any(), any());
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
}

@Test
Expand All @@ -353,13 +387,13 @@ public void shouldUseCorrectBlockNumberWhenSpecified() {
when(blockchainQueries.getBlockHashByNumber(anyLong())).thenReturn(Optional.of(Hash.ZERO));
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO))
.thenReturn(Optional.of(mock(BlockHeader.class)));
when(transactionSimulator.process(any(), any(), any(), any(), any()))
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
.thenReturn(Optional.empty());

method.response(request);

verify(blockchainQueries).getBlockHeaderByHash(eq(Hash.ZERO));
verify(transactionSimulator).process(any(), any(), any(), any(), any());
verify(transactionSimulator).process(any(), eq(Optional.empty()), any(), any(), any(), any());
}

@Test
Expand Down Expand Up @@ -431,7 +465,7 @@ private void internalAutoSelectIsAllowedExceedingBalance(
.build();

verify(transactionSimulator)
.process(any(), eq(transactionValidationParams), any(), any(), any());
.process(any(), eq(Optional.empty()), eq(transactionValidationParams), any(), any(), any());
}

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

private JsonRpcRequestContext ethCallRequestWithStateOverrides(
final CallParameter callParameter,
final String blockNumberInHex,
final AccountOverrideMap overrides) {
return new JsonRpcRequestContext(
new JsonRpcRequest(
"2.0", "eth_call", new Object[] {callParameter, blockNumberInHex, overrides}));
}

private void mockTransactionProcessorSuccessResult(final JsonRpcResponse jsonRpcResponse) {
when(transactionSimulator.process(any(), any(), any(), any(), any()))
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), any(), any()))
.thenReturn(Optional.of(jsonRpcResponse));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"data": "0x12a7b914"
},
"latest",
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {}
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x0000000000000000000000000000000000000000000000000000000000000001"
},
"statusCode": 200
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"data": "0x12a7b914"
},
"latest",
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0xde0b6b3a7640000",
"nonce": 88
},
"0xb9741079a300Cb3B8f324CdDB847c0d1d273a05E": {
"stateDiff": {
"0x1cf7945003fc5b59d2f6736f0704557aa805c4f2844084ccd1173b8d56946962": "0x000000000000000000000000000000000000000000000000000000110ed03bf7"
}
}
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x0000000000000000000000000000000000000000000000000000000000000001"
},
"statusCode": 200
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"value": "0x000002"
},
"latest",
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0x000001"
},
"0xb9741079a300Cb3B8f324CdDB847c0d1d273a05E": {
"stateDiff": {
"0x1cf7945003fc5b59d2f6736f0704557aa805c4f2844084ccd1173b8d56946962": "0x000000000000000000000000000000000000000000000000000000110ed03bf7"
}
}
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"error" : {
"code" : -32004,
"message" : "Upfront cost exceeds account balance"
}
},
"statusCode": 200
}
Loading