Skip to content

Commit

Permalink
EIP-1153: Transient storage opcodes (#4118)
Browse files Browse the repository at this point in the history
* Implement EIP-1153: Transient storage opcodes
* Added a new ByteCodeBuilder class to make it easier to construct the E2E test cases.
* currently targeting cancun

Signed-off-by: Cody Born <codyborn@outlook.com>
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
Signed-off-by: Jiri Peinlich <jiri.peinlich@gmail.com>
Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
Signed-off-by: Karim TAAM <karim.t2am@gmail.com>
Signed-off-by: Diego López León <dieguitoll@gmail.com>
Signed-off-by: Antony Denyer <git@antonydenyer.co.uk>
Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Signed-off-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net>
Co-authored-by: Jiri Peinlich <jiri.peinlich@gmail.com>
Co-authored-by: Daniel Lehrner <daniel.lehrner@consensys.net>
Co-authored-by: Justin Florentine <justin+github@florentine.us>
Co-authored-by: garyschulte <garyschulte@gmail.com>
Co-authored-by: Gabriel Trintinalia <gabriel.trintinalia@consensys.net>
Co-authored-by: Sally MacFarlane <sally.macfarlane@consensys.net>
Co-authored-by: matkt <karim.t2am@gmail.com>
Co-authored-by: Diego López León <dieguitoll@gmail.com>
Co-authored-by: Antony Denyer <git@antonydenyer.co.uk>
Co-authored-by: Miguel Rojo <miguelangel.rojofernandez@mastercard.com>
Co-authored-by: Miguel Angel Rojo <freemanz1486@gmail.com>
Co-authored-by: mark-terry <36909937+mark-terry@users.noreply.github.com>
Co-authored-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: Danno Ferrin <danno.ferrin@swirldslabs.com>
  • Loading branch information
16 people committed Feb 3, 2023
1 parent 98f68ea commit 53e11e3
Show file tree
Hide file tree
Showing 12 changed files with 1,239 additions and 4 deletions.
@@ -0,0 +1,91 @@
/*
* 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.ethereum.vm.operations;

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

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.MessageFrameTestFixture;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.operation.TLoadOperation;
import org.hyperledger.besu.evm.operation.TStoreOperation;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;

@State(Scope.Thread)
public class TransientStorageOperationBenchmark {

private OperationBenchmarkHelper operationBenchmarkHelper;
private TStoreOperation tstore;
private TLoadOperation tload;
private MessageFrame frame;

private MessageFrame createMessageFrame(final Address address) {
final Blockchain blockchain = mock(Blockchain.class);

final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
final WorldUpdater worldStateUpdater = worldStateArchive.getMutable().updater();
final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader();
final MessageFrame benchmarkFrame =
new MessageFrameTestFixture()
.address(address)
.worldUpdater(worldStateUpdater)
.blockHeader(blockHeader)
.blockchain(blockchain)
.build();
worldStateUpdater.getOrCreate(address).getMutable().setBalance(Wei.of(1));
worldStateUpdater.commit();

return benchmarkFrame;
}

@Setup
public void prepare() throws Exception {
operationBenchmarkHelper = OperationBenchmarkHelper.create();
CancunGasCalculator gasCalculator = new CancunGasCalculator();
tstore = new TStoreOperation(gasCalculator);
tload = new TLoadOperation(gasCalculator);
frame = createMessageFrame(Address.fromHexString("0x18675309"));
}

@TearDown
public void cleanUp() throws Exception {
operationBenchmarkHelper.cleanUp();
}

@Benchmark
public Bytes executeOperation() {
frame.pushStackItem(UInt256.ONE);
frame.pushStackItem(UInt256.fromHexString("0x01"));
tstore.execute(frame, null);
frame.pushStackItem(UInt256.fromHexString("0x01"));
tload.execute(frame, null);
return frame.popStackItem();
}
}
24 changes: 22 additions & 2 deletions evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
Expand Up @@ -16,6 +16,7 @@

import org.hyperledger.besu.evm.gascalculator.BerlinGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ByzantiumGasCalculator;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
Expand Down Expand Up @@ -111,6 +112,8 @@
import org.hyperledger.besu.evm.operation.StopOperation;
import org.hyperledger.besu.evm.operation.SubOperation;
import org.hyperledger.besu.evm.operation.SwapOperation;
import org.hyperledger.besu.evm.operation.TLoadOperation;
import org.hyperledger.besu.evm.operation.TStoreOperation;
import org.hyperledger.besu.evm.operation.TimestampOperation;
import org.hyperledger.besu.evm.operation.XorOperation;

Expand Down Expand Up @@ -724,6 +727,15 @@ public static void registerShanghaiOperations(
registry.put(new Create2Operation(gasCalculator, SHANGHAI_INIT_CODE_SIZE_LIMIT));
}

/**
* Cancun evm.
*
* @param evmConfiguration the evm configuration
* @return the evm
*/
public static EVM cancun(final EvmConfiguration evmConfiguration) {
return cancun(DEV_NET_CHAIN_ID, evmConfiguration);
}
/**
* Cancun evm.
*
Expand All @@ -732,7 +744,7 @@ public static void registerShanghaiOperations(
* @return the evm
*/
public static EVM cancun(final BigInteger chainId, final EvmConfiguration evmConfiguration) {
return cancun(new ShanghaiGasCalculator(), chainId, evmConfiguration);
return cancun(new CancunGasCalculator(), chainId, evmConfiguration);
}

/**
Expand Down Expand Up @@ -780,12 +792,20 @@ public static void registerCancunOperations(
final GasCalculator gasCalculator,
final BigInteger chainID) {
registerShanghaiOperations(registry, gasCalculator, chainID);

// EIP-4844
registry.put(new DataHashOperation(gasCalculator));

// TSTORE/TLOAD
registry.put(new TStoreOperation(gasCalculator));
registry.put(new TLoadOperation(gasCalculator));

// EOFV1
registry.put(new RelativeJumpOperation(gasCalculator));
registry.put(new RelativeJumpIfOperation(gasCalculator));
registry.put(new RelativeJumpVectorOperation(gasCalculator));
registry.put(new CallFOperation(gasCalculator));
registry.put(new RetFOperation(gasCalculator));
registry.put(new DataHashOperation(gasCalculator));
}

/**
Expand Down
49 changes: 49 additions & 0 deletions evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java
Expand Up @@ -42,8 +42,10 @@
import java.util.function.Consumer;
import java.util.function.Function;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.bytes.MutableBytes;
Expand Down Expand Up @@ -240,6 +242,8 @@ public enum Type {
private final Map<String, Object> contextVariables;
private final Optional<List<Hash>> versionedHashes;

private final Table<Address, Bytes32, Bytes32> transientStorage = HashBasedTable.create();

// Miscellaneous fields.
private Optional<ExceptionalHaltReason> exceptionalHaltReason = Optional.empty();
private Operation currentOperation;
Expand Down Expand Up @@ -1287,6 +1291,7 @@ public void setCurrentOperation(final Operation currentOperation) {
public Multimap<Address, Bytes32> getWarmedUpStorage() {
return warmedUpStorage;
}

/**
* Gets maybe updated memory.
*
Expand All @@ -1305,6 +1310,50 @@ public Optional<StorageEntry> getMaybeUpdatedStorage() {
return maybeUpdatedStorage;
}

/**
* Gets the transient storage value, including values from parent frames if not set
*
* @param accountAddress The address of the executing context
* @param slot the slot to retrieve
* @return the data value read
*/
public Bytes32 getTransientStorageValue(final Address accountAddress, final Bytes32 slot) {
Bytes32 data = transientStorage.get(accountAddress, slot);

if (data != null) {
return data;
}

if (parentMessageFrame != null) {
data = parentMessageFrame.getTransientStorageValue(accountAddress, slot);
}
if (data == null) {
data = Bytes32.ZERO;
}
transientStorage.put(accountAddress, slot, data);

return data;
}

/**
* Gets the transient storage value, including values from parent frames if not set
*
* @param accountAddress The address of the executing context
* @param slot the slot to set
* @param value the value to set in the transient store
*/
public void setTransientStorageValue(
final Address accountAddress, final Bytes32 slot, final Bytes32 value) {
transientStorage.put(accountAddress, slot, value);
}

/** Writes the transient storage to the parent frame, if one exists */
public void commitTransientStorage() {
if (parentMessageFrame != null) {
parentMessageFrame.transientStorage.putAll(transientStorage);
}
}

/**
* Accessor for versionedHashes, if present.
*
Expand Down
Expand Up @@ -38,7 +38,9 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
// new constants for EIP-2929
private static final long COLD_SLOAD_COST = 2100L;
private static final long COLD_ACCOUNT_ACCESS_COST = 2600L;
private static final long WARM_STORAGE_READ_COST = 100L;
/** Warm storage read, defined in EIP-2929 */
protected static final long WARM_STORAGE_READ_COST = 100L;

private static final long ACCESS_LIST_ADDRESS_COST = 2400L;
/** The constant ACCESS_LIST_STORAGE_COST. */
protected static final long ACCESS_LIST_STORAGE_COST = 1900L;
Expand Down
@@ -0,0 +1,39 @@
/*
* 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.evm.gascalculator;

/**
* Gas Calculator for Cancun
*
* <UL>
* <LI>Gas costs for TSTORE/TLOAD
* </UL>
*/
public class CancunGasCalculator extends LondonGasCalculator {

private static final long TLOAD_GAS = WARM_STORAGE_READ_COST;
private static final long TSTORE_GAS = WARM_STORAGE_READ_COST;

// EIP-1153
@Override
public long getTransientLoadOperationGasCost() {
return TLOAD_GAS;
}

@Override
public long getTransientStoreOperationGasCost() {
return TSTORE_GAS;
}
}
Expand Up @@ -487,4 +487,22 @@ default long getMaxRefundQuotient() {
*/
// what would be the gas for a PMT with hash of all non-zeros
long getMaximumTransactionCost(int size);

/**
* Returns the cost of a loading from Transient Storage
*
* @return the cost of a TLOAD from a storage slot
*/
default long getTransientLoadOperationGasCost() {
return 0L;
}

/**
* Returns the cost of a storing to Transient Storage
*
* @return the cost of a TSTORE to a storage slot
*/
default long getTransientStoreOperationGasCost() {
return 0L;
}
}
@@ -0,0 +1,57 @@
/*
* 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.evm.operation;

import org.hyperledger.besu.evm.EVM;
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.FixedStack.OverflowException;
import org.hyperledger.besu.evm.internal.FixedStack.UnderflowException;

import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;

/** Implements the TLOAD operation defined in EIP-1153 */
public class TLoadOperation extends AbstractOperation {

/**
* TLoad operation
*
* @param gasCalculator gas calculator for costing
*/
public TLoadOperation(final GasCalculator gasCalculator) {
super(0xb3, "TLOAD", 1, 1, gasCalculator);
}

@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
final long cost = gasCalculator().getTransientLoadOperationGasCost();
try {
final Bytes32 slot = UInt256.fromBytes(frame.popStackItem());
if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else {
frame.pushStackItem(frame.getTransientStorageValue(frame.getRecipientAddress(), slot));

return new OperationResult(cost, null);
}
} catch (final UnderflowException ufe) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
} catch (final OverflowException ofe) {
return new OperationResult(cost, ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
}
}

0 comments on commit 53e11e3

Please sign in to comment.