Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP-3860: Limit and meter initcode #4726

Merged
merged 4 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -50,6 +50,7 @@
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ShandongGasCalculator;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.gascalculator.TangerineWhistleGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
Expand Down Expand Up @@ -79,6 +80,7 @@ public abstract class MainnetProtocolSpecs {
public static final int FRONTIER_CONTRACT_SIZE_LIMIT = Integer.MAX_VALUE;

public static final int SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT = 24576;
public static final int SHANDONG_CONTRACT_SIZE_LIMIT = 2 * SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT;

private static final Address RIPEMD160_PRECOMPILE =
Address.fromHexString("0x0000000000000000000000000000000000000003");
Expand Down Expand Up @@ -649,8 +651,7 @@ static ProtocolSpecBuilder shandongDefinition(
genesisConfigOptions.isZeroBaseFee()
? FeeMarket.zeroBaseFee(londonForkBlockNumber)
: FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas());
final int contractSizeLimit =
configContractSizeLimit.orElse(SPURIOUS_DRAGON_CONTRACT_SIZE_LIMIT);
final int contractSizeLimit = configContractSizeLimit.orElse(SHANDONG_CONTRACT_SIZE_LIMIT);

return parisDefinition(
chainId,
Expand All @@ -660,6 +661,7 @@ static ProtocolSpecBuilder shandongDefinition(
genesisConfigOptions,
quorumCompatibilityMode,
evmConfiguration)
.gasCalculator(ShandongGasCalculator::new)
.evmBuilder(
(gasCalculator, jdCacheConfig) ->
MainnetEVMs.shandong(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
package org.hyperledger.besu.ethereum.vm.operations;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs.SHANDONG_CONTRACT_SIZE_LIMIT;
import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -71,6 +73,7 @@ public class CreateOperationTest {
+ "F3" // RETURN
);
public static final String SENDER = "0xdeadc0de00000000000000000000000000000000";
private static final int SHANDONG_CREATE_GAS = 41240;

@Test
public void createFromMemoryMutationSafe() {
Expand Down Expand Up @@ -175,6 +178,63 @@ public void notEnoughValue() {
assertThat(messageFrame.getStackItem(0)).isEqualTo(UInt256.ZERO);
}

@Test
public void shandongMaxInitCodeSizeCreate() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SHANDONG_CONTRACT_SIZE_LIMIT);
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final MessageFrame messageFrame =
testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1, messageFrameStack);

when(account.getMutable()).thenReturn(mutableAccount);
when(account.getNonce()).thenReturn(55L);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(worldUpdater.getAccount(any())).thenReturn(account);
when(worldUpdater.get(any())).thenReturn(account);
when(worldUpdater.getSenderAccount(any())).thenReturn(account);
when(worldUpdater.getOrCreate(any())).thenReturn(newAccount);
when(newAccount.getMutable()).thenReturn(newMutableAccount);
when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY);
when(worldUpdater.updater()).thenReturn(worldUpdater);

final EVM evm = MainnetEVMs.shandong(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, evm);
final MessageFrame createFrame = messageFrameStack.peek();
final ContractCreationProcessor ccp =
new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of());
ccp.process(createFrame, OperationTracer.NO_TRACING);

final Log log = createFrame.getLogs().get(0);
final String calculatedTopic = log.getTopics().get(0).toUnprefixedHexString();
assertThat(calculatedTopic).isEqualTo(TOPIC);
assertThat(result.getGasCost()).isEqualTo(SHANDONG_CREATE_GAS);
}

@Test
public void shandongMaxInitCodeSizePlus1Create() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SHANDONG_CONTRACT_SIZE_LIMIT + 1);
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final MessageFrame messageFrame =
testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1, messageFrameStack);

when(account.getMutable()).thenReturn(mutableAccount);
when(account.getNonce()).thenReturn(55L);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(worldUpdater.getAccount(any())).thenReturn(account);
when(worldUpdater.get(any())).thenReturn(account);
when(worldUpdater.getSenderAccount(any())).thenReturn(account);
when(worldUpdater.getOrCreate(any())).thenReturn(newAccount);
when(newAccount.getMutable()).thenReturn(newMutableAccount);
when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY);
when(worldUpdater.updater()).thenReturn(worldUpdater);

final EVM evm = MainnetEVMs.shandong(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, evm);
assertThat(messageFrame.getStackItem(0)).isEqualTo(UInt256.ZERO);
assertThat(result.getGasCost()).isEqualTo(SHANDONG_CREATE_GAS);
}

@NotNull
private MessageFrame testMemoryFrame(
final UInt256 memoryOffset,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.hyperledger.besu.evm.gascalculator;
/*
* Copyright contributors to Hyperledger 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
*/
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;

import org.hyperledger.besu.evm.frame.MessageFrame;

import org.apache.tuweni.bytes.Bytes;

public class ShandongGasCalculator extends LondonGasCalculator {

private static final long INIT_CODE_COST = 2L;

@Override
public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreation) {
long intrinsicGasCost = super.transactionIntrinsicGasCost(payload, isContractCreation);
if (isContractCreation) {
return clampedAdd(intrinsicGasCost, calculateInitGasCost(payload.size()));
} else {
return intrinsicGasCost;
}
}

@Override
public long createOperationGasCost(final MessageFrame frame) {
final long initCodeLength = clampedToLong(frame.getStackItem(2));
return clampedAdd(super.createOperationGasCost(frame), calculateInitGasCost(initCodeLength));
}

private static long calculateInitGasCost(final long initCodeLength) {
final int dataLength = (int) Math.ceil(initCodeLength / 32.0);
return dataLength * INIT_CODE_COST;
}
}