From 9cbbaba46a8430492a67cfa00f5f23fda69fe87c Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Fri, 17 May 2024 17:14:55 +0300 Subject: [PATCH] Tests Signed-off-by: Fabio Di Fabio --- .../ethereum/api/query/BlockchainQueries.java | 9 +- .../internal/methods/EthGasPriceTest.java | 131 +++++++++++++----- .../api/query/BlockchainQueriesTest.java | 13 +- 3 files changed, 102 insertions(+), 51 deletions(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java index 4d5cf7eb037..16b683ed712 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java @@ -982,7 +982,8 @@ public Wei gasPrice() { protocolSchedule.getForNextBlockHeader(chainHeadHeader, System.currentTimeMillis()); final var nextBlockFeeMarket = nextBlockProtocolSpec.getFeeMarket(); final Wei[] gasCollection = - LongStream.range(Math.max(0, blockHeight - apiConfig.getGasPriceBlocks()), blockHeight) + LongStream.rangeClosed( + Math.max(0, blockHeight - apiConfig.getGasPriceBlocks() + 1), blockHeight) .mapToObj( l -> blockchain @@ -1011,7 +1012,7 @@ public Wei gasPrice() { /** * Return the min gas required for a tx to be mineable. On networks with gas price fee market it * is just the minGasPrice, while on networks with base fee market it is the max between the - * minGasPrice and the baseFee for the next block + minPriorityFeePerGas. + * minGasPrice and the baseFee for the next block. * * @return the min gas required for a tx to be mineable. */ @@ -1029,9 +1030,7 @@ private Wei gasPriceLowerBound( if (nextBlockFeeMarket.implementsBaseFee()) { return UInt256s.max( - getNextBlockBaseFee(chainHeadHeader, (BaseFeeMarket) nextBlockFeeMarket) - .add(miningParameters.getMinPriorityFeePerGas()), - minGasPrice); + getNextBlockBaseFee(chainHeadHeader, (BaseFeeMarket) nextBlockFeeMarket), minGasPrice); } return minGasPrice; diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java index c7a8deb69b3..e0bde3961f8 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java @@ -41,6 +41,7 @@ import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.feemarket.CancunFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.LondonFeeMarket; import org.hyperledger.besu.evm.log.LogsBloomFilter; @@ -63,6 +64,8 @@ public class EthGasPriceTest { private static final String ETH_METHOD = "eth_gasPrice"; private static final long DEFAULT_BLOCK_GAS_LIMIT = 100_000; private static final long DEFAULT_BLOCK_GAS_USED = 21_000; + private static final Wei DEFAULT_MIN_GAS_PRICE = Wei.of(1_000); + private static final Wei DEFAULT_BASE_FEE = Wei.of(100_000); @Mock private ProtocolSchedule protocolSchedule; @Mock private Blockchain blockchain; @@ -72,7 +75,8 @@ public class EthGasPriceTest { @BeforeEach public void setUp() { ApiConfiguration apiConfig = createDefaultApiConfiguration(); - miningParameters = MiningParameters.newDefault(); + miningParameters = + MiningParameters.newDefault().setMinTransactionGasPrice(DEFAULT_MIN_GAS_PRICE); method = createEthGasPriceMethod(apiConfig); } @@ -84,7 +88,7 @@ public void returnsCorrectMethodName() { @Test public void shouldReturnMinValueWhenNoTransactionsExist() { final JsonRpcRequestContext request = requestWithParams(); - final String expectedWei = "0x4d2"; + final String expectedWei = "0x4d2"; // minGasPrice > nextBlockBaseFee miningParameters.setMinTransactionGasPrice(Wei.fromHexString(expectedWei)); final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); @@ -101,10 +105,31 @@ public void shouldReturnMinValueWhenNoTransactionsExist() { verifyNoMoreInteractions(blockchain); } + @Test + public void shouldReturnBaseFeeAsMinValueOnGenesis() { + final JsonRpcRequestContext request = requestWithParams(); + final String expectedWei = + DEFAULT_BASE_FEE.toShortHexString(); // nextBlockBaseFee > minGasPrice + miningParameters.setMinTransactionGasPrice(Wei.fromHexString(expectedWei)); + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); + + mockBaseFeeMarket(); + + mockBlockchain(0, 0); + + final JsonRpcResponse actualResponse = method.response(request); + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); + + verify(blockchain).getChainHeadBlockNumber(); + verify(blockchain, VerificationModeFactory.times(1)).getBlockByNumber(anyLong()); + verifyNoMoreInteractions(blockchain); + } + @Test public void shouldReturnMedianWhenTransactionsExist() { final JsonRpcRequestContext request = requestWithParams(); - final String expectedWei = "0x389fd980"; // 950Wei, gas prices are 900-999 wei. + final String expectedWei = "0x911c70"; // 9.51 mwei, gas prices are 9.01-10 mwei. final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); @@ -123,7 +148,7 @@ public void shouldReturnMedianWhenTransactionsExist() { @Test public void shortChainQueriesAllBlocks() { final JsonRpcRequestContext request = requestWithParams(); - final String expectedWei = "0x2625a00"; + final String expectedWei = "0x64190"; // 410 kwei final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); @@ -135,7 +160,7 @@ public void shortChainQueriesAllBlocks() { assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); verify(blockchain).getChainHeadBlockNumber(); - verify(blockchain, VerificationModeFactory.times(80)).getBlockByNumber(anyLong()); + verify(blockchain, VerificationModeFactory.times(81)).getBlockByNumber(anyLong()); verifyNoMoreInteractions(blockchain); } @@ -145,9 +170,9 @@ public void shortChainQueriesAllBlocks() { */ @Test public void shouldReturnLimitedPriceWhenLowerBoundIsPresent() { - long gasPrice = 5000000; - long lowerBoundGasPrice = gasPrice + 1; - verifyGasPriceLimit(lowerBoundGasPrice, null, lowerBoundGasPrice); + long expectedGasPrice = 31_841 * 2; + long lowerBoundCoefficient = 200; + verifyGasPriceLimit(lowerBoundCoefficient, null, expectedGasPrice); } /** @@ -156,9 +181,9 @@ public void shouldReturnLimitedPriceWhenLowerBoundIsPresent() { */ @Test public void shouldReturnLimitedPriceWhenUpperBoundIsPresent() { - long gasPrice = 5000000; - long upperBoundGasPrice = gasPrice - 1; - verifyGasPriceLimit(null, upperBoundGasPrice, upperBoundGasPrice); + long expectedGasPrice = (long) (31_841 * 1.5); + long upperBoundCoefficient = 150; + verifyGasPriceLimit(null, upperBoundCoefficient, expectedGasPrice); } /** @@ -167,27 +192,37 @@ public void shouldReturnLimitedPriceWhenUpperBoundIsPresent() { */ @Test public void shouldReturnActualGasPriceWhenWithinBoundRange() { - long gasPrice = 5000000; - long lowerBoundGasPrice = gasPrice - 1; - long upperBoundGasPrice = gasPrice + 1; - verifyGasPriceLimit(lowerBoundGasPrice, upperBoundGasPrice, gasPrice); + long gasPrice = 60_000; + long lowerBoundCoefficient = 120; + long upperBoundCoefficient = 200; + verifyGasPriceLimit(lowerBoundCoefficient, upperBoundCoefficient, gasPrice); } + // @Test + // public void shouldReturnConfiguredGasPriceLowerBoundAtGenesis() { + // mockBaseFeeMarket(); + // mockBlockchain(0, 0); + // + // } + /** * Helper method to verify the gas price limit. * - * @param lowerBound The lower bound of the gas price. - * @param upperBound The upper bound of the gas price. + * @param lowerBoundCoefficient The lower bound of the gas price. + * @param upperBoundCoefficient The upper bound of the gas price. * @param expectedGasPrice The expected gas price. */ private void verifyGasPriceLimit( - final Long lowerBound, final Long upperBound, final long expectedGasPrice) { + final Long lowerBoundCoefficient, + final Long upperBoundCoefficient, + final long expectedGasPrice) { miningParameters.setMinTransactionGasPrice(Wei.of(100)); mockBaseFeeMarket(); var apiConfig = - createApiConfiguration(Optional.ofNullable(lowerBound), Optional.ofNullable(upperBound)); + createApiConfiguration( + Optional.ofNullable(lowerBoundCoefficient), Optional.ofNullable(upperBoundCoefficient)); method = createEthGasPriceMethod(apiConfig); final JsonRpcRequestContext request = requestWithParams(); @@ -198,6 +233,9 @@ private void verifyGasPriceLimit( final var chainHeadBlockNumber = 10L; mockBlockchain(chainHeadBlockNumber, 1); + // baseFee for next block = 0x7c61 (31_841) + // BlockchainQueries returns gasPrice = 0x5b8d80 (60_000) + final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); } @@ -210,31 +248,50 @@ private void mockBaseFeeMarket() { } private void mockBlockchain(final long chainHeadBlockNumber, final int txsNum) { + mockBlockchain(DEFAULT_BASE_FEE, chainHeadBlockNumber, txsNum); + } + + private void mockBlockchain( + final Wei genesisBaseFee, final long chainHeadBlockNumber, final int txsNum) { final var blocksByNumber = new HashMap(); + final var genesisBlock = createFakeBlock(0, 0, genesisBaseFee); + blocksByNumber.put(0L, genesisBlock); + + final var baseFeeMarket = new CancunFeeMarket(0, Optional.empty()); + + var baseFee = genesisBaseFee; + for (long i = 1; i <= chainHeadBlockNumber; i++) { + final var parentHeader = blocksByNumber.get(i - 1).getHeader(); + baseFee = + baseFeeMarket.computeBaseFee( + i, + parentHeader.getBaseFee().get(), + parentHeader.getGasUsed(), + parentHeader.getGasLimit()); + blocksByNumber.put(i, createFakeBlock(i, txsNum, baseFee)); + } + when(blockchain.getChainHeadBlockNumber()).thenReturn(chainHeadBlockNumber); when(blockchain.getBlockByNumber(anyLong())) .thenAnswer( - invocation -> - Optional.of( - blocksByNumber.computeIfAbsent( - invocation.getArgument(0, Long.class), - blockNumber -> createFakeBlock(blockNumber, txsNum)))); + invocation -> Optional.of(blocksByNumber.get(invocation.getArgument(0, Long.class)))); when(blockchain.getChainHeadHeader()) - .thenReturn( - blocksByNumber - .computeIfAbsent( - chainHeadBlockNumber, blockNumber -> createFakeBlock(blockNumber, txsNum)) - .getHeader()); + .thenReturn(blocksByNumber.get(chainHeadBlockNumber).getHeader()); } - private Block createFakeBlock(final long height, final int txsNum) { - return createFakeBlock(height, txsNum, DEFAULT_BLOCK_GAS_LIMIT, DEFAULT_BLOCK_GAS_USED); + private Block createFakeBlock(final long height, final int txsNum, final Wei baseFee) { + return createFakeBlock( + height, txsNum, baseFee, DEFAULT_BLOCK_GAS_LIMIT, DEFAULT_BLOCK_GAS_USED * txsNum); } private Block createFakeBlock( - final long height, final int txsNum, final long gasLimit, final long gasUsed) { + final long height, + final int txsNum, + final Wei baseFee, + final long gasLimit, + final long gasUsed) { return new Block( new BlockHeader( Hash.EMPTY, @@ -250,7 +307,7 @@ private Block createFakeBlock( gasUsed, 0, Bytes.EMPTY, - Wei.of(100), + baseFee, Hash.EMPTY, 0, null, @@ -265,7 +322,7 @@ private Block createFakeBlock( i -> new Transaction.Builder() .nonce(i) - .gasPrice(Wei.of(height * 1000000L)) + .gasPrice(Wei.of(height * 10_000L)) .gasLimit(gasUsed) .value(Wei.ZERO) .build()) @@ -282,15 +339,15 @@ private ApiConfiguration createDefaultApiConfiguration() { } private ApiConfiguration createApiConfiguration( - final Optional lowerBound, final Optional upperBound) { + final Optional lowerBoundCoefficient, final Optional upperBoundCoefficient) { ImmutableApiConfiguration.Builder builder = ImmutableApiConfiguration.builder(); - lowerBound.ifPresent( + lowerBoundCoefficient.ifPresent( value -> builder .isGasAndPriorityFeeLimitingEnabled(true) .lowerBoundGasAndPriorityFeeCoefficient(value)); - upperBound.ifPresent( + upperBoundCoefficient.ifPresent( value -> builder .isGasAndPriorityFeeLimitingEnabled(true) diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesTest.java index 9c1392e8830..a49b290e4db 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesTest.java @@ -507,6 +507,9 @@ public void getLatestBlockOmmerByIndexShouldReturnExpectedOmmerHeader() { assertThat(retrievedOmmerBlockHeader).isEqualTo(ommerBlockHeader); } + @Test + public void getGasPriceLowerBound() {} + private void assertBlockMatchesResult( final Block targetBlock, final BlockWithMetadata result) { assertThat(result.getHeader()).isEqualTo(targetBlock.getHeader()); @@ -600,13 +603,5 @@ private BlockchainWithData( } } - private static class BlockData { - final Block block; - final List receipts; - - private BlockData(final Block block, final List receipts) { - this.block = block; - this.receipts = receipts; - } - } + private record BlockData(Block block, List receipts) {} }