Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

TraceService: return results for transactions in block #6087

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Deprecations

### Additions and Improvements
- TraceService: return results for transactions in block [#6086](https://github.com/hyperledger/besu/pull/6086)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- TraceService: return results for transactions in block [#6086](https://github.com/hyperledger/besu/pull/6086)
- TraceService: return results for transactions in block [#6087](https://github.com/hyperledger/besu/pull/6087)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#6086 is closed? this one is #6087


### Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.BlockTraceResult;
import org.hyperledger.besu.plugin.data.TransactionTraceResult;
import org.hyperledger.besu.plugin.services.TraceService;
import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;

Expand Down Expand Up @@ -72,10 +75,9 @@ public TraceServiceImpl(
* @param tracer an instance of OperationTracer
*/
@Override
public void traceBlock(final long blockNumber, final BlockAwareOperationTracer tracer) {
checkArgument(tracer != null);
final Optional<Block> block = blockchainQueries.getBlockchain().getBlockByNumber(blockNumber);
block.ifPresent(value -> trace(value, tracer));
public BlockTraceResult traceBlock(
final long blockNumber, final BlockAwareOperationTracer tracer) {
return traceBlock(blockchainQueries.getBlockchain().getBlockByNumber(blockNumber), tracer);
}

/**
Expand All @@ -85,10 +87,41 @@ public void traceBlock(final long blockNumber, final BlockAwareOperationTracer t
* @param tracer an instance of OperationTracer
*/
@Override
public void traceBlock(final Hash hash, final BlockAwareOperationTracer tracer) {
public BlockTraceResult traceBlock(final Hash hash, final BlockAwareOperationTracer tracer) {
return traceBlock(blockchainQueries.getBlockchain().getBlockByHash(hash), tracer);
}

private BlockTraceResult traceBlock(
final Optional<Block> maybeBlock, final BlockAwareOperationTracer tracer) {
checkArgument(tracer != null);
final Optional<Block> block = blockchainQueries.getBlockchain().getBlockByHash(hash);
block.ifPresent(value -> trace(value, tracer));
if (maybeBlock.isEmpty()) {
return BlockTraceResult.empty();
}

final Optional<List<TransactionProcessingResult>> results = trace(maybeBlock.get(), tracer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it more efficient to assign a variable to maybeBlock.get() - it's used below as well


if (results.isEmpty()) {
return BlockTraceResult.empty();
}

final BlockTraceResult.Builder builder = BlockTraceResult.builder();

final List<TransactionProcessingResult> transactionProcessingResults = results.get();
final List<Transaction> transactions = maybeBlock.get().getBody().getTransactions();
for (int i = 0; i < transactionProcessingResults.size(); i++) {
final TransactionProcessingResult transactionProcessingResult =
transactionProcessingResults.get(i);
final TransactionTraceResult transactionTraceResult =
transactionProcessingResult.isInvalid()
? TransactionTraceResult.error(
transactions.get(i).getHash(),
transactionProcessingResult.getValidationResult().getErrorMessage())
: TransactionTraceResult.success(transactions.get(i).getHash());

builder.addTransactionTraceResult(transactionTraceResult);
}

return builder.build();
}

/**
Expand Down Expand Up @@ -136,15 +169,20 @@ public void trace(
});
}

private void trace(final Block block, final BlockAwareOperationTracer tracer) {
private Optional<List<TransactionProcessingResult>> trace(
final Block block, final BlockAwareOperationTracer tracer) {
LOG.debug("Tracing block {}", block.toLogString());
final Blockchain blockchain = blockchainQueries.getBlockchain();
Tracer.processTracing(
blockchainQueries,
block.getHash(),
traceableState ->
Optional.of(trace(blockchain, block, new ChainUpdater(traceableState), tracer)));

final Optional<List<TransactionProcessingResult>> results =
Tracer.processTracing(
blockchainQueries,
block.getHash(),
traceableState ->
Optional.of(trace(blockchain, block, new ChainUpdater(traceableState), tracer)));
tracer.traceEndBlock(block.getHeader(), block.getBody());

return results;
}

private List<TransactionProcessingResult> trace(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.worldstate.WorldView;
import org.hyperledger.besu.plugin.data.BlockTraceResult;
import org.hyperledger.besu.plugin.data.TransactionTraceResult;
import org.hyperledger.besu.plugin.services.TraceService;
import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;

Expand Down Expand Up @@ -115,7 +117,18 @@ void shouldReturnTheCorrectWorldViewForTxStartEnd() {
final TxStartEndTracer txStartEndTracer = new TxStartEndTracer();

// block contains 1 transaction
traceService.traceBlock(31, txStartEndTracer);
final BlockTraceResult blockTraceResult = traceService.traceBlock(31, txStartEndTracer);

assertThat(blockTraceResult).isNotNull();

final List<TransactionTraceResult> transactionTraceResults =
blockTraceResult.transactionTraceResults();
assertThat(transactionTraceResults.size()).isEqualTo(1);

assertThat(transactionTraceResults.get(0).getTxHash()).isNotNull();
assertThat(transactionTraceResults.get(0).getStatus())
.isEqualTo(TransactionTraceResult.Status.SUCCESS);
assertThat(transactionTraceResults.get(0).errorMessage()).isEmpty();

assertThat(txStartEndTracer.txStartWorldView).isNotNull();
assertThat(txStartEndTracer.txEndWorldView).isNotNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,8 @@ public TransactionProcessingResult processTransaction(
LOG.error("Critical Exception Processing Transaction", re);
return TransactionProcessingResult.invalid(
ValidationResult.invalid(
TransactionInvalidReason.INTERNAL_ERROR, "Internal Error in Besu - " + re));
TransactionInvalidReason.INTERNAL_ERROR,
"Internal Error in Besu - " + re + "\n" + printableStackTraceFromThrowable(re)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this look like in reality?

Copy link
Contributor Author

@daniellehrner daniellehrner Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an example:

Internal Error in Besu - java.lang.IllegalStateException
at com.google.common.base.Preconditions.checkState(Preconditions.java:486)
at net.consensys.linea.zktracer.module.hub.stack.Stack.processInstruction(Stack.java:333)
at net.consensys.linea.zktracer.module.hub.Hub.handleStack(Hub.java:376)
at net.consensys.linea.zktracer.module.hub.Hub.processStateExec(Hub.java:470)
at net.consensys.linea.zktracer.module.hub.Hub.tracePreOpcode(Hub.java:705)
at net.consensys.linea.zktracer.ZkTracer.tracePreExecution(ZkTracer.java:101)
at org.hyperledger.besu.evm.EVM.runToHalt(EVM.java:187)
at org.hyperledger.besu.evm.processor.AbstractMessageProcessor.codeExecute(AbstractMessageProcessor.java:174)
at org.hyperledger.besu.evm.processor.AbstractMessageProcessor.process(AbstractMessageProcessor.java:196)
at org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor.process(MainnetTransactionProcessor.java:511)
at org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor.processTransaction(MainnetTransactionProcessor.java:418)
at org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor.processTransaction(MainnetTransactionProcessor.java:206)
at org.hyperledger.besu.services.TraceServiceImpl.lambda$trace$6(TraceServiceImpl.java:217)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.hyperledger.besu.services.TraceServiceImpl.trace(TraceServiceImpl.java:202)
at org.hyperledger.besu.services.TraceServiceImpl.lambda$trace$4(TraceServiceImpl.java:182)
at org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer.lambda$processTracing$0(Tracer.java:52)
at org.hyperledger.besu.ethereum.api.query.BlockchainQueries.lambda$getAndMapWorldState$23(BlockchainQueries.java:923)
at java.base/java.util.Optional.flatMap(Optional.java:289)
at org.hyperledger.besu.ethereum.api.query.BlockchainQueries.getAndMapWorldState(BlockchainQueries.java:919)

}
}

Expand All @@ -525,4 +526,14 @@ protected long refunded(
final long refundAllowance = Math.min(maxRefundAllowance, gasRefund);
return gasRemaining + refundAllowance;
}

private String printableStackTraceFromThrowable(final RuntimeException re) {
final StringBuilder builder = new StringBuilder();

for (final StackTraceElement stackTraceElement : re.getStackTrace()) {
builder.append("\tat ").append(stackTraceElement.toString()).append("\n");
}

return builder.toString();
}
}
2 changes: 1 addition & 1 deletion plugin-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files
knownHash = 'j6NRklFHlG35Pq/t6t/oJBrT8DbYOyruGq3cJNh4ENw='
knownHash = 'MtslBKSKFkbHlLJZZ0j4Nv6CMKizULVXztr1tmDa9qA='
}
check.dependsOn('checkAPIChanges')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* 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.plugin.data;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

/**
* Represents the result of tracing a block, containing information about the transaction traces.
*/
public class BlockTraceResult {
final List<TransactionTraceResult> transactionTraceResults;

/**
* Constructs a BlockTraceResult with the given list of transaction trace results.
*
* @param transactionTraceResults The list of transaction trace results to be associated with this
* block.
*/
public BlockTraceResult(final List<TransactionTraceResult> transactionTraceResults) {
this.transactionTraceResults = transactionTraceResults;
}

/**
* Creates an empty BlockTraceResult with no transaction trace results.
*
* @return An empty BlockTraceResult.
*/
public static BlockTraceResult empty() {
return new BlockTraceResult(new ArrayList<>());
}

/**
* Get the list of transaction trace results for this block.
*
* @return The list of transaction trace results.
*/
public List<TransactionTraceResult> transactionTraceResults() {
return transactionTraceResults;
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final BlockTraceResult that = (BlockTraceResult) o;
return transactionTraceResults.equals(that.transactionTraceResults());
}

@Override
public int hashCode() {
return Objects.hash(transactionTraceResults);
}

@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("BlockTraceResult{transactionTraceResults=[");

final Iterator<TransactionTraceResult> iterator = transactionTraceResults.iterator();
while (iterator.hasNext()) {
builder.append(iterator.next().toString());

if (iterator.hasNext()) {
builder.append(",");
}
}
builder.append("]}");
return builder.toString();
}

/**
* Creates a new builder to construct a BlockTraceResult.
*
* @return A new BlockTraceResult.Builder instance.
*/
public static Builder builder() {
return new Builder();
}

/** A builder class for constructing a BlockTraceResult. */
public static class Builder {
List<TransactionTraceResult> transactionTraceResults = new ArrayList<>();

/**
* Adds a transaction trace result to the builder.
*
* @param transactionTraceResult The transaction trace result to add to the builder.
* @return This builder instance, for method chaining.
*/
public Builder addTransactionTraceResult(final TransactionTraceResult transactionTraceResult) {
transactionTraceResults.add(transactionTraceResult);

return this;
}

/**
* Constructs a BlockTraceResult using the transaction trace results added to the builder.
*
* @return A BlockTraceResult containing the added transaction trace results.
*/
public BlockTraceResult build() {
return new BlockTraceResult(transactionTraceResults);
}
}
}
Loading
Loading