Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d63e96e
Migrate wei operations to EVM v2 (first commit)
ahamlat Apr 13, 2026
ff6d03e
Update SelfBalance benchmarks
ahamlat Apr 13, 2026
de6a840
spotless
ahamlat Apr 13, 2026
436c308
Address comments.
ahamlat Apr 15, 2026
b7d8cab
Merge branch 'main' into wei-opcodes-on-evm-v2
ahamlat Apr 15, 2026
edcc531
Add Javadoc
ahamlat Apr 15, 2026
f969516
Add unit tests
ahamlat Apr 16, 2026
648b921
Merge branch 'main' into wei-opcodes-on-evm-v2
ahamlat Apr 16, 2026
3ecd7d3
spotless
ahamlat Apr 16, 2026
8009532
Update datatypes/src/main/java/org/hyperledger/besu/datatypes/Wei.java
ahamlat Apr 17, 2026
3cdf3af
Update evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.…
ahamlat Apr 17, 2026
310ffbf
Update evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.…
ahamlat Apr 17, 2026
e38f455
Update datatypes/src/main/java/org/hyperledger/besu/datatypes/Wei.java
ahamlat Apr 17, 2026
6a30d3f
Merge branch 'main' into wei-opcodes-on-evm-v2
ahamlat Apr 17, 2026
c406646
Apply refactoring changes
ahamlat Apr 17, 2026
0820192
remove basefee field
ahamlat Apr 20, 2026
bb8e572
Merge branch 'main' into wei-opcodes-on-evm-v2
ahamlat Apr 20, 2026
8ed8bfb
Fix merge issue
ahamlat Apr 20, 2026
5c79af7
Address more comments
ahamlat Apr 20, 2026
5acec25
Remove not used field
ahamlat Apr 20, 2026
1826010
spotless
ahamlat Apr 20, 2026
98b66d1
Undo Balance operation change and add more unit tests
ahamlat Apr 21, 2026
89ab0d2
Merge branch 'main' into wei-opcodes-on-evm-v2
ahamlat Apr 21, 2026
aec17bc
Update AddOperationV2 after merge with main
ahamlat Apr 21, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,8 @@
import org.hyperledger.besu.evm.operation.SelfBalanceOperation;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import org.apache.tuweni.bytes.Bytes;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
Expand All @@ -44,6 +41,7 @@
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;

@State(Scope.Thread)
@Warmup(iterations = 2, time = 3, timeUnit = TimeUnit.SECONDS)
Expand All @@ -52,65 +50,41 @@
@BenchmarkMode(Mode.AverageTime)
public class SelfBalanceOperationBenchmark {

private static final int NUMBER_ADDRESSES = 20000;
private static final Random RANDOM_GENERATOR = new Random();
private SelfBalanceOperation operation;
private MessageFrame[] frames;
private int executingFrameIndex = 0;

private MessageFrame createMessageFrame(
final Blockchain blockchain,
final WorldUpdater worldUpdater,
final ExecutionContextTestFixture executionContextTestFixture,
final Address address) {
final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader();
return new MessageFrameTestFixture()
.address(address)
.worldUpdater(worldUpdater)
.blockHeader(blockHeader)
.executionContextTestFixture(executionContextTestFixture)
.blockchain(blockchain)
.build();
}

private WorldUpdater createWorldUpdater(final Blockchain blockchain, final Address[] addresses)
throws IOException {
final WorldUpdater worldStateUpdater;
try (WorldStateArchive worldStateArchive = createBonsaiInMemoryWorldStateArchive(blockchain)) {
worldStateUpdater = worldStateArchive.getWorldState().updater();
}
for (Address address : addresses) {
worldStateUpdater.getOrCreate(address).setBalance(Wei.of(1));
}
worldStateUpdater.commit();
return worldStateUpdater;
}
private MessageFrame frame;

@Setup
public void prepare() throws Exception {
operation = new SelfBalanceOperation(mock(GasCalculator.class));
final Blockchain blockchain = mock(Blockchain.class);
final Address[] addresses = new Address[NUMBER_ADDRESSES];
for (int j = 0; j < addresses.length; j++) {
final byte[] address = new byte[Address.SIZE];
RANDOM_GENERATOR.nextBytes(address);
addresses[j] = Address.wrap(Bytes.wrap(address));
final Address address = Address.fromHexString("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");

final WorldUpdater worldUpdater;
try (WorldStateArchive archive = createBonsaiInMemoryWorldStateArchive(blockchain)) {
worldUpdater = archive.getWorldState().updater();
}
frames = new MessageFrame[NUMBER_ADDRESSES];
final WorldUpdater worldUpdater = createWorldUpdater(blockchain, addresses);
worldUpdater.getOrCreate(address).setBalance(Wei.of(1));
worldUpdater.commit();

final ExecutionContextTestFixture executionContextTestFixture =
ExecutionContextTestFixture.create();
for (int i = 0; i < NUMBER_ADDRESSES; i++) {
frames[i] =
createMessageFrame(blockchain, worldUpdater, executionContextTestFixture, addresses[0]);
}
final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader();
frame =
new MessageFrameTestFixture()
.address(address)
.worldUpdater(worldUpdater)
.blockHeader(blockHeader)
.executionContextTestFixture(executionContextTestFixture)
.blockchain(blockchain)
.build();

// Pre-warm: force trie path into memory
worldUpdater.get(address);
}

@Benchmark
public void executeOperation() {
final MessageFrame executingFrame = frames[executingFrameIndex++];
operation.execute(executingFrame, null);
executingFrame.popStackItem();
executingFrameIndex = executingFrameIndex % NUMBER_ADDRESSES;
public void executeOperation(final Blackhole blackhole) {
blackhole.consume(operation.execute(frame, null));
frame.popStackItem();
}
}
Comment thread
ahamlat marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright contributors to Besu.
*
* 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.ethereum.vm.operations.v2;

import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createBonsaiInMemoryWorldStateArchive;
import static org.mockito.Mockito.mock;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.v2.operation.SelfBalanceOperationV2;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

import java.util.concurrent.TimeUnit;

import org.apache.tuweni.bytes.Bytes32;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;

@State(Scope.Thread)
@Warmup(iterations = 2, time = 3, timeUnit = TimeUnit.SECONDS)
@OutputTimeUnit(value = TimeUnit.NANOSECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public class SelfBalanceOperationBenchmarkV2 {

private SelfBalanceOperationV2 operation;
private MessageFrame frame;

@Setup
public void setup() throws Exception {
operation = new SelfBalanceOperationV2(mock(GasCalculator.class));
final Blockchain blockchain = mock(Blockchain.class);
final Address address = Address.fromHexString("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");

final WorldUpdater worldUpdater;
try (WorldStateArchive archive = createBonsaiInMemoryWorldStateArchive(blockchain)) {
worldUpdater = archive.getWorldState().updater();
}
worldUpdater.getOrCreate(address).setBalance(Wei.of(1));
worldUpdater.commit();

frame =
MessageFrame.builder()
.enableEvmV2(true)
.worldUpdater(worldUpdater)
.originator(Address.ZERO)
.gasPrice(Wei.ONE)
.blobGasPrice(Wei.ONE)
.blockValues(mock(BlockValues.class))
.miningBeneficiary(Address.ZERO)
.blockHashLookup((__, ___) -> Hash.ZERO)
.type(MessageFrame.Type.MESSAGE_CALL)
.initialGas(Long.MAX_VALUE)
.address(address)
.contract(address)
.inputData(Bytes32.ZERO)
.sender(Address.ZERO)
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(Code.EMPTY_CODE)
.completer(__ -> {})
.build();

// Pre-warm: force trie path into memory
Comment thread
ahamlat marked this conversation as resolved.
worldUpdater.get(address);
}

@Benchmark
public void executeOperation(final Blackhole blackhole) {
blackhole.consume(operation.execute(frame, null));
frame.setTopV2(frame.stackTopV2() - 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ public void writeTo(final RLPOutput out) {
out.writeBytes(mixHashOrPrevRandao);
out.writeLong(nonce);
do {
if (baseFee == null) break;
out.writeUInt256Scalar(baseFee);
if (maybeBaseFee.isEmpty()) break;
out.writeUInt256Scalar(maybeBaseFee.get());

if (withdrawalsRoot == null) break;
out.writeBytes(withdrawalsRoot.getBytes());
Expand Down Expand Up @@ -488,7 +488,7 @@ public String toString() {
sb.append("gasUsed=").append(gasUsed).append(", ");
sb.append("timestamp=").append(timestamp).append(", ");
sb.append("extraData=").append(extraData).append(", ");
sb.append("baseFee=").append(baseFee).append(", ");
sb.append("baseFee=").append(maybeBaseFee.orElse(null)).append(", ");
sb.append("mixHashOrPrevRandao=").append(mixHashOrPrevRandao).append(", ");
sb.append("nonce=").append(nonce).append(", ");
if (withdrawalsRoot != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public class ProcessableBlockHeader
// The block creation timestamp (seconds since the unix epoch)
protected final long timestamp;
// base fee is included for post EIP-1559 blocks
protected final Wei baseFee;
// store optional base fee to compute it at build time
Comment thread
ahamlat marked this conversation as resolved.
protected final Optional<Wei> maybeBaseFee;
// prevRandao is included for post-merge blocks
protected final Bytes32 mixHashOrPrevRandao;
// parentBeaconBlockRoot is included for Cancun
Expand All @@ -66,7 +67,7 @@ protected ProcessableBlockHeader(
this.number = number;
this.gasLimit = gasLimit;
this.timestamp = timestamp;
this.baseFee = baseFee;
this.maybeBaseFee = Optional.ofNullable(baseFee);
this.mixHashOrPrevRandao = mixHashOrPrevRandao;
this.parentBeaconBlockRoot = parentBeaconBlockRoot;
this.slotNumber = slotNumber;
Expand Down Expand Up @@ -149,7 +150,7 @@ public long getTimestamp() {
*/
@Override
public Optional<Wei> getBaseFee() {
return Optional.ofNullable(baseFee);
return maybeBaseFee;
}

/**
Expand Down Expand Up @@ -216,7 +217,9 @@ public String toString() {
sb.append("difficulty=").append(difficulty).append(", ");
sb.append("gasLimit=").append(gasLimit).append(", ");
sb.append("timestamp=").append(timestamp).append(", ");
sb.append("baseFee=").append(baseFee).append(", ");
if (maybeBaseFee.isPresent()) {
sb.append("baseFee=").append(maybeBaseFee.get()).append(", ");
}
sb.append("mixHashOrPrevRandao=").append(mixHashOrPrevRandao).append(", ");
if (parentBeaconBlockRoot != null) {
sb.append("parentBeaconBlockRoot=").append(parentBeaconBlockRoot).append(", ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ public BlockHeader parentBlockHeader(final ProtocolSpec protocolSpec) {
if (protocolSpec.getWithdrawalsProcessor().isPresent()) {
builder.withdrawalsRoot(BodyValidation.withdrawalsRoot(withdrawals));
}
if ((baseFee == null || baseFee.isEmpty()) && protocolSpec.getFeeMarket().implementsBaseFee()) {
if ((maybeBaseFee.isEmpty() || maybeBaseFee.get().isEmpty())
&& protocolSpec.getFeeMarket().implementsBaseFee()) {
builder.baseFee(
((BaseFeeMarket) protocolSpec.getFeeMarket())
.computeBaseFee(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ public enum Type {
// significant)
private final long[] stackDataV2;
private int stackTopV2;
private final int stackMaxSizeV2;
private Bytes output = Bytes.EMPTY;
private Bytes returnData = Bytes.EMPTY;
private Code createdCode = null;
Expand Down Expand Up @@ -282,6 +283,7 @@ private MessageFrame(
this.stack = new OperandStack(txValues.maxStackSize());
this.stackDataV2 = enableEvmV2 ? new long[txValues.maxStackSize() * 4] : null;
this.stackTopV2 = 0;
this.stackMaxSizeV2 = txValues.maxStackSize();
this.pc = 0;
this.recipient = recipient;
this.contract = contract;
Expand Down Expand Up @@ -518,10 +520,20 @@ public void setTopV2(final int newTop) {
* @param n the number of items required
* @return true if the stack contains at least n items
*/
public boolean stackHasItems(final int n) {
public boolean stackHasItemsV2(final int n) {
return stackTopV2 >= n;
}

/**
* Returns true if the stack has space for {@code n} more items.
*
* @param n the number of additional items
* @return true if the stack can accommodate n more items
*/
public boolean stackHasSpaceV2(final int n) {
return stackTopV2 + n <= stackMaxSizeV2;
}

// ---------------------------------------------------------------------------
// endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,16 @@
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.OverflowException;
import org.hyperledger.besu.evm.internal.UnderflowException;
import org.hyperledger.besu.evm.operation.AbstractOperation;

/** The Abstract fixed cost operation. */
abstract class AbstractFixedCostOperationV2 extends AbstractOperation {

/** Shared underflow response for static operation methods. */
static final OperationResult UNDERFLOW_RESPONSE =
new OperationResult(0L, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
/** The Abstract fixed cost operation for V2 (long[] stack) operations. */
abstract class AbstractFixedCostOperationV2 extends AbstractOperationV2 {

/** The Success response. */
protected final OperationResult successResponse;

/** The Out of gas response. */
protected final OperationResult outOfGasResponse;

private final OperationResult underflowResponse;
private final OperationResult overflowResponse;

/** The Gas cost. */
protected final long gasCost;

Expand All @@ -62,24 +52,14 @@ protected AbstractFixedCostOperationV2(
gasCost = fixedCost;
successResponse = new OperationResult(gasCost, null);
outOfGasResponse = new OperationResult(gasCost, ExceptionalHaltReason.INSUFFICIENT_GAS);
underflowResponse =
new OperationResult(gasCost, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
overflowResponse = new OperationResult(gasCost, ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
Comment thread
ahamlat marked this conversation as resolved.
}

@Override
public final OperationResult execute(final MessageFrame frame, final EVM evm) {
try {
if (frame.getRemainingGas() < gasCost) {
return outOfGasResponse;
} else {
return executeFixedCostOperation(frame, evm);
}
} catch (final UnderflowException ufe) {
return underflowResponse;
} catch (final OverflowException ofe) {
return overflowResponse;
Comment thread
siladu marked this conversation as resolved.
if (frame.getRemainingGas() < gasCost) {
return outOfGasResponse;
}
return executeFixedCostOperation(frame, evm);
}

/**
Expand Down
Loading
Loading