Skip to content

Commit

Permalink
TraceService: return results for transactions in block (#6087)
Browse files Browse the repository at this point in the history
* TraceService: return results for transactions in block

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
  • Loading branch information
daniellehrner committed Oct 27, 2023
1 parent accac1c commit a60b31b
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 18 deletions.
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)

### 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);

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)));
}
}

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);
}
}
}

0 comments on commit a60b31b

Please sign in to comment.