Add world context to transaction tracing API (#5836)

* Add world context to transaction tracing API

Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu>

* Update changelog with PR ID

Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu>

* Add the Transaction to traceEndTransaction

Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu>

* Rebase on main

Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu>

* Add receipt-linked information to the transaction tracer

Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu>

* added test

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

---------

Signed-off-by: Franklin Delehelle <franklin.delehelle@odena.eu>
Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
Co-authored-by: Daniel Lehrner <daniel.lehrner@consensys.net>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/5882/head
delehef 1 year ago committed by GitHub
parent ab291ad370
commit 35385611ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 15
      besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java
  3. 106
      besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java
  4. 11
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java
  5. 19
      evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java
  6. 12
      evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java

@ -2,6 +2,7 @@
## 23.7.3
### Additions and Improvements
- Add access to an immutable world view to start/end transaction hooks in the tracing API[#5836](https://github.com/hyperledger/besu/pull/5836)
### Breaking Changes
- Removed support for Kotti network (ETC) [#5816](https://github.com/hyperledger/besu/pull/5816)

@ -173,12 +173,12 @@ public class TraceServiceImpl implements TraceService {
.map(parent -> calculateExcessBlobGasForParent(protocolSpec, parent))
.orElse(BlobGas.ZERO));
tracer.traceStartTransaction(transaction);
final WorldUpdater worldUpdater = chainUpdater.getNextUpdater();
tracer.traceStartTransaction(worldUpdater, transaction);
final TransactionProcessingResult result =
transactionProcessor.processTransaction(
blockchain,
chainUpdater.getNextUpdater(),
worldUpdater,
header,
transaction,
header.getCoinbase(),
@ -188,7 +188,14 @@ public class TraceServiceImpl implements TraceService {
blobGasPrice);
long transactionGasUsed = transaction.getGasLimit() - result.getGasRemaining();
tracer.traceEndTransaction(result.getOutput(), transactionGasUsed, 0);
tracer.traceEndTransaction(
worldUpdater,
transaction,
result.isSuccessful(),
result.getOutput(),
result.getLogs(),
transactionGasUsed,
0);
results.add(result);
});

@ -17,14 +17,21 @@ package org.hyperledger.besu.services;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
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.services.TraceService;
import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
@ -102,4 +109,103 @@ class TraceServiceImplTest {
assertThat(worldStateArchive.getMutable().get(addressToVerify).getNonce())
.isEqualTo(persistedNonceForAccount);
}
@Test
void shouldReturnTheCorrectWorldViewForTxStartEnd() {
final TxStartEndTracer txStartEndTracer = new TxStartEndTracer();
// block contains 1 transaction
traceService.traceBlock(31, txStartEndTracer);
assertThat(txStartEndTracer.txStartWorldView).isNotNull();
assertThat(txStartEndTracer.txEndWorldView).isNotNull();
assertThat(txStartEndTracer.txStartTransaction.getNonce())
.isEqualTo(txStartEndTracer.txEndTransaction.getNonce())
.isEqualTo(30);
assertThat(txStartEndTracer.txStartTransaction.getGasLimit())
.isEqualTo(txStartEndTracer.txEndTransaction.getGasLimit())
.isEqualTo(314159);
assertThat(txStartEndTracer.txStartTransaction.getTo().get())
.isEqualTo(txStartEndTracer.txEndTransaction.getTo().get())
.isEqualTo(Address.fromHexString("0x6295ee1b4f6dd65047762f924ecd367c17eabf8f"));
assertThat(txStartEndTracer.txStartTransaction.getValue())
.isEqualTo(txStartEndTracer.txEndTransaction.getValue())
.isEqualTo(
Wei.fromHexString(
"0x000000000000000000000000000000000000000000000000000000000000000a"));
assertThat(txStartEndTracer.txStartTransaction.getPayload())
.isEqualTo(txStartEndTracer.txEndTransaction.getPayload())
.isEqualTo(Bytes.fromHexString("0xfd408767"));
assertThat(txStartEndTracer.txEndStatus).isTrue();
assertThat(txStartEndTracer.txEndOutput).isEqualTo(Bytes.fromHexString("0x"));
assertThat(txStartEndTracer.txEndGasUsed).isEqualTo(24303);
assertThat(txStartEndTracer.txEndTimeNs).isNotNull();
assertThat(txStartEndTracer.txEndLogs).isNotEmpty();
final Log actualLog = txStartEndTracer.txEndLogs.get(0);
assertThat(actualLog.getLogger())
.isEqualTo(Address.fromHexString("0x6295ee1b4f6dd65047762f924ecd367c17eabf8f"));
assertThat(actualLog.getData())
.isEqualTo(
Bytes.fromHexString(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe9000000000000000000000000000000000000000000000000000000000000002a"));
assertThat(actualLog.getTopics()).hasSize(4);
assertThat(actualLog.getTopics().get(0))
.isEqualTo(
Bytes.fromHexString(
"0xd5f0a30e4be0c6be577a71eceb7464245a796a7e6a55c0d971837b250de05f4e"));
assertThat(actualLog.getTopics().get(1))
.isEqualTo(
Bytes.fromHexString(
"0x0000000000000000000000000000000000000000000000000000000000000001"));
assertThat(actualLog.getTopics().get(2))
.isEqualTo(
Bytes.fromHexString(
"0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"));
assertThat(actualLog.getTopics().get(3))
.isEqualTo(
Bytes.fromHexString(
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
}
private static class TxStartEndTracer implements BlockAwareOperationTracer {
public WorldView txStartWorldView;
public WorldView txEndWorldView;
public Transaction txStartTransaction;
public Transaction txEndTransaction;
public boolean txEndStatus;
public Bytes txEndOutput;
public List<Log> txEndLogs;
public long txEndGasUsed;
public Long txEndTimeNs;
@Override
public void traceStartTransaction(final WorldView worldView, final Transaction transaction) {
txStartWorldView = worldView;
txStartTransaction = transaction;
}
@Override
public void traceEndTransaction(
final WorldView worldView,
final Transaction transaction,
final boolean status,
final Bytes output,
final List<Log> logs,
final long gasUsed,
final long timeNs) {
txEndWorldView = worldView;
txEndTransaction = transaction;
txEndStatus = status;
txEndOutput = output;
txEndLogs = logs;
txEndGasUsed = gasUsed;
txEndTimeNs = timeNs;
}
}
}

@ -270,6 +270,7 @@ public class T8nExecutor {
final TransactionProcessingResult result;
try {
tracer = tracerManager.getManagedTracer(i, transaction.getHash());
tracer.traceStartTransaction(worldStateUpdater, transaction);
result =
processor.processTransaction(
blockchain,
@ -312,12 +313,18 @@ public class T8nExecutor {
.getGasCalculator()
.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.getTo().isEmpty());
tracer.traceEndTransaction(
result.getOutput(), gasUsed - intrinsicGas, timer.elapsed(TimeUnit.NANOSECONDS));
TransactionReceipt receipt =
protocolSpec
.getTransactionReceiptFactory()
.create(transaction.getType(), result, worldState, gasUsed);
tracer.traceEndTransaction(
worldStateUpdater,
transaction,
result.isSuccessful(),
result.getOutput(),
result.getLogs(),
gasUsed - intrinsicGas,
timer.elapsed(TimeUnit.NANOSECONDS));
Bytes gasUsedInTransaction = Bytes.ofUnsignedLong(transactionGasUsed);
receipts.add(receipt);
ObjectNode receiptObject = receiptsArray.addObject();

@ -17,8 +17,11 @@ package org.hyperledger.besu.evm.tracing;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.worldstate.WorldView;
import java.util.List;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
@ -67,18 +70,30 @@ public interface OperationTracer {
/**
* Trace the start of a transaction.
*
* @param worldView an immutable view of the execution context
* @param transaction the transaction which will be processed
*/
default void traceStartTransaction(final Transaction transaction) {}
default void traceStartTransaction(final WorldView worldView, final Transaction transaction) {}
/**
* Trace the end of a transaction.
*
* @param worldView an immutable view of the execution context
* @param tx the transaction that just concluded
* @param status true if the transaction is successful, false otherwise
* @param output the bytes output from the transaction
* @param logs the logs emitted by this transaction
* @param gasUsed the gas used by the entire transaction
* @param timeNs the time in nanoseconds it took to execute the transaction
*/
default void traceEndTransaction(final Bytes output, final long gasUsed, final long timeNs) {}
default void traceEndTransaction(
final WorldView worldView,
final Transaction tx,
final boolean status,
final Bytes output,
final List<Log> logs,
final long gasUsed,
final long timeNs) {}
/**
* Trace the entering of a new context

@ -18,10 +18,13 @@ package org.hyperledger.besu.evm.tracing;
import static com.google.common.base.Strings.padStart;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.worldstate.WorldView;
import java.io.PrintStream;
import java.io.PrintWriter;
@ -216,7 +219,14 @@ public class StandardJsonTracer implements OperationTracer {
}
@Override
public void traceEndTransaction(final Bytes output, final long gasUsed, final long timeNs) {
public void traceEndTransaction(
final WorldView _worldView,
final Transaction _tx,
final boolean _status,
final Bytes output,
final List<Log> _logs,
final long gasUsed,
final long timeNs) {
final StringBuilder sb = new StringBuilder(1024);
sb.append("{");
if (output.size() > 0) {

Loading…
Cancel
Save