Implement EIP-3198 BASEFEE opcode (#2123)

Signed-off-by: Abdelhamid Bakhta <abdelhamid.bakhta@consensys.net>
pull/2131/head
Abdelhamid Bakhta 4 years ago committed by GitHub
parent 11060f065f
commit 703ff8dce7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetEvmRegistries.java
  2. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  3. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java
  4. 48
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/BaseFeeOperation.java
  5. 90
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/BaseFeeOperationTest.java

@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.vm.operations.AddOperation;
import org.hyperledger.besu.ethereum.vm.operations.AddressOperation; import org.hyperledger.besu.ethereum.vm.operations.AddressOperation;
import org.hyperledger.besu.ethereum.vm.operations.AndOperation; import org.hyperledger.besu.ethereum.vm.operations.AndOperation;
import org.hyperledger.besu.ethereum.vm.operations.BalanceOperation; import org.hyperledger.besu.ethereum.vm.operations.BalanceOperation;
import org.hyperledger.besu.ethereum.vm.operations.BaseFeeOperation;
import org.hyperledger.besu.ethereum.vm.operations.BlockHashOperation; import org.hyperledger.besu.ethereum.vm.operations.BlockHashOperation;
import org.hyperledger.besu.ethereum.vm.operations.ByteOperation; import org.hyperledger.besu.ethereum.vm.operations.ByteOperation;
import org.hyperledger.besu.ethereum.vm.operations.CallCodeOperation; import org.hyperledger.besu.ethereum.vm.operations.CallCodeOperation;
@ -144,6 +145,14 @@ abstract class MainnetEvmRegistries {
return new EVM(registry, gasCalculator); return new EVM(registry, gasCalculator);
} }
static EVM london(final GasCalculator gasCalculator, final BigInteger chainId) {
final OperationRegistry registry = new OperationRegistry();
registerLondonOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION, chainId);
return new EVM(registry, gasCalculator);
}
private static void registerFrontierOpcodes( private static void registerFrontierOpcodes(
final OperationRegistry registry, final OperationRegistry registry,
final GasCalculator gasCalculator, final GasCalculator gasCalculator,
@ -278,4 +287,13 @@ abstract class MainnetEvmRegistries {
new SStoreOperation(gasCalculator, SStoreOperation.EIP_1706_MINIMUM), new SStoreOperation(gasCalculator, SStoreOperation.EIP_1706_MINIMUM),
Account.DEFAULT_VERSION); Account.DEFAULT_VERSION);
} }
private static void registerLondonOpcodes(
final OperationRegistry registry,
final GasCalculator gasCalculator,
final int accountVersion,
final BigInteger chainId) {
registerIstanbulOpcodes(registry, gasCalculator, accountVersion, chainId);
registry.put(new BaseFeeOperation(gasCalculator), Account.DEFAULT_VERSION);
}
} }

@ -456,8 +456,7 @@ public abstract class MainnetProtocolSpecs {
.name("Berlin"); .name("Berlin");
} }
// TODO EIP-1559 change for the actual fork name when known static ProtocolSpecBuilder londonDefinition(
static ProtocolSpecBuilder eip1559Definition(
final Optional<BigInteger> chainId, final Optional<BigInteger> chainId,
final Optional<TransactionPriceCalculator> transactionPriceCalculator, final Optional<TransactionPriceCalculator> transactionPriceCalculator,
final OptionalInt contractSizeLimit, final OptionalInt contractSizeLimit,
@ -501,7 +500,10 @@ public abstract class MainnetProtocolSpecs {
Account.DEFAULT_VERSION, Account.DEFAULT_VERSION,
transactionPriceCalculator.orElseThrow(), transactionPriceCalculator.orElseThrow(),
CoinbaseFeePriceCalculator.eip1559())) CoinbaseFeePriceCalculator.eip1559()))
.name("EIP-1559") .evmBuilder(
gasCalculator ->
MainnetEvmRegistries.london(gasCalculator, chainId.orElse(BigInteger.ZERO)))
.name("London")
.transactionPriceCalculator(transactionPriceCalculator.orElseThrow()) .transactionPriceCalculator(transactionPriceCalculator.orElseThrow())
.eip1559(Optional.of(eip1559)) .eip1559(Optional.of(eip1559))
.gasBudgetCalculator(TransactionGasBudgetCalculator.eip1559(eip1559)) .gasBudgetCalculator(TransactionGasBudgetCalculator.eip1559(eip1559))

@ -253,7 +253,7 @@ public class ProtocolScheduleBuilder {
eip1559Block, eip1559Block,
new BuilderMapEntry( new BuilderMapEntry(
eip1559Block, eip1559Block,
MainnetProtocolSpecs.eip1559Definition( MainnetProtocolSpecs.londonDefinition(
chainId, chainId,
transactionPriceCalculator, transactionPriceCalculator,
config.getContractSizeLimit(), config.getContractSizeLimit(),

@ -0,0 +1,48 @@
/*
* Copyright ConsenSys AG.
*
* 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 org.hyperledger.besu.ethereum.vm.EVM;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
public class BaseFeeOperation extends AbstractFixedCostOperation {
public BaseFeeOperation(final GasCalculator gasCalculator) {
super(0x48, "BASEFEE", 0, 1, false, 1, gasCalculator, gasCalculator.getBaseTierGasCost());
}
@Override
public OperationResult executeFixedCostOperation(final MessageFrame frame, final EVM evm) {
if (frame.getBlockHeader().getBaseFee().isEmpty()) {
return new OperationResult(
Optional.of(gasCost), Optional.of(ExceptionalHaltReason.INVALID_OPERATION));
}
frame.pushStackItem(
frame
.getBlockHeader()
.getBaseFee()
.map(Bytes::ofUnsignedLong)
.map(Bytes32::leftPad)
.orElseThrow());
return successResponse;
}
}

@ -0,0 +1,90 @@
/*
* Copyright ConsenSys AG.
*
* 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.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.ethereum.mainnet.BerlinGasCalculator;
import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.Operation;
import org.hyperledger.besu.ethereum.vm.Operation.OperationResult;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.Test;
public class BaseFeeOperationTest {
private final GasCalculator gasCalculator = new BerlinGasCalculator();
@Test
public void shouldReturnGasCost() {
final MessageFrame frame = createMessageFrame(100, Optional.of(5L));
final Operation operation = new BaseFeeOperation(gasCalculator);
final OperationResult result = operation.execute(frame, null);
assertThat(result.getGasCost()).contains(gasCalculator.getBaseTierGasCost());
assertSuccessResult(result);
}
@Test
public void shouldWriteBaseFeeToStack() {
final MessageFrame frame = createMessageFrame(100, Optional.of(5L));
final Operation operation = new BaseFeeOperation(gasCalculator);
final OperationResult result = operation.execute(frame, null);
verify(frame).pushStackItem(eq(Bytes32.leftPad(Bytes.ofUnsignedLong(5L))));
assertSuccessResult(result);
}
@Test
public void shouldHaltIfNoBaseFeeInBlockHeader() {
final MessageFrame frame = createMessageFrame(100, Optional.empty());
final Operation operation = new BaseFeeOperation(gasCalculator);
final OperationResult result = operation.execute(frame, null);
assertExceptionalHalt(result, ExceptionalHaltReason.INVALID_OPERATION);
}
private void assertSuccessResult(final OperationResult result) {
assertThat(result).isNotNull();
assertThat(result.getHaltReason()).isEmpty();
}
private void assertExceptionalHalt(
final OperationResult result, final ExceptionalHaltReason reason) {
assertThat(result).isNotNull();
assertThat(result.getHaltReason()).contains(reason);
}
private MessageFrame createMessageFrame(final long initialGas, final Optional<Long> baseFee) {
return createMessageFrame(Gas.of(initialGas), baseFee);
}
private MessageFrame createMessageFrame(final Gas initialGas, final Optional<Long> baseFee) {
final MessageFrame frame = mock(MessageFrame.class);
when(frame.getRemainingGas()).thenReturn(initialGas);
final BlockHeader blockHeader = mock(BlockHeader.class);
when(blockHeader.getBaseFee()).thenReturn(baseFee);
when(frame.getBlockHeader()).thenReturn(blockHeader);
return frame;
}
}
Loading…
Cancel
Save