Skip to content
Open
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 @@ -179,6 +179,7 @@ public static SyncingNodeManager create(
recentChainData,
blockImporter,
blockEventsListenerRouter,
() -> DataAvailabilitySampler.NOOP,
pendingBlockPool,
futureBlocks,
invalidBlockRoots,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import static tech.pegasys.teku.spec.logic.common.statetransition.availability.DataAndValidationResult.notRequiredResultFuture;

import java.util.Optional;
import org.apache.logging.log4j.Logger;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
Expand All @@ -36,6 +37,12 @@ public boolean initiateDataAvailabilityCheck() {
public SafeFuture<DataAndValidationResult<Data>> getAvailabilityCheckResult() {
return notRequiredResultFuture();
}

@Override
public SafeFuture<DataAndValidationResult<Data>> getAndLogAvailabilityCheckResult(
final Logger log) {
return getAvailabilityCheckResult();
}
}

AvailabilityChecker<?> NOOP = new NOOP<>();
Expand All @@ -53,4 +60,14 @@ public SafeFuture<DataAndValidationResult<Data>> getAvailabilityCheckResult() {
boolean initiateDataAvailabilityCheck();

SafeFuture<DataAndValidationResult<Data>> getAvailabilityCheckResult();

default SafeFuture<DataAndValidationResult<Data>> getAndLogAvailabilityCheckResult(
final Logger log) {
return getAvailabilityCheckResult().thenPeek(result -> logAvailabilityCheckResult(log, result));
}

default void logAvailabilityCheckResult(
final Logger log, final DataAndValidationResult<Data> result) {
log.debug("Data availability check result: {}", result.toLogString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.epbs.versions.gloas.SignedExecutionPayloadEnvelope;

@FunctionalInterface
public interface AvailabilityCheckerFactory<T> {
Expand All @@ -25,4 +26,9 @@ public interface AvailabilityCheckerFactory<T> {
block -> AvailabilityChecker.NOOP_DATACOLUMN_SIDECAR;

AvailabilityChecker<T> createAvailabilityChecker(SignedBeaconBlock block);

default AvailabilityChecker<T> createAvailabilityChecker(
final SignedBeaconBlock block, final SignedExecutionPayloadEnvelope signedEnvelope) {
return createAvailabilityChecker(block);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -875,10 +875,14 @@ public AvailabilityChecker<?> createAvailabilityCheckerOnBlock(final SignedBeaco
}

public AvailabilityChecker<?> createAvailabilityCheckerOnExecutionPayloadEnvelope(
final SignedBeaconBlock block) {
final SignedBeaconBlock block, final SignedExecutionPayloadEnvelope signedEnvelope) {
return AvailabilityChecker.NOOP;
}

public boolean isDataAvailabilityCheckDeferredToExecutionPayloadEnvelope() {
return false;
}

// Used for computing committee indices when producing attestations.
public int computeCommitteeIndexForAttestation(
final UInt64 slot,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,19 @@ public AvailabilityChecker<?> createAvailabilityCheckerOnBlock(final SignedBeaco

@Override
public AvailabilityChecker<?> createAvailabilityCheckerOnExecutionPayloadEnvelope(
final SignedBeaconBlock block) {
final SignedBeaconBlock block, final SignedExecutionPayloadEnvelope signedEnvelope) {
final AvailabilityCheckerFactory<UInt64> factory =
this.dataColumnSidecarAvailabilityCheckerFactory;
if (factory == null) {
throw new IllegalStateException(
"DataColumnSidecarAvailabilityCheckerFactory not initialized");
}
return factory.createAvailabilityChecker(block);
return factory.createAvailabilityChecker(block, signedEnvelope);
}

@Override
Copy link
Copy Markdown
Contributor

@StefanBratanov StefanBratanov Jun 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we can get rid of createAvailabilityCheckerOnBlock and createAvailabilityCheckerOnExecutionPayloadEnvelope and have only createAvailabilityChecker and this boolean method and have a simple if in on_block, I think better naming is isDataAvailabilityDeferredUntilExecutionPayloadProcessing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we move all logic in ForkChoice. I don't think it's good.
image

public boolean isDataAvailabilityCheckDeferredToExecutionPayloadEnvelope() {
return true;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import tech.pegasys.teku.spec.datastructures.blocks.BlockCheckpoints;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState;
import tech.pegasys.teku.spec.datastructures.epbs.versions.gloas.SignedExecutionPayloadEnvelope;
import tech.pegasys.teku.spec.datastructures.forkchoice.ForkChoiceNode;
import tech.pegasys.teku.spec.datastructures.forkchoice.ForkChoiceReorgContext;
import tech.pegasys.teku.spec.datastructures.forkchoice.MutableStore;
Expand Down Expand Up @@ -72,6 +73,12 @@ class ForkChoiceUtilTest {

private final ForkChoiceUtil forkChoiceUtil = spec.getGenesisSpec().getForkChoiceUtil();

@Test
void isDataAvailabilityCheckDeferredToExecutionPayloadEnvelope_shouldDefaultToFalse() {
assertThat(forkChoiceUtil.isDataAvailabilityCheckDeferredToExecutionPayloadEnvelope())
.isFalse();
}

@Test
void getAncestors_shouldGetSimpleSequenceOfAncestors() {
chainBuilder.generateBlocksUpToSlot(10);
Expand Down Expand Up @@ -332,20 +339,23 @@ void createAvailabilityCheckerOnExecutionPayloadEnvelope_shouldCreateExpectedAva
mock(AvailabilityCheckerFactory.class);

final SignedBeaconBlock block = mock(SignedBeaconBlock.class);
final SignedExecutionPayloadEnvelope signedEnvelope =
mock(SignedExecutionPayloadEnvelope.class);

spec.reinitializeForTesting(
blobSidecarAvailabilityCheckerFactory,
dataColumnSidecarAvailabilityCheckerFactory,
KZG.DISABLED);

final AvailabilityChecker<?> availabilityChecker =
util.createAvailabilityCheckerOnExecutionPayloadEnvelope(block);
util.createAvailabilityCheckerOnExecutionPayloadEnvelope(block, signedEnvelope);

switch (milestone) {
case PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB, ELECTRA, FULU ->
assertThat(availabilityChecker).isSameAs(AvailabilityChecker.NOOP);
case GLOAS, HEZE ->
verify(dataColumnSidecarAvailabilityCheckerFactory).createAvailabilityChecker(block);
verify(dataColumnSidecarAvailabilityCheckerFactory)
.createAvailabilityChecker(block, signedEnvelope);
default -> throw new IllegalStateException("Unexpected milestone " + milestone);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ void setUp() {
justifiedState = dataStructureUtil.randomBeaconState(gloasSlot);
}

@Test
void isDataAvailabilityCheckDeferredToExecutionPayloadEnvelope_shouldReturnTrue() {
assertThat(forkChoiceUtil.isDataAvailabilityCheckDeferredToExecutionPayloadEnvelope()).isTrue();
}

@Test
void getPayloadAttributeWithdrawalsUsesEffectiveState() {
final UInt64 stateSlot = gloasSlot.plus(spec.getSlotsPerEpoch(gloasSlot));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,9 @@ public void enableBlockImportOnCompletion(final SignedBeaconBlock block) {}

void removeAllForBlock(SlotAndBlockRoot slotAndBlockRoot);

default void onBlockImported(final SignedBeaconBlock block) {
removeAllForBlock(block.getSlotAndBlockRoot());
}

void enableBlockImportOnCompletion(SignedBeaconBlock block);
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public void removeAllForBlock(final SlotAndBlockRoot slotAndBlockRoot) {
lookupBlockEventsListener(slotAndBlockRoot.getSlot()).removeAllForBlock(slotAndBlockRoot);
}

@Override
public void onBlockImported(final SignedBeaconBlock block) {
lookupBlockEventsListener(block.getSlot()).onBlockImported(block);
}

@Override
public void enableBlockImportOnCompletion(final SignedBeaconBlock block) {
lookupBlockEventsListener(block.getSlot()).enableBlockImportOnCompletion(block);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.RejectedExecutionException;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes32;
Expand All @@ -38,6 +39,7 @@
import tech.pegasys.teku.spec.logic.common.statetransition.results.BlockImportResult.FailureReason;
import tech.pegasys.teku.statetransition.blobs.BlockEventsListener;
import tech.pegasys.teku.statetransition.blobs.RemoteOrigin;
import tech.pegasys.teku.statetransition.execution.ExecutionPayloadEventsListener;
import tech.pegasys.teku.statetransition.execution.ReceivedExecutionPayloadEventsChannel;
import tech.pegasys.teku.statetransition.util.FutureItems;
import tech.pegasys.teku.statetransition.util.PendingBlockPool;
Expand All @@ -56,6 +58,7 @@ public class BlockManager extends Service
private final RecentChainData recentChainData;
private final BlockImporter blockImporter;
private final BlockEventsListener blockEventsListener;
private final Supplier<ExecutionPayloadEventsListener> executionPayloadEventsListenerSupplier;
private final PendingBlockPool pendingBlockPool;
private final BlockValidator blockValidator;
private final TimeProvider timeProvider;
Expand All @@ -79,6 +82,7 @@ public BlockManager(
final RecentChainData recentChainData,
final BlockImporter blockImporter,
final BlockEventsListener blockEventsListener,
final Supplier<ExecutionPayloadEventsListener> executionPayloadEventsListenerSupplier,
final PendingBlockPool pendingBlockPool,
final FutureItems<SignedBeaconBlock> futureBlocks,
final Map<Bytes32, BlockImportResult> invalidBlockRoots,
Expand All @@ -89,6 +93,7 @@ public BlockManager(
this.recentChainData = recentChainData;
this.blockImporter = blockImporter;
this.blockEventsListener = blockEventsListener;
this.executionPayloadEventsListenerSupplier = executionPayloadEventsListenerSupplier;
this.pendingBlockPool = pendingBlockPool;
this.futureBlocks = futureBlocks;
this.invalidBlockRoots = invalidBlockRoots;
Expand Down Expand Up @@ -206,7 +211,7 @@ public void onBlockValidated(final SignedBeaconBlock block) {

@Override
public void onBlockImported(final SignedBeaconBlock block, final boolean executionOptimistic) {
blockEventsListener.removeAllForBlock(block.getSlotAndBlockRoot());
blockEventsListener.onBlockImported(block);
pendingBlockPool.remove(block);
// Check if any pending blocks can now be imported
final List<SignedBeaconBlock> children =
Expand All @@ -222,6 +227,9 @@ public void onExecutionPayloadValidated(final SignedExecutionPayloadEnvelope exe
@Override
public void onExecutionPayloadImported(
final SignedExecutionPayloadEnvelope executionPayload, final boolean executionOptimistic) {
executionPayloadEventsListenerSupplier
.get()
.onExecutionPayloadImported(executionPayload.getSlotAndBlockRoot());
final ParentExecutionPayloadDependency parentExecutionPayloadDependency =
new ParentExecutionPayloadDependency(
executionPayload.getBeaconBlockRoot(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,19 @@ public void onSlot(final UInt64 slot) {
.values()
.removeIf(
tracker -> {
if (tracker.slot().isLessThan(firstNonFinalizedSlot)
|| recentChainData.containsBlock(tracker.blockRoot())) {
final boolean isCleanupDue;
if (tracker.slot().isLessThan(firstNonFinalizedSlot)) {
isCleanupDue = true;
} else {
final boolean blockImported = recentChainData.containsBlock(tracker.blockRoot());
final boolean isDataAvailabilityDeferred =
spec.atSlot(tracker.slot())
.getForkChoiceUtil()
.isDataAvailabilityCheckDeferredToExecutionPayloadEnvelope();
isCleanupDue = blockImported && !isDataAvailabilityDeferred;
}

if (isCleanupDue) {
// Outdated
if (!tracker.completionFuture().isDone()) {
// make sure the future releases any pending waiters
Expand Down Expand Up @@ -285,6 +296,16 @@ public void onNewBlock(final SignedBeaconBlock block, final Optional<RemoteOrigi
}
}

@Override
Comment thread
cursor[bot] marked this conversation as resolved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have instead of the two new methods added to BlockEventsListener:

public interface DataAvailabilitySampler extends BlockEventsListener, ReceivedBlockEventsChannel, ReceivedExecutionPayloadEventsChannel

To avoid repetition and delegation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this as well
So we keep onNewBlock, removeAllForBlock and enableBlockImportOnCompletion in BlockEventsListener and still use it in DasSamplerBasic and BlockBlobSidecarsTrackersPoolImpl
But we also subscribe DasSamplerBasic and BlockBlobSidecarsTrackersPoolImpl to events in ReceivedBlockEventsChannel so it's spread between 2 sources

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ive moved only payload event
looks good

public void onBlockImported(final SignedBeaconBlock block) {
if (spec.atSlot(block.getSlot())
.getForkChoiceUtil()
.isDataAvailabilityCheckDeferredToExecutionPayloadEnvelope()) {
return;
}
removeAllForBlock(block.getSlotAndBlockRoot());
}

@Override
public void removeAllForBlock(final SlotAndBlockRoot slotAndBlockRoot) {
final DataColumnSamplingTracker removed =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.epbs.versions.gloas.SignedExecutionPayloadEnvelope;
import tech.pegasys.teku.spec.logic.common.statetransition.availability.AvailabilityCheckerFactory;
import tech.pegasys.teku.statetransition.forkchoice.DataColumnSidecarAvailabilityChecker;
import tech.pegasys.teku.storage.client.RecentChainData;
Expand All @@ -42,4 +43,11 @@ public DataColumnSidecarAvailabilityChecker createAvailabilityChecker(
return new DataColumnSidecarAvailabilityChecker(
dataAvailabilitySamplerSupplier.get(), spec, recentChainData, block);
}

@Override
public DataColumnSidecarAvailabilityChecker createAvailabilityChecker(
final SignedBeaconBlock block, final SignedExecutionPayloadEnvelope signedEnvelope) {
return new DataColumnSidecarAvailabilityChecker(
dataAvailabilitySamplerSupplier.get(), spec, recentChainData, block, signedEnvelope);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
import tech.pegasys.teku.spec.datastructures.util.DataColumnSlotAndIdentifier;
import tech.pegasys.teku.statetransition.blobs.BlockEventsListener;
import tech.pegasys.teku.statetransition.blobs.RemoteOrigin;
import tech.pegasys.teku.statetransition.execution.ExecutionPayloadEventsListener;

public interface DataAvailabilitySampler extends BlockEventsListener {
public interface DataAvailabilitySampler
extends BlockEventsListener, ExecutionPayloadEventsListener {

enum SamplingEligibilityStatus {
NOT_REQUIRED_OLD_EPOCH,
Expand Down Expand Up @@ -61,6 +63,9 @@ public void onNewBlock(
@Override
public void removeAllForBlock(final SlotAndBlockRoot slotAndBlockRoot) {}

@Override
public void onExecutionPayloadImported(final SlotAndBlockRoot slotAndBlockRoot) {}

@Override
public void enableBlockImportOnCompletion(final SignedBeaconBlock block) {}
};
Expand All @@ -86,4 +91,9 @@ default void onNewValidatedDataColumnSidecar(
onNewValidatedDataColumnSidecar(
DataColumnSlotAndIdentifier.fromDataColumn(dataColumnSidecar), remoteOrigin);
}

@Override
default void onExecutionPayloadImported(final SlotAndBlockRoot slotAndBlockRoot) {
removeAllForBlock(slotAndBlockRoot);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public SafeFuture<InternalValidationResult> validateAndImportExecutionPayload(
final UInt64 earliestExecutionPayloadArrivalTimestamp =
recordExecutionPayloadArrivalTimestamp(
signedExecutionPayload, executionPayloadArrivalTimestamp);
final Bytes32 beaconBlockRoot = signedExecutionPayload.getBeaconBlockRoot();
final Bytes32 executionPayloadEnvelopeRoot = signedExecutionPayload.hashTreeRoot();
final SafeFuture<InternalValidationResult> validationResult =
executionPayloadGossipValidator.validate(signedExecutionPayload);
Expand All @@ -135,8 +136,7 @@ public SafeFuture<InternalValidationResult> validateAndImportExecutionPayload(
receivedExecutionPayloadEventsChannelPublisher.onExecutionPayloadValidated(
signedExecutionPayload);
acceptedExecutionPayloadEnvelopeRoots.add(executionPayloadEnvelopeRoot);
// cache the seen `beacon_block_root` when the gossip checks pass
recentSeenExecutionPayloads.add(signedExecutionPayload.getBeaconBlockRoot());
recentSeenExecutionPayloads.add(beaconBlockRoot);
recordExecutionPayloadAvailability(
signedExecutionPayload, earliestExecutionPayloadArrivalTimestamp);
importExecutionPayload(signedExecutionPayload).finishError(LOG);
Expand Down Expand Up @@ -190,6 +190,7 @@ public SafeFuture<ExecutionPayloadImportResult> importExecutionPayload(
receivedExecutionPayloadEventsChannelPublisher.onExecutionPayloadImported(
signedExecutionPayload, result.isImportedOptimistically());
} else {
recentSeenExecutionPayloads.remove(signedExecutionPayload.getBeaconBlockRoot());
switch (result.getFailureReason()) {
case FAILED_EXECUTION -> {
LOG.error(
Expand All @@ -211,6 +212,7 @@ public SafeFuture<ExecutionPayloadImportResult> importExecutionPayload(
})
.exceptionally(
ex -> {
recentSeenExecutionPayloads.remove(signedExecutionPayload.getBeaconBlockRoot());
final String internalErrorMessage =
String.format(
"Internal error while importing execution payload: %s. Execution payload content: %s",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.statetransition.execution;

import tech.pegasys.teku.spec.datastructures.blocks.SlotAndBlockRoot;

public interface ExecutionPayloadEventsListener {
ExecutionPayloadEventsListener NOOP = slotAndBlockRoot -> {};

void onExecutionPayloadImported(SlotAndBlockRoot slotAndBlockRoot);
}
Loading
Loading