EIP-4844 testing support (#5702)

Add two new fields to reference tests (versionedHashes, maxFeePerDataGas)
Rename DataHash to BlobHash (to align with EIP4844 text)

Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
pull/5727/head
Danno Ferrin 1 year ago committed by GitHub
parent ba2fc3d667
commit 403297b874
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      datatypes/src/main/java/org/hyperledger/besu/datatypes/VersionedHash.java
  2. 16
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java
  3. 6
      evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
  4. 18
      evm/src/main/java/org/hyperledger/besu/evm/operation/BlobHashOperation.java
  5. 12
      evm/src/test/java/org/hyperledger/besu/evm/operations/BlobHashOperationTest.java

@ -17,6 +17,7 @@ package org.hyperledger.besu.datatypes;
import java.util.Objects; import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.Bytes32;
@ -37,7 +38,7 @@ public class VersionedHash {
public static final byte SHA256_VERSION_ID = 1; public static final byte SHA256_VERSION_ID = 1;
/** A default versioned hash, nonsensical but valid. */ /** A default versioned hash, nonsensical but valid. */
public static VersionedHash DEFAULT_VERSIONED_HASH = public static final VersionedHash DEFAULT_VERSIONED_HASH =
new VersionedHash(SHA256_VERSION_ID, Hash.ZERO); new VersionedHash(SHA256_VERSION_ID, Hash.ZERO);
/** /**
@ -68,6 +69,21 @@ public class VersionedHash {
this.hashish = typedHash; this.hashish = typedHash;
} }
/**
* Parse a hexadecimal string representing a versioned hash value.
*
* @param str A hexadecimal string (with or without the leading '0x') representing a valid hash
* value.
* @return The parsed hash.
* @throws NullPointerException if the provided string is {@code null}.
* @throws IllegalArgumentException if the string is either not hexadecimal, or not the valid
* representation of a versioned hash (not 32 bytes or bad version).
*/
@JsonCreator
public static VersionedHash fromHexString(final String str) {
return new VersionedHash(Bytes32.fromHexString(str));
}
/** /**
* Convert it to raw bytes. * Convert it to raw bytes.
* *

@ -19,6 +19,7 @@ import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.evm.AccessListEntry; import org.hyperledger.besu.evm.AccessListEntry;
@ -71,6 +72,8 @@ public class StateTestVersionedTransaction {
private final List<Wei> values; private final List<Wei> values;
private final List<Bytes> payloads; private final List<Bytes> payloads;
private final Optional<List<List<AccessListEntry>>> maybeAccessLists; private final Optional<List<List<AccessListEntry>>> maybeAccessLists;
private final Wei maxFeePerDataGas;
private final List<VersionedHash> blobVersionedHashes;
/** /**
* Constructor for populating a mock transaction with json data. * Constructor for populating a mock transaction with json data.
@ -98,7 +101,9 @@ public class StateTestVersionedTransaction {
@JsonProperty("secretKey") final String secretKey, @JsonProperty("secretKey") final String secretKey,
@JsonProperty("data") final String[] data, @JsonProperty("data") final String[] data,
@JsonDeserialize(using = StateTestAccessListDeserializer.class) @JsonProperty("accessLists") @JsonDeserialize(using = StateTestAccessListDeserializer.class) @JsonProperty("accessLists")
final List<List<AccessListEntry>> maybeAccessLists) { final List<List<AccessListEntry>> maybeAccessLists,
@JsonProperty("maxFeePerDataGas") final String maxFeePerDataGas,
@JsonProperty("blobVersionedHashes") final List<VersionedHash> blobVersionedHashes) {
this.nonce = Bytes.fromHexStringLenient(nonce).toLong(); this.nonce = Bytes.fromHexStringLenient(nonce).toLong();
this.gasPrice = Optional.ofNullable(gasPrice).map(Wei::fromHexString).orElse(null); this.gasPrice = Optional.ofNullable(gasPrice).map(Wei::fromHexString).orElse(null);
@ -116,9 +121,16 @@ public class StateTestVersionedTransaction {
this.values = parseArray(value, Wei::fromHexString); this.values = parseArray(value, Wei::fromHexString);
this.payloads = parseArray(data, Bytes::fromHexString); this.payloads = parseArray(data, Bytes::fromHexString);
this.maybeAccessLists = Optional.ofNullable(maybeAccessLists); this.maybeAccessLists = Optional.ofNullable(maybeAccessLists);
this.maxFeePerDataGas =
Optional.ofNullable(maxFeePerDataGas).map(Wei::fromHexString).orElse(null);
this.blobVersionedHashes = blobVersionedHashes;
} }
private static <T> List<T> parseArray(final String[] array, final Function<String, T> parseFct) { private static <T> List<T> parseArray(final String[] array, final Function<String, T> parseFct) {
if (array == null) {
return null;
}
final List<T> res = new ArrayList<>(array.length); final List<T> res = new ArrayList<>(array.length);
for (final String str : array) { for (final String str : array) {
try { try {
@ -148,6 +160,8 @@ public class StateTestVersionedTransaction {
Optional.ofNullable(maxPriorityFeePerGas).ifPresent(transactionBuilder::maxPriorityFeePerGas); Optional.ofNullable(maxPriorityFeePerGas).ifPresent(transactionBuilder::maxPriorityFeePerGas);
maybeAccessLists.ifPresent( maybeAccessLists.ifPresent(
accessLists -> transactionBuilder.accessList(accessLists.get(indexes.data))); accessLists -> transactionBuilder.accessList(accessLists.get(indexes.data)));
Optional.ofNullable(maxFeePerDataGas).ifPresent(transactionBuilder::maxFeePerDataGas);
transactionBuilder.versionedHashes(blobVersionedHashes);
transactionBuilder.guessType(); transactionBuilder.guessType();
if (transactionBuilder.getTransactionType().requiresChainId()) { if (transactionBuilder.getTransactionType().requiresChainId()) {

@ -32,6 +32,7 @@ import org.hyperledger.besu.evm.operation.AddressOperation;
import org.hyperledger.besu.evm.operation.AndOperation; import org.hyperledger.besu.evm.operation.AndOperation;
import org.hyperledger.besu.evm.operation.BalanceOperation; import org.hyperledger.besu.evm.operation.BalanceOperation;
import org.hyperledger.besu.evm.operation.BaseFeeOperation; import org.hyperledger.besu.evm.operation.BaseFeeOperation;
import org.hyperledger.besu.evm.operation.BlobHashOperation;
import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.operation.BlockHashOperation;
import org.hyperledger.besu.evm.operation.ByteOperation; import org.hyperledger.besu.evm.operation.ByteOperation;
import org.hyperledger.besu.evm.operation.CallCodeOperation; import org.hyperledger.besu.evm.operation.CallCodeOperation;
@ -48,7 +49,6 @@ import org.hyperledger.besu.evm.operation.CodeSizeOperation;
import org.hyperledger.besu.evm.operation.CoinbaseOperation; import org.hyperledger.besu.evm.operation.CoinbaseOperation;
import org.hyperledger.besu.evm.operation.Create2Operation; import org.hyperledger.besu.evm.operation.Create2Operation;
import org.hyperledger.besu.evm.operation.CreateOperation; import org.hyperledger.besu.evm.operation.CreateOperation;
import org.hyperledger.besu.evm.operation.DataHashOperation;
import org.hyperledger.besu.evm.operation.DelegateCallOperation; import org.hyperledger.besu.evm.operation.DelegateCallOperation;
import org.hyperledger.besu.evm.operation.DifficultyOperation; import org.hyperledger.besu.evm.operation.DifficultyOperation;
import org.hyperledger.besu.evm.operation.DivOperation; import org.hyperledger.besu.evm.operation.DivOperation;
@ -847,8 +847,8 @@ public class MainnetEVMs {
registry.put(new TStoreOperation(gasCalculator)); registry.put(new TStoreOperation(gasCalculator));
registry.put(new TLoadOperation(gasCalculator)); registry.put(new TLoadOperation(gasCalculator));
// EIP-4844 DATAHASH // EIP-4844 BLOBHASH
registry.put(new DataHashOperation(gasCalculator)); registry.put(new BlobHashOperation(gasCalculator));
// EIP-5656 MCOPY // EIP-5656 MCOPY
registry.put(new MCopyOperation(gasCalculator)); registry.put(new MCopyOperation(gasCalculator));

@ -24,24 +24,25 @@ import java.util.List;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
/** /**
* The DataHash operation. https://eips.ethereum.org/EIPS/eip-4844 * The BlobHash operation. As specified in <a
* href="https://eips.ethereum.org/EIPS/eip-4844">EIP-4844</a>
* *
* <p>Reads index from the top of the stack as big-endian uint256, and replaces it on the stack with * <p>Reads index from the top of the stack as big-endian uint256, and replaces it on the stack with
* tx.message.blob_versioned_hashes[index] if index &lt; len(tx.message.blob_versioned_hashes), and * tx.message.blob_versioned_hashes[index] if index &lt; len(tx.message.blob_versioned_hashes), and
* otherwise with a zeroed bytes32 value. * otherwise with a zeroed bytes32 value.
*/ */
public class DataHashOperation extends AbstractOperation { public class BlobHashOperation extends AbstractOperation {
/** DATAHASH opcode number */ /** BLOBHASH opcode number */
public static final int OPCODE = 0x49; public static final int OPCODE = 0x49;
/** /**
* Instantiates a new DataHash operation. * Instantiates a new BlobHash operation.
* *
* @param gasCalculator the gas calculator * @param gasCalculator the gas calculator
*/ */
public DataHashOperation(final GasCalculator gasCalculator) { public BlobHashOperation(final GasCalculator gasCalculator) {
super(OPCODE, "DATAHASH", 1, 1, gasCalculator); super(OPCODE, "BLOBHASH", 1, 1, gasCalculator);
} }
@Override @Override
@ -67,9 +68,4 @@ public class DataHashOperation extends AbstractOperation {
} }
return new OperationResult(3, null); return new OperationResult(3, null);
} }
@Override
public boolean isVirtualOperation() {
return super.isVirtualOperation();
}
} }

@ -26,7 +26,7 @@ import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator; import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.operation.DataHashOperation; import org.hyperledger.besu.evm.operation.BlobHashOperation;
import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.operation.Operation;
import java.util.ArrayList; import java.util.ArrayList;
@ -38,7 +38,7 @@ import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
class DataHashOperationTest { class BlobHashOperationTest {
private static final String testVersionedHash = private static final String testVersionedHash =
"0x01cafebabeb0b0facedeadbeefbeef0001cafebabeb0b0facedeadbeefbeef00"; "0x01cafebabeb0b0facedeadbeefbeef0001cafebabeb0b0facedeadbeefbeef00";
@ -47,7 +47,7 @@ class DataHashOperationTest {
void putsHashOnStack() { void putsHashOnStack() {
VersionedHash version0Hash = new VersionedHash(Bytes32.fromHexStringStrict(testVersionedHash)); VersionedHash version0Hash = new VersionedHash(Bytes32.fromHexStringStrict(testVersionedHash));
List<VersionedHash> versionedHashes = Arrays.asList(version0Hash); List<VersionedHash> versionedHashes = Arrays.asList(version0Hash);
DataHashOperation getHash = new DataHashOperation(new LondonGasCalculator()); BlobHashOperation getHash = new BlobHashOperation(new LondonGasCalculator());
MessageFrame frame = mock(MessageFrame.class); MessageFrame frame = mock(MessageFrame.class);
when(frame.popStackItem()).thenReturn(Bytes.of(0)); when(frame.popStackItem()).thenReturn(Bytes.of(0));
when(frame.getVersionedHashes()).thenReturn(Optional.of(versionedHashes)); when(frame.getVersionedHashes()).thenReturn(Optional.of(versionedHashes));
@ -63,7 +63,7 @@ class DataHashOperationTest {
EVM fakeEVM = mock(EVM.class); EVM fakeEVM = mock(EVM.class);
DataHashOperation getHash = new DataHashOperation(new CancunGasCalculator()); BlobHashOperation getHash = new BlobHashOperation(new CancunGasCalculator());
MessageFrame frame = mock(MessageFrame.class); MessageFrame frame = mock(MessageFrame.class);
when(frame.popStackItem()).thenReturn(Bytes.of(0)); when(frame.popStackItem()).thenReturn(Bytes.of(0));
when(frame.getVersionedHashes()).thenReturn(Optional.empty()); when(frame.getVersionedHashes()).thenReturn(Optional.empty());
@ -84,7 +84,7 @@ class DataHashOperationTest {
void pushZeroOnVersionIndexOutOFBounds() { void pushZeroOnVersionIndexOutOFBounds() {
VersionedHash version0Hash = new VersionedHash(Bytes32.fromHexStringStrict(testVersionedHash)); VersionedHash version0Hash = new VersionedHash(Bytes32.fromHexStringStrict(testVersionedHash));
List<VersionedHash> versionedHashes = Arrays.asList(version0Hash); List<VersionedHash> versionedHashes = Arrays.asList(version0Hash);
DataHashOperation getHash = new DataHashOperation(new CancunGasCalculator()); BlobHashOperation getHash = new BlobHashOperation(new CancunGasCalculator());
MessageFrame frame = mock(MessageFrame.class); MessageFrame frame = mock(MessageFrame.class);
when(frame.popStackItem()).thenReturn(Bytes.of(1)); when(frame.popStackItem()).thenReturn(Bytes.of(1));
when(frame.getVersionedHashes()).thenReturn(Optional.of(versionedHashes)); when(frame.getVersionedHashes()).thenReturn(Optional.of(versionedHashes));
@ -99,7 +99,7 @@ class DataHashOperationTest {
public void pushZeroWhenPopsMissingUint256SizedIndex() { public void pushZeroWhenPopsMissingUint256SizedIndex() {
VersionedHash version0Hash = new VersionedHash(Bytes32.fromHexStringStrict(testVersionedHash)); VersionedHash version0Hash = new VersionedHash(Bytes32.fromHexStringStrict(testVersionedHash));
List<VersionedHash> versionedHashes = Arrays.asList(version0Hash); List<VersionedHash> versionedHashes = Arrays.asList(version0Hash);
DataHashOperation getHash = new DataHashOperation(new CancunGasCalculator()); BlobHashOperation getHash = new BlobHashOperation(new CancunGasCalculator());
MessageFrame frame = mock(MessageFrame.class); MessageFrame frame = mock(MessageFrame.class);
when(frame.popStackItem()).thenReturn(Bytes32.repeat((byte) 0x2C)); when(frame.popStackItem()).thenReturn(Bytes32.repeat((byte) 0x2C));
when(frame.getVersionedHashes()).thenReturn(Optional.of(versionedHashes)); when(frame.getVersionedHashes()).thenReturn(Optional.of(versionedHashes));
Loading…
Cancel
Save