Fix BlockchainQueries::gasPrice when chain head block body still not fully persisted (#7482)

* Fix BlockchainQueries::gasPrice when chain head block body still not fully persisted

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/7486/head
Fabio Di Fabio 3 months ago committed by GitHub
parent f9048cf3e2
commit 0dffe63c13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 33
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java
  3. 35
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java
  4. 9
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/Blockchain.java
  5. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java
  6. 5
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/t8n/T8nBlockchain.java
  7. 5
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestBlockchain.java

@ -13,6 +13,7 @@
### Bug fixes ### Bug fixes
- Fix tracing in precompiled contracts when halting for out of gas [#7318](https://github.com/hyperledger/besu/issues/7318) - Fix tracing in precompiled contracts when halting for out of gas [#7318](https://github.com/hyperledger/besu/issues/7318)
- Correctly release txpool save and restore lock in case of exceptions [#7473](https://github.com/hyperledger/besu/pull/7473) - Correctly release txpool save and restore lock in case of exceptions [#7473](https://github.com/hyperledger/besu/pull/7473)
- Fix for `eth_gasPrice` could not retrieve block error [#7482](https://github.com/hyperledger/besu/pull/7482)
## 24.8.0 ## 24.8.0

@ -60,6 +60,7 @@ import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.LongStream; import java.util.stream.LongStream;
import java.util.stream.Stream;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
@ -976,27 +977,35 @@ public class BlockchainQueries {
} }
public Wei gasPrice() { public Wei gasPrice() {
final long blockHeight = headBlockNumber(); final Block chainHeadBlock = blockchain.getChainHeadBlock();
final var chainHeadHeader = blockchain.getChainHeadHeader(); final var chainHeadHeader = chainHeadBlock.getHeader();
final long blockHeight = chainHeadHeader.getNumber();
final var nextBlockProtocolSpec = final var nextBlockProtocolSpec =
protocolSchedule.getForNextBlockHeader(chainHeadHeader, System.currentTimeMillis()); protocolSchedule.getForNextBlockHeader(chainHeadHeader, System.currentTimeMillis());
final var nextBlockFeeMarket = nextBlockProtocolSpec.getFeeMarket(); final var nextBlockFeeMarket = nextBlockProtocolSpec.getFeeMarket();
final Wei[] gasCollection = final Wei[] gasCollection =
LongStream.rangeClosed( Stream.concat(
Math.max(0, blockHeight - apiConfig.getGasPriceBlocks() + 1), blockHeight) LongStream.range(
.mapToObj( Math.max(0, blockHeight - apiConfig.getGasPriceBlocks() + 1), blockHeight)
l -> .mapToObj(
blockchain l ->
.getBlockByNumber(l) blockchain
.map(Block::getBody) .getBlockByNumber(l)
.map(BlockBody::getTransactions) .orElseThrow(
.orElseThrow( () ->
() -> new IllegalStateException("Could not retrieve block #" + l))) new IllegalStateException(
"Could not retrieve block #" + l))),
Stream.of(chainHeadBlock))
.map(Block::getBody)
.map(BlockBody::getTransactions)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.filter(t -> t.getGasPrice().isPresent()) .filter(t -> t.getGasPrice().isPresent())
.map(t -> t.getGasPrice().get()) .map(t -> t.getGasPrice().get())
.sorted() .sorted()
.toArray(Wei[]::new); .toArray(Wei[]::new);
return (gasCollection == null || gasCollection.length == 0) return (gasCollection == null || gasCollection.length == 0)
? gasPriceLowerBound(chainHeadHeader, nextBlockFeeMarket) ? gasPriceLowerBound(chainHeadHeader, nextBlockFeeMarket)
: UInt256s.max( : UInt256s.max(

@ -17,7 +17,9 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -61,7 +63,6 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.internal.verification.VerificationModeFactory;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@ -106,8 +107,8 @@ public class EthGasPriceTest {
final JsonRpcResponse actualResponse = method.response(request); final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(blockchain).getChainHeadBlockNumber(); verify(blockchain).getChainHeadBlock();
verify(blockchain, VerificationModeFactory.times(100)).getBlockByNumber(anyLong()); verify(blockchain, times(99)).getBlockByNumber(anyLong());
verifyNoMoreInteractions(blockchain); verifyNoMoreInteractions(blockchain);
} }
@ -127,8 +128,7 @@ public class EthGasPriceTest {
final JsonRpcResponse actualResponse = method.response(request); final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(blockchain).getChainHeadBlockNumber(); verify(blockchain).getChainHeadBlock();
verify(blockchain, VerificationModeFactory.times(1)).getBlockByNumber(anyLong());
verifyNoMoreInteractions(blockchain); verifyNoMoreInteractions(blockchain);
} }
@ -146,8 +146,8 @@ public class EthGasPriceTest {
final JsonRpcResponse actualResponse = method.response(request); final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(blockchain).getChainHeadBlockNumber(); verify(blockchain).getChainHeadBlock();
verify(blockchain, VerificationModeFactory.times(100)).getBlockByNumber(anyLong()); verify(blockchain, times(99)).getBlockByNumber(anyLong());
verifyNoMoreInteractions(blockchain); verifyNoMoreInteractions(blockchain);
} }
@ -165,8 +165,8 @@ public class EthGasPriceTest {
final JsonRpcResponse actualResponse = method.response(request); final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(blockchain).getChainHeadBlockNumber(); verify(blockchain).getChainHeadBlock();
verify(blockchain, VerificationModeFactory.times(81)).getBlockByNumber(anyLong()); verify(blockchain, times(80)).getBlockByNumber(anyLong());
verifyNoMoreInteractions(blockchain); verifyNoMoreInteractions(blockchain);
} }
@ -252,8 +252,7 @@ public class EthGasPriceTest {
final JsonRpcResponse actualResponse = method.response(request); final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
verify(blockchain).getChainHeadBlockNumber(); verify(blockchain).getChainHeadBlock();
verify(blockchain, VerificationModeFactory.times(1)).getBlockByNumber(anyLong());
verifyNoMoreInteractions(blockchain); verifyNoMoreInteractions(blockchain);
} }
@ -328,12 +327,14 @@ public class EthGasPriceTest {
blocksByNumber.put(i, createFakeBlock(i, txsNum, baseFee)); blocksByNumber.put(i, createFakeBlock(i, txsNum, baseFee));
} }
when(blockchain.getChainHeadBlockNumber()).thenReturn(chainHeadBlockNumber); when(blockchain.getChainHeadBlock()).thenReturn(blocksByNumber.get(chainHeadBlockNumber));
when(blockchain.getBlockByNumber(anyLong())) if (chainHeadBlockNumber > 1) {
.thenAnswer( when(blockchain.getBlockByNumber(anyLong()))
invocation -> Optional.of(blocksByNumber.get(invocation.getArgument(0, Long.class)))); .thenAnswer(
invocation -> Optional.of(blocksByNumber.get(invocation.getArgument(0, Long.class))));
when(blockchain.getChainHeadHeader()) }
lenient()
.when(blockchain.getChainHeadHeader())
.thenReturn(blocksByNumber.get(chainHeadBlockNumber).getHeader()); .thenReturn(blocksByNumber.get(chainHeadBlockNumber).getHeader());
} }

@ -167,6 +167,15 @@ public interface Blockchain {
*/ */
Optional<BlockBody> getBlockBody(Hash blockHeaderHash); Optional<BlockBody> getBlockBody(Hash blockHeaderHash);
/**
* Safe version of {@code getBlockBody} (it should take any locks necessary to ensure any block
* updates that might be taking place have been completed first)
*
* @param blockHeaderHash The hash of the block whose header we want to retrieve.
* @return The block body corresponding to this block hash.
*/
Optional<BlockBody> getBlockBodySafe(Hash blockHeaderHash);
/** /**
* Given a block's hash, returns the list of transaction receipts associated with this block's * Given a block's hash, returns the list of transaction receipts associated with this block's
* transactions. Associated block is not necessarily on the canonical chain. * transactions. Associated block is not necessarily on the canonical chain.

@ -299,7 +299,10 @@ public class DefaultBlockchain implements MutableBlockchain {
@Override @Override
public Block getChainHeadBlock() { public Block getChainHeadBlock() {
return new Block(chainHeader, blockchainStorage.getBlockBody(chainHeader.getHash()).get()); return new Block(
chainHeader,
getBlockBody(chainHeader.getHash())
.orElseGet(() -> getBlockBodySafe(chainHeader.getHash()).get()));
} }
@Override @Override
@ -337,6 +340,11 @@ public class DefaultBlockchain implements MutableBlockchain {
.orElseGet(() -> blockchainStorage.getBlockBody(blockHeaderHash)); .orElseGet(() -> blockchainStorage.getBlockBody(blockHeaderHash));
} }
@Override
public synchronized Optional<BlockBody> getBlockBodySafe(final Hash blockHeaderHash) {
return getBlockBody(blockHeaderHash);
}
@Override @Override
public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) { public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) {
return transactionReceiptsCache return transactionReceiptsCache

@ -145,6 +145,11 @@ public class T8nBlockchain implements Blockchain {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public synchronized Optional<BlockBody> getBlockBodySafe(final Hash blockHeaderHash) {
return getBlockBody(blockHeaderHash);
}
@Override @Override
public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) { public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) {
// Deterministic, but just not implemented. // Deterministic, but just not implemented.

@ -142,6 +142,11 @@ public class ReferenceTestBlockchain implements Blockchain {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public synchronized Optional<BlockBody> getBlockBodySafe(final Hash blockHeaderHash) {
return getBlockBody(blockHeaderHash);
}
@Override @Override
public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) { public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) {
// Deterministic, but just not implemented. // Deterministic, but just not implemented.

Loading…
Cancel
Save