From d736203a9dd815715bbe2e936aa69555c727e5ea Mon Sep 17 00:00:00 2001 From: garyschulte Date: Wed, 15 Mar 2023 08:00:17 -0700 Subject: [PATCH] Feature/minimal safe tracing (#5197) tracing endpoints keep open a worldstate for the duration of tracing and response generation. --------- Signed-off-by: Karim TAAM Signed-off-by: garyschulte Co-authored-by: Karim TAAM --- .../api/graphql/GraphQLDataFetchers.java | 8 +- .../pojoadapter/BlockAdapterBase.java | 4 +- .../internal/pojoadapter/LogAdapter.java | 2 +- .../pojoadapter/PendingStateAdapter.java | 2 +- .../pojoadapter/TransactionAdapter.java | 12 +- .../internal/methods/AbstractTraceByHash.java | 36 ++++-- .../internal/methods/DebugAccountAt.java | 87 ++++++++------ .../internal/methods/DebugAccountRange.java | 20 ++-- .../DebugStandardTraceBlockToFile.java | 25 ++-- .../internal/methods/DebugStorageRangeAt.java | 45 ++++---- .../internal/methods/DebugTraceBlock.java | 17 ++- .../methods/DebugTraceBlockByHash.java | 22 +++- .../methods/DebugTraceBlockByNumber.java | 16 ++- .../methods/DebugTraceTransaction.java | 11 +- .../jsonrpc/internal/methods/EthGetProof.java | 9 +- .../jsonrpc/internal/methods/TraceBlock.java | 31 +++-- .../internal/methods/TraceCallMany.java | 14 +-- .../methods/TraceReplayBlockTransactions.java | 16 ++- .../internal/processor/BlockReplay.java | 72 ++++-------- .../internal/processor/BlockTracer.java | 24 ++-- .../jsonrpc/internal/processor/Tracer.java | 107 ++++++++++++++++++ .../internal/processor/TransactionTracer.java | 45 ++++---- .../jsonrpc/methods/DebugJsonRpcMethods.java | 7 +- .../jsonrpc/methods/TraceJsonRpcMethods.java | 5 +- .../ethereum/api/query/BlockchainQueries.java | 27 ++--- .../besu/ethereum/api/util/TraceUtil.java | 100 ---------------- .../internal/methods/DebugAccountAtTest.java | 42 ++++++- .../DebugStandardTraceBadBlockToFileTest.java | 22 +++- .../DebugStandardTraceBlockToFileTest.java | 13 ++- .../methods/DebugStorageRangeAtTest.java | 27 +++-- .../methods/DebugTraceBlockByHashTest.java | 33 +++++- .../methods/DebugTraceBlockByNumberTest.java | 33 +++++- .../internal/methods/DebugTraceBlockTest.java | 39 +++++-- .../methods/DebugTraceTransactionTest.java | 49 ++++++-- .../internal/methods/EthGetProofTest.java | 56 +++++---- .../processor/TransactionTracerTest.java | 28 ++--- .../ethereum/retesteth/RetestethContext.java | 6 +- 37 files changed, 671 insertions(+), 441 deletions(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/Tracer.java delete mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/util/TraceUtil.java diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java index dd96c68eec4..fbf3b602c05 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java @@ -224,9 +224,9 @@ DataFetcher> getAccountDataFetcher() { ws -> { final Account account = ws.get(addr); if (account == null) { - return new EmptyAccountAdapter(addr); + return Optional.of(new EmptyAccountAdapter(addr)); } - return new AccountAdapter(account); + return Optional.of(new AccountAdapter(account)); }) .or( () -> { @@ -246,9 +246,9 @@ DataFetcher> getAccountDataFetcher() { ws -> { final Account account = ws.get(addr); if (account == null) { - return new EmptyAccountAdapter(addr); + return Optional.of(new EmptyAccountAdapter(addr)); } - return new AccountAdapter(account); + return Optional.of(new AccountAdapter(account)); }); } }; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java index 957a6e98551..afa63fe862a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java @@ -96,7 +96,7 @@ public Optional getMiner(final DataFetchingEnvironment environment) } return query - .getAndMapWorldState(blockNumber, ws -> ws.get(header.getCoinbase())) + .getAndMapWorldState(blockNumber, ws -> Optional.ofNullable(ws.get(header.getCoinbase()))) .map(account -> (AdapterBase) new AccountAdapter(account)) .or(() -> Optional.of(new EmptyAccountAdapter(header.getCoinbase()))); } @@ -150,7 +150,7 @@ public Optional getAccount(final DataFetchingEnvironment environ bn, ws -> { final Address address = environment.getArgument("address"); - return new AccountAdapter(ws.get(address)); + return Optional.of(new AccountAdapter(ws.get(address))); }); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java index 52f55ea3ff0..20c1e31feba 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java @@ -64,6 +64,6 @@ public Optional getAccount(final DataFetchingEnvironment environ } return query.getAndMapWorldState( - blockNumber, ws -> new AccountAdapter(ws.get(logWithMetadata.getLogger()))); + blockNumber, ws -> Optional.of(new AccountAdapter(ws.get(logWithMetadata.getLogger())))); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java index 61300b3fe21..f3462f4209d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java @@ -66,7 +66,7 @@ public Optional getAccount( final Long blockNumber = dataFetchingEnvironment.getArgument("blockNumber"); final long latestBlockNumber = blockchainQuery.latestBlock().get().getHeader().getNumber(); return blockchainQuery - .getAndMapWorldState(latestBlockNumber, ws -> ws.get(addr)) + .getAndMapWorldState(latestBlockNumber, ws -> Optional.ofNullable(ws.get(addr))) .map(AccountAdapter::new); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java index 454590539df..72f2f3b8abe 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java @@ -85,8 +85,9 @@ public Optional getFrom(final DataFetchingEnvironment environmen return query.getAndMapWorldState( blockNumber, mutableWorldState -> - new AccountAdapter( - mutableWorldState.get(transactionWithMetadata.getTransaction().getSender()))); + Optional.of( + new AccountAdapter( + mutableWorldState.get(transactionWithMetadata.getTransaction().getSender())))); } public Optional getTo(final DataFetchingEnvironment environment) { @@ -102,9 +103,7 @@ public Optional getTo(final DataFetchingEnvironment environment) transactionWithMetadata .getTransaction() .getTo() - .map(address -> new AccountAdapter(address, ws.get(address))) - // safe because mapWorldState returns Optional.ofNullable - .orElse(null)); + .map(address -> new AccountAdapter(address, ws.get(address)))); } public Optional getValue() { @@ -174,7 +173,8 @@ public Optional getCreatedContract(final DataFetchingEnvironment return Optional.empty(); } final long blockNumber = bn.orElseGet(txBlockNumber::get); - return query.getAndMapWorldState(blockNumber, ws -> new AccountAdapter(ws.get(addr.get()))); + return query.getAndMapWorldState( + blockNumber, ws -> Optional.of(new AccountAdapter(ws.get(addr.get())))); } } return Optional.empty(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractTraceByHash.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractTraceByHash.java index 4cb1fd1de0f..281d7248108 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractTraceByHash.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractTraceByHash.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTraceGenerator; @@ -28,6 +29,7 @@ import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import java.util.Collections; +import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Stream; @@ -62,19 +64,33 @@ private Stream getTraceBlock(final Block block, final Hash transactio if (block == null || block.getBody().getTransactions().isEmpty()) { return Stream.empty(); } - final TransactionTrace transactionTrace = getTransactionTrace(block, transactionHash); - return getTraceStream(transactionTrace, block); + return Tracer.processTracing( + blockchainQueries, + Optional.of(block.getHeader()), + mutableWorldState -> { + final TransactionTrace transactionTrace = getTransactionTrace(block, transactionHash); + return Optional.ofNullable(getTraceStream(transactionTrace, block)); + }) + .orElse(Stream.empty()); } private TransactionTrace getTransactionTrace(final Block block, final Hash transactionHash) { - return blockTracerSupplier - .get() - .trace(block, new DebugOperationTracer(new TraceOptions(false, false, true))) - .map(BlockTrace::getTransactionTraces) - .orElse(Collections.emptyList()) - .stream() - .filter(trxTrace -> trxTrace.getTransaction().getHash().equals(transactionHash)) - .findFirst() + return Tracer.processTracing( + blockchainQueries, + Optional.of(block.getHeader()), + mutableWorldState -> { + return blockTracerSupplier + .get() + .trace( + mutableWorldState, + block, + new DebugOperationTracer(new TraceOptions(false, false, true))) + .map(BlockTrace::getTransactionTraces) + .orElse(Collections.emptyList()) + .stream() + .filter(trxTrace -> trxTrace.getTransaction().getHash().equals(transactionHash)) + .findFirst(); + }) .orElseThrow(); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountAt.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountAt.java index bea7efb9aec..a6cde35bb4a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountAt.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountAt.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; 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; @@ -87,42 +88,56 @@ protected Object resultByBlockHash( requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS); } - final Optional transactionTrace = - blockTracerSupplier - .get() - .trace(blockHash, new DebugOperationTracer(new TraceOptions(false, true, true))) - .map(BlockTrace::getTransactionTraces) - .orElse(Collections.emptyList()) - .stream() - .filter( - trxTrace -> - trxTrace - .getTransaction() - .getHash() - .equals(transactions.get(txIndex).getTransaction().getHash())) - .findFirst(); - - if (transactionTrace.isEmpty()) { - return new JsonRpcErrorResponse( - requestContext.getRequest().getId(), JsonRpcError.TRANSACTION_NOT_FOUND); - } - - Optional account = - transactionTrace.get().getTraceFrames().stream() - .map(traceFrame -> traceFrame.getWorldUpdater().get(address)) - .filter(Objects::nonNull) - .filter(a -> a.getAddress().equals(address)) - .findFirst(); - if (account.isEmpty()) { - return new JsonRpcErrorResponse( - requestContext.getRequest().getId(), JsonRpcError.NO_ACCOUNT_FOUND); - } - - return debugAccountAtResult( - account.get().getCode(), - Quantity.create(account.get().getNonce()), - Quantity.create(account.get().getBalance()), - Quantity.create(account.get().getCodeHash())); + return Tracer.processTracing( + blockchainQueries.get(), + Optional.of(block.get().getHeader()), + mutableWorldState -> { + final Optional transactionTrace = + blockTracerSupplier + .get() + .trace( + mutableWorldState, + blockHash, + new DebugOperationTracer(new TraceOptions(false, true, true))) + .map(BlockTrace::getTransactionTraces) + .orElse(Collections.emptyList()) + .stream() + .filter( + trxTrace -> + trxTrace + .getTransaction() + .getHash() + .equals(transactions.get(txIndex).getTransaction().getHash())) + .findFirst(); + + if (transactionTrace.isEmpty()) { + return Optional.of( + new JsonRpcErrorResponse( + requestContext.getRequest().getId(), JsonRpcError.TRANSACTION_NOT_FOUND)); + } + + Optional account = + transactionTrace.get().getTraceFrames().stream() + .map(traceFrame -> traceFrame.getWorldUpdater().get(address)) + .filter(Objects::nonNull) + .filter(a -> a.getAddress().equals(address)) + .findFirst(); + if (account.isEmpty()) { + return Optional.of( + new JsonRpcErrorResponse( + requestContext.getRequest().getId(), JsonRpcError.NO_ACCOUNT_FOUND)); + } + + return Optional.of( + debugAccountAtResult( + account.get().getCode(), + Quantity.create(account.get().getNonce()), + Quantity.create(account.get().getBalance()), + Quantity.create(account.get().getCodeHash()))); + }) + .orElse( + new JsonRpcErrorResponse( + requestContext.getRequest().getId(), JsonRpcError.WORLD_STATE_UNAVAILABLE)); } protected ImmutableDebugAccountAtResult debugAccountAtResult( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountRange.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountRange.java index 777cc3f98bf..4ca5cb0685f 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountRange.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountRange.java @@ -88,15 +88,17 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { accounts.remove(maxResults); } - return new JsonRpcSuccessResponse( - requestContext.getRequest().getId(), - new DebugAccountRangeAtResult( - accounts.stream() - .collect( - Collectors.toMap( - account -> account.getAddressHash().toString(), - account -> account.getAddress().orElse(Address.ZERO).toString())), - nextKey.toString())); + return Optional.of( + new JsonRpcSuccessResponse( + requestContext.getRequest().getId(), + new DebugAccountRangeAtResult( + accounts.stream() + .collect( + Collectors.toMap( + account -> account.getAddressHash().toString(), + account -> + account.getAddress().orElse(Address.ZERO).toString())), + nextKey.toString()))); }) .orElse(emptyResponse(requestContext)); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java index 6c3d904bc0d..d9cab6769e4 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFile.java @@ -20,6 +20,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.parameters.TransactionTraceParams; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; @@ -29,11 +30,11 @@ import org.hyperledger.besu.ethereum.core.Block; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.Supplier; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Suppliers; public class DebugStandardTraceBlockToFile implements JsonRpcMethod { @@ -79,14 +80,18 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { protected List traceBlock( final Block block, final Optional transactionTraceParams) { - return transactionTracerSupplier - .get() - .traceTransactionToFile( - block.getHash(), transactionTraceParams, dataDir.resolve(TRACE_PATH)); - } - - protected Object emptyResult() { - final ObjectMapper mapper = new ObjectMapper(); - return mapper.createArrayNode(); + return Tracer.processTracing( + blockchainQueries.get(), + Optional.of(block.getHeader()), + mutableWorldState -> + Optional.of( + transactionTracerSupplier + .get() + .traceTransactionToFile( + mutableWorldState, + block.getHash(), + transactionTraceParams, + dataDir.resolve(TRACE_PATH)))) + .orElse(new ArrayList<>()); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAt.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAt.java index 3c5db065ee7..0ea50b41aca 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAt.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAt.java @@ -20,6 +20,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockReplay; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer.TraceableState; 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.results.DebugStorageRangeAtResult; @@ -29,7 +31,6 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.AccountStorageEntry; -import org.hyperledger.besu.evm.worldstate.WorldState; import java.util.Collections; import java.util.NavigableMap; @@ -85,36 +86,38 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { return emptyResponse(requestContext); } - final Optional optional = + final Optional maybeTransactionIndex = blockchainQueries.get().transactionByBlockHashAndIndex(blockHash, transactionIndex); - return optional - .map( - transactionWithMetadata -> - (blockReplay + return Tracer.processTracing( + blockchainQueries.get(), + Optional.of(blockHeaderOptional.get()), + mutableWorldState -> { + if (maybeTransactionIndex.isEmpty()) { + return Optional.of( + extractStorageAt( + requestContext, accountAddress, startKey, limit, mutableWorldState)); + } else { + return blockReplay .get() .afterTransactionInBlock( + mutableWorldState, blockHash, - transactionWithMetadata.getTransaction().getHash(), + maybeTransactionIndex.get().getTransaction().getHash(), (transaction, blockHeader, blockchain, - worldState, transactionProcessor, protocolSpec) -> extractStorageAt( - requestContext, accountAddress, startKey, limit, worldState)) - .orElseGet(() -> emptyResponse(requestContext)))) - .orElseGet( - () -> - blockchainQueries - .get() - .getAndMapWorldState( - blockHeaderOptional.get().getNumber(), - worldState -> - extractStorageAt( - requestContext, accountAddress, startKey, limit, worldState)) - .orElseGet(() -> emptyResponse(requestContext))); + requestContext, + accountAddress, + startKey, + limit, + mutableWorldState)); + } + }) + .orElse(emptyResponse(requestContext)); } private Optional hashFromParameter(final BlockParameterOrBlockHash blockParameter) { @@ -137,7 +140,7 @@ private JsonRpcSuccessResponse extractStorageAt( final Address accountAddress, final Hash startKey, final int limit, - final WorldState worldState) { + final TraceableState worldState) { final Account account = worldState.get(accountAddress); final NavigableMap entries = account.storageEntriesFrom(startKey, limit + 1); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlock.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlock.java index 97892ebda93..762728edbe4 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlock.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlock.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TransactionTraceParams; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; 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; @@ -33,6 +34,7 @@ import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import java.util.Collection; +import java.util.Optional; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; @@ -79,11 +81,16 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { if (this.blockchain.blockByHash(block.getHeader().getParentHash()).isPresent()) { final Collection results = - blockTracerSupplier - .get() - .trace(block, new DebugOperationTracer(traceOptions)) - .map(BlockTrace::getTransactionTraces) - .map(DebugTraceTransactionResult::of) + Tracer.processTracing( + blockchain, + Optional.of(block.getHeader()), + mutableWorldState -> { + return blockTracerSupplier + .get() + .trace(mutableWorldState, block, new DebugOperationTracer(traceOptions)) + .map(BlockTrace::getTransactionTraces) + .map(DebugTraceTransactionResult::of); + }) .orElse(null); return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), results); } else { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHash.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHash.java index f600b090c74..3b5f5e734f7 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHash.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHash.java @@ -20,9 +20,11 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TransactionTraceParams; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; 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.results.DebugTraceTransactionResult; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.debug.TraceOptions; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; @@ -32,9 +34,13 @@ public class DebugTraceBlockByHash implements JsonRpcMethod { private final Supplier blockTracerSupplier; + private final Supplier blockchainQueries; - public DebugTraceBlockByHash(final Supplier blockTracerSupplier) { + public DebugTraceBlockByHash( + final Supplier blockTracerSupplier, + final Supplier blockchainQueriesSupplier) { this.blockTracerSupplier = blockTracerSupplier; + this.blockchainQueries = blockchainQueriesSupplier; } @Override @@ -52,11 +58,15 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { .orElse(TraceOptions.DEFAULT); final Collection results = - blockTracerSupplier - .get() - .trace(blockHash, new DebugOperationTracer(traceOptions)) - .map(BlockTrace::getTransactionTraces) - .map(DebugTraceTransactionResult::of) + Tracer.processTracing( + blockchainQueries.get(), + blockHash, + mutableWorldState -> + blockTracerSupplier + .get() + .trace(mutableWorldState, blockHash, new DebugOperationTracer(traceOptions)) + .map(BlockTrace::getTransactionTraces) + .map(DebugTraceTransactionResult::of)) .orElse(null); return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), results); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumber.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumber.java index 95f8a65732a..977a1d87936 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumber.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumber.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TransactionTraceParams; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.DebugTraceTransactionResult; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.debug.TraceOptions; @@ -62,11 +63,16 @@ protected Object resultByBlockNumber( return blockHash .flatMap( hash -> - blockTracerSupplier - .get() - .trace(hash, new DebugOperationTracer(traceOptions)) - .map(BlockTrace::getTransactionTraces) - .map(DebugTraceTransactionResult::of)) + Tracer.processTracing( + blockchainQueriesSupplier.get(), + hash, + mutableWorldState -> { + return blockTracerSupplier + .get() + .trace(mutableWorldState, hash, new DebugOperationTracer(traceOptions)) + .map(BlockTrace::getTransactionTraces) + .map(DebugTraceTransactionResult::of); + })) .orElse(null); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransaction.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransaction.java index 4ca20c77cd6..1ce30e243e5 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransaction.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransaction.java @@ -18,6 +18,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.parameters.TransactionTraceParams; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; @@ -74,9 +75,13 @@ private DebugTraceTransactionResult debugTraceTransactionResult( final DebugOperationTracer execTracer = new DebugOperationTracer(traceOptions); - return transactionTracer - .traceTransaction(blockHash, hash, execTracer) - .map(DebugTraceTransactionResult::new) + return Tracer.processTracing( + blockchain, + blockHash, + mutableWorldState -> + transactionTracer + .traceTransaction(mutableWorldState, blockHash, hash, execTracer) + .map(DebugTraceTransactionResult::new)) .orElse(null); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProof.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProof.java index b2d50075792..6075f2be28b 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProof.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProof.java @@ -72,9 +72,12 @@ protected Object resultByBlockHash( new JsonRpcSuccessResponse( requestContext.getRequest().getId(), GetProofResult.buildGetProofResult(address, proof))) - .orElse( - new JsonRpcErrorResponse( - requestContext.getRequest().getId(), JsonRpcError.NO_ACCOUNT_FOUND)); + .or( + () -> + Optional.of( + new JsonRpcErrorResponse( + requestContext.getRequest().getId(), + JsonRpcError.NO_ACCOUNT_FOUND))); }) .orElse( new JsonRpcErrorResponse( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java index 64e4b421d52..0658fe6c94a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceBlock.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTraceGenerator; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.RewardTraceGenerator; @@ -89,15 +90,27 @@ protected ArrayNodeWrapper traceBlock( } final ArrayNodeWrapper resultArrayNode = new ArrayNodeWrapper(MAPPER.createArrayNode()); - blockTracerSupplier - .get() - .trace(block, new DebugOperationTracer(new TraceOptions(false, false, true))) - .ifPresent( - blockTrace -> - generateTracesFromTransactionTraceAndBlock( - filterParameter, blockTrace.getTransactionTraces(), block, resultArrayNode)); - - generateRewardsFromBlock(filterParameter, block, resultArrayNode); + Tracer.processTracing( + blockchainQueriesSupplier.get(), + Optional.of(block.getHeader()), + mutableWorldState -> { + blockTracerSupplier + .get() + .trace( + mutableWorldState, + block, + new DebugOperationTracer(new TraceOptions(false, false, true))) + .ifPresent( + blockTrace -> + generateTracesFromTransactionTraceAndBlock( + filterParameter, + blockTrace.getTransactionTraces(), + block, + resultArrayNode)); + + generateRewardsFromBlock(filterParameter, block, resultArrayNode); + return Optional.empty(); + }); return resultArrayNode; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java index 6f78d28ab1d..d863caf0caf 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java @@ -130,20 +130,20 @@ protected Object resultByBlockNumber( }); } catch (final TransactionInvalidException e) { LOG.error("Invalid transaction simulator result"); - return new JsonRpcErrorResponse( - requestContext.getRequest().getId(), INTERNAL_ERROR); + return Optional.of( + new JsonRpcErrorResponse(requestContext.getRequest().getId(), INTERNAL_ERROR)); } catch (final EmptySimulatorResultException e) { LOG.error( "Empty simulator result, call params: {}, blockHeader: {} ", JsonCallParameterUtil.validateAndGetCallParams(requestContext), blockHeader); - return new JsonRpcErrorResponse( - requestContext.getRequest().getId(), INTERNAL_ERROR); + return Optional.of( + new JsonRpcErrorResponse(requestContext.getRequest().getId(), INTERNAL_ERROR)); } catch (final Exception e) { - return new JsonRpcErrorResponse( - requestContext.getRequest().getId(), INTERNAL_ERROR); + return Optional.of( + new JsonRpcErrorResponse(requestContext.getRequest().getId(), INTERNAL_ERROR)); } - return traceCallResults; + return Optional.of(traceCallResults); }); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceReplayBlockTransactions.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceReplayBlockTransactions.java index 22c88e1db14..1aed9088dd9 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceReplayBlockTransactions.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceReplayBlockTransactions.java @@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TraceTypeParameter.TraceType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TraceFormatter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TraceWriter; @@ -113,11 +114,16 @@ private Object traceBlock(final Block block, final TraceTypeParameter traceTypeP final TraceOptions traceOptions = new TraceOptions(false, false, traceTypes.contains(VM_TRACE) || traceTypes.contains(TRACE)); - return blockTracerSupplier - .get() - .trace(block, new DebugOperationTracer(traceOptions)) - .map(BlockTrace::getTransactionTraces) - .map((traces) -> generateTracesFromTransactionTrace(traces, block, traceTypes)) + return Tracer.processTracing( + blockchainQueriesSupplier.get(), + Optional.of(block.getHeader()), + mutableWorldState -> { + return blockTracerSupplier + .get() + .trace(mutableWorldState, block, new DebugOperationTracer(traceOptions)) + .map(BlockTrace::getTransactionTraces) + .map((traces) -> generateTracesFromTransactionTrace(traces, block, traceTypes)); + }) .orElse(null); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java index 98e44c822ec..618489480d5 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java @@ -17,11 +17,11 @@ import org.hyperledger.besu.datatypes.DataGas; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer.TraceableState; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; @@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; -import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.util.List; import java.util.Optional; @@ -38,15 +37,10 @@ public class BlockReplay { private final ProtocolSchedule protocolSchedule; private final Blockchain blockchain; - private final WorldStateArchive worldStateArchive; - public BlockReplay( - final ProtocolSchedule protocolSchedule, - final Blockchain blockchain, - final WorldStateArchive worldStateArchive) { + public BlockReplay(final ProtocolSchedule protocolSchedule, final Blockchain blockchain) { this.protocolSchedule = protocolSchedule; this.blockchain = blockchain; - this.worldStateArchive = worldStateArchive; } public Optional block( @@ -54,7 +48,7 @@ public Optional block( return performActionWithBlock( block.getHeader(), block.getBody(), - (body, header, blockchain, mutableWorldState, transactionProcessor, protocolSpec) -> { + (body, header, blockchain, transactionProcessor, protocolSpec) -> { final Wei dataGasPrice = protocolSpec .getFeeMarket() @@ -69,12 +63,7 @@ public Optional block( .map( transaction -> action.performAction( - transaction, - header, - blockchain, - mutableWorldState, - transactionProcessor, - dataGasPrice)) + transaction, header, blockchain, transactionProcessor, dataGasPrice)) .toList(); return Optional.of(new BlockTrace(transactionTraces)); }); @@ -86,10 +75,13 @@ public Optional block( } public Optional beforeTransactionInBlock( - final Hash blockHash, final Hash transactionHash, final TransactionAction action) { + final TraceableState mutableWorldState, + final Hash blockHash, + final Hash transactionHash, + final TransactionAction action) { return performActionWithBlock( blockHash, - (body, header, blockchain, mutableWorldState, transactionProcessor, protocolSpec) -> { + (body, header, blockchain, transactionProcessor, protocolSpec) -> { final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(header, blockchain); final Wei dataGasPrice = protocolSpec @@ -104,12 +96,7 @@ public Optional beforeTransactionInBlock( if (transaction.getHash().equals(transactionHash)) { return Optional.of( action.performAction( - transaction, - header, - blockchain, - mutableWorldState, - transactionProcessor, - dataGasPrice)); + transaction, header, blockchain, transactionProcessor, dataGasPrice)); } else { transactionProcessor.processTransaction( blockchain, @@ -128,15 +115,19 @@ public Optional beforeTransactionInBlock( } public Optional afterTransactionInBlock( - final Hash blockHash, final Hash transactionHash, final TransactionAction action) { + final TraceableState mutableWorldState, + final Hash blockHash, + final Hash transactionHash, + final TransactionAction action) { return beforeTransactionInBlock( + mutableWorldState, blockHash, transactionHash, - (transaction, blockHeader, blockchain, worldState, transactionProcessor, dataGasPrice) -> { + (transaction, blockHeader, blockchain, transactionProcessor, dataGasPrice) -> { final ProtocolSpec spec = protocolSchedule.getByBlockHeader(blockHeader); transactionProcessor.processTransaction( blockchain, - worldState.updater(), + mutableWorldState.updater(), blockHeader, transaction, spec.getMiningBeneficiaryCalculator().calculateBeneficiary(blockHeader), @@ -145,7 +136,7 @@ public Optional afterTransactionInBlock( TransactionValidationParams.blockReplay(), dataGasPrice); return action.performAction( - transaction, blockHeader, blockchain, worldState, transactionProcessor, dataGasPrice); + transaction, blockHeader, blockchain, transactionProcessor, dataGasPrice); }); } @@ -168,30 +159,7 @@ private Optional performActionWithBlock( } final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(header); final MainnetTransactionProcessor transactionProcessor = protocolSpec.getTransactionProcessor(); - final BlockHeader previous = blockchain.getBlockHeader(header.getParentHash()).orElse(null); - if (previous == null) { - return Optional.empty(); - } - try (final MutableWorldState worldState = - worldStateArchive - .getMutable(previous.getStateRoot(), previous.getBlockHash(), false) - .map( - ws -> { - if (!ws.isPersistable()) { - return ws.copy(); - } - return ws; - }) - .orElseThrow( - () -> - new IllegalArgumentException( - "Missing worldstate for stateroot " - + previous.getStateRoot().toShortHexString()))) { - return action.perform( - body, header, blockchain, worldState, transactionProcessor, protocolSpec); - } catch (Exception ex) { - return Optional.empty(); - } + return action.perform(body, header, blockchain, transactionProcessor, protocolSpec); } private Optional getBlock(final Hash blockHash) { @@ -217,7 +185,6 @@ Optional perform( BlockBody body, BlockHeader blockHeader, Blockchain blockchain, - MutableWorldState worldState, MainnetTransactionProcessor transactionProcessor, ProtocolSpec protocolSpec); } @@ -228,7 +195,6 @@ T performAction( Transaction transaction, BlockHeader blockHeader, Blockchain blockchain, - MutableWorldState worldState, MainnetTransactionProcessor transactionProcessor, Wei dataGasPrice); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java index 599980e5c60..bfccaac9e2b 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java @@ -16,6 +16,7 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; @@ -37,22 +38,23 @@ public BlockTracer(final BlockReplay blockReplay) { this.blockReplay = blockReplay; } - public Optional trace(final Hash blockHash, final DebugOperationTracer tracer) { - return blockReplay.block(blockHash, prepareReplayAction(tracer)); + public Optional trace( + final Tracer.TraceableState mutableWorldState, + final Hash blockHash, + final DebugOperationTracer tracer) { + return blockReplay.block(blockHash, prepareReplayAction(mutableWorldState, tracer)); } - public Optional trace(final Block block, final DebugOperationTracer tracer) { - return blockReplay.block(block, prepareReplayAction(tracer)); + public Optional trace( + final Tracer.TraceableState mutableWorldState, + final Block block, + final DebugOperationTracer tracer) { + return blockReplay.block(block, prepareReplayAction(mutableWorldState, tracer)); } private BlockReplay.TransactionAction prepareReplayAction( - final DebugOperationTracer tracer) { - return (transaction, - header, - blockchain, - mutableWorldState, - transactionProcessor, - dataGasPrice) -> { + final MutableWorldState mutableWorldState, final DebugOperationTracer tracer) { + return (transaction, header, blockchain, transactionProcessor, dataGasPrice) -> { // if we have no prior updater, it must be the first TX, so use the block's initial state if (chainedUpdater == null) { chainedUpdater = mutableWorldState.updater(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/Tracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/Tracer.java new file mode 100644 index 00000000000..136ff0a91ff --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/Tracer.java @@ -0,0 +1,107 @@ +/* + * 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.processor; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.apache.tuweni.bytes.Bytes32; + +public class Tracer { + + public static Optional processTracing( + final BlockchainQueries blockchainQueries, + final Hash blockHash, + final Function> mapper) { + return processTracing( + blockchainQueries, blockchainQueries.getBlockHeaderByHash(blockHash), mapper); + } + + public static Optional processTracing( + final BlockchainQueries blockchainQueries, + final Optional blockHeader, + final Function> mapper) { + return blockHeader + .map(BlockHeader::getParentHash) + .flatMap( + parentHash -> + blockchainQueries.getAndMapWorldState( + parentHash, + mutableWorldState -> mapper.apply(new TraceableState(mutableWorldState)))); + } + + /** + * This class force the use of the processTracing method to do tracing. processTracing allows you + * to cleanly manage the worldstate, to close it etc + */ + public static class TraceableState implements MutableWorldState { + private final MutableWorldState mutableWorldState; + + private TraceableState(final MutableWorldState mutableWorldState) { + this.mutableWorldState = mutableWorldState; + } + + @Override + public MutableWorldState copy() { + throw new UnsupportedOperationException( + "This method is not supported and will not exist in the future version of MutableWorldState interface"); + } + + @Override + public void persist(final BlockHeader blockHeader) { + mutableWorldState.persist(blockHeader); + } + + @Override + public WorldUpdater updater() { + return mutableWorldState.updater(); + } + + @Override + public Hash rootHash() { + return mutableWorldState.rootHash(); + } + + @Override + public Hash frontierRootHash() { + return mutableWorldState.rootHash(); + } + + @Override + public Stream streamAccounts(final Bytes32 startKeyHash, final int limit) { + return mutableWorldState.streamAccounts(startKeyHash, limit); + } + + @Override + public Account get(final Address address) { + return mutableWorldState.get(address); + } + + @Override + public void close() throws Exception { + mutableWorldState.close(); + } + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java index ef35a072440..6375b7befed 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java @@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TransactionTraceParams; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.debug.TraceOptions; import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; @@ -63,25 +64,32 @@ public TransactionTracer(final BlockReplay blockReplay) { } public Optional traceTransaction( - final Hash blockHash, final Hash transactionHash, final DebugOperationTracer tracer) { - return blockReplay.beforeTransactionInBlock( - blockHash, - transactionHash, - (transaction, header, blockchain, worldState, transactionProcessor, dataGasPrice) -> { - final TransactionProcessingResult result = - processTransaction( - header, - blockchain, - worldState.updater(), - transaction, - transactionProcessor, - tracer, - dataGasPrice); - return new TransactionTrace(transaction, result, tracer.getTraceFrames()); - }); + final Tracer.TraceableState mutableWorldState, + final Hash blockHash, + final Hash transactionHash, + final DebugOperationTracer tracer) { + Optional transactionTrace = + blockReplay.beforeTransactionInBlock( + mutableWorldState, + blockHash, + transactionHash, + (transaction, header, blockchain, transactionProcessor, dataGasPrice) -> { + final TransactionProcessingResult result = + processTransaction( + header, + blockchain, + mutableWorldState.updater(), + transaction, + transactionProcessor, + tracer, + dataGasPrice); + return new TransactionTrace(transaction, result, tracer.getTraceFrames()); + }); + return transactionTrace; } public List traceTransactionToFile( + final MutableWorldState mutableWorldState, final Hash blockHash, final Optional transactionTraceParams, final Path traceDir) { @@ -104,8 +112,8 @@ public List traceTransactionToFile( return blockReplay .performActionWithBlock( blockHash, - (body, header, blockchain, worldState, transactionProcessor, protocolSpec) -> { - WorldUpdater stackedUpdater = worldState.updater().updater(); + (body, header, blockchain, transactionProcessor, protocolSpec) -> { + WorldUpdater stackedUpdater = mutableWorldState.updater().updater(); final List traces = new ArrayList<>(); final Wei dataGasPrice = protocolSpec @@ -180,7 +188,6 @@ private TransactionProcessingResult processTransaction( final MainnetTransactionProcessor transactionProcessor, final OperationTracer tracer, final Wei dataGasPrice) { - return transactionProcessor.processTransaction( blockchain, worldUpdater, diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java index 4a337137fa5..cce9ada72a3 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java @@ -85,10 +85,7 @@ protected String getApiGroup() { @Override protected Map create() { final BlockReplay blockReplay = - new BlockReplay( - protocolSchedule, - blockchainQueries.getBlockchain(), - blockchainQueries.getWorldStateArchive()); + new BlockReplay(protocolSchedule, blockchainQueries.getBlockchain()); return mapOf( new DebugTraceTransaction(blockchainQueries, new TransactionTracer(blockReplay)), @@ -103,7 +100,7 @@ protected Map create() { new DebugSetHead(blockchainQueries, protocolContext), new DebugReplayBlock(blockchainQueries, protocolContext, protocolSchedule), new DebugTraceBlockByNumber(() -> new BlockTracer(blockReplay), blockchainQueries), - new DebugTraceBlockByHash(() -> new BlockTracer(blockReplay)), + new DebugTraceBlockByHash(() -> new BlockTracer(blockReplay), () -> blockchainQueries), new DebugBatchSendRawTransaction(transactionPool), new DebugGetBadBlocks(blockchainQueries, protocolSchedule, blockResult), new DebugStandardTraceBlockToFile( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/TraceJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/TraceJsonRpcMethods.java index 3aabdee3722..eac87abef8a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/TraceJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/TraceJsonRpcMethods.java @@ -56,10 +56,7 @@ protected String getApiGroup() { @Override protected Map create() { final BlockReplay blockReplay = - new BlockReplay( - protocolSchedule, - blockchainQueries.getBlockchain(), - blockchainQueries.getWorldStateArchive()); + new BlockReplay(protocolSchedule, blockchainQueries.getBlockchain()); return mapOf( new TraceReplayBlockTransactions( () -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java index c0f14dbf6b5..e3d74b205b1 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java @@ -303,7 +303,8 @@ public long getTransactionCount(final Address address, final long blockNumber) { * @return The number of transactions sent from the given address. */ public long getTransactionCount(final Address address, final Hash blockHash) { - return getAndMapWorldState(blockHash, worldState -> worldState.get(address)) + return getAndMapWorldState( + blockHash, worldState -> Optional.ofNullable(worldState.get(address))) .map(Account::getNonce) .orElse(0L); } @@ -859,24 +860,18 @@ public List matchingLogs( * @return the world state at the block number */ public Optional getAndMapWorldState( - final Hash blockHash, final Function mapper) { + final Hash blockHash, final Function> mapper) { + return blockchain .getBlockHeader(blockHash) .flatMap( blockHeader -> { - try (final var worldState = + try (var ws = worldStateArchive - .getMutable(blockHeader.getStateRoot(), blockHeader.getHash(), false) - .map( - ws -> { - if (!ws.isPersistable()) { - return ws.copy(); - } - return ws; - }) + .getMutable(blockHeader.getStateRoot(), blockHeader.getBlockHash(), false) .orElse(null)) { - if (worldState != null) { - return Optional.ofNullable(mapper.apply(worldState)); + if (ws != null) { + return mapper.apply(ws); } } catch (Exception ex) { LOG.error("failed worldstate query for " + blockHash.toShortHexString(), ex); @@ -894,7 +889,7 @@ public Optional getAndMapWorldState( * @return the world state at the block number */ public Optional getAndMapWorldState( - final long blockNumber, final Function mapper) { + final long blockNumber, final Function> mapper) { final Hash blockHash = getBlockHeaderByNumber(blockNumber).map(BlockHeader::getHash).orElse(Hash.EMPTY); return getAndMapWorldState(blockHash, mapper); @@ -965,7 +960,9 @@ private Optional fromAccount( return getAndMapWorldState( blockHash, worldState -> - Optional.ofNullable(worldState.get(address)).map(getter).orElse(noAccountValue)); + Optional.ofNullable(worldState.get(address)) + .map(getter) + .or(() -> Optional.ofNullable(noAccountValue))); } private List formatTransactions( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/util/TraceUtil.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/util/TraceUtil.java deleted file mode 100644 index efdb3e89285..00000000000 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/util/TraceUtil.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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.util; - -import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTraceGenerator; -import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; -import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; -import org.hyperledger.besu.ethereum.core.Block; -import org.hyperledger.besu.ethereum.debug.TraceOptions; -import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; - -import java.util.Collections; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; - -public class TraceUtil { - - private TraceUtil() { - throw new IllegalStateException("Utility class"); - } - - public static Stream resultByTransactionHash( - final Hash transactionHash, - final BlockchainQueries blockchainQueries, - final Supplier blockTracerSupplier, - final ProtocolSchedule protocolSchedule) { - return blockchainQueries - .transactionByHash(transactionHash) - .flatMap(TransactionWithMetadata::getBlockNumber) - .flatMap(blockNumber -> blockchainQueries.getBlockchain().getBlockByNumber(blockNumber)) - .map(block -> getTraceBlock(block, transactionHash, blockTracerSupplier, protocolSchedule)) - .orElse(Stream.empty()); - } - - private static Stream getTraceBlock( - final Block block, - final Hash transactionHash, - final Supplier blockTracerSupplier, - final ProtocolSchedule protocolSchedule) { - if (block == null || block.getBody().getTransactions().isEmpty()) { - return Stream.empty(); - } - final TransactionTrace transactionTrace = - getTransactionTrace(block, transactionHash, blockTracerSupplier); - return getTraceStream(protocolSchedule, transactionTrace, block); - } - - private static TransactionTrace getTransactionTrace( - final Block block, - final Hash transactionHash, - final Supplier blockTracerSupplier) { - return blockTracerSupplier - .get() - .trace(block, new DebugOperationTracer(new TraceOptions(false, false, true))) - .map(BlockTrace::getTransactionTraces) - .orElse(Collections.emptyList()) - .stream() - .filter(trxTrace -> trxTrace.getTransaction().getHash().equals(transactionHash)) - .findFirst() - .orElseThrow(); - } - - private static Stream getTraceStream( - final ProtocolSchedule protocolSchedule, - final TransactionTrace transactionTrace, - final Block block) { - return FlatTraceGenerator.generateFromTransactionTraceAndBlock( - protocolSchedule, transactionTrace, block) - .map(FlatTrace.class::cast); - } - - public static JsonNode arrayNodeFromTraceStream(final Stream traceStream) { - final ObjectMapper mapper = new ObjectMapper(); - final ArrayNode resultArrayNode = mapper.createArrayNode(); - traceStream.forEachOrdered(resultArrayNode::addPOJO); - return resultArrayNode; - } -} diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountAtTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountAtTest.java index 03f247c3936..cc97c40bb38 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountAtTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountAtTest.java @@ -16,6 +16,7 @@ import static org.hyperledger.besu.evm.account.Account.MAX_NONCE; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; @@ -24,6 +25,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; 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; @@ -34,6 +36,7 @@ import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.evm.account.Account; @@ -41,12 +44,14 @@ import java.util.Collections; import java.util.Optional; +import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; @@ -55,7 +60,10 @@ class DebugAccountAtTest { @Mock private BlockTracer blockTracer; @Mock private BlockchainQueries blockchainQueries; - @Mock private BlockWithMetadata blockWithMetadata; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private BlockWithMetadata blockWithMetadata; + @Mock private BlockHeader blockHeader; @Mock private TransactionWithMetadata transactionWithMetadata; @Mock private BlockTrace blockTrace; @@ -63,6 +71,8 @@ class DebugAccountAtTest { @Mock private TraceFrame traceFrame; @Mock private Transaction transaction; @Mock private WorldUpdater worldUpdater; + @Mock private MutableWorldState worldState; + @Mock private Account account; private static DebugAccountAt debugAccountAt; @@ -140,6 +150,15 @@ void testInvalidParamsResponseTooHigh() { @Test void testTransactionNotFoundResponse() { + doAnswer( + invocation -> + invocation + .>>getArgument( + 1) + .apply(worldState)) + .when(blockchainQueries) + .getAndMapWorldState(any(), any()); + setupMockBlock(); Mockito.when(blockWithMetadata.getTransactions()) .thenReturn(Collections.singletonList(transactionWithMetadata)); @@ -156,6 +175,15 @@ void testTransactionNotFoundResponse() { @Test void testNoAccountFoundResponse() { + doAnswer( + invocation -> + invocation + .>>getArgument( + 1) + .apply(worldState)) + .when(blockchainQueries) + .getAndMapWorldState(any(), any()); + setupMockTransaction(); setupMockBlock(); @@ -172,6 +200,15 @@ void testNoAccountFoundResponse() { @Test void shouldBeSuccessfulWhenTransactionsAndAccountArePresent() { + doAnswer( + invocation -> + invocation + .>>getArgument( + 1) + .apply(worldState)) + .when(blockchainQueries) + .getAndMapWorldState(any(), any()); + final String codeString = "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063b27b880414610030575b"; final Bytes code = Bytes.fromHexString(codeString); @@ -214,7 +251,8 @@ private void setupMockTransaction() { Mockito.when(blockchainQueries.blockByHash(any())).thenReturn(Optional.of(blockWithMetadata)); Mockito.when(blockWithMetadata.getTransactions()) .thenReturn(Collections.singletonList(transactionWithMetadata)); - Mockito.when(blockTracer.trace(any(Hash.class), any())).thenReturn(Optional.of(blockTrace)); + Mockito.when(blockTracer.trace(any(Tracer.TraceableState.class), any(Hash.class), any())) + .thenReturn(Optional.of(blockTrace)); Mockito.when(blockTrace.getTransactionTraces()) .thenReturn(Collections.singletonList(transactionTrace)); Mockito.when(transactionTrace.getTransaction()).thenReturn(transaction); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFileTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFileTest.java index 27e48157c8f..1c749df131e 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFileTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBadBlockToFileTest.java @@ -17,12 +17,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; 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.processor.TransactionTracer; +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.query.BlockchainQueries; import org.hyperledger.besu.ethereum.chain.BadBlockManager; @@ -31,6 +33,7 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; @@ -38,7 +41,9 @@ import java.util.HashMap; import java.util.List; import java.util.Optional; +import java.util.function.Function; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -49,6 +54,8 @@ public class DebugStandardTraceBadBlockToFileTest { private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class); private final Blockchain blockchain = mock(Blockchain.class); + private final MutableWorldState mutableWorldState = mock(MutableWorldState.class); + private final ProtocolSchedule protocolSchedule = mock(ProtocolSchedule.class); private final TransactionTracer transactionTracer = mock(TransactionTracer.class); private final ProtocolSpec protocolSpec = mock(ProtocolSpec.class); @@ -57,6 +64,18 @@ public class DebugStandardTraceBadBlockToFileTest { new DebugStandardTraceBadBlockToFile( () -> transactionTracer, blockchainQueries, protocolSchedule, folder.getRoot().toPath()); + @Before + public void setup() { + doAnswer( + invocation -> + invocation + .>>getArgument( + 1) + .apply(mutableWorldState)) + .when(blockchainQueries) + .getAndMapWorldState(any(), any()); + } + @Test public void nameShouldBeDebugTraceTransaction() { assertThat(debugStandardTraceBadBlockToFile.getName()) @@ -89,7 +108,8 @@ public void shouldTraceTheTransactionUsingTheTransactionTracer() { when(blockchainQueries.getBlockchain()).thenReturn(blockchain); when(blockchain.getChainHeadHeader()).thenReturn(new BlockHeaderTestFixture().buildHeader()); when(protocolSchedule.getByBlockHeader(blockHeader)).thenReturn(protocolSpec); - when(transactionTracer.traceTransactionToFile(eq(block.getHash()), any(), any())) + when(transactionTracer.traceTransactionToFile( + any(MutableWorldState.class), eq(block.getHash()), any(), any())) .thenReturn(paths); final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugStandardTraceBadBlockToFile.response(request); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java index b49cb440487..ddae8a94ffe 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStandardTraceBlockToFileTest.java @@ -18,6 +18,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; @@ -28,6 +29,8 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.util.ArrayList; import java.util.HashMap; @@ -37,13 +40,17 @@ import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.mockito.Answers; public class DebugStandardTraceBlockToFileTest { @ClassRule public static final TemporaryFolder folder = new TemporaryFolder(); - private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class); + private final WorldStateArchive archive = + mock(WorldStateArchive.class, Answers.RETURNS_DEEP_STUBS); private final Blockchain blockchain = mock(Blockchain.class); + private final BlockchainQueries blockchainQueries = + spy(new BlockchainQueries(blockchain, archive)); private final TransactionTracer transactionTracer = mock(TransactionTracer.class); private final DebugStandardTraceBlockToFile debugStandardTraceBlockToFile = new DebugStandardTraceBlockToFile( @@ -75,8 +82,10 @@ public void shouldTraceTheTransactionUsingTheTransactionTracer() { when(blockchainQueries.getBlockchain()).thenReturn(blockchain); when(blockchain.getBlockByHash(block.getHash())).thenReturn(Optional.of(block)); + when(blockchain.getBlockHeader(genesis.getHash())).thenReturn(Optional.of(genesis.getHeader())); - when(transactionTracer.traceTransactionToFile(eq(block.getHash()), any(), any())) + when(transactionTracer.traceTransactionToFile( + any(MutableWorldState.class), eq(block.getHash()), any(), any())) .thenReturn(paths); final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugStandardTraceBlockToFile.response(request); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAtTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAtTest.java index 488f0d05fe6..5767b1b0085 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAtTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAtTest.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.entry; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -27,6 +28,8 @@ 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.processor.BlockReplay; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; +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.results.DebugStorageRangeAtResult; import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata; @@ -48,11 +51,13 @@ import java.util.NavigableMap; import java.util.Optional; import java.util.TreeMap; +import java.util.function.Function; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; import org.junit.Before; import org.junit.Test; +import org.mockito.Answers; import org.mockito.invocation.InvocationOnMock; public class DebugStorageRangeAtTest { @@ -61,16 +66,16 @@ public class DebugStorageRangeAtTest { private static final Bytes32 START_KEY_HASH = Bytes32.fromHexString("0x22"); private final Blockchain blockchain = mock(Blockchain.class); private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class); - private final BlockReplay blockReplay = mock(BlockReplay.class); + private final BlockReplay blockReplay = mock(BlockReplay.class, Answers.RETURNS_DEEP_STUBS); private final DebugStorageRangeAt debugStorageRangeAt = new DebugStorageRangeAt(blockchainQueries, blockReplay); - private final MutableWorldState worldState = mock(MutableWorldState.class); + private final Tracer.TraceableState worldState = mock(Tracer.TraceableState.class); private final Account account = mock(Account.class); private final MainnetTransactionProcessor transactionProcessor = mock(MainnetTransactionProcessor.class); private final Transaction transaction = mock(Transaction.class); - private final BlockHeader blockHeader = mock(BlockHeader.class); + private final BlockHeader blockHeader = mock(BlockHeader.class, Answers.RETURNS_DEEP_STUBS); private final Hash blockHash = Hash.fromHexString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); private final Hash transactionHash = @@ -113,10 +118,19 @@ public void shouldRetrieveStorageRange_fullValues() { })); when(blockchainQueries.blockByHash(blockHash)).thenReturn(Optional.of(blockWithMetadata)); + doAnswer( + invocation -> + invocation + .>>getArgument( + 1) + .apply(worldState)) + .when(blockchainQueries) + .getAndMapWorldState(any(), any()); when(blockchainQueries.transactionByBlockHashAndIndex(blockHash, TRANSACTION_INDEX)) .thenReturn(Optional.of(transactionWithMetadata)); when(worldState.get(accountAddress)).thenReturn(account); - when(blockReplay.afterTransactionInBlock(eq(blockHash), eq(transactionHash), any())) + when(blockReplay.afterTransactionInBlock( + any(Tracer.TraceableState.class), any(Hash.class), eq(transactionHash), any())) .thenAnswer(this::callAction); final List entries = new ArrayList<>(); @@ -155,8 +169,7 @@ public void shouldRetrieveStorageRange_fullValues() { private Object callAction(final InvocationOnMock invocation) { //noinspection rawtypes return Optional.of( - ((BlockReplay.TransactionAction) invocation.getArgument(2)) - .performAction( - transaction, blockHeader, blockchain, worldState, transactionProcessor, Wei.ZERO)); + ((BlockReplay.TransactionAction) invocation.getArgument(3)) + .performAction(transaction, blockHeader, blockchain, transactionProcessor, Wei.ZERO)); } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHashTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHashTest.java index 4c255ab6f2f..253dffb1dce 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHashTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHashTest.java @@ -16,6 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -26,8 +27,13 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace; +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.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; @@ -35,19 +41,41 @@ import java.util.Collection; import java.util.Optional; import java.util.OptionalLong; +import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; +import org.junit.Before; import org.junit.Test; +import org.mockito.Answers; public class DebugTraceBlockByHashTest { private final BlockTracer blockTracer = mock(BlockTracer.class); + + private final BlockchainQueries blockchainQueries = + mock(BlockchainQueries.class, Answers.RETURNS_DEEP_STUBS); + private final MutableWorldState mutableWorldState = mock(MutableWorldState.class); + private final BlockHeader blockHeader = mock(BlockHeader.class, Answers.RETURNS_DEEP_STUBS); private final DebugTraceBlockByHash debugTraceBlockByHash = - new DebugTraceBlockByHash(() -> blockTracer); + new DebugTraceBlockByHash(() -> blockTracer, () -> blockchainQueries); private final Hash blockHash = Hash.fromHexString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + @Before + public void setUp() { + doAnswer( + invocation -> + invocation + .>>getArgument( + 1) + .apply(mutableWorldState)) + .when(blockchainQueries) + .getAndMapWorldState(any(), any()); + when(blockchainQueries.getBlockHeaderByHash(any(Hash.class))) + .thenReturn(Optional.of(blockHeader)); + } + @Test public void nameShouldBeDebugTraceBlockByHash() { assertThat(debugTraceBlockByHash.getName()).isEqualTo("debug_traceBlockByHash"); @@ -99,7 +127,8 @@ public void shouldReturnCorrectResponse() { when(transaction2Trace.getResult()).thenReturn(transaction2Result); when(transaction1Result.getOutput()).thenReturn(Bytes.fromHexString("1234")); when(transaction2Result.getOutput()).thenReturn(Bytes.fromHexString("1234")); - when(blockTracer.trace(eq(blockHash), any())).thenReturn(Optional.of(blockTrace)); + when(blockTracer.trace(any(Tracer.TraceableState.class), eq(blockHash), any())) + .thenReturn(Optional.of(blockTrace)); final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugTraceBlockByHash.response(request); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumberTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumberTest.java index dc0f67daa0b..2b78ec562da 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumberTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumberTest.java @@ -18,7 +18,7 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -28,26 +28,35 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace; +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.results.DebugTraceTransactionResult; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import java.util.Collection; import java.util.Optional; import java.util.OptionalLong; +import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; import org.junit.Test; +import org.mockito.Answers; public class DebugTraceBlockByNumberTest { - private final BlockchainQueries blockchain = mock(BlockchainQueries.class); - private final BlockTracer blockTracer = mock(BlockTracer.class); + private final BlockchainQueries blockchainQueries = + mock(BlockchainQueries.class, Answers.RETURNS_DEEP_STUBS); + + private final Tracer.TraceableState worldState = mock(Tracer.TraceableState.class); + private final BlockTracer blockTracer = mock(BlockTracer.class, Answers.RETURNS_DEEP_STUBS); private final DebugTraceBlockByNumber debugTraceBlockByNumber = - new DebugTraceBlockByNumber(() -> blockTracer, blockchain); + new DebugTraceBlockByNumber(() -> blockTracer, blockchainQueries); private final Hash blockHash = Hash.fromHexString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); @@ -104,8 +113,20 @@ public void shouldReturnCorrectResponse() { when(transaction2Trace.getResult()).thenReturn(transaction2Result); when(transaction1Result.getOutput()).thenReturn(Bytes.fromHexString("1234")); when(transaction2Result.getOutput()).thenReturn(Bytes.fromHexString("1234")); - when(blockchain.getBlockHashByNumber(blockNumber)).thenReturn(Optional.of(blockHash)); - when(blockTracer.trace(eq(blockHash), any())).thenReturn(Optional.of(blockTrace)); + when(blockchainQueries.getBlockHashByNumber(blockNumber)).thenReturn(Optional.of(blockHash)); + when(blockchainQueries.getBlockHeaderByHash(any(Hash.class))) + .thenReturn(Optional.of(mock(BlockHeader.class, Answers.RETURNS_DEEP_STUBS))); + + doAnswer( + invocation -> + invocation + .>> + getArgument(1) + .apply(worldState)) + .when(blockchainQueries) + .getAndMapWorldState(any(), any()); + when(blockTracer.trace(any(Tracer.TraceableState.class), any(Hash.class), any())) + .thenReturn(Optional.of(blockTrace)); final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugTraceBlockByNumber.response(request); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockTest.java index cf7256435bd..df337f44f2f 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockTest.java @@ -18,7 +18,9 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import org.hyperledger.besu.datatypes.Wei; @@ -26,17 +28,20 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; 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.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.util.Collection; import java.util.Collections; @@ -45,12 +50,17 @@ import org.apache.tuweni.bytes.Bytes; import org.junit.Test; +import org.mockito.Answers; import org.mockito.Mockito; public class DebugTraceBlockTest { private final BlockTracer blockTracer = mock(BlockTracer.class); - private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class); + private final WorldStateArchive archive = + mock(WorldStateArchive.class, Answers.RETURNS_DEEP_STUBS); + private final Blockchain blockchain = mock(Blockchain.class); + private final BlockchainQueries blockchainQueries = + spy(new BlockchainQueries(blockchain, archive)); private final DebugTraceBlock debugTraceBlock = new DebugTraceBlock(() -> blockTracer, new MainnetBlockHeaderFunctions(), blockchainQueries); @@ -117,17 +127,22 @@ public void shouldReturnCorrectResponse() { when(transaction2Trace.getResult()).thenReturn(transaction2Result); when(transaction1Result.getOutput()).thenReturn(Bytes.fromHexString("1234")); when(transaction2Result.getOutput()).thenReturn(Bytes.fromHexString("1234")); - when(blockTracer.trace(Mockito.eq(block), any())).thenReturn(Optional.of(blockTrace)); - - when(blockchainQueries.blockByHash(parentBlock.getHash())) - .thenReturn( - Optional.of( - new BlockWithMetadata<>( - parentBlock.getHeader(), - Collections.emptyList(), - Collections.emptyList(), - parentBlock.getHeader().getDifficulty(), - parentBlock.calculateSize()))); + when(blockTracer.trace(any(Tracer.TraceableState.class), Mockito.eq(block), any())) + .thenReturn(Optional.of(blockTrace)); + + when(blockchain.getBlockHeader(parentBlock.getHash())) + .thenReturn(Optional.of(parentBlock.getHeader())); + doAnswer( + invocation -> + Optional.of( + new BlockWithMetadata<>( + parentBlock.getHeader(), + Collections.emptyList(), + Collections.emptyList(), + parentBlock.getHeader().getDifficulty(), + parentBlock.calculateSize()))) + .when(blockchainQueries) + .blockByHash(parentBlock.getHash()); final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugTraceBlock.response(request); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java index afa43a0220a..c7c0aca44a6 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -24,13 +25,17 @@ import org.hyperledger.besu.datatypes.Wei; 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.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTracer; +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.results.DebugTraceTransactionResult; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.StructLog; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; @@ -42,17 +47,23 @@ import java.util.Map; import java.util.Optional; import java.util.OptionalLong; +import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +import org.junit.Before; import org.junit.Test; +import org.mockito.Answers; public class DebugTraceTransactionTest { - private final BlockchainQueries blockchain = mock(BlockchainQueries.class); + private final BlockchainQueries blockchainQueries = + mock(BlockchainQueries.class, Answers.RETURNS_DEEP_STUBS); + private final BlockHeader blockHeader = mock(BlockHeader.class, Answers.RETURNS_DEEP_STUBS); + private final MutableWorldState mutableWorldState = mock(MutableWorldState.class); private final TransactionTracer transactionTracer = mock(TransactionTracer.class); private final DebugTraceTransaction debugTraceTransaction = - new DebugTraceTransaction(blockchain, transactionTracer); + new DebugTraceTransaction(blockchainQueries, transactionTracer); private final Transaction transaction = mock(Transaction.class); private final Hash blockHash = @@ -60,6 +71,22 @@ public class DebugTraceTransactionTest { private final Hash transactionHash = Hash.fromHexString("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); + @Before + public void setup() { + doAnswer(__ -> Optional.of(blockHeader)) + .when(blockchainQueries) + .getBlockHeaderByHash(any(Hash.class)); + + doAnswer( + invocation -> + invocation + .>>getArgument( + 1) + .apply(mutableWorldState)) + .when(blockchainQueries) + .getAndMapWorldState(any(), any()); + } + @Test public void nameShouldBeDebugTraceTransaction() { assertThat(debugTraceTransaction.getName()).isEqualTo("debug_traceTransaction"); @@ -116,11 +143,14 @@ public void shouldTraceTheTransactionUsingTheTransactionTracer() { when(transaction.getGasLimit()).thenReturn(100L); when(result.getGasRemaining()).thenReturn(27L); when(result.getOutput()).thenReturn(Bytes.fromHexString("1234")); - when(blockchain.headBlockNumber()).thenReturn(12L); - when(blockchain.transactionByHash(transactionHash)) + when(blockchainQueries.headBlockNumber()).thenReturn(12L); + when(blockchainQueries.transactionByHash(transactionHash)) .thenReturn(Optional.of(transactionWithMetadata)); when(transactionTracer.traceTransaction( - eq(blockHash), eq(transactionHash), any(DebugOperationTracer.class))) + any(Tracer.TraceableState.class), + eq(blockHash), + eq(transactionHash), + any(DebugOperationTracer.class))) .thenReturn(Optional.of(transactionTrace)); final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugTraceTransaction.response(request); @@ -180,10 +210,13 @@ public void shouldNotTraceTheTransactionIfNotFound() { when(transaction.getGasLimit()).thenReturn(100L); when(result.getGasRemaining()).thenReturn(27L); when(result.getOutput()).thenReturn(Bytes.fromHexString("1234")); - when(blockchain.headBlockNumber()).thenReturn(12L); - when(blockchain.transactionByHash(transactionHash)).thenReturn(Optional.empty()); + when(blockchainQueries.headBlockNumber()).thenReturn(12L); + when(blockchainQueries.transactionByHash(transactionHash)).thenReturn(Optional.empty()); when(transactionTracer.traceTransaction( - eq(blockHash), eq(transactionHash), any(DebugOperationTracer.class))) + any(Tracer.TraceableState.class), + eq(blockHash), + eq(transactionHash), + any(DebugOperationTracer.class))) .thenReturn(Optional.of(transactionTrace)); final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) debugTraceTransaction.response(request); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java index 0aff535b5a9..b7f8cebefe9 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java @@ -17,10 +17,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.account.Account.MAX_NONCE; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import org.hyperledger.besu.datatypes.Address; @@ -53,6 +55,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; @@ -62,8 +65,13 @@ @MockitoSettings(strictness = Strictness.LENIENT) class EthGetProofTest { @Mock private Blockchain blockchain; - @Mock private BlockchainQueries blockchainQueries; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private WorldStateArchive archive; + @Mock private ChainHead chainHead; + @Mock private BlockHeader blockHeader; + private BlockchainQueries blockchainQueries; private EthGetProof method; private final String JSON_RPC_VERSION = "2.0"; @@ -77,7 +85,14 @@ class EthGetProofTest { @BeforeEach public void setUp() { - method = new EthGetProof(blockchainQueries); + blockchainQueries = spy(new BlockchainQueries(blockchain, archive)); + when(blockchainQueries.getBlockchain()).thenReturn(blockchain); + when(blockchainQueries.headBlockNumber()).thenReturn(14L); + when(blockchain.getChainHead()).thenReturn(chainHead); + when(chainHead.getBlockHeader()).thenReturn(blockHeader); + when(blockHeader.getBlockHash()).thenReturn(Hash.ZERO); + when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(mock(BlockHeader.class))); + method = spy(new EthGetProof(blockchainQueries)); } @Test @@ -88,11 +103,6 @@ void returnsCorrectMethodName() { @Test void errorWhenNoAddressAccountSupplied() { final JsonRpcRequestContext request = requestWithParams(null, null, "latest"); - when(blockchainQueries.getBlockchain()).thenReturn(blockchain); - when(blockchain.getChainHead()).thenReturn(chainHead); - final BlockHeader blockHeader = mock(BlockHeader.class); - when(chainHead.getBlockHeader()).thenReturn(blockHeader); - when(blockHeader.getBlockHash()).thenReturn(Hash.ZERO); Assertions.assertThatThrownBy(() -> method.response(request)) .isInstanceOf(InvalidJsonRpcParameters.class) @@ -102,11 +112,6 @@ void errorWhenNoAddressAccountSupplied() { @Test void errorWhenNoStorageKeysSupplied() { final JsonRpcRequestContext request = requestWithParams(address.toString(), null, "latest"); - when(blockchainQueries.getBlockchain()).thenReturn(blockchain); - when(blockchain.getChainHead()).thenReturn(chainHead); - final BlockHeader blockHeader = mock(BlockHeader.class); - when(chainHead.getBlockHeader()).thenReturn(blockHeader); - when(blockHeader.getBlockHash()).thenReturn(Hash.ZERO); Assertions.assertThatThrownBy(() -> method.response(request)) .isInstanceOf(InvalidJsonRpcParameters.class) @@ -124,14 +129,12 @@ void errorWhenNoBlockNumberSupplied() { @Test void errorWhenAccountNotFound() { - generateWorldState(); - + when(archive.getAccountProof(any(Hash.class), any(Address.class), any())) + .thenReturn(Optional.empty()); final JsonRpcErrorResponse expectedResponse = new JsonRpcErrorResponse(null, JsonRpcError.NO_ACCOUNT_FOUND); - when(blockchainQueries.headBlockNumber()).thenReturn(14L); - final JsonRpcRequestContext request = requestWithParams( Address.fromHexString("0x0000000000000000000000000000000000000000"), @@ -146,11 +149,10 @@ void errorWhenAccountNotFound() { @Test void errorWhenWorldStateUnavailable() { - when(blockchainQueries.headBlockNumber()).thenReturn(14L); - when(blockchainQueries.getAndMapWorldState(any(), any())).thenReturn(Optional.empty()); - final JsonRpcErrorResponse expectedResponse = new JsonRpcErrorResponse(null, JsonRpcError.WORLD_STATE_UNAVAILABLE); + when(archive.getMutable(any(Hash.class), any(Hash.class), anyBoolean())) + .thenReturn(Optional.empty()); final JsonRpcRequestContext request = requestWithParams( @@ -168,8 +170,6 @@ void getProof() { final GetProofResult expectedResponse = generateWorldState(); - when(blockchainQueries.headBlockNumber()).thenReturn(14L); - final JsonRpcRequestContext request = requestWithParams( address.toString(), new String[] {storageKey.toString()}, String.valueOf(blockNumber)); @@ -200,9 +200,7 @@ private GetProofResult generateWorldState() { final Hash storageRoot = Hash.fromHexString("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); - final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class); - - when(blockchainQueries.getWorldStateArchive()).thenReturn(worldStateArchive); + when(blockchainQueries.getWorldStateArchive()).thenReturn(archive); final StateTrieAccountValue stateTrieAccountValue = mock(StateTrieAccountValue.class); when(stateTrieAccountValue.getBalance()).thenReturn(balance); @@ -225,17 +223,17 @@ private GetProofResult generateWorldState() { "0x2222222222222222222222222222222222222222222222222222222222222222"))); when(worldStateProof.getStorageValue(storageKey)).thenReturn(UInt256.ZERO); - when(worldStateArchive.getAccountProof(eq(rootHash), eq(address), anyList())) + when(archive.getAccountProof(eq(rootHash), eq(address), anyList())) .thenReturn(Optional.of(worldStateProof)); final MutableWorldState mutableWorldState = mock(MutableWorldState.class); when(mutableWorldState.rootHash()).thenReturn(rootHash); doAnswer( invocation -> - Optional.of( - invocation - .>getArgument(1) - .apply(mutableWorldState))) + invocation + .>>getArgument( + 1) + .apply(mutableWorldState)) .when(blockchainQueries) .getAndMapWorldState(any(), any()); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java index 0728e53f2f7..e2124a67c3f 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java @@ -27,7 +27,6 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; @@ -36,7 +35,6 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; -import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.tracing.StandardJsonTracer; import org.hyperledger.besu.evm.worldstate.StackedUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -67,8 +65,6 @@ public class TransactionTracerTest { @Mock private ProtocolSchedule protocolSchedule; @Mock private Blockchain blockchain; - @Mock private WorldStateArchive worldStateArchive; - @Mock private BlockHeader blockHeader; @Mock private BlockBody blockBody; @@ -83,7 +79,7 @@ public class TransactionTracerTest { @Mock private ProtocolSpec protocolSpec; - @Mock private MutableWorldState mutableWorldState; + @Mock private Tracer.TraceableState mutableWorldState; @Mock private MainnetTransactionProcessor transactionProcessor; @@ -104,29 +100,25 @@ public class TransactionTracerTest { @Before public void setUp() throws Exception { - transactionTracer = - new TransactionTracer(new BlockReplay(protocolSchedule, blockchain, worldStateArchive)); + transactionTracer = new TransactionTracer(new BlockReplay(protocolSchedule, blockchain)); when(transaction.getHash()).thenReturn(transactionHash); when(otherTransaction.getHash()).thenReturn(otherTransactionHash); when(blockHeader.getNumber()).thenReturn(12L); when(blockHeader.getHash()).thenReturn(blockHash); when(blockHeader.getParentHash()).thenReturn(previousBlockHash); - when(previousBlockHeader.getStateRoot()).thenReturn(Hash.ZERO); - when(worldStateArchive.getMutable(Hash.ZERO, null, false)) - .thenReturn(Optional.of(mutableWorldState)); when(protocolSchedule.getByBlockHeader(blockHeader)).thenReturn(protocolSpec); when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor); when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase); when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0L)); when(blockchain.getChainHeadHeader()).thenReturn(blockHeader); when(protocolSpec.getBadBlocksManager()).thenReturn(new BadBlockManager()); - when(mutableWorldState.copy()).thenReturn(mutableWorldState); } @Test public void traceTransactionShouldReturnNoneWhenBlockHeaderNotFound() { final Optional transactionTrace = - transactionTracer.traceTransaction(invalidBlockHash, transactionHash, tracer); + transactionTracer.traceTransaction( + mutableWorldState, invalidBlockHash, transactionHash, tracer); assertThat(transactionTrace).isEmpty(); } @@ -140,7 +132,7 @@ public void traceTransactionShouldReturnTraceFramesFromExecutionTracer() { when(tracer.getTraceFrames()).thenReturn(traceFrames); final Optional transactionTrace = - transactionTracer.traceTransaction(blockHash, transactionHash, tracer); + transactionTracer.traceTransaction(mutableWorldState, blockHash, transactionHash, tracer); assertThat(transactionTrace.map(TransactionTrace::getTraceFrames)).contains(traceFrames); } @@ -157,7 +149,7 @@ public void traceTransactionShouldReturnTraceFramesFromExecutionTracer() { when(tracer.getTraceFrames()).thenReturn(traceFrames); final Optional transactionTrace = - transactionTracer.traceTransaction(blockHash, transactionHash, tracer); + transactionTracer.traceTransaction(mutableWorldState, blockHash, transactionHash, tracer); assertThat(transactionTrace.map(TransactionTrace::getTraceFrames)).contains(traceFrames); } @@ -188,7 +180,7 @@ public void traceTransactionShouldReturnResultFromProcessTransaction() { .thenReturn(result); final Optional transactionTrace = - transactionTracer.traceTransaction(blockHash, transactionHash, tracer); + transactionTracer.traceTransaction(mutableWorldState, blockHash, transactionHash, tracer); assertThat(transactionTrace.map(TransactionTrace::getResult)).contains(result); } @@ -203,7 +195,7 @@ public void traceTransactionShouldReturnEmptyResultWhenTransactionNotInCurrentBl when(blockchain.getBlockBody(blockHash)).thenReturn(Optional.of(blockBody)); final Optional transactionTrace = - transactionTracer.traceTransaction(blockHash, transactionHash, tracer); + transactionTracer.traceTransaction(mutableWorldState, blockHash, transactionHash, tracer); assertThat(transactionTrace).isEmpty(); } @@ -215,7 +207,7 @@ public void traceTransactionShouldReturnEmptyResultWhenBlockIsNotAvailable() { when(blockchain.getBlockBody(blockHash)).thenReturn(Optional.empty()); final Optional transactionTrace = - transactionTracer.traceTransaction(blockHash, transactionHash, tracer); + transactionTracer.traceTransaction(mutableWorldState, blockHash, transactionHash, tracer); assertThat(transactionTrace).isEmpty(); } @@ -235,6 +227,7 @@ public void traceTransactionToFileShouldReturnEmptyListWhenNoTransaction() { when(mutableWorldState.updater()).thenReturn(updater); final List transactionTraces = transactionTracer.traceTransactionToFile( + mutableWorldState, blockHash, Optional.of(ImmutableTransactionTraceParams.builder().build()), traceDir.getRoot().toPath()); @@ -277,6 +270,7 @@ public void traceTransactionToFileShouldReturnResultFromProcessTransaction() thr final List transactionTraces = transactionTracer.traceTransactionToFile( + mutableWorldState, blockHash, Optional.of(ImmutableTransactionTraceParams.builder().build()), traceDir.getRoot().toPath()); diff --git a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java index 270cc1e7771..76da2e86c89 100644 --- a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java +++ b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java @@ -196,11 +196,7 @@ private boolean buildContext( 1000, 8); - blockReplay = - new BlockReplay( - protocolSchedule, - blockchainQueries.getBlockchain(), - blockchainQueries.getWorldStateArchive()); + blockReplay = new BlockReplay(protocolSchedule, blockchainQueries.getBlockchain()); // mining support