Skip to content

Commit 77b1cc0

Browse files
fab-10cloudspores
authored andcommitted
Fix BlockchainQueries::gasPrice when chain head block body still not fully persisted (besu-eth#7482)
* Fix BlockchainQueries::gasPrice when chain head block body still not fully persisted Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Signed-off-by: Ade Lucas <ade.lucas@consensys.net>
1 parent e741dc3 commit 77b1cc0

File tree

7 files changed

+68
-30
lines changed

7 files changed

+68
-30
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
### Bug fixes
1414
- Fix tracing in precompiled contracts when halting for out of gas [#7318](https://github.com/hyperledger/besu/issues/7318)
1515
- Correctly release txpool save and restore lock in case of exceptions [#7473](https://github.com/hyperledger/besu/pull/7473)
16+
- Fix for `eth_gasPrice` could not retrieve block error [#7482](https://github.com/hyperledger/besu/pull/7482)
1617

1718
## 24.8.0
1819

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import java.util.stream.Collectors;
6161
import java.util.stream.IntStream;
6262
import java.util.stream.LongStream;
63+
import java.util.stream.Stream;
6364

6465
import org.apache.tuweni.bytes.Bytes;
6566
import org.apache.tuweni.units.bigints.UInt256;
@@ -976,27 +977,35 @@ public <U> Optional<U> getAndMapWorldState(
976977
}
977978

978979
public Wei gasPrice() {
979-
final long blockHeight = headBlockNumber();
980-
final var chainHeadHeader = blockchain.getChainHeadHeader();
980+
final Block chainHeadBlock = blockchain.getChainHeadBlock();
981+
final var chainHeadHeader = chainHeadBlock.getHeader();
982+
final long blockHeight = chainHeadHeader.getNumber();
983+
981984
final var nextBlockProtocolSpec =
982985
protocolSchedule.getForNextBlockHeader(chainHeadHeader, System.currentTimeMillis());
983986
final var nextBlockFeeMarket = nextBlockProtocolSpec.getFeeMarket();
987+
984988
final Wei[] gasCollection =
985-
LongStream.rangeClosed(
986-
Math.max(0, blockHeight - apiConfig.getGasPriceBlocks() + 1), blockHeight)
987-
.mapToObj(
988-
l ->
989-
blockchain
990-
.getBlockByNumber(l)
991-
.map(Block::getBody)
992-
.map(BlockBody::getTransactions)
993-
.orElseThrow(
994-
() -> new IllegalStateException("Could not retrieve block #" + l)))
989+
Stream.concat(
990+
LongStream.range(
991+
Math.max(0, blockHeight - apiConfig.getGasPriceBlocks() + 1), blockHeight)
992+
.mapToObj(
993+
l ->
994+
blockchain
995+
.getBlockByNumber(l)
996+
.orElseThrow(
997+
() ->
998+
new IllegalStateException(
999+
"Could not retrieve block #" + l))),
1000+
Stream.of(chainHeadBlock))
1001+
.map(Block::getBody)
1002+
.map(BlockBody::getTransactions)
9951003
.flatMap(Collection::stream)
9961004
.filter(t -> t.getGasPrice().isPresent())
9971005
.map(t -> t.getGasPrice().get())
9981006
.sorted()
9991007
.toArray(Wei[]::new);
1008+
10001009
return (gasCollection == null || gasCollection.length == 0)
10011010
? gasPriceLowerBound(chainHeadHeader, nextBlockFeeMarket)
10021011
: UInt256s.max(

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import static org.assertj.core.api.Assertions.assertThat;
1818
import static org.mockito.ArgumentMatchers.any;
1919
import static org.mockito.ArgumentMatchers.anyLong;
20+
import static org.mockito.Mockito.lenient;
2021
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.times;
2123
import static org.mockito.Mockito.verify;
2224
import static org.mockito.Mockito.verifyNoMoreInteractions;
2325
import static org.mockito.Mockito.when;
@@ -61,7 +63,6 @@
6163
import org.junit.jupiter.params.provider.Arguments;
6264
import org.junit.jupiter.params.provider.MethodSource;
6365
import org.mockito.Mock;
64-
import org.mockito.internal.verification.VerificationModeFactory;
6566
import org.mockito.junit.jupiter.MockitoExtension;
6667

6768
@ExtendWith(MockitoExtension.class)
@@ -106,8 +107,8 @@ public void shouldReturnMinValueWhenNoTransactionsExist() {
106107
final JsonRpcResponse actualResponse = method.response(request);
107108
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
108109

109-
verify(blockchain).getChainHeadBlockNumber();
110-
verify(blockchain, VerificationModeFactory.times(100)).getBlockByNumber(anyLong());
110+
verify(blockchain).getChainHeadBlock();
111+
verify(blockchain, times(99)).getBlockByNumber(anyLong());
111112
verifyNoMoreInteractions(blockchain);
112113
}
113114

@@ -127,8 +128,7 @@ public void shouldReturnBaseFeeAsMinValueOnGenesis() {
127128
final JsonRpcResponse actualResponse = method.response(request);
128129
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
129130

130-
verify(blockchain).getChainHeadBlockNumber();
131-
verify(blockchain, VerificationModeFactory.times(1)).getBlockByNumber(anyLong());
131+
verify(blockchain).getChainHeadBlock();
132132
verifyNoMoreInteractions(blockchain);
133133
}
134134

@@ -146,8 +146,8 @@ public void shouldReturnMedianWhenTransactionsExist() {
146146
final JsonRpcResponse actualResponse = method.response(request);
147147
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
148148

149-
verify(blockchain).getChainHeadBlockNumber();
150-
verify(blockchain, VerificationModeFactory.times(100)).getBlockByNumber(anyLong());
149+
verify(blockchain).getChainHeadBlock();
150+
verify(blockchain, times(99)).getBlockByNumber(anyLong());
151151
verifyNoMoreInteractions(blockchain);
152152
}
153153

@@ -165,8 +165,8 @@ public void shortChainQueriesAllBlocks() {
165165
final JsonRpcResponse actualResponse = method.response(request);
166166
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
167167

168-
verify(blockchain).getChainHeadBlockNumber();
169-
verify(blockchain, VerificationModeFactory.times(81)).getBlockByNumber(anyLong());
168+
verify(blockchain).getChainHeadBlock();
169+
verify(blockchain, times(80)).getBlockByNumber(anyLong());
170170
verifyNoMoreInteractions(blockchain);
171171
}
172172

@@ -252,8 +252,7 @@ public void ethGasPriceAtGenesis(
252252
final JsonRpcResponse actualResponse = method.response(request);
253253
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
254254

255-
verify(blockchain).getChainHeadBlockNumber();
256-
verify(blockchain, VerificationModeFactory.times(1)).getBlockByNumber(anyLong());
255+
verify(blockchain).getChainHeadBlock();
257256
verifyNoMoreInteractions(blockchain);
258257
}
259258

@@ -328,12 +327,14 @@ private void mockBlockchain(
328327
blocksByNumber.put(i, createFakeBlock(i, txsNum, baseFee));
329328
}
330329

331-
when(blockchain.getChainHeadBlockNumber()).thenReturn(chainHeadBlockNumber);
332-
when(blockchain.getBlockByNumber(anyLong()))
333-
.thenAnswer(
334-
invocation -> Optional.of(blocksByNumber.get(invocation.getArgument(0, Long.class))));
335-
336-
when(blockchain.getChainHeadHeader())
330+
when(blockchain.getChainHeadBlock()).thenReturn(blocksByNumber.get(chainHeadBlockNumber));
331+
if (chainHeadBlockNumber > 1) {
332+
when(blockchain.getBlockByNumber(anyLong()))
333+
.thenAnswer(
334+
invocation -> Optional.of(blocksByNumber.get(invocation.getArgument(0, Long.class))));
335+
}
336+
lenient()
337+
.when(blockchain.getChainHeadHeader())
337338
.thenReturn(blocksByNumber.get(chainHeadBlockNumber).getHeader());
338339
}
339340

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/Blockchain.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,15 @@ default boolean contains(final Hash blockHash) {
167167
*/
168168
Optional<BlockBody> getBlockBody(Hash blockHeaderHash);
169169

170+
/**
171+
* Safe version of {@code getBlockBody} (it should take any locks necessary to ensure any block
172+
* updates that might be taking place have been completed first)
173+
*
174+
* @param blockHeaderHash The hash of the block whose header we want to retrieve.
175+
* @return The block body corresponding to this block hash.
176+
*/
177+
Optional<BlockBody> getBlockBodySafe(Hash blockHeaderHash);
178+
170179
/**
171180
* Given a block's hash, returns the list of transaction receipts associated with this block's
172181
* transactions. Associated block is not necessarily on the canonical chain.

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,10 @@ public BlockHeader getChainHeadHeader() {
299299

300300
@Override
301301
public Block getChainHeadBlock() {
302-
return new Block(chainHeader, blockchainStorage.getBlockBody(chainHeader.getHash()).get());
302+
return new Block(
303+
chainHeader,
304+
getBlockBody(chainHeader.getHash())
305+
.orElseGet(() -> getBlockBodySafe(chainHeader.getHash()).get()));
303306
}
304307

305308
@Override
@@ -337,6 +340,11 @@ public Optional<BlockBody> getBlockBody(final Hash blockHeaderHash) {
337340
.orElseGet(() -> blockchainStorage.getBlockBody(blockHeaderHash));
338341
}
339342

343+
@Override
344+
public synchronized Optional<BlockBody> getBlockBodySafe(final Hash blockHeaderHash) {
345+
return getBlockBody(blockHeaderHash);
346+
}
347+
340348
@Override
341349
public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) {
342350
return transactionReceiptsCache

ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/t8n/T8nBlockchain.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ public Optional<BlockBody> getBlockBody(final Hash blockHeaderHash) {
145145
throw new UnsupportedOperationException();
146146
}
147147

148+
@Override
149+
public synchronized Optional<BlockBody> getBlockBodySafe(final Hash blockHeaderHash) {
150+
return getBlockBody(blockHeaderHash);
151+
}
152+
148153
@Override
149154
public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) {
150155
// Deterministic, but just not implemented.

ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestBlockchain.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ public Optional<BlockBody> getBlockBody(final Hash blockHeaderHash) {
142142
throw new UnsupportedOperationException();
143143
}
144144

145+
@Override
146+
public synchronized Optional<BlockBody> getBlockBodySafe(final Hash blockHeaderHash) {
147+
return getBlockBody(blockHeaderHash);
148+
}
149+
145150
@Override
146151
public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) {
147152
// Deterministic, but just not implemented.

0 commit comments

Comments
 (0)