Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

### Additions and Improvements
- Implement optional sender balance checks in the layered txpool [#9176](https://github.com/hyperledger/besu/pull/9176)
- Add `--cache-last-block-headers` flag to cache the last n block headers persisted to the blockchain [#9223](https://github.com/hyperledger/besu/pull/9223)
- Manage unexpected exceptions during block creation [#9208](https://github.com/hyperledger/besu/pull/9208)

### Bug fixes
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,12 @@ void setUserName(final String userName) {
description = "Specifies the number of last blocks to cache (default: ${DEFAULT-VALUE})")
private final Integer numberOfBlocksToCache = 0;

@CommandLine.Option(
names = {"--cache-last-block-headers"},
description =
"Specifies the number of last block headers to cache (default: ${DEFAULT-VALUE})")
private final Integer numberOfBlockHeadersToCache = 0;

@CommandLine.Option(
names = {"--cache-precompiles"},
description = "Specifies whether to cache precompile results (default: ${DEFAULT-VALUE})")
Expand Down Expand Up @@ -1821,6 +1827,7 @@ public BesuControllerBuilder setupControllerBuilder() {
.randomPeerPriority(p2PDiscoveryOptions.randomPeerPriority)
.chainPruningConfiguration(unstableChainPruningOptions.toDomainObject())
.cacheLastBlocks(numberOfBlocksToCache)
.cacheLastBlockHeaders(numberOfBlockHeadersToCache)
.genesisStateHashCacheEnabled(genesisStateHashCacheEnabled)
.apiConfiguration(apiConfigurationSupplier.get())
.besuComponent(besuComponent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
protected Optional<BesuComponent> besuComponent = Optional.empty();

private int numberOfBlocksToCache = 0;
private int numberOfBlockHeadersToCache = 0;

/** whether parallel transaction processing is enabled or not */
protected boolean isParallelTxProcessingEnabled;
Expand Down Expand Up @@ -502,6 +503,17 @@ public BesuControllerBuilder cacheLastBlocks(final Integer numberOfBlocksToCache
return this;
}

/**
* Sets the number of block headers to cache.
*
* @param numberOfBlockHeadersToCache the number of block headers to cache
* @return the besu controller builder
*/
public BesuControllerBuilder cacheLastBlockHeaders(final Integer numberOfBlockHeadersToCache) {
this.numberOfBlockHeadersToCache = numberOfBlockHeadersToCache;
return this;
}

/**
* sets the networkConfiguration in the builder
*
Expand Down Expand Up @@ -618,7 +630,8 @@ public BesuController build() {
metricsSystem,
reorgLoggingThreshold,
dataDirectory.toString(),
numberOfBlocksToCache);
numberOfBlocksToCache,
numberOfBlockHeadersToCache);
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader =
besuComponent
.map(BesuComponent::getCachedMerkleTrieLoader)
Expand Down
12 changes: 12 additions & 0 deletions app/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2674,6 +2674,18 @@ public void cacheLastBlocksOptionShouldWork() {
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}

@Test
public void cacheLastBlockHeadersOptionShouldWork() {
int numberOfBlockHeaderToCache = 5000;
parseCommand("--cache-last-block-headers", String.valueOf(numberOfBlockHeaderToCache));
verify(mockControllerBuilder).cacheLastBlockHeaders(intArgumentCaptor.capture());
verify(mockControllerBuilder).build();

assertThat(intArgumentCaptor.getValue()).isEqualTo(numberOfBlockHeaderToCache);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}

@Test
public void genesisStateHashCacheEnabledShouldWork() throws IOException {
final Path genesisFile = createFakeGenesisFile(GENESIS_VALID_JSON);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,10 @@ public void initMocks() throws Exception {
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.besuComponent(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.cacheLastBlocks(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.cacheLastBlockHeaders(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.genesisStateHashCacheEnabled(any()))
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.apiConfiguration(any())).thenReturn(mockControllerBuilder);

when(mockControllerBuilder.build()).thenReturn(mockController);
lenient().when(mockController.getProtocolManager()).thenReturn(mockEthProtocolManager);
lenient().when(mockController.getProtocolSchedule()).thenReturn(mockProtocolSchedule);
Expand Down
1 change: 1 addition & 0 deletions app/src/test/resources/everything_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ rpc-http-max-request-content-length = 5242880
rpc-max-logs-range=100
json-pretty-print-enabled=false
cache-last-blocks=512
cache-last-block-headers=5000
cache-precompiles=true
rpc-gas-cap = 50000000
rpc-max-trace-filter-range=100
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,11 @@ public class DefaultBlockchain implements MutableBlockchain {

private Comparator<BlockHeader> blockChoiceRule;

private final int numberOfBlocksToCache;
private final Optional<Cache<Hash, BlockHeader>> blockHeadersCache;
private final Optional<Cache<Hash, BlockBody>> blockBodiesCache;
private final Optional<Cache<Hash, List<TransactionReceipt>>> transactionReceiptsCache;
private final Optional<Cache<Hash, Difficulty>> totalDifficultyCache;
private final Optional<Cache<Hash, BlockAccessList>> blockAccessListCache;
private Optional<Cache<Hash, BlockHeader>> blockHeadersCache;
private Optional<Cache<Hash, BlockBody>> blockBodiesCache;
private Optional<Cache<Hash, List<TransactionReceipt>>> transactionReceiptsCache;
private Optional<Cache<Hash, Difficulty>> totalDifficultyCache;
private Optional<Cache<Hash, BlockAccessList>> blockAccessListCache;

private Counter gasUsedCounter = NoOpMetricsSystem.NO_OP_COUNTER;
private Counter numberOfTransactionsCounter = NoOpMetricsSystem.NO_OP_COUNTER;
Expand All @@ -97,7 +96,7 @@ private DefaultBlockchain(
final BlockchainStorage blockchainStorage,
final MetricsSystem metricsSystem,
final long reorgLoggingThreshold) {
this(genesisBlock, blockchainStorage, metricsSystem, reorgLoggingThreshold, null, 0);
this(genesisBlock, blockchainStorage, metricsSystem, reorgLoggingThreshold, null, 0, 0);
}

private DefaultBlockchain(
Expand All @@ -106,7 +105,8 @@ private DefaultBlockchain(
final MetricsSystem metricsSystem,
final long reorgLoggingThreshold,
final String dataDirectory,
final int numberOfBlocksToCache) {
final int numberOfBlocksToCache,
final int numberOfBlockHeadersToCache) {
checkNotNull(genesisBlock);
checkNotNull(blockchainStorage);
checkNotNull(metricsSystem);
Expand All @@ -127,40 +127,63 @@ private DefaultBlockchain(

this.reorgLoggingThreshold = reorgLoggingThreshold;
this.blockChoiceRule = heaviestChainBlockChoiceRule;
this.numberOfBlocksToCache = numberOfBlocksToCache;

if (numberOfBlocksToCache != 0) {
blockHeadersCache =
Optional.of(
CacheBuilder.newBuilder().recordStats().maximumSize(numberOfBlocksToCache).build());
initializeCaches(metricsSystem, numberOfBlockHeadersToCache, numberOfBlocksToCache);
createCounters(metricsSystem);
createGauges(metricsSystem);
}

private void initializeCaches(
final MetricsSystem metricsSystem, final int headersCacheSize, final int blocksCacheSize) {
if (headersCacheSize == 0 && blocksCacheSize == 0) {
setAllCachesEmpty();
return;
}

final int headersSize = Math.max(headersCacheSize, blocksCacheSize);
blockHeadersCache =
Optional.of(CacheBuilder.newBuilder().recordStats().maximumSize(headersSize).build());

if (blocksCacheSize != 0) {
blockBodiesCache =
Optional.of(
CacheBuilder.newBuilder().recordStats().maximumSize(numberOfBlocksToCache).build());
Optional.of(CacheBuilder.newBuilder().recordStats().maximumSize(blocksCacheSize).build());
transactionReceiptsCache =
Optional.of(
CacheBuilder.newBuilder().recordStats().maximumSize(numberOfBlocksToCache).build());
Optional.of(CacheBuilder.newBuilder().recordStats().maximumSize(blocksCacheSize).build());
totalDifficultyCache =
Optional.of(
CacheBuilder.newBuilder().recordStats().maximumSize(numberOfBlocksToCache).build());
Optional.of(CacheBuilder.newBuilder().recordStats().maximumSize(blocksCacheSize).build());
blockAccessListCache =
Optional.of(
CacheBuilder.newBuilder().recordStats().maximumSize(numberOfBlocksToCache).build());
metricsSystem.createGuavaCacheCollector(BLOCKCHAIN, "blockHeaders", blockHeadersCache.get());
metricsSystem.createGuavaCacheCollector(BLOCKCHAIN, "blockBodies", blockBodiesCache.get());
metricsSystem.createGuavaCacheCollector(
BLOCKCHAIN, "transactionReceipts", transactionReceiptsCache.get());
metricsSystem.createGuavaCacheCollector(
BLOCKCHAIN, "totalDifficulty", totalDifficultyCache.get());
Optional.of(CacheBuilder.newBuilder().recordStats().maximumSize(blocksCacheSize).build());
registerCacheMetrics(metricsSystem);
} else {
blockHeadersCache = Optional.empty();
blockBodiesCache = Optional.empty();
transactionReceiptsCache = Optional.empty();
totalDifficultyCache = Optional.empty();
blockAccessListCache = Optional.empty();
// Only headers cache is created, rest are empty
setBlockCachesEmpty();
registerHeadersCacheMetrics(metricsSystem);
}
}

createCounters(metricsSystem);
createGauges(metricsSystem);
private void setAllCachesEmpty() {
blockHeadersCache = Optional.empty();
setBlockCachesEmpty();
}

private void setBlockCachesEmpty() {
blockBodiesCache = Optional.empty();
transactionReceiptsCache = Optional.empty();
totalDifficultyCache = Optional.empty();
blockAccessListCache = Optional.empty();
}

private void registerCacheMetrics(final MetricsSystem metricsSystem) {
registerHeadersCacheMetrics(metricsSystem);
metricsSystem.createGuavaCacheCollector(BLOCKCHAIN, "blockBodies", blockBodiesCache.get());
metricsSystem.createGuavaCacheCollector(
BLOCKCHAIN, "transactionReceipts", transactionReceiptsCache.get());
metricsSystem.createGuavaCacheCollector(
BLOCKCHAIN, "totalDifficulty", totalDifficultyCache.get());
}

private void registerHeadersCacheMetrics(final MetricsSystem metricsSystem) {
metricsSystem.createGuavaCacheCollector(BLOCKCHAIN, "blockHeaders", blockHeadersCache.get());
}

private void createCounters(final MetricsSystem metricsSystem) {
Expand Down Expand Up @@ -237,6 +260,7 @@ public static MutableBlockchain createMutable(
metricsSystem,
reorgLoggingThreshold,
null,
0,
0);
}

Expand All @@ -253,6 +277,7 @@ public static MutableBlockchain createMutable(
metricsSystem,
reorgLoggingThreshold,
dataDirectory,
0,
0);
}

Expand All @@ -262,15 +287,17 @@ public static MutableBlockchain createMutable(
final MetricsSystem metricsSystem,
final long reorgLoggingThreshold,
final String dataDirectory,
final int numberOfBlocksToCache) {
final int numberOfBlocksToCache,
final int numberOgBlockHeadersToCache) {
checkNotNull(genesisBlock);
return new DefaultBlockchain(
Optional.of(genesisBlock),
blockchainStorage,
metricsSystem,
reorgLoggingThreshold,
dataDirectory,
numberOfBlocksToCache);
numberOfBlocksToCache,
numberOgBlockHeadersToCache);
}

public static Blockchain create(
Expand Down Expand Up @@ -451,7 +478,7 @@ public synchronized void appendBlock(
final Block block,
final List<TransactionReceipt> receipts,
final Optional<BlockAccessList> blockAccessList) {
if (numberOfBlocksToCache != 0) cacheBlockData(block, receipts, blockAccessList);
cacheBlockData(block, receipts, blockAccessList);
appendBlockHelper(new BlockWithReceipts(block, receipts), false, true);
}

Expand All @@ -460,7 +487,7 @@ public synchronized void appendBlockWithoutIndexingTransactions(
final Block block,
final List<TransactionReceipt> receipts,
final Optional<BlockAccessList> blockAccessList) {
if (numberOfBlocksToCache != 0) cacheBlockData(block, receipts, blockAccessList);
cacheBlockData(block, receipts, blockAccessList);
appendBlockHelper(new BlockWithReceipts(block, receipts), false, false);
}

Expand All @@ -481,7 +508,7 @@ public synchronized void storeBlock(
final Block block,
final List<TransactionReceipt> receipts,
final Optional<BlockAccessList> blockAccessList) {
if (numberOfBlocksToCache != 0) cacheBlockData(block, receipts, blockAccessList);
cacheBlockData(block, receipts, blockAccessList);
appendBlockHelper(new BlockWithReceipts(block, receipts), true, true);
}

Expand Down Expand Up @@ -592,6 +619,7 @@ public synchronized void unsafeImportBlock(
final Block block,
final List<TransactionReceipt> transactionReceipts,
final Optional<Difficulty> maybeTotalDifficulty) {
cacheBlockData(block, transactionReceipts, Optional.empty());
final BlockchainStorage.Updater updater = blockchainStorage.updater();
final Hash blockHash = block.getHash();
updater.putBlockHeader(blockHash, block.getHeader());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ public void testCacheUsedWhenNumberOfBlocksToCacheNotZero() {
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock, "/data/test", 512);
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock, "/data/test", 512, 0);

final BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions()
Expand Down Expand Up @@ -1015,6 +1015,36 @@ public void testCacheUsedWhenNumberOfBlocksToCacheNotZero() {
.isEqualTo(newBlock.getHeader().getDifficulty());
}

@Test
public void testCacheUsedWhenNumberOfBlockHeadersToCacheNotZero() {
final BlockDataGenerator gen = new BlockDataGenerator();
final KeyValueStorage kvStore = new InMemoryKeyValueStorage();
final KeyValueStorage kvStoreVariables = new InMemoryKeyValueStorage();
final Block genesisBlock = gen.genesisBlock();
final DefaultBlockchain blockchain =
createMutableBlockchain(kvStore, kvStoreVariables, genesisBlock, "/data/test", 0, 10000);

final BlockDataGenerator.BlockOptions options =
new BlockDataGenerator.BlockOptions()
.setBlockNumber(1L)
.setParentHash(genesisBlock.getHash());
final Block newBlock = gen.block(options);
final List<TransactionReceipt> receipts = gen.receipts(newBlock);

assertThat(blockchain.getBlockHeadersCache()).isNotEmpty();
assertThat(blockchain.getBlockBodiesCache()).isEmpty();
assertThat(blockchain.getTransactionReceiptsCache()).isEmpty();
assertThat(blockchain.getTotalDifficultyCache()).isEmpty();

assertThat(blockchain.getBlockHeadersCache().get().size()).isEqualTo(0);

blockchain.appendBlock(newBlock, receipts);

assertThat(blockchain.getBlockHeadersCache().get().size()).isEqualTo(1);
assertThat(blockchain.getBlockHeadersCache().get().getIfPresent(newBlock.getHash()))
.isEqualTo(newBlock.getHeader());
}

/*
* Check that block header, block body, block number, transaction locations, and receipts for this
* block are all stored.
Expand Down Expand Up @@ -1106,15 +1136,17 @@ private DefaultBlockchain createMutableBlockchain(
final KeyValueStorage kvStorageVariables,
final Block genesisBlock,
final String dataDirectory,
final int numberOfBlocksToCache) {
final int numberOfBlocksToCache,
final int numberOfBlockHeadersToCache) {
return (DefaultBlockchain)
DefaultBlockchain.createMutable(
genesisBlock,
createStorage(kvStore, kvStorageVariables),
new NoOpMetricsSystem(),
0,
dataDirectory,
numberOfBlocksToCache);
numberOfBlocksToCache,
numberOfBlockHeadersToCache);
}

@Test
Expand Down