Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
"MAXIMUM_GOSSIP_CLOCK_DISPARITY" : "500",
"TARGET_COMMITTEE_SIZE" : "128",
"TERMINAL_BLOCK_HASH" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"MIN_BUILDER_WITHDRAWABILITY_DELAY" : "64",
"MIN_BUILDER_WITHDRAWABILITY_DELAY" : "8192",
"CONSOLIDATION_CHURN_LIMIT_QUOTIENT" : "65536",
"DOMAIN_DEPOSIT" : "0x03000000",
"DOMAIN_CONTRIBUTION_AND_PROOF" : "0x09000000",
Expand Down Expand Up @@ -195,4 +195,4 @@
"PROPOSER_REORG_CUTOFF_BPS" : "1667",
"BLS_WITHDRAWAL_PREFIX" : "0x00",
"MIN_ACTIVATION_BALANCE" : "32000000000"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright Consensys Software Inc., 2026
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.spec.datastructures.state.beaconstate.common;

import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.infrastructure.collections.cache.Cache;
import tech.pegasys.teku.infrastructure.collections.cache.LRUCache;
import tech.pegasys.teku.infrastructure.collections.cache.NoOpCache;
import tech.pegasys.teku.infrastructure.ssz.SszList;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.gloas.BeaconStateGloas;
import tech.pegasys.teku.spec.datastructures.state.versions.gloas.Builder;

public class BuilderIndexCache {
private final Cache<BLSPublicKey, Integer> builderIndices;
private final AtomicInteger lastCachedIndex;

private static final int INDEX_NONE = -1;
public static final BuilderIndexCache NO_OP_INSTANCE =
new BuilderIndexCache(NoOpCache.getNoOpCache(), INDEX_NONE);

@VisibleForTesting
BuilderIndexCache(final Cache<BLSPublicKey, Integer> builderIndices, final int lastCachedIndex) {
this.builderIndices = builderIndices;
this.lastCachedIndex = new AtomicInteger(lastCachedIndex);
}

public BuilderIndexCache() {
this.builderIndices = LRUCache.create(Integer.MAX_VALUE - 1);
this.lastCachedIndex = new AtomicInteger(INDEX_NONE);
}

public Optional<Integer> getBuilderIndex(final BeaconState state, final BLSPublicKey publicKey) {
final SszList<Builder> builders = BeaconStateGloas.required(state).getBuilders();
final Optional<Integer> builderIndex = builderIndices.getCached(publicKey);
if (builderIndex.isPresent()) {
// The cache is shared across states, so a cached index may be stale for the state being
// queried
return builderIndex.filter(index -> index < builders.size());
}
return findIndexFromState(builders, publicKey);
}

public void invalidateWithNewValue(final BLSPublicKey pubKey, final int updatedIndex) {
builderIndices.invalidateWithNewValue(pubKey, updatedIndex);
}

public void invalidate(final BLSPublicKey pubKey) {
builderIndices.invalidate(pubKey);
}

public BuilderIndexCache copy() {
return new BuilderIndexCache(builderIndices.copy(), lastCachedIndex.get());
}

@VisibleForTesting
int getLastCachedIndex() {
return lastCachedIndex.get();
}

@VisibleForTesting
int getCacheSize() {
return builderIndices.size();
}

private void updateLastIndex(final int i) {
lastCachedIndex.updateAndGet(curr -> Math.max(curr, i));
}

@VisibleForTesting
Cache<BLSPublicKey, Integer> getBuilderIndices() {
return builderIndices;
}

private Optional<Integer> findIndexFromState(
final SszList<Builder> builders, final BLSPublicKey publicKey) {
final int initialCacheSize = getCacheSize();
for (int i = Math.max(lastCachedIndex.get() + 1, 0); i < builders.size(); i++) {
final BLSPublicKey pubKey = builders.get(i).getPublicKey();
builderIndices.invalidateWithNewValue(pubKey, i);
if (pubKey.equals(publicKey)) {
if (initialCacheSize < getCacheSize()) {
updateLastIndex(i);
}
return Optional.of(i);
}
}
if (initialCacheSize < getCacheSize()) {
updateLastIndex(getCacheSize() - 1);
}
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class TransitionCaches {
NoOpCache.getNoOpCache(),
ProgressiveTotalBalancesUpdates.NOOP,
NoOpCache.getNoOpCache(),
NoOpCache.getNoOpCache()) {
BuilderIndexCache.NO_OP_INSTANCE) {

@Override
public TransitionCaches copy() {
Expand Down Expand Up @@ -88,7 +88,7 @@ public static TransitionCaches getNoOp() {
private final Cache<UInt64, List<UInt64>> effectiveBalances;
private final Cache<UInt64, UInt64> baseRewardPerIncrement;
private final Cache<UInt64, BLSPublicKey> buildersPubKeys;
private final Cache<BLSPublicKey, Integer> builderIndexCache;
private final BuilderIndexCache builderIndexCache;

private final Cache<UInt64, Map<UInt64, SyncSubcommitteeAssignments>> syncCommitteeCache;

Expand All @@ -110,7 +110,7 @@ private TransitionCaches() {
baseRewardPerIncrement = LRUCache.create(MAX_BASE_REWARD_PER_INCREMENT_CACHE);
progressiveTotalBalances = ProgressiveTotalBalancesUpdates.NOOP;
buildersPubKeys = LRUCache.create(Integer.MAX_VALUE - 1);
builderIndexCache = LRUCache.create(Integer.MAX_VALUE - 1);
builderIndexCache = new BuilderIndexCache();
}

private TransitionCaches(
Expand All @@ -128,7 +128,7 @@ private TransitionCaches(
final Cache<UInt64, UInt64> baseRewardPerIncrement,
final ProgressiveTotalBalancesUpdates progressiveTotalBalances,
final Cache<UInt64, BLSPublicKey> buildersPubKeys,
final Cache<BLSPublicKey, Integer> builderIndexCache) {
final BuilderIndexCache builderIndexCache) {
this.activeValidators = activeValidators;
this.beaconProposerIndex = beaconProposerIndex;
this.beaconCommittee = beaconCommittee;
Expand Down Expand Up @@ -239,13 +239,8 @@ public Cache<UInt64, BLSPublicKey> getBuildersPubKeys() {
return buildersPubKeys;
}

/**
* (builder pub key) -> (builder index) cache
*
* <p>More complicated cache such as the {@link ValidatorIndexCache} is not required since the
* builders in the state are expected to be a tiny number initially
*/
public Cache<BLSPublicKey, Integer> getBuilderIndexCache() {
/** (builder pub key) -> (builder index) cache */
public BuilderIndexCache getBuilderIndexCache() {
return builderIndexCache;
}

Expand All @@ -268,6 +263,8 @@ public TransitionCaches copy() {
syncCommitteeCache.copy(),
baseRewardPerIncrement.copy(),
progressiveTotalBalances.copy(),
// Unlike validators, builder indices can be reassigned, so the builder caches must be
// copied to prevent invalidations in one branch from corrupting lookups in another.
buildersPubKeys.copy(),
builderIndexCache.copy());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ public ValidatorIndexCache() {

public Optional<Integer> getValidatorIndex(
final BeaconState state, final BLSPublicKey publicKey) {
final SszList<Validator> validators = state.getValidators();
final Optional<Integer> validatorIndex = validatorIndices.getCached(publicKey);
if (validatorIndex.isPresent()) {
return validatorIndex.filter(index -> index < state.getValidators().size());
// The cache is shared across states, so a cached index may be stale for the state being
// queried
return validatorIndex.filter(index -> index < validators.size());
}

return findIndexFromState(state.getValidators(), publicKey);
return findIndexFromState(validators, publicKey);
}

public void invalidateWithNewValue(final BLSPublicKey pubKey, final int updatedIndex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ public static SpecLogicGloas create(
attestationUtil,
miscHelpers,
withdrawalsHelpers,
executionRequestsProcessor);
blockProcessor);
final BlockProposalUtil blockProposalUtil =
new BlockProposalUtilFulu(schemaDefinitions, blockProcessor, config.getFuluForkEpoch());

Expand All @@ -248,7 +248,8 @@ public static SpecLogicGloas create(
beaconStateAccessors,
predicates,
beaconStateMutators,
miscHelpers);
miscHelpers,
validatorsUtil);

// Data column sidecar util
final DataColumnSidecarUtil dataColumnSidecarUtil = new DataColumnSidecarUtilGloas(miscHelpers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import tech.pegasys.teku.bls.BLSSignatureVerifier;
import tech.pegasys.teku.infrastructure.ssz.SszList;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
Expand Down Expand Up @@ -63,6 +65,8 @@

public class BlockProcessorGloas extends BlockProcessorFulu {

private static final Logger LOG = LogManager.getLogger();

private final PredicatesGloas predicatesGloas;
private final SchemaDefinitionsGloas schemaDefinitionsGloas;
private final MiscHelpersGloas miscHelpersGloas;
Expand Down Expand Up @@ -153,9 +157,62 @@ public void processParentExecutionPayload(
throw new BlockProcessingException(
"The execution requests root in the latest committed bid does not match the parent execution requests in the block");
}
applyParentExecutionPayload(stateGloas, requests, validatorExitContextSupplier);
}

// apply_parent_execution_payload
public void applyParentExecutionPayload(
final MutableBeaconStateGloas state,
final ExecutionRequests requests,
final Supplier<ValidatorExitContext> validatorExitContextSupplier) {
final ExecutionPayloadBid parentBid = state.getLatestExecutionPayloadBid();
final UInt64 parentSlot = parentBid.getSlot();
final UInt64 parentEpoch = miscHelpers.computeEpochAtSlot(parentSlot);

// Process execution requests from parent's payload. The execution requests are processed at
// state.slot (child's slot), not the parent's slot.
final long startTimeMillis = System.currentTimeMillis();
LOG.debug("Starting processing {} deposit requests", requests.getDeposits().size());
executionRequestsProcessorGloas.processDepositRequests(state, requests.getDeposits());
LOG.debug(
"Finished processing {} deposit requests. Pending deposits: {}, builders: {}. Took {} ms.",
requests.getDeposits().size(),
state.getPendingDeposits().size(),
state.getBuilders().size(),
System.currentTimeMillis() - startTimeMillis);
executionRequestsProcessorGloas.processWithdrawalRequests(
state, requests.getWithdrawals(), validatorExitContextSupplier);
executionRequestsProcessorGloas.processConsolidationRequests(
state, requests.getConsolidations());

// Settle the builder payment
if (parentEpoch.equals(beaconStateAccessorsGloas.getCurrentEpoch(state))) {
final UInt64 paymentIndex =
parentSlot.mod(specConfig.getSlotsPerEpoch()).plus(specConfig.getSlotsPerEpoch());
beaconStateMutatorsGloas.settleBuilderPayment(state, paymentIndex);
} else if (parentEpoch.equals(beaconStateAccessorsGloas.getPreviousEpoch(state))) {
final UInt64 paymentIndex = parentSlot.mod(specConfig.getSlotsPerEpoch());
beaconStateMutatorsGloas.settleBuilderPayment(state, paymentIndex);
} else if (parentBid.getValue().isGreaterThan(UInt64.ZERO)) {
// Parent is older than the previous epoch, its payment entry has been
// evicted from builder_pending_payments. Append the withdrawal directly.
state
.getBuilderPendingWithdrawals()
.append(
schemaDefinitionsGloas
.getBuilderPendingWithdrawalSchema()
.create(
parentBid.getFeeRecipient(),
parentBid.getValue(),
parentBid.getBuilderIndex()));
}

executionRequestsProcessorGloas.applyParentExecutionPayload(
stateGloas, requests, validatorExitContextSupplier);
// Update parent payload availability and latest block hash
state.setExecutionPayloadAvailability(
state
.getExecutionPayloadAvailability()
.withBit(parentSlot.mod(specConfig.getSlotsPerHistoricalRoot()).intValue()));
state.setLatestBlockHash(parentBid.getBlockHash());
}

// process_withdrawals with only state as a parameter
Expand Down
Loading
Loading