Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/trace callmany #3468

Merged
merged 38 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0440c0a
failing tests
pinges Feb 21, 2022
39d1034
delete accidentally commited zip file
pinges Feb 21, 2022
95c17f0
work in progress
pinges Feb 21, 2022
b00a61c
wip jackson tuple parsing
frankisawesome Feb 21, 2022
ae04667
wip jackson tuple parsing
frankisawesome Feb 21, 2022
f99f340
unit test + working code
frankisawesome Feb 22, 2022
375a107
unit test working
pinges Feb 22, 2022
ae4080c
merge master + spotless
frankisawesome Feb 22, 2022
7775a12
Merge branch 'main' into feature/trace-callmany
frankisawesome Feb 22, 2022
4f39fb1
test except for state diff tests working
pinges Feb 22, 2022
e6e9290
add empty array test
frankisawesome Feb 22, 2022
d4fe6d7
merged
pinges Feb 22, 2022
5ee32bf
merged again
pinges Feb 22, 2022
2a87aa2
make the tests work by filtering expected and actual response
pinges Mar 1, 2022
66cc9d9
handle invalid call
pinges Mar 1, 2022
5592148
make exception class static
frankisawesome Mar 1, 2022
1270ef4
better variable naming/comment
frankisawesome Mar 1, 2022
2ff7bc5
Merge branch 'main' into feature/trace-callmany
frankisawesome Mar 1, 2022
c9bcc7a
remove stray warning suppression
frankisawesome Mar 1, 2022
5c9bda1
add spdx header
frankisawesome Mar 1, 2022
f5337e0
fix unit test
pinges Mar 1, 2022
c6fddeb
Merge branch 'feature/trace-callmany' of github.com:pinges/besu into …
pinges Mar 1, 2022
7ee27a3
more logging/error handling
frankisawesome Mar 1, 2022
accfaa9
some minor fixes
pinges Mar 2, 2022
104048f
Merge branch 'main' into feature/trace-callmany
pinges Mar 2, 2022
dc2272a
Merge branch 'main' into feature/trace-callmany
frankisawesome Mar 2, 2022
6b169e9
make the merge work
frankisawesome Mar 2, 2022
3301ff1
more error logging
frankisawesome Mar 3, 2022
c4f0086
code review comments bundle
frankisawesome Mar 3, 2022
c70328e
Merge branch 'main' into feature/trace-callmany
frankisawesome Mar 3, 2022
79b21f2
fix typo
frankisawesome Mar 4, 2022
5563054
Merge branch 'feature/trace-callmany' of github.com:pinges/besu into …
frankisawesome Mar 4, 2022
bfc8475
Merge branch 'main' into feature/trace-callmany
frankisawesome Mar 4, 2022
c29d29c
fix code smells
frankisawesome Mar 7, 2022
037a252
fix conflict
frankisawesome Mar 7, 2022
8740704
changes after review
pinges Mar 7, 2022
5cac849
changes after review
pinges Mar 7, 2022
abff1d6
fix complile error
pinges Mar 7, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public enum RpcMethod {
TRACE_REPLAY_BLOCK_TRANSACTIONS("trace_replayBlockTransactions"),
TRACE_BLOCK("trace_block"),
TRACE_CALL("trace_call"),
TRACE_CALL_MANY("trace_callMany"),
TRACE_TRANSACTION("trace_transaction"),
TRACE_FILTER("trace_filter"),
TX_POOL_BESU_STATISTICS("txpool_besuStatistics"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@
import java.util.Optional;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TraceCall extends AbstractTraceByBlock implements JsonRpcMethod {
private static final Logger LOG = LoggerFactory.getLogger(TraceCall.class);

public TraceCall(
final BlockchainQueries blockchainQueries,
final ProtocolSchedule protocolSchedule,
Expand Down Expand Up @@ -86,6 +91,15 @@ protected Object resultByBlockNumber(
maybeBlockHeader.get());

if (maybeSimulatorResult.isEmpty()) {
LOG.error(
"Empty simulator result, call params: {}, blockHeader: {} ",
JsonCallParameterUtil.validateAndGetCallParams(requestContext),
maybeBlockHeader.get());
return new JsonRpcErrorResponse(requestContext.getRequest().getId(), INTERNAL_ERROR);
}

if (maybeSimulatorResult.get().isInvalid()) {
LOG.error("Invalid simulator result: " + maybeSimulatorResult);
return new JsonRpcErrorResponse(requestContext.getRequest().getId(), INTERNAL_ERROR);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;

import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.BLOCK_NOT_FOUND;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.INTERNAL_ERROR;

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.parameters.BlockParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TraceCallManyParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TraceTypeParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace;
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.results.tracing.flat.FlatTrace;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.MixInIgnoreRevertReason;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TraceCallMany extends TraceCall implements JsonRpcMethod {

private static final Logger LOG = LoggerFactory.getLogger(TraceCallMany.class);
private static final ObjectMapper MAPPER_IGNORE_REVERT_REASON = new ObjectMapper();

public TraceCallMany(
final BlockchainQueries blockchainQueries,
final ProtocolSchedule protocolSchedule,
final TransactionSimulator transactionSimulator) {
super(blockchainQueries, protocolSchedule, transactionSimulator);

// The trace_call specification does not output the revert reason, so we have to remove it
pinges marked this conversation as resolved.
Show resolved Hide resolved
MAPPER_IGNORE_REVERT_REASON.addMixIn(FlatTrace.class, MixInIgnoreRevertReason.class);
}

@Override
public String getName() {
return transactionSimulator != null ? RpcMethod.TRACE_CALL_MANY.getMethodName() : null;
}

@Override
protected BlockParameter blockParameter(final JsonRpcRequestContext request) {
final Optional<BlockParameter> maybeBlockParameter =
request.getOptionalParameter(2, BlockParameter.class);

if (maybeBlockParameter.isPresent()) {
return maybeBlockParameter.get();
}

return BlockParameter.LATEST;
}

@Override
protected Object resultByBlockNumber(
final JsonRpcRequestContext requestContext, final long blockNumber) {

if (requestContext.getRequest().getParamLength() != 2) {
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
}

final TraceCallManyParameter[] transactionsAndTraceTypeParameters;
try {
transactionsAndTraceTypeParameters =
requestContext.getRequiredParameter(0, TraceCallManyParameter[].class);
pinges marked this conversation as resolved.
Show resolved Hide resolved
} catch (final Exception e) {
LOG.error("Error parsing trace call many parameter: {}", e.getLocalizedMessage());
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
}

final Optional<BlockHeader> maybeBlockHeader =
blockchainQueries.get().getBlockHeaderByNumber(blockNumber);
pinges marked this conversation as resolved.
Show resolved Hide resolved

if (maybeBlockHeader.isEmpty()) {
return new JsonRpcErrorResponse(requestContext.getRequest().getId(), BLOCK_NOT_FOUND);
}

final List<JsonNode> traceCallResults = new ArrayList<>();

final WorldUpdater updater = transactionSimulator.getWorldUpdater(maybeBlockHeader.get());
pinges marked this conversation as resolved.
Show resolved Hide resolved
try {
Arrays.stream(transactionsAndTraceTypeParameters)
.forEachOrdered(
param -> {
final WorldUpdater finalUpdater = updater.updater();
pinges marked this conversation as resolved.
Show resolved Hide resolved
try {
traceCallResults.add(
getSingleCallResult(
param.getTuple().getJsonCallParameter(),
param.getTuple().getTraceTypeParameter(),
maybeBlockHeader.get(),
finalUpdater));
} catch (final TransactionInvalidException e) {
LOG.error("Invalid transaction simulator result");
throw new RuntimeException();
} catch (final EmptySimulatorResultException e) {
LOG.error(
"Empty simulator result, call params: {}, blockHeader: {} ",
JsonCallParameterUtil.validateAndGetCallParams(requestContext),
maybeBlockHeader.get());
throw new RuntimeException();
}
finalUpdater.commit();
});
} catch (final Exception e) {
return new JsonRpcErrorResponse(requestContext.getRequest().getId(), INTERNAL_ERROR);
}

return traceCallResults;
}

private JsonNode getSingleCallResult(
final JsonCallParameter callParameter,
final TraceTypeParameter traceTypeParameter,
final BlockHeader header,
final WorldUpdater worldUpdater) {
final Set<TraceTypeParameter.TraceType> traceTypes = traceTypeParameter.getTraceTypes();
final DebugOperationTracer tracer = new DebugOperationTracer(buildTraceOptions(traceTypes));
final Optional<TransactionSimulatorResult> maybeSimulatorResult =
transactionSimulator.processWithWorldUpdater(
callParameter, buildTransactionValidationParams(), tracer, header, worldUpdater);

LOG.trace("Executing {} call for transaction {}", traceTypeParameter, callParameter);
if (maybeSimulatorResult.isEmpty()) {
throw new EmptySimulatorResultException();
}
if (maybeSimulatorResult.get().isInvalid()) {
pinges marked this conversation as resolved.
Show resolved Hide resolved
throw new TransactionInvalidException();
}

final TransactionTrace transactionTrace =
new TransactionTrace(
maybeSimulatorResult.get().getTransaction(),
maybeSimulatorResult.get().getResult(),
tracer.getTraceFrames());

final Block block = blockchainQueries.get().getBlockchain().getChainHeadBlock();

return getTraceCallResult(
protocolSchedule, traceTypes, maybeSimulatorResult, transactionTrace, block);
}

private static class TransactionInvalidException extends RuntimeException {
TransactionInvalidException() {
super();
}
}

private static class EmptySimulatorResultException extends RuntimeException {
EmptySimulatorResultException() {
super();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters;

import java.io.IOException;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

public class TraceCallManyParameter {
TraceCallParamterTuple params;

@JsonCreator
public TraceCallManyParameter(
@JsonDeserialize(using = TraceCallParameterDeserializer.class)
final TraceCallParamterTuple parameters) {
this.params = parameters;
}

public TraceCallParamterTuple getTuple() {
return this.params;
}
}

class TraceCallParameterDeserializer extends StdDeserializer<TraceCallParamterTuple> {

public TraceCallParameterDeserializer(final Class<?> vc) {
super(vc);
}

public TraceCallParameterDeserializer() {
this(null);
}

@Override
public TraceCallParamterTuple deserialize(final JsonParser p, final DeserializationContext ctxt)
throws IOException {
final ObjectMapper mapper = new ObjectMapper();
final JsonNode tupleNode = p.getCodec().readTree(p);
return new TraceCallParamterTuple(
mapper.readValue(tupleNode.get(0).toString(), JsonCallParameter.class),
mapper.readValue(tupleNode.get(1).toString(), TraceTypeParameter.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters;

public class TraceCallParamterTuple {
frankisawesome marked this conversation as resolved.
Show resolved Hide resolved
private final JsonCallParameter jsonCallParameter;
private final TraceTypeParameter traceTypeParameter;

public TraceCallParamterTuple(
final JsonCallParameter callParameter, final TraceTypeParameter traceTypeParameter) {
this.jsonCallParameter = callParameter;
this.traceTypeParameter = traceTypeParameter;
}

public JsonCallParameter getJsonCallParameter() {
return jsonCallParameter;
}

public TraceTypeParameter getTraceTypeParameter() {
return traceTypeParameter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceBlock;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceCall;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceCallMany;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceFilter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceReplayBlockTransactions;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceTransaction;
Expand Down Expand Up @@ -66,6 +67,14 @@ protected Map<String, JsonRpcMethod> create() {
() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
new TraceBlock(() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries),
new TraceCall(
blockchainQueries,
protocolSchedule,
new TransactionSimulator(
blockchainQueries.getBlockchain(),
blockchainQueries.getWorldStateArchive(),
protocolSchedule,
privacyParameters)),
new TraceCallMany(
blockchainQueries,
protocolSchedule,
new TransactionSimulator(
Expand Down
Loading