Encapsulate London Block transaction gas price validation (#2660)

Signed-off-by: garyschulte <garyschulte@gmail.com>
pull/2664/head
garyschulte 3 years ago committed by GitHub
parent 92955d2894
commit cc335eab63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/feemarket/FeeMarketException.java
  2. 74
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BaseFeeBlockBodyValidator.java
  3. 43
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidator.java
  4. 1
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  5. 115
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/BaseFeeBlockBodyValidatorTest.java

@ -25,6 +25,11 @@ public class FeeMarketException extends Exception {
"Invalid block header: basefee should not be present before fee market fork block");
}
public static FeeMarketException MissingBaseFeeMarket() {
return new FeeMarketException(
"Incorrectly configured ProtocolSchedule: requires BaseFeeMarket");
}
private FeeMarketException(final String reason) {
super(reason);
}

@ -0,0 +1,74 @@
/*
* 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.mainnet;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.feemarket.TransactionPriceCalculator;
import java.util.List;
import java.util.Optional;
import com.google.common.annotations.VisibleForTesting;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class BaseFeeBlockBodyValidator extends MainnetBlockBodyValidator {
private static final Logger LOG = LogManager.getLogger();
public BaseFeeBlockBodyValidator(final ProtocolSchedule protocolSchedule) {
super(protocolSchedule);
}
@Override
public boolean validateBodyLight(
final ProtocolContext context,
final Block block,
final List<TransactionReceipt> receipts,
final HeaderValidationMode ommerValidationMode) {
return super.validateBodyLight(context, block, receipts, ommerValidationMode)
&& validateTransactionGasPrice(block);
}
@VisibleForTesting
boolean validateTransactionGasPrice(final Block block) {
final BlockBody body = block.getBody();
final List<Transaction> transactions = body.getTransactions();
final TransactionPriceCalculator transactionPriceCalculator =
protocolSchedule
.getByBlockNumber(block.getHeader().getNumber())
.getFeeMarket()
.getTransactionPriceCalculator();
for (final Transaction transaction : transactions) {
final Optional<Long> baseFee = block.getHeader().getBaseFee();
final Wei price = transactionPriceCalculator.price(transaction, baseFee);
if (price.compareTo(Wei.of(baseFee.orElseThrow())) < 0) {
LOG.warn(
"Invalid block: transaction gas price {} must be greater than base fee {}",
price.toString(),
baseFee.orElseThrow());
return false;
}
}
return true;
}
}

@ -20,15 +20,10 @@ import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.LogsBloomFilter;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.feemarket.TransactionPriceCalculator;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
@ -42,7 +37,7 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
private static final int MAX_OMMERS = 2;
private static final int MAX_GENERATION = 6;
private final ProtocolSchedule protocolSchedule;
protected final ProtocolSchedule protocolSchedule;
public MainnetBlockBodyValidator(final ProtocolSchedule protocolSchedule) {
this.protocolSchedule = protocolSchedule;
@ -104,10 +99,6 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
return false;
}
if (!validateTransactionGasPrice(block)) {
return false;
}
return true;
}
@ -262,36 +253,4 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
}
return false;
}
private boolean validateTransactionGasPrice(final Block block) {
return Optional.of(
protocolSchedule.getByBlockNumber(block.getHeader().getNumber()).getFeeMarket())
.filter(FeeMarket::implementsBaseFee)
.map(
baseFeeMarket -> {
final BlockBody body = block.getBody();
final List<Transaction> transactions = body.getTransactions();
final TransactionPriceCalculator transactionPriceCalculator =
protocolSchedule
.getByBlockNumber(block.getHeader().getNumber())
.getFeeMarket()
.getTransactionPriceCalculator();
for (final Transaction transaction : transactions) {
final Optional<Long> baseFee = block.getHeader().getBaseFee();
final Wei price = transactionPriceCalculator.price(transaction, baseFee);
if (price.compareTo(Wei.of(baseFee.orElseThrow())) < 0) {
LOG.warn(
"Invalid block: transaction gas price {} must be greater than base fee {}",
price.toString(),
baseFee.orElseThrow());
return false;
}
}
return true;
})
// not a baseFeeMarket, no need to validate transaction gasPrices:
.orElse(Boolean.TRUE);
}
}

@ -528,6 +528,7 @@ public abstract class MainnetProtocolSpecs {
MainnetBlockHeaderValidator.createBaseFeeMarketValidator(londonFeeMarket))
.ommerHeaderValidatorBuilder(
MainnetBlockHeaderValidator.createBaseFeeMarketOmmerValidator(londonFeeMarket))
.blockBodyValidatorBuilder(BaseFeeBlockBodyValidator::new)
.name(LONDON_FORK_NAME);
}

@ -0,0 +1,115 @@
/*
* 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.mainnet;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class BaseFeeBlockBodyValidatorTest {
private static final KeyPair keyPair = SignatureAlgorithmFactory.getInstance().generateKeyPair();
@Mock ProtocolSpec protocolSpec;
@Mock ProtocolSchedule protocolSchedule;
@Mock Block block;
@Mock BlockHeader blockHeader;
BaseFeeBlockBodyValidator blockBodyValidator;
@Before
public void setup() {
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0L));
when(protocolSchedule.getByBlockNumber(anyLong())).thenReturn(protocolSpec);
when(block.getHeader()).thenReturn(blockHeader);
blockBodyValidator = new BaseFeeBlockBodyValidator(protocolSchedule);
}
@Test
public void BlockBodyValidatorSucceed() {
when(blockHeader.getBaseFee()).thenReturn(Optional.of(10L));
when(block.getBody())
.thenReturn(
new BlockBody(
List.of(
// eip1559 transaction
new TransactionTestFixture()
.maxFeePerGas(Optional.of(Wei.of(10L)))
.maxPriorityFeePerGas(Optional.of(Wei.of(1L)))
.type(TransactionType.EIP1559)
.createTransaction(keyPair),
// frontier transaction
new TransactionTestFixture().gasPrice(Wei.of(10L)).createTransaction(keyPair)),
Collections.emptyList()));
assertThat(blockBodyValidator.validateTransactionGasPrice(block)).isTrue();
}
@Test
public void BlockBodyValidatorFail_GasPrice() {
when(blockHeader.getBaseFee()).thenReturn(Optional.of(10L));
when(block.getBody())
.thenReturn(
new BlockBody(
List.of(
// underpriced frontier transaction
new TransactionTestFixture().gasPrice(Wei.of(9L)).createTransaction(keyPair)),
Collections.emptyList()));
assertThat(blockBodyValidator.validateTransactionGasPrice(block)).isFalse();
}
@Test
public void BlockBodyValidatorFail_MaxFeePerGas() {
when(blockHeader.getBaseFee()).thenReturn(Optional.of(10L));
when(block.getBody())
.thenReturn(
new BlockBody(
List.of(
// underpriced eip1559 transaction
new TransactionTestFixture()
.maxFeePerGas(Optional.of(Wei.of(1L)))
.maxPriorityFeePerGas(Optional.of(Wei.of(10L)))
.type(TransactionType.EIP1559)
.createTransaction(keyPair)),
Collections.emptyList()));
assertThat(blockBodyValidator.validateTransactionGasPrice(block)).isFalse();
}
}
Loading…
Cancel
Save