Update execution-spec-tests support (#6942)

Update t8n, b11r, and friends to support current execution spec
* bonsai migration introduced issues with deleted storage/accounts
* status on pre-constantinople json was invalid
* track refund across tx, not call frame
* allow tests to be executed by name
* fix withdrawal self-destruct test
* trace support in execution-spec-tests
* fix blob tests
* t8n results changes

Signed-off-by: Danno Ferrin <danno@numisight.com>
pull/6964/head
Danno Ferrin 7 months ago committed by GitHub
parent 941ab01426
commit 960145683f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/B11rSubCommand.java
  2. 39
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java
  3. 15
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java
  4. 172
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java
  5. 102
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java
  6. 4
      ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-blobs-per-tx.json
  7. 4
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldStateStorage.java
  8. 25
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java
  9. 32
      ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java
  10. 21
      ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java
  11. 14
      evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java
  12. 5
      evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java
  13. 1
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java
  14. 1
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java

@ -56,7 +56,7 @@ import picocli.CommandLine.ParentCommand;
@Command(
name = COMMAND_NAME,
aliases = {COMMAND_ALIAS},
description = "Execute an Ethereum State Test.",
description = "Block Builder subcommand.",
mixinStandardHelpOptions = true,
versionProvider = VersionProvider.class)
public class B11rSubCommand implements Runnable {

@ -26,6 +26,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
@ -33,13 +34,11 @@ import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.account.AccountStorageEntry;
import org.hyperledger.besu.evm.code.CodeInvalid;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.log.LogsBloomFilter;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.metrics.MetricsSystemModule;
import org.hyperledger.besu.util.LogConfigurator;
@ -58,7 +57,7 @@ import java.util.Comparator;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.NavigableMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@ -474,8 +473,9 @@ public class EvmToolCommand implements Runnable {
lastTime = stopwatch.elapsed().toNanos();
stopwatch.reset();
if (showJsonAlloc && lastLoop) {
initialMessageFrame.getSelfDestructs().forEach(updater::deleteAccount);
updater.commit();
WorldState worldState = component.getWorldState();
MutableWorldState worldState = component.getWorldState();
dumpWorldState(worldState, out);
}
} while (remainingIters-- > 0);
@ -486,34 +486,39 @@ public class EvmToolCommand implements Runnable {
}
}
public static void dumpWorldState(final WorldState worldState, final PrintWriter out) {
public static void dumpWorldState(final MutableWorldState worldState, final PrintWriter out) {
out.println("{");
worldState
.streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE)
.sorted(Comparator.comparing(o -> o.getAddress().get().toHexString()))
.forEach(
account -> {
out.println(
" \"" + account.getAddress().map(Address::toHexString).orElse("-") + "\": {");
a -> {
var account = worldState.get(a.getAddress().get());
out.println(" \"" + account.getAddress().toHexString() + "\": {");
if (account.getCode() != null && !account.getCode().isEmpty()) {
out.println(" \"code\": \"" + account.getCode().toHexString() + "\",");
}
NavigableMap<Bytes32, AccountStorageEntry> storageEntries =
account.storageEntriesFrom(Bytes32.ZERO, Integer.MAX_VALUE);
var storageEntries =
account.storageEntriesFrom(Bytes32.ZERO, Integer.MAX_VALUE).values().stream()
.map(
e ->
Map.entry(
e.getKey().get(),
account.getStorageValue(UInt256.fromBytes(e.getKey().get()))))
.filter(e -> !e.getValue().isZero())
.sorted(Map.Entry.comparingByKey())
.toList();
if (!storageEntries.isEmpty()) {
out.println(" \"storage\": {");
out.println(
STORAGE_JOINER.join(
storageEntries.values().stream()
storageEntries.stream()
.map(
accountStorageEntry ->
e ->
" \""
+ accountStorageEntry
.getKey()
.map(UInt256::toQuantityHexString)
.orElse("-")
+ e.getKey().toQuantityHexString()
+ "\": \""
+ accountStorageEntry.getValue().toQuantityHexString()
+ e.getValue().toQuantityHexString()
+ "\"")
.toList()));
out.println(" },");

@ -85,6 +85,11 @@ public class StateTestSubCommand implements Runnable {
description = "Force the state tests to run on a specific fork.")
private String fork = null;
@Option(
names = {"--test-name"},
description = "Limit execution to one named test.")
private String testName = null;
@Option(
names = {"--data-index"},
description = "Limit execution to one data variable.")
@ -173,10 +178,12 @@ public class StateTestSubCommand implements Runnable {
private void executeStateTest(final Map<String, GeneralStateTestCaseSpec> generalStateTests) {
for (final Map.Entry<String, GeneralStateTestCaseSpec> generalStateTestEntry :
generalStateTests.entrySet()) {
generalStateTestEntry
.getValue()
.finalStateSpecs()
.forEach((__, specs) -> traceTestSpecs(generalStateTestEntry.getKey(), specs));
if (testName == null || testName.equals(generalStateTestEntry.getKey())) {
generalStateTestEntry
.getValue()
.finalStateSpecs()
.forEach((__, specs) -> traceTestSpecs(generalStateTestEntry.getKey(), specs));
}
}
}

@ -18,6 +18,7 @@ package org.hyperledger.besu.evmtool;
import static org.hyperledger.besu.ethereum.core.Transaction.REPLAY_PROTECTED_V_BASE;
import static org.hyperledger.besu.ethereum.core.Transaction.REPLAY_PROTECTED_V_MIN;
import static org.hyperledger.besu.ethereum.core.Transaction.REPLAY_UNPROTECTED_V_BASE;
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent;
import static org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules.shouldClearEmptyAccounts;
import org.hyperledger.besu.config.StubGenesisConfigOptions;
@ -26,7 +27,6 @@ import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.VersionedHash;
@ -37,6 +37,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ParentBeaconBlockRootHelper;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
@ -50,7 +51,7 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.AccountStorageEntry;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@ -63,10 +64,11 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableMap;
import java.util.Map;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.StreamSupport;
import com.fasterxml.jackson.databind.JsonNode;
@ -252,20 +254,40 @@ public class T8nExecutor {
final MainnetTransactionProcessor processor = protocolSpec.getTransactionProcessor();
final WorldUpdater worldStateUpdater = worldState.updater();
final ReferenceTestBlockchain blockchain = new ReferenceTestBlockchain(blockHeader.getNumber());
final Wei blobGasPrice =
protocolSpec
.getFeeMarket()
.blobGasPricePerGas(blockHeader.getExcessBlobGas().orElse(BlobGas.ZERO));
.blobGasPricePerGas(calculateExcessBlobGasForParent(protocolSpec, blockHeader));
long blobGasLimit = protocolSpec.getGasLimitCalculator().currentBlobGasLimit();
referenceTestEnv
.getParentBeaconBlockRoot()
.ifPresent(
bytes32 ->
ParentBeaconBlockRootHelper.storeParentBeaconBlockRoot(
worldStateUpdater.updater(), referenceTestEnv.getTimestamp(), bytes32));
List<TransactionReceipt> receipts = new ArrayList<>();
List<RejectedTransaction> invalidTransactions = new ArrayList<>(rejections);
List<Transaction> validTransactions = new ArrayList<>();
ArrayNode receiptsArray = objectMapper.createArrayNode();
long gasUsed = 0;
long blobGasUsed = 0;
for (int i = 0; i < transactions.size(); i++) {
Transaction transaction = transactions.get(i);
final Stopwatch timer = Stopwatch.createStarted();
GasCalculator gasCalculator = protocolSpec.getGasCalculator();
int blobCount = transaction.getBlobCount();
blobGasUsed += gasCalculator.blobGasCost(blobCount);
if (blobGasUsed > blobGasLimit) {
invalidTransactions.add(
new RejectedTransaction(
i,
String.format(
"blob gas (%d) would exceed block maximum %d", blobGasUsed, blobGasLimit)));
continue;
}
final OperationTracer tracer; // You should have picked Mercy.
final TransactionProcessingResult result;
@ -304,52 +326,51 @@ public class T8nExecutor {
if (result.isInvalid()) {
invalidTransactions.add(
new RejectedTransaction(i, result.getValidationResult().getErrorMessage()));
continue;
}
validTransactions.add(transaction);
long transactionGasUsed = transaction.getGasLimit() - result.getGasRemaining();
gasUsed += transactionGasUsed;
long intrinsicGas =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.getTo().isEmpty());
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();
receiptObject.put(
"root", receipt.getStateRoot() == null ? "0x" : receipt.getStateRoot().toHexString());
int status = receipt.getStatus();
receiptObject.put("status", "0x" + Math.max(status, 0));
receiptObject.put("cumulativeGasUsed", Bytes.ofUnsignedLong(gasUsed).toQuantityHexString());
receiptObject.put("logsBloom", receipt.getBloomFilter().toHexString());
if (result.getLogs().isEmpty()) {
receiptObject.putNull("logs");
} else {
validTransactions.add(transaction);
long transactionGasUsed = transaction.getGasLimit() - result.getGasRemaining();
gasUsed += transactionGasUsed;
long intrinsicGas =
protocolSpec
.getGasCalculator()
.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.getTo().isEmpty());
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();
receiptObject.put(
"root", receipt.getStateRoot() == null ? "0x" : receipt.getStateRoot().toHexString());
receiptObject.put("status", "0x" + receipt.getStatus());
receiptObject.put("cumulativeGasUsed", Bytes.ofUnsignedLong(gasUsed).toQuantityHexString());
receiptObject.put("logsBloom", receipt.getBloomFilter().toHexString());
if (result.getLogs().isEmpty()) {
receiptObject.putNull("logs");
} else {
ArrayNode logsArray = receiptObject.putArray("logs");
for (Log log : result.getLogs()) {
logsArray.addPOJO(log);
}
ArrayNode logsArray = receiptObject.putArray("logs");
for (Log log : result.getLogs()) {
logsArray.addPOJO(log);
}
receiptObject.put("transactionHash", transaction.getHash().toHexString());
receiptObject.put(
"contractAddress", transaction.contractAddress().orElse(Address.ZERO).toHexString());
receiptObject.put("gasUsed", gasUsedInTransaction.toQuantityHexString());
receiptObject.put("blockHash", Hash.ZERO.toHexString());
receiptObject.put("transactionIndex", Bytes.ofUnsignedLong(i).toQuantityHexString());
}
receiptObject.put("transactionHash", transaction.getHash().toHexString());
receiptObject.put(
"contractAddress", transaction.contractAddress().orElse(Address.ZERO).toHexString());
receiptObject.put("gasUsed", gasUsedInTransaction.toQuantityHexString());
receiptObject.put("blockHash", Hash.ZERO.toHexString());
receiptObject.put("transactionIndex", Bytes.ofUnsignedLong(i).toQuantityHexString());
}
final ObjectNode resultObject = objectMapper.createObjectNode();
@ -366,19 +387,19 @@ public class T8nExecutor {
.incrementBalance(reward);
}
worldStateUpdater.commit();
// Invoke the withdrawal processor to handle CL withdrawals.
if (!referenceTestEnv.getWithdrawals().isEmpty()) {
try {
protocolSpec
.getWithdrawalsProcessor()
.ifPresent(
p -> p.processWithdrawals(referenceTestEnv.getWithdrawals(), worldStateUpdater));
p -> p.processWithdrawals(referenceTestEnv.getWithdrawals(), worldState.updater()));
} catch (RuntimeException re) {
resultObject.put("exception", re.getMessage());
}
}
worldStateUpdater.commit();
worldState.persist(blockHeader);
resultObject.put("stateRoot", worldState.rootHash().toHexString());
@ -411,41 +432,48 @@ public class T8nExecutor {
blockHeader
.getWithdrawalsRoot()
.ifPresent(wr -> resultObject.put("withdrawalsRoot", wr.toHexString()));
blockHeader
.getBlobGasUsed()
.ifPresentOrElse(
bgu -> resultObject.put("blobGasUsed", Bytes.ofUnsignedLong(bgu).toQuantityHexString()),
() ->
blockHeader
.getExcessBlobGas()
.ifPresent(ebg -> resultObject.put("blobGasUsed", "0x0")));
AtomicLong bgHolder = new AtomicLong(blobGasUsed);
blockHeader
.getExcessBlobGas()
.ifPresent(ebg -> resultObject.put("currentExcessBlobGas", ebg.toShortHexString()));
.ifPresent(
ebg -> {
resultObject.put(
"currentExcessBlobGas",
calculateExcessBlobGasForParent(protocolSpec, blockHeader)
.toBytes()
.toQuantityHexString());
resultObject.put(
"blobGasUsed", Bytes.ofUnsignedLong(bgHolder.longValue()).toQuantityHexString());
});
ObjectNode allocObject = objectMapper.createObjectNode();
worldState
.streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE)
.sorted(Comparator.comparing(o -> o.getAddress().get().toHexString()))
.forEach(
account -> {
ObjectNode accountObject =
allocObject.putObject(
account.getAddress().map(Address::toHexString).orElse("0x"));
a -> {
var account = worldState.get(a.getAddress().get());
ObjectNode accountObject = allocObject.putObject(account.getAddress().toHexString());
if (account.getCode() != null && !account.getCode().isEmpty()) {
accountObject.put("code", account.getCode().toHexString());
}
NavigableMap<Bytes32, AccountStorageEntry> storageEntries =
account.storageEntriesFrom(Bytes32.ZERO, Integer.MAX_VALUE);
var storageEntries =
account.storageEntriesFrom(Bytes32.ZERO, Integer.MAX_VALUE).values().stream()
.map(
e ->
Map.entry(
e.getKey().get(),
account.getStorageValue(UInt256.fromBytes(e.getKey().get()))))
.filter(e -> !e.getValue().isZero())
.sorted(Map.Entry.comparingByKey())
.toList();
if (!storageEntries.isEmpty()) {
ObjectNode storageObject = accountObject.putObject("storage");
storageEntries.values().stream()
.sorted(Comparator.comparing(a -> a.getKey().get()))
.forEach(
accountStorageEntry ->
storageObject.put(
accountStorageEntry.getKey().map(UInt256::toHexString).orElse("0x"),
accountStorageEntry.getValue().toHexString()));
storageEntries.forEach(
accountStorageEntry ->
storageObject.put(
accountStorageEntry.getKey().toHexString(),
accountStorageEntry.getValue().toHexString()));
}
accountObject.put("balance", account.getBalance().toShortHexString());
if (account.getNonce() != 0) {

@ -25,14 +25,19 @@ import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.tracing.StandardJsonTracer;
import org.hyperledger.besu.evmtool.T8nExecutor.RejectedTransaction;
import org.hyperledger.besu.util.LogConfigurator;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -49,7 +54,9 @@ import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerRequest;
import picocli.CommandLine;
import picocli.CommandLine.ParentCommand;
@SuppressWarnings("java:S106") // using standard output is the point of this class
@CommandLine.Command(
name = "t8n-server",
description = "Run Ethereum State Test server",
@ -66,6 +73,24 @@ public class T8nServerSubCommand implements Runnable {
description = "Port to bind to")
private int port = 3000;
@CommandLine.Option(
names = {"--output.basedir"},
paramLabel = "full path",
description = "The output ")
private final Path outDir = Path.of(".");
@ParentCommand private final EvmToolCommand parentCommand;
@SuppressWarnings("unused")
public T8nServerSubCommand() {
// PicoCLI requires this
this(null);
}
T8nServerSubCommand(final EvmToolCommand parentCommand) {
this.parentCommand = parentCommand;
}
@Override
public void run() {
LogConfigurator.setLevel("", "OFF");
@ -144,6 +169,57 @@ public class T8nServerSubCommand implements Runnable {
}
}
T8nExecutor.TracerManager tracerManager;
if (parentCommand.showJsonResults) {
tracerManager =
new T8nExecutor.TracerManager() {
private final Map<OperationTracer, FileOutputStream> outputStreams =
new HashMap<>();
@Override
public OperationTracer getManagedTracer(final int txIndex, final Hash txHash)
throws Exception {
outDir.toFile().mkdirs();
var traceDest =
new FileOutputStream(
outDir
.resolve(
String.format("trace-%d-%s.jsonl", txIndex, txHash.toHexString()))
.toFile());
var jsonTracer =
new StandardJsonTracer(
new PrintStream(traceDest),
parentCommand.showMemory,
!parentCommand.hideStack,
parentCommand.showReturnData,
parentCommand.showStorage);
outputStreams.put(jsonTracer, traceDest);
return jsonTracer;
}
@Override
public void disposeTracer(final OperationTracer tracer) throws IOException {
if (outputStreams.containsKey(tracer)) {
outputStreams.remove(tracer).close();
}
}
};
} else {
tracerManager =
new T8nExecutor.TracerManager() {
@Override
public OperationTracer getManagedTracer(final int txIndex, final Hash txHash) {
return OperationTracer.NO_TRACING;
}
@Override
public void disposeTracer(final OperationTracer tracer) {
// single-test mode doesn't need to track tracers
}
};
}
result =
T8nExecutor.runTest(
chainId,
@ -154,17 +230,7 @@ public class T8nServerSubCommand implements Runnable {
initialWorldState,
transactions,
rejections,
new T8nExecutor.TracerManager() {
@Override
public OperationTracer getManagedTracer(final int txIndex, final Hash txHash) {
return OperationTracer.NO_TRACING;
}
@Override
public void disposeTracer(final OperationTracer tracer) {
// No output streams to dispose of
}
});
tracerManager);
}
ObjectNode outputObject = objectMapper.createObjectNode();
@ -172,15 +238,11 @@ public class T8nServerSubCommand implements Runnable {
outputObject.set("body", result.bodyBytes());
outputObject.set("result", result.resultObject());
try {
String response =
objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(outputObject);
req.response().setChunked(true);
req.response().putHeader("Content-Type", "application/json").send(response);
} catch (JsonProcessingException e) {
req.response().setStatusCode(500).end(e.getMessage());
}
} catch (Throwable t) {
String response =
objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(outputObject);
req.response().setChunked(true);
req.response().putHeader("Content-Type", "application/json").send(response);
} catch (Exception t) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos, true, StandardCharsets.UTF_8);
t.printStackTrace(ps);

@ -111,8 +111,8 @@
"gasUsed": "0x5208",
"currentBaseFee": "0x7",
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"blobGasUsed": "0x0",
"currentExcessBlobGas": "0x0"
"currentExcessBlobGas": "0x0",
"blobGasUsed": "0x20000"
}
}
}

@ -25,6 +25,7 @@ import org.hyperledger.besu.evm.account.AccountStorageEntry;
import org.hyperledger.besu.evm.worldstate.WorldState;
import java.util.Comparator;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Optional;
import java.util.TreeMap;
@ -53,7 +54,7 @@ public class BonsaiReferenceTestWorldStateStorage extends BonsaiWorldStateLayerS
.stream()
.collect(
Collectors.toMap(
e -> e.getKey(),
Map.Entry::getKey,
e ->
AccountStorageEntry.create(
UInt256.fromBytes(RLP.decodeValue(e.getValue())),
@ -80,6 +81,7 @@ public class BonsaiReferenceTestWorldStateStorage extends BonsaiWorldStateLayerS
BonsaiAccount.fromRLP(context, address, entry.getValue(), false))))
.filter(Optional::isPresent)
.map(Optional::get)
.filter(acct -> context.updater().getAccount(acct.getAddress().orElse(null)) != null)
.sorted(Comparator.comparing(account -> account.getAddress().orElse(Address.ZERO)));
}
}

@ -107,10 +107,8 @@ public class ReferenceTestEnv extends BlockHeader {
@JsonProperty("currentBeaconRoot") final String currentBeaconRoot,
@JsonProperty("currentBlobGasUsed") final String currentBlobGasUsed,
@JsonProperty("currentCoinbase") final String coinbase,
@JsonProperty("currentDataGasUsed") final String currentDataGasUsed,
@JsonProperty("currentDifficulty") final String difficulty,
@JsonProperty("currentExcessBlobGas") final String currentExcessBlobGas,
@JsonProperty("currentExcessDataGas") final String currentExcessDataGas,
@JsonProperty("currentGasLimit") final String gasLimit,
@JsonProperty("currentNumber") final String number,
@JsonProperty("currentRandom") final String random,
@ -119,10 +117,8 @@ public class ReferenceTestEnv extends BlockHeader {
@JsonProperty("currentWithdrawalsRoot") final String currentWithdrawalsRoot,
@JsonProperty("parentBaseFee") final String parentBaseFee,
@JsonProperty("parentBlobGasUsed") final String parentBlobGasUsed,
@JsonProperty("parentDataGasUsed") final String parentDataGasUsed,
@JsonProperty("parentDifficulty") final String parentDifficulty,
@JsonProperty("parentExcessBlobGas") final String parentExcessBlobGas,
@JsonProperty("parentExcessDataGas") final String parentExcessDataGas,
@JsonProperty("parentGasLimit") final String parentGasLimit,
@JsonProperty("parentGasUsed") final String parentGasUsed,
@JsonProperty("parentTimestamp") final String parentTimestamp,
@ -145,12 +141,8 @@ public class ReferenceTestEnv extends BlockHeader {
Optional.ofNullable(random).map(Difficulty::fromHexString).orElse(Difficulty.ZERO),
0L,
currentWithdrawalsRoot == null ? null : Hash.fromHexString(currentWithdrawalsRoot),
currentBlobGasUsed == null
? currentDataGasUsed == null ? null : Long.decode(currentDataGasUsed)
: Long.decode(currentBlobGasUsed),
currentExcessBlobGas == null
? currentExcessDataGas == null ? null : BlobGas.fromHexString(currentExcessDataGas)
: BlobGas.fromHexString(currentExcessBlobGas),
currentBlobGasUsed == null ? null : Long.decode(currentBlobGasUsed),
currentExcessBlobGas == null ? null : BlobGas.of(Long.decode(currentExcessBlobGas)),
beaconRoot == null ? null : Bytes32.fromHexString(beaconRoot),
null, // depositsRoot
null, // exitsRoot
@ -160,9 +152,8 @@ public class ReferenceTestEnv extends BlockHeader {
this.parentGasUsed = parentGasUsed;
this.parentGasLimit = parentGasLimit;
this.parentTimestamp = parentTimestamp;
this.parentExcessBlobGas =
parentExcessBlobGas == null ? parentExcessDataGas : parentExcessBlobGas;
this.parentBlobGasUsed = parentBlobGasUsed == null ? parentDataGasUsed : parentBlobGasUsed;
this.parentExcessBlobGas = parentExcessBlobGas;
this.parentBlobGasUsed = parentBlobGasUsed;
this.withdrawals =
withdrawals == null
? List.of()
@ -230,12 +221,8 @@ public class ReferenceTestEnv extends BlockHeader {
null)));
}
if (parentExcessBlobGas != null && parentBlobGasUsed != null) {
builder.excessBlobGas(
BlobGas.of(
protocolSpec
.getGasCalculator()
.computeExcessBlobGas(
Long.decode(parentExcessBlobGas), Long.decode(parentBlobGasUsed))));
builder.excessBlobGas(BlobGas.of(Long.decode(parentExcessBlobGas)));
builder.blobGasUsed(Long.decode(parentBlobGasUsed));
}
return builder.buildBlockHeader();

@ -101,12 +101,13 @@ public class BlockchainReferenceTestTools {
return params.generate(filePath);
}
@SuppressWarnings("java:S5960") // this is actually test code
public static void executeTest(final BlockchainReferenceTestCaseSpec spec) {
final BlockHeader genesisBlockHeader = spec.getGenesisBlockHeader();
final MutableWorldState worldState =
spec.getWorldStateArchive()
.getMutable(genesisBlockHeader.getStateRoot(), genesisBlockHeader.getHash())
.get();
.orElseThrow();
final ProtocolSchedule schedule =
REFERENCE_TEST_PROTOCOL_SCHEDULES.getByName(spec.getNetwork());
@ -126,18 +127,7 @@ public class BlockchainReferenceTestTools {
final ProtocolSpec protocolSpec = schedule.getByBlockHeader(block.getHeader());
final BlockImporter blockImporter = protocolSpec.getBlockImporter();
EVM evm = protocolSpec.getEvm();
if (evm.getEvmConfiguration().worldUpdaterMode() == WorldUpdaterMode.JOURNALED) {
assumeThat(
worldState
.streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE)
.anyMatch(AccountState::isEmpty))
.withFailMessage("Journaled account configured and empty account detected")
.isFalse();
assumeThat(EvmSpecVersion.SPURIOUS_DRAGON.compareTo(evm.getEvmVersion()) > 0)
.withFailMessage("Journaled account configured and fork prior to the merge specified")
.isFalse();
}
verifyJournaledEVMAccountCompatability(worldState, protocolSpec);
final HeaderValidationMode validationMode =
"NoProof".equalsIgnoreCase(spec.getSealEngine())
@ -154,4 +144,20 @@ public class BlockchainReferenceTestTools {
Assertions.assertThat(blockchain.getChainHeadHash()).isEqualTo(spec.getLastBlockHash());
}
static void verifyJournaledEVMAccountCompatability(
final MutableWorldState worldState, final ProtocolSpec protocolSpec) {
EVM evm = protocolSpec.getEvm();
if (evm.getEvmConfiguration().worldUpdaterMode() == WorldUpdaterMode.JOURNALED) {
assumeThat(
worldState
.streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE)
.anyMatch(AccountState::isEmpty))
.withFailMessage("Journaled account configured and empty account detected")
.isFalse();
assumeThat(EvmSpecVersion.SPURIOUS_DRAGON.compareTo(evm.getEvmVersion()) > 0)
.withFailMessage("Journaled account configured and fork prior to the merge specified")
.isFalse();
}
}
}

@ -15,14 +15,13 @@
package org.hyperledger.besu.ethereum.vm;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
@ -39,11 +38,7 @@ import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.EvmSpecVersion;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.AccountState;
import org.hyperledger.besu.evm.internal.EvmConfiguration.WorldUpdaterMode;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.testutil.JsonTestParameters;
@ -121,24 +116,14 @@ public class GeneralStateReferenceTestTools {
return params.generate(filePath);
}
@SuppressWarnings("java:S5960") // this is actually test support code, not production code
public static void executeTest(final GeneralStateTestCaseEipSpec spec) {
final BlockHeader blockHeader = spec.getBlockHeader();
final ReferenceTestWorldState initialWorldState = spec.getInitialWorldState();
final Transaction transaction = spec.getTransaction();
ProtocolSpec protocolSpec = protocolSpec(spec.getFork());
EVM evm = protocolSpec.getEvm();
if (evm.getEvmConfiguration().worldUpdaterMode() == WorldUpdaterMode.JOURNALED) {
assumeThat(
initialWorldState
.streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE)
.anyMatch(AccountState::isEmpty))
.withFailMessage("Journaled account configured and empty account detected")
.isFalse();
assumeThat(EvmSpecVersion.SPURIOUS_DRAGON.compareTo(evm.getEvmVersion()) > 0)
.withFailMessage("Journaled account configured and fork prior to the merge specified")
.isFalse();
}
BlockchainReferenceTestTools.verifyJournaledEVMAccountCompatability(initialWorldState, protocolSpec);
// Sometimes the tests ask us assemble an invalid transaction. If we have
// no valid transaction then there is no test. GeneralBlockChain tests

@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkState;
import static java.util.Collections.emptySet;
import org.hyperledger.besu.collections.trie.BytesTrieSet;
import org.hyperledger.besu.collections.undo.UndoScalar;
import org.hyperledger.besu.collections.undo.UndoSet;
import org.hyperledger.besu.collections.undo.UndoTable;
import org.hyperledger.besu.datatypes.Address;
@ -220,7 +221,6 @@ public class MessageFrame {
// Transaction state fields.
private final List<Log> logs = new ArrayList<>();
private long gasRefund = 0L;
private final Map<Address, Wei> refunds = new HashMap<>();
// Execution Environment fields.
@ -414,7 +414,8 @@ public class MessageFrame {
* @return the amount of gas available, after deductions.
*/
public long decrementRemainingGas(final long amount) {
return this.gasRemaining -= amount;
this.gasRemaining -= amount;
return this.gasRemaining;
}
/**
@ -892,12 +893,12 @@ public class MessageFrame {
* @param amount The amount to increment the refund
*/
public void incrementGasRefund(final long amount) {
this.gasRefund += amount;
this.txValues.gasRefunds().set(this.txValues.gasRefunds().get() + amount);
}
/** Clear the accumulated gas refund. */
public void clearGasRefund() {
gasRefund = 0L;
this.txValues.gasRefunds().set(0L);
}
/**
@ -906,7 +907,7 @@ public class MessageFrame {
* @return accumulated gas refund
*/
public long getGasRefund() {
return gasRefund;
return txValues.gasRefunds().get();
}
/**
@ -1731,7 +1732,8 @@ public class MessageFrame {
versionedHashes,
UndoTable.of(HashBasedTable.create()),
UndoSet.of(new BytesTrieSet<>(Address.SIZE)),
UndoSet.of(new BytesTrieSet<>(Address.SIZE)));
UndoSet.of(new BytesTrieSet<>(Address.SIZE)),
new UndoScalar<>(0L));
updater = worldUpdater;
newStatic = isStatic;
} else {

@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.evm.frame;
import org.hyperledger.besu.collections.undo.UndoScalar;
import org.hyperledger.besu.collections.undo.UndoSet;
import org.hyperledger.besu.collections.undo.UndoTable;
import org.hyperledger.besu.datatypes.Address;
@ -47,7 +48,8 @@ public record TxValues(
Optional<List<VersionedHash>> versionedHashes,
UndoTable<Address, Bytes32, Bytes32> transientStorage,
UndoSet<Address> creates,
UndoSet<Address> selfDestructs) {
UndoSet<Address> selfDestructs,
UndoScalar<Long> gasRefunds) {
/**
* For all data stored in this record, undo the changes since the mark.
@ -60,5 +62,6 @@ public record TxValues(
transientStorage.undo(mark);
creates.undo(mark);
selfDestructs.undo(mark);
gasRefunds.undo(mark);
}
}

@ -289,7 +289,6 @@ public abstract class AbstractCallOperation extends AbstractOperation {
frame.addLogs(childFrame.getLogs());
frame.addSelfDestructs(childFrame.getSelfDestructs());
frame.addCreates(childFrame.getCreates());
frame.incrementGasRefund(childFrame.getGasRefund());
final long gasRemaining = childFrame.getRemainingGas();
frame.incrementRemainingGas(gasRemaining);

@ -191,7 +191,6 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
frame.addLogs(childFrame.getLogs());
frame.addSelfDestructs(childFrame.getSelfDestructs());
frame.addCreates(childFrame.getCreates());
frame.incrementGasRefund(childFrame.getGasRefund());
if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
Address createdAddress = childFrame.getContractAddress();

Loading…
Cancel
Save