Skip to content

Commit 0775def

Browse files
authored
Merge bal devnet 7 into glamsterdam devnet 6 (#10774)
Signed-off-by: Karim Taam <karim.t2am@gmail.com>
1 parent 6c95bf3 commit 0775def

46 files changed

Lines changed: 1901 additions & 735 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- BFT option `xemptyblockperiodseconds` has been taken out of experimental and been renamed `emptyblockperiodseconds`. The old config option is deprecated and will be removed in a future release.
1919

2020
### Bug fixes
21+
- `testing_buildBlockV1` now sets the mining coinbase to the requested `suggestedFeeRecipient` before building, so the block header coinbase matches the account credited transaction fees and the EIP-7928 block access list. Previously the header used the node's configured coinbase (`Address.ZERO` on a block builder), so a non-zero `suggestedFeeRecipient` produced a block that self-rejected on `engine_newPayload` re-execution with a block access list hash mismatch.
2122

2223
### Additions and Improvements
2324
- The option to set a different block period for empty BFT blocks (`emptyblockperiodseconds`) is no longer experimental. The experimental flag `xemptyblockperiodseconds` will be removed in a future release.

app/src/main/java/org/hyperledger/besu/cli/options/BalConfigurationOptions.java

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
import org.hyperledger.besu.ethereum.mainnet.BalConfiguration;
1818
import org.hyperledger.besu.ethereum.mainnet.ImmutableBalConfiguration;
1919

20-
import java.time.Duration;
21-
2220
import picocli.CommandLine;
2321

2422
/** Command-line options for configuring Block Access List behaviour. */
@@ -33,19 +31,6 @@ public BalConfigurationOptions() {}
3331
"Allows disabling BAL-based perfect parallelization even when BALs are present.")
3432
boolean balPerfectParallelizationEnabled = true;
3533

36-
@CommandLine.Option(
37-
names = {"--Xbal-lenient-on-state-root-mismatch"},
38-
hidden = true,
39-
description =
40-
"Log an error instead of throwing when the BAL-computed state root does not match the synchronously computed root.")
41-
boolean balLenientOnStateRootMismatch = true;
42-
43-
@CommandLine.Option(
44-
names = {"--Xbal-trust-state-root"},
45-
hidden = true,
46-
description = "Trust the BAL-computed state root without verification.")
47-
boolean balTrustStateRoot = true;
48-
4934
@CommandLine.Option(
5035
names = {"--Xbal-state-root-enabled"},
5136
hidden = true,
@@ -60,20 +45,6 @@ public BalConfigurationOptions() {}
6045
description = "Log the constructed and block's BAL when they differ.")
6146
boolean balLogBalsOnMismatch = false;
6247

63-
@CommandLine.Option(
64-
names = {"--Xbal-state-root-timeout"},
65-
hidden = true,
66-
paramLabel = "<INTEGER>",
67-
description = "Timeout in milliseconds when waiting for the BAL-computed state root.")
68-
private long balStateRootTimeoutMs = -1;
69-
70-
@CommandLine.Option(
71-
names = {"--Xbal-processing-timeout"},
72-
hidden = true,
73-
paramLabel = "<INTEGER>",
74-
description = "Timeout in milliseconds when waiting for BAL transaction processing results.")
75-
private long balProcessingTimeoutMs = -1;
76-
7748
@CommandLine.Option(
7849
names = {"--Xbal-prefetch-reading-enabled"},
7950
hidden = true,
@@ -91,7 +62,7 @@ public BalConfigurationOptions() {}
9162
names = {"--Xbal-prefetch-batch-size"},
9263
hidden = true,
9364
description = "Enable custom BAL prefetch batch size (default: ${DEFAULT-VALUE}).")
94-
int balPreFetchBatch = 100;
65+
int balPreFetchBatch = 8;
9566

9667
/**
9768
* Builds the immutable {@link BalConfiguration} corresponding to the parsed CLI options.
@@ -102,14 +73,10 @@ public BalConfiguration toDomainObject() {
10273
return ImmutableBalConfiguration.builder()
10374
.isPerfectParallelizationEnabled(balPerfectParallelizationEnabled)
10475
.shouldLogBalsOnMismatch(balLogBalsOnMismatch)
105-
.isBalLenientOnStateRootMismatch(balLenientOnStateRootMismatch)
106-
.isBalStateRootTrusted(balTrustStateRoot)
10776
.isBalStateRootEnabled(balStateRootEnabled)
10877
.isBalPreFetchReadingEnabled(balPreFetchReadingEnabled)
10978
.isBalPreFetchSortingEnabled(balPreFetchSortingEnabled)
11079
.balPreFetchBatchSize(balPreFetchBatch)
111-
.balStateRootTimeout(Duration.ofMillis(balStateRootTimeoutMs))
112-
.balProcessingTimeout(Duration.ofMillis(balProcessingTimeoutMs))
11380
.build();
11481
}
11582
}

app/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -664,16 +664,8 @@ public BesuController build() {
664664
preloadBlockHeaderCache(blockchain, scheduler);
665665
}
666666

667-
boolean balStateRootTrusted = balConfiguration.isBalStateRootTrusted();
668-
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader;
669-
if (balStateRootTrusted) {
670-
bonsaiCachedMerkleTrieLoader = new NoopBonsaiCachedMerkleTrieLoader();
671-
} else {
672-
bonsaiCachedMerkleTrieLoader =
673-
besuComponent
674-
.map(BesuComponent::getCachedMerkleTrieLoader)
675-
.orElseGet(() -> new BonsaiCachedMerkleTrieLoader(metricsSystem));
676-
}
667+
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader =
668+
new NoopBonsaiCachedMerkleTrieLoader();
677669

678670
final var worldStateHealerSupplier = new AtomicReference<WorldStateHealer>();
679671

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/testing/TestingBuildBlockV1.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
211211
try {
212212
final Address coinbase = payloadAttributes.getSuggestedFeeRecipient();
213213

214+
// The header coinbase is sourced from miningConfiguration (see
215+
// BlockHeaderBuilder.createPending), whereas transaction fees and the EIP-7928
216+
// block access list are credited to the mining beneficiary derived from the
217+
// suggestedFeeRecipient below. Without aligning the two, the built block's header
218+
// records the node's configured coinbase (Address.ZERO on a filler) while the BAL
219+
// credits suggestedFeeRecipient, so re-execution on engine_newPayload recomputes a
220+
// BAL under the header coinbase and the block self-rejects with a BAL hash mismatch.
221+
miningConfiguration.setCoinbase(coinbase);
222+
214223
final TestingBlockCreator blockCreator =
215224
new TestingBlockCreator(
216225
miningConfiguration,

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import static org.assertj.core.api.Assertions.assertThat;
1818
import static org.mockito.ArgumentMatchers.any;
19+
import static org.mockito.Mockito.verify;
1920
import static org.mockito.Mockito.when;
2021

2122
import org.hyperledger.besu.datatypes.Address;
@@ -149,6 +150,31 @@ void shouldReturnErrorWhenMissingSuggestedFeeRecipient() {
149150
assertThat(errorResponse.getError().getCode()).isEqualTo(RpcErrorType.INVALID_PARAMS.getCode());
150151
}
151152

153+
@Test
154+
void shouldPropagateSuggestedFeeRecipientToMiningCoinbase() {
155+
// The built block header coinbase is sourced from the mining configuration (see
156+
// BlockHeaderBuilder.createPending), whereas transaction fees and the EIP-7928 block
157+
// access list are credited to the mining beneficiary derived from suggestedFeeRecipient.
158+
// Building must propagate suggestedFeeRecipient into the mining configuration so the two
159+
// agree; otherwise the built header records the node's configured coinbase (Address.ZERO
160+
// on a block-builder node) while the block access list credits suggestedFeeRecipient, and
161+
// the block self-rejects on engine_newPayload re-execution with a BAL hash mismatch.
162+
final BlockHeader parentHeader =
163+
new BlockHeaderTestFixture().timestamp(DEFAULT_TIMESTAMP).buildHeader();
164+
when(blockchain.getBlockHeader(any(Hash.class))).thenReturn(Optional.of(parentHeader));
165+
final Address suggestedFeeRecipient =
166+
Address.fromHexString("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
167+
168+
// Block creation itself fails fast under the mocked protocol schedule, but the coinbase
169+
// must already have been propagated to the mining configuration before the build is
170+
// attempted.
171+
method.response(
172+
requestWithSuggestedFeeRecipient(
173+
parentHeader.getHash().toHexString(), DEFAULT_TIMESTAMP + 1, suggestedFeeRecipient));
174+
175+
verify(miningConfiguration).setCoinbase(suggestedFeeRecipient);
176+
}
177+
152178
@Test
153179
void verifyBlockAccessListEncodingFormat() {
154180
final BlockAccessList blockAccessList = createSampleBlockAccessList();
@@ -352,6 +378,22 @@ private JsonRpcRequestContext requestWithTransactions(
352378
new Object[] {parentHash, payloadAttributes, transactions, null}));
353379
}
354380

381+
private JsonRpcRequestContext requestWithSuggestedFeeRecipient(
382+
final String parentHash, final long timestamp, final Address suggestedFeeRecipient) {
383+
final Map<String, Object> payloadAttributes = new LinkedHashMap<>();
384+
payloadAttributes.put("timestamp", Bytes.ofUnsignedLong(timestamp).toQuantityHexString());
385+
payloadAttributes.put("prevRandao", Hash.ZERO.toHexString());
386+
payloadAttributes.put("suggestedFeeRecipient", suggestedFeeRecipient.toHexString());
387+
payloadAttributes.put("withdrawals", Collections.emptyList());
388+
payloadAttributes.put("parentBeaconBlockRoot", Bytes32.ZERO.toHexString());
389+
390+
return new JsonRpcRequestContext(
391+
new JsonRpcRequest(
392+
"2.0",
393+
"testing_buildBlockV1",
394+
new Object[] {parentHash, payloadAttributes, new String[0], null}));
395+
}
396+
355397
private JsonRpcRequestContext requestWithMissingSuggestedFeeRecipient(
356398
final String parentHash, final long timestamp) {
357399
final Map<String, Object> payloadAttributes = new LinkedHashMap<>();

ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/TestingBuildBlockIntegrationTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,6 @@ private TestContext createTestContext(final boolean withBAL) {
391391
new BadBlockManager(),
392392
false,
393393
ImmutableBalConfiguration.builder()
394-
.isBalStateRootTrusted(withBAL)
395394
.isPerfectParallelizationEnabled(withBAL)
396395
.build(),
397396
new NoOpMetricsSystem())

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -342,23 +342,6 @@ public BlockProcessingResult processBlock(
342342
applyPartialBlockAccessView(
343343
transactionProcessingResult.getPartialBlockAccessView(), blockAccessListBuilder);
344344

345-
if (blockAccessListBuilder.isPresent()) {
346-
final BlockAccessListItemSizeCheck itemSizeCheck =
347-
protocolSpec
348-
.getBlockAccessListValidator()
349-
.validateExecutedBlockAccessListItemSize(
350-
blockAccessListBuilder.get().eip7928ItemCount(), blockHeader, protocolSpec);
351-
if (itemSizeCheck.isOverBudget()) {
352-
final String errorMessage =
353-
itemSizeCheck.overBudgetError().orElseThrow().errorMessage();
354-
LOG.error(errorMessage);
355-
if (worldState instanceof BonsaiWorldState) {
356-
((BonsaiWorldStateUpdateAccumulator) blockUpdater).reset();
357-
}
358-
return new BlockProcessingResult(Optional.empty(), errorMessage);
359-
}
360-
}
361-
362345
if (transactionUpdater instanceof StackedUpdater<?, ?>) {
363346
transactionUpdater.commit();
364347
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BalConfiguration.java

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
*/
1515
package org.hyperledger.besu.ethereum.mainnet;
1616

17-
import java.time.Duration;
18-
1917
import org.immutables.value.Value;
2018

2119
/** Configuration options for Block Access List (BAL) processing. */
@@ -24,11 +22,9 @@ public interface BalConfiguration {
2422

2523
BalConfiguration DEFAULT = ImmutableBalConfiguration.builder().build();
2624

27-
/** Returns whether the BAL-computed state root should be trusted without verification. */
28-
@Value.Default
29-
default boolean isBalStateRootTrusted() {
30-
return true;
31-
}
25+
/** Configuration with BAL state root disabled (uses standard accumulator-based root). */
26+
BalConfiguration DISABLED =
27+
ImmutableBalConfiguration.builder().isBalStateRootEnabled(false).build();
3228

3329
/**
3430
* Returns whether to use the BAL-based state root commit path when a BAL is available. When
@@ -45,15 +41,6 @@ default boolean isPerfectParallelizationEnabled() {
4541
return true;
4642
}
4743

48-
/**
49-
* Returns whether mismatches between BAL and synchronously computed state roots should only log
50-
* an error instead of throwing an exception.
51-
*/
52-
@Value.Default
53-
default boolean isBalLenientOnStateRootMismatch() {
54-
return true;
55-
}
56-
5744
/** Returns whether prefetching of state data based on BAL read operations is enabled. */
5845
@Value.Default
5946
default boolean isBalPreFetchReadingEnabled() {
@@ -72,26 +59,12 @@ default boolean shouldLogBalsOnMismatch() {
7259
return false;
7360
}
7461

75-
/** Returns the timeout to use when waiting for the BAL-computed state root. */
76-
@Value.Default
77-
default Duration getBalStateRootTimeout() {
78-
return Duration.ofSeconds(-1);
79-
}
80-
81-
/** Returns the timeout to use when waiting for BAL transaction processing results. */
82-
@Value.Default
83-
default Duration getBalProcessingTimeout() {
84-
return Duration.ofSeconds(-1);
85-
}
86-
8762
/**
8863
* Returns the batch size for prefetch operations. A value of 0 or negative means no batching
8964
* (fetch all at once).
90-
*
91-
* @return the batch size for prefetch operations
9265
*/
9366
@Value.Default
9467
default int getBalPreFetchBatchSize() {
95-
return 100; // Default: no batching, fetch all at once
68+
return 8;
9669
}
9770
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/block/access/list/BlockAccessList.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ public boolean isEmpty() {
6868

6969
public long eip7928ItemCount() {
7070
long totalStorageKeys = 0;
71-
for (AccountChanges accountChange : accountChanges) {
71+
for (int i = 0, accountChangesSize = accountChanges.size(); i < accountChangesSize; i++) {
72+
AccountChanges accountChange = accountChanges.get(i);
7273
totalStorageKeys += accountChange.storageChanges().size();
7374
totalStorageKeys += accountChange.storageReads().size();
7475
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright contributors to Besu.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.ethereum.mainnet.block.access.list;
16+
17+
import org.hyperledger.besu.datatypes.Address;
18+
import org.hyperledger.besu.datatypes.Hash;
19+
import org.hyperledger.besu.datatypes.StorageSlotKey;
20+
21+
import java.util.HashMap;
22+
import java.util.List;
23+
import java.util.Map;
24+
import java.util.Optional;
25+
26+
/**
27+
* Address-indexed view of a {@link BlockAccessList}, built once per block. Does not resolve
28+
* transaction indices or materialize state — use {@link BlockAccessListOverlay} for per-transaction
29+
* prior state.
30+
*/
31+
public final class BlockAccessListAddressView {
32+
33+
private final Map<Address, AccountEntry> accountEntries;
34+
35+
private BlockAccessListAddressView(final Map<Address, AccountEntry> accountEntries) {
36+
this.accountEntries = accountEntries;
37+
}
38+
39+
public static BlockAccessListAddressView of(final BlockAccessList blockAccessList) {
40+
final List<BlockAccessList.AccountChanges> accountChanges = blockAccessList.accountChanges();
41+
final Map<Address, AccountEntry> entries = HashMap.newHashMap(accountChanges.size());
42+
for (final BlockAccessList.AccountChanges changes : accountChanges) {
43+
entries.put(changes.address(), new AccountEntry(changes));
44+
}
45+
return new BlockAccessListAddressView(entries);
46+
}
47+
48+
public Optional<BlockAccessList.AccountChanges> getAccountChanges(final Address address) {
49+
final AccountEntry entry = accountEntries.get(address);
50+
return entry == null ? Optional.empty() : Optional.of(entry.accountChanges);
51+
}
52+
53+
public Optional<Hash> getAddressHash(final Address address) {
54+
final AccountEntry entry = accountEntries.get(address);
55+
return entry == null ? Optional.empty() : Optional.of(entry.addressHash);
56+
}
57+
58+
Optional<BlockAccessList.SlotChanges> getSlotChanges(
59+
final Address address, final StorageSlotKey storageSlotKey) {
60+
final AccountEntry entry = accountEntries.get(address);
61+
return entry == null ? Optional.empty() : entry.slotChanges(storageSlotKey);
62+
}
63+
64+
private static final class AccountEntry {
65+
private final BlockAccessList.AccountChanges accountChanges;
66+
private final Hash addressHash;
67+
private final Map<StorageSlotKey, BlockAccessList.SlotChanges> storageBySlot;
68+
69+
private AccountEntry(final BlockAccessList.AccountChanges accountChanges) {
70+
this.accountChanges = accountChanges;
71+
this.addressHash = accountChanges.address().addressHash();
72+
this.storageBySlot = buildStorageBySlot(accountChanges.storageChanges());
73+
}
74+
75+
private static Map<StorageSlotKey, BlockAccessList.SlotChanges> buildStorageBySlot(
76+
final List<BlockAccessList.SlotChanges> storageChanges) {
77+
if (storageChanges.isEmpty()) {
78+
return Map.of();
79+
}
80+
final Map<StorageSlotKey, BlockAccessList.SlotChanges> built =
81+
HashMap.newHashMap(storageChanges.size());
82+
for (final BlockAccessList.SlotChanges slotChange : storageChanges) {
83+
built.put(slotChange.slot(), slotChange);
84+
}
85+
return built;
86+
}
87+
88+
Optional<BlockAccessList.SlotChanges> slotChanges(final StorageSlotKey storageSlotKey) {
89+
return Optional.ofNullable(storageBySlot.get(storageSlotKey));
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)