diff --git a/beacon/validator/src/integrationTest/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerIntegrationTest.java b/beacon/validator/src/integrationTest/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerIntegrationTest.java index 7bdce7d7fd6..7ed4010551c 100644 --- a/beacon/validator/src/integrationTest/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerIntegrationTest.java +++ b/beacon/validator/src/integrationTest/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerIntegrationTest.java @@ -29,7 +29,7 @@ import tech.pegasys.teku.beacon.sync.events.SyncState; import tech.pegasys.teku.beacon.sync.events.SyncStateProvider; import tech.pegasys.teku.beacon.sync.events.SyncStateTracker; -import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformanceFactory; +import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionAndPublishingPerformanceFactory; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.metrics.StubMetricsSystem; import tech.pegasys.teku.infrastructure.metrics.Validator.ValidatorDutyMetricUtils; @@ -121,7 +121,8 @@ public class ValidatorApiHandlerIntegrationTest { syncCommitteeMessagePool, syncCommitteeContributionPool, syncCommitteeSubscriptionManager, - new BlockProductionPerformanceFactory(new SystemTimeProvider(), true, 0)); + new BlockProductionAndPublishingPerformanceFactory( + new SystemTimeProvider(), __ -> UInt64.ZERO, true, 0, 0)); @BeforeEach public void setup() { diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactory.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactory.java index d5d88c9174d..766ce6051b9 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactory.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactory.java @@ -18,6 +18,7 @@ import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; @@ -37,7 +38,9 @@ SafeFuture createUnsignedBlock( Optional requestedBuilderBoostFactor, BlockProductionPerformance blockProductionPerformance); - SafeFuture unblindSignedBlockIfBlinded(SignedBeaconBlock maybeBlindedBlock); + SafeFuture unblindSignedBlockIfBlinded( + SignedBeaconBlock maybeBlindedBlock, BlockPublishingPerformance blockPublishingPerformance); - List createBlobSidecars(SignedBlockContainer blockContainer); + List createBlobSidecars( + SignedBlockContainer blockContainer, BlockPublishingPerformance blockPublishingPerformance); } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDeneb.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDeneb.java index c2fef608644..ee35b270828 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDeneb.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDeneb.java @@ -18,6 +18,7 @@ import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; @@ -70,8 +71,12 @@ public SafeFuture createUnsignedBlock( } @Override - public List createBlobSidecars(final SignedBlockContainer blockContainer) { - return operationSelector.createBlobSidecarsSelector().apply(blockContainer); + public List createBlobSidecars( + final SignedBlockContainer blockContainer, + final BlockPublishingPerformance blockPublishingPerformance) { + return operationSelector + .createBlobSidecarsSelector(blockPublishingPerformance) + .apply(blockContainer); } private BlockContents createBlockContents( diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0.java index db8875e0af9..cc05e2885fa 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0.java @@ -22,6 +22,7 @@ import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; @@ -94,16 +95,20 @@ private BlockContainerAndMetaData beaconBlockAndStateToBlockContainerAndMetaData @Override public SafeFuture unblindSignedBlockIfBlinded( - final SignedBeaconBlock maybeBlindedBlock) { + final SignedBeaconBlock maybeBlindedBlock, + final BlockPublishingPerformance blockPublishingPerformance) { if (maybeBlindedBlock.isBlinded()) { return spec.unblindSignedBeaconBlock( - maybeBlindedBlock.getSignedBlock(), operationSelector.createBlockUnblinderSelector()); + maybeBlindedBlock.getSignedBlock(), + operationSelector.createBlockUnblinderSelector(blockPublishingPerformance)); } return SafeFuture.completedFuture(maybeBlindedBlock); } @Override - public List createBlobSidecars(final SignedBlockContainer blockContainer) { + public List createBlobSidecars( + final SignedBlockContainer blockContainer, + final BlockPublishingPerformance blockPublishingPerformance) { return Collections.emptyList(); } } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java index 610069b46c1..5e7e8bce826 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java @@ -25,6 +25,7 @@ import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.infrastructure.unsigned.UInt64; @@ -409,7 +410,8 @@ private SafeFuture builderSetKzgCommitments( .thenAccept(bodyBuilder::blobKzgCommitments); } - public Consumer createBlockUnblinderSelector() { + public Consumer createBlockUnblinderSelector( + final BlockPublishingPerformance blockPublishingPerformance) { return bodyUnblinder -> { final SignedBeaconBlock signedBlindedBlock = bodyUnblinder.getSignedBlindedBeaconBlock(); @@ -432,7 +434,7 @@ public Consumer createBlockUnblinderSelector() { bodyUnblinder.setExecutionPayloadSupplier( () -> executionLayerBlockProductionManager - .getUnblindedPayload(signedBlindedBlock) + .getUnblindedPayload(signedBlindedBlock, blockPublishingPerformance) .thenApply(BuilderPayload::getExecutionPayload)); } }; @@ -442,7 +444,8 @@ public Function> createBlobsBundleSelector( return block -> getCachedExecutionBlobsBundle(block.getSlot()); } - public Function> createBlobSidecarsSelector() { + public Function> createBlobSidecarsSelector( + final BlockPublishingPerformance blockPublishingPerformance) { return blockContainer -> { final UInt64 slot = blockContainer.getSlot(); final SignedBeaconBlock block = blockContainer.getSignedBlock(); @@ -479,12 +482,17 @@ public Function> createBlobSidecarsSelec proofs = blockContainer.getKzgProofs().orElseThrow(); } - return IntStream.range(0, blobs.size()) - .mapToObj( - index -> - miscHelpersDeneb.constructBlobSidecar( - block, UInt64.valueOf(index), blobs.get(index), proofs.get(index))) - .toList(); + final List blobSidecars = + IntStream.range(0, blobs.size()) + .mapToObj( + index -> + miscHelpersDeneb.constructBlobSidecar( + block, UInt64.valueOf(index), blobs.get(index), proofs.get(index))) + .toList(); + + blockPublishingPerformance.blobSidecarsPrepared(); + + return blobSidecars; }; } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/MilestoneBasedBlockFactory.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/MilestoneBasedBlockFactory.java index 7ba58d5bd5d..637e34bcf5c 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/MilestoneBasedBlockFactory.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/MilestoneBasedBlockFactory.java @@ -22,6 +22,7 @@ import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; @@ -84,15 +85,22 @@ public SafeFuture createUnsignedBlock( @Override public SafeFuture unblindSignedBlockIfBlinded( - final SignedBeaconBlock maybeBlindedBlock) { + final SignedBeaconBlock maybeBlindedBlock, + final BlockPublishingPerformance blockPublishingPerformance) { final SpecMilestone milestone = getMilestone(maybeBlindedBlock.getSlot()); - return registeredFactories.get(milestone).unblindSignedBlockIfBlinded(maybeBlindedBlock); + return registeredFactories + .get(milestone) + .unblindSignedBlockIfBlinded(maybeBlindedBlock, blockPublishingPerformance); } @Override - public List createBlobSidecars(final SignedBlockContainer blockContainer) { + public List createBlobSidecars( + final SignedBlockContainer blockContainer, + BlockPublishingPerformance blockPublishingPerformance) { final SpecMilestone milestone = getMilestone(blockContainer.getSlot()); - return registeredFactories.get(milestone).createBlobSidecars(blockContainer); + return registeredFactories + .get(milestone) + .createBlobSidecars(blockContainer, blockPublishingPerformance); } private SpecMilestone getMilestone(final UInt64 slot) { diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandler.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandler.java index 2f8b20bc6fe..f8d3f183d86 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandler.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandler.java @@ -20,7 +20,6 @@ import static tech.pegasys.teku.infrastructure.metrics.Validator.DutyType.ATTESTATION_PRODUCTION; import static tech.pegasys.teku.infrastructure.metrics.Validator.ValidatorDutyMetricUtils.startTimer; import static tech.pegasys.teku.infrastructure.metrics.Validator.ValidatorDutyMetricsSteps.CREATE; -import static tech.pegasys.teku.infrastructure.time.TimeUtilities.secondsToMillis; import static tech.pegasys.teku.spec.config.SpecConfig.GENESIS_SLOT; import com.google.common.annotations.VisibleForTesting; @@ -54,8 +53,9 @@ import tech.pegasys.teku.ethereum.json.types.validator.SyncCommitteeDuties; import tech.pegasys.teku.ethereum.json.types.validator.SyncCommitteeDuty; import tech.pegasys.teku.ethereum.json.types.validator.SyncCommitteeSelectionProof; +import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionAndPublishingPerformanceFactory; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; -import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformanceFactory; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.infrastructure.unsigned.UInt64; @@ -116,7 +116,8 @@ public class ValidatorApiHandler implements ValidatorApiChannel { */ private static final int DUTY_EPOCH_TOLERANCE = 1; - private final BlockProductionPerformanceFactory blockProductionPerformanceFactory; + private final BlockProductionAndPublishingPerformanceFactory + blockProductionAndPublishingPerformanceFactory; private final ChainDataProvider chainDataProvider; private final NodeDataProvider nodeDataProvider; private final CombinedChainDataClient combinedChainDataClient; @@ -160,8 +161,10 @@ public ValidatorApiHandler( final SyncCommitteeMessagePool syncCommitteeMessagePool, final SyncCommitteeContributionPool syncCommitteeContributionPool, final SyncCommitteeSubscriptionManager syncCommitteeSubscriptionManager, - final BlockProductionPerformanceFactory blockProductionPerformanceFactory) { - this.blockProductionPerformanceFactory = blockProductionPerformanceFactory; + final BlockProductionAndPublishingPerformanceFactory + blockProductionAndPublishingPerformanceFactory) { + this.blockProductionAndPublishingPerformanceFactory = + blockProductionAndPublishingPerformanceFactory; this.chainDataProvider = chainDataProvider; this.nodeDataProvider = nodeDataProvider; this.combinedChainDataClient = combinedChainDataClient; @@ -323,21 +326,14 @@ public SafeFuture> createUnsignedBlock( return NodeSyncingException.failedFuture(); } final BlockProductionPerformance blockProductionPerformance = - blockProductionPerformanceFactory.create(slot); + blockProductionAndPublishingPerformanceFactory.createForProduction(slot); return forkChoiceTrigger .prepareForBlockProduction(slot, blockProductionPerformance) .thenCompose( __ -> combinedChainDataClient.getStateForBlockProduction( slot, forkChoiceTrigger.isForkChoiceOverrideLateBlockEnabled())) - .thenPeek( - maybeState -> { - maybeState.ifPresent( - state -> - blockProductionPerformance.slotTime( - () -> secondsToMillis(spec.computeTimeAtSlot(state, slot)))); - blockProductionPerformance.getStateAtSlot(); - }) + .thenPeek(__ -> blockProductionPerformance.getStateAtSlot()) .thenCompose( blockSlotState -> createBlock( @@ -630,13 +626,18 @@ private SafeFuture processAggregateAndProof( public SafeFuture sendSignedBlock( final SignedBlockContainer maybeBlindedBlockContainer, final BroadcastValidationLevel broadcastValidationLevel) { + final BlockPublishingPerformance blockPublishingPerformance = + blockProductionAndPublishingPerformanceFactory.createForPublishing( + maybeBlindedBlockContainer.getSlot()); return blockPublisher - .sendSignedBlock(maybeBlindedBlockContainer, broadcastValidationLevel) + .sendSignedBlock( + maybeBlindedBlockContainer, broadcastValidationLevel, blockPublishingPerformance) .exceptionally( ex -> { final String reason = getRootCauseMessage(ex); return SendSignedBlockResult.rejected(reason); - }); + }) + .alwaysRun(blockPublishingPerformance::complete); } @Override diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisher.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisher.java index f0be29bc0b5..ee70f300bb4 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisher.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisher.java @@ -19,6 +19,7 @@ import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; @@ -55,18 +56,19 @@ public AbstractBlockPublisher( @Override public SafeFuture sendSignedBlock( final SignedBlockContainer blockContainer, - final BroadcastValidationLevel broadcastValidationLevel) { + final BroadcastValidationLevel broadcastValidationLevel, + final BlockPublishingPerformance blockPublishingPerformance) { return blockFactory - .unblindSignedBlockIfBlinded(blockContainer.getSignedBlock()) + .unblindSignedBlockIfBlinded(blockContainer.getSignedBlock(), blockPublishingPerformance) .thenPeek(performanceTracker::saveProducedBlock) .thenCompose( signedBlock -> { // creating blob sidecars after unblinding the block to ensure in the blinded flow we // will have the cached builder payload final List blobSidecars = - blockFactory.createBlobSidecars(blockContainer); + blockFactory.createBlobSidecars(blockContainer, blockPublishingPerformance); return gossipAndImportUnblindedSignedBlockAndBlobSidecars( - signedBlock, blobSidecars, broadcastValidationLevel); + signedBlock, blobSidecars, broadcastValidationLevel, blockPublishingPerformance); }) .thenCompose(result -> calculateResult(blockContainer, result)); } @@ -75,13 +77,15 @@ public SafeFuture sendSignedBlock( gossipAndImportUnblindedSignedBlockAndBlobSidecars( final SignedBeaconBlock block, final List blobSidecars, - final BroadcastValidationLevel broadcastValidationLevel) { + final BroadcastValidationLevel broadcastValidationLevel, + final BlockPublishingPerformance blockPublishingPerformance) { if (broadcastValidationLevel == BroadcastValidationLevel.NOT_REQUIRED) { // when broadcast validation is disabled, we can publish the block (and blob sidecars) // immediately and then import - publishBlockAndBlobSidecars(block, blobSidecars); - return importBlockAndBlobSidecars(block, blobSidecars, broadcastValidationLevel); + publishBlockAndBlobSidecars(block, blobSidecars, blockPublishingPerformance); + return importBlockAndBlobSidecars( + block, blobSidecars, broadcastValidationLevel, blockPublishingPerformance); } // when broadcast validation is enabled, we need to wait for the validation to complete before @@ -89,14 +93,15 @@ public SafeFuture sendSignedBlock( final SafeFuture blockImportAndBroadcastValidationResults = - importBlockAndBlobSidecars(block, blobSidecars, broadcastValidationLevel); + importBlockAndBlobSidecars( + block, blobSidecars, broadcastValidationLevel, blockPublishingPerformance); blockImportAndBroadcastValidationResults .thenCompose(BlockImportAndBroadcastValidationResults::broadcastValidationResult) .thenAccept( broadcastValidationResult -> { if (broadcastValidationResult == BroadcastValidationResult.SUCCESS) { - publishBlockAndBlobSidecars(block, blobSidecars); + publishBlockAndBlobSidecars(block, blobSidecars, blockPublishingPerformance); LOG.debug("Block (and blob sidecars) publishing initiated"); } else { LOG.warn( @@ -118,10 +123,13 @@ public SafeFuture sendSignedBlock( abstract SafeFuture importBlockAndBlobSidecars( SignedBeaconBlock block, List blobSidecars, - BroadcastValidationLevel broadcastValidationLevel); + BroadcastValidationLevel broadcastValidationLevel, + BlockPublishingPerformance blockPublishingPerformance); abstract void publishBlockAndBlobSidecars( - SignedBeaconBlock block, List blobSidecars); + SignedBeaconBlock block, + List blobSidecars, + BlockPublishingPerformance blockPublishingPerformance); private SafeFuture calculateResult( final SignedBlockContainer maybeBlindedBlockContainer, diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisher.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisher.java index 33efc17b03b..0a861dd8fc8 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisher.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisher.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.validator.coordinator.publisher; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockContainer; import tech.pegasys.teku.spec.datastructures.validator.BroadcastValidationLevel; @@ -21,5 +22,7 @@ /** Used to publish blocks (unblinded and blinded) and blob sidecars */ public interface BlockPublisher { SafeFuture sendSignedBlock( - SignedBlockContainer blockContainer, BroadcastValidationLevel broadcastValidationLevel); + SignedBlockContainer blockContainer, + BroadcastValidationLevel broadcastValidationLevel, + BlockPublishingPerformance blockPublishingPerformance); } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisherDeneb.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisherDeneb.java index d0038341f7b..2eee9a561d9 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisherDeneb.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisherDeneb.java @@ -14,6 +14,7 @@ package tech.pegasys.teku.validator.coordinator.publisher; import java.util.List; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.networking.eth2.gossip.BlobSidecarGossipChannel; import tech.pegasys.teku.networking.eth2.gossip.BlockGossipChannel; @@ -51,16 +52,22 @@ public BlockPublisherDeneb( SafeFuture importBlockAndBlobSidecars( final SignedBeaconBlock block, final List blobSidecars, - final BroadcastValidationLevel broadcastValidationLevel) { + final BroadcastValidationLevel broadcastValidationLevel, + final BlockPublishingPerformance blockPublishingPerformance) { // provide blobs for the block before importing it blockBlobSidecarsTrackersPool.onCompletedBlockAndBlobSidecars(block, blobSidecars); - return blockImportChannel.importBlock(block, broadcastValidationLevel); + return blockImportChannel + .importBlock(block, broadcastValidationLevel) + .thenPeek(__ -> blockPublishingPerformance.blockImportCompleted()); } @Override void publishBlockAndBlobSidecars( - final SignedBeaconBlock block, final List blobSidecars) { + final SignedBeaconBlock block, + final List blobSidecars, + BlockPublishingPerformance blockPublishingPerformance) { blockGossipChannel.publishBlock(block); blobSidecarGossipChannel.publishBlobSidecars(blobSidecars); + blockPublishingPerformance.blockAndBlobSidecarsPublishingInitiated(); } } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisherPhase0.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisherPhase0.java index 36dbb881309..21ca2b370b1 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisherPhase0.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/BlockPublisherPhase0.java @@ -14,6 +14,7 @@ package tech.pegasys.teku.validator.coordinator.publisher; import java.util.List; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.networking.eth2.gossip.BlockGossipChannel; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; @@ -42,13 +43,19 @@ public BlockPublisherPhase0( SafeFuture importBlockAndBlobSidecars( final SignedBeaconBlock block, final List blobSidecars, - final BroadcastValidationLevel broadcastValidationLevel) { - return blockImportChannel.importBlock(block, broadcastValidationLevel); + final BroadcastValidationLevel broadcastValidationLevel, + final BlockPublishingPerformance blockPublishingPerformance) { + return blockImportChannel + .importBlock(block, broadcastValidationLevel) + .thenPeek(__ -> blockPublishingPerformance.blockImportCompleted()); } @Override void publishBlockAndBlobSidecars( - final SignedBeaconBlock block, final List blobSidecars) { + final SignedBeaconBlock block, + final List blobSidecars, + final BlockPublishingPerformance blockPublishingPerformance) { blockGossipChannel.publishBlock(block); + blockPublishingPerformance.blockPublishingInitiated(); } } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/MilestoneBasedBlockPublisher.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/MilestoneBasedBlockPublisher.java index 1d6137d7b6d..2dabbf3ebac 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/MilestoneBasedBlockPublisher.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/MilestoneBasedBlockPublisher.java @@ -17,6 +17,7 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.networking.eth2.gossip.BlobSidecarGossipChannel; import tech.pegasys.teku.networking.eth2.gossip.BlockGossipChannel; @@ -79,10 +80,11 @@ public MilestoneBasedBlockPublisher( @Override public SafeFuture sendSignedBlock( final SignedBlockContainer blockContainer, - final BroadcastValidationLevel broadcastValidationLevel) { + final BroadcastValidationLevel broadcastValidationLevel, + BlockPublishingPerformance blockPublishingPerformance) { final SpecMilestone blockMilestone = spec.atSlot(blockContainer.getSlot()).getMilestone(); return registeredPublishers .get(blockMilestone) - .sendSignedBlock(blockContainer, broadcastValidationLevel); + .sendSignedBlock(blockContainer, broadcastValidationLevel, blockPublishingPerformance); } } diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java index c4c72093395..bdcd0f95592 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java @@ -36,6 +36,7 @@ import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.bls.BLSSignatureVerifier; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.infrastructure.unsigned.UInt64; @@ -289,11 +290,13 @@ protected SignedBeaconBlock assertBlockUnblinded( final BlockFactory blockFactory = createBlockFactory(spec); // no need to prepare blobs bundle when only testing block unblinding - when(executionLayer.getUnblindedPayload(blindedBlock)) + when(executionLayer.getUnblindedPayload(blindedBlock, BlockPublishingPerformance.NOOP)) .thenReturn(SafeFuture.completedFuture(executionPayload)); final SignedBeaconBlock unblindedBlock = - blockFactory.unblindSignedBlockIfBlinded(blindedBlock).join(); + blockFactory + .unblindSignedBlockIfBlinded(blindedBlock, BlockPublishingPerformance.NOOP) + .join(); assertThat(unblindedBlock).isNotNull(); assertThat(unblindedBlock.hashTreeRoot()).isEqualTo(blindedBlock.hashTreeRoot()); @@ -302,7 +305,7 @@ protected SignedBeaconBlock assertBlockUnblinded( .isEqualTo(Optional.empty()); if (blindedBlock.isBlinded()) { - verify(executionLayer).getUnblindedPayload(blindedBlock); + verify(executionLayer).getUnblindedPayload(blindedBlock, BlockPublishingPerformance.NOOP); assertThat(unblindedBlock.getMessage().getBody().getOptionalExecutionPayload()) .hasValue(executionPayload); } else { @@ -364,7 +367,8 @@ protected BlockAndBlobSidecars createBlockAndBlobSidecars( when(executionLayer.getCachedUnblindedPayload(signedBlockContainer.getSlot())) .thenReturn(builderPayload); - final List blobSidecars = blockFactory.createBlobSidecars(signedBlockContainer); + final List blobSidecars = + blockFactory.createBlobSidecars(signedBlockContainer, BlockPublishingPerformance.NOOP); return new BlockAndBlobSidecars(signedBlockContainer, blobSidecars); } diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDenebTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDenebTest.java index 13d834c83d9..67103b9e60e 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDenebTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDenebTest.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.stream.IntStream; import org.junit.jupiter.api.Test; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.ssz.SszCollection; import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.spec.Spec; @@ -97,7 +98,7 @@ void unblindSignedBlock_shouldUnblindBeaconBlock() { final SignedBeaconBlock unblindedBlock = assertBlockUnblinded(blindedBlock, spec); - verify(executionLayer).getUnblindedPayload(unblindedBlock); + verify(executionLayer).getUnblindedPayload(unblindedBlock, BlockPublishingPerformance.NOOP); assertThat(unblindedBlock.isBlinded()).isFalse(); assertThat(unblindedBlock).isEqualTo(expectedUnblindedBlock); diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java index 7efce12065c..8dddab4934d 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java @@ -35,6 +35,7 @@ import org.junit.jupiter.api.Test; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.metrics.StubMetricsSystem; import tech.pegasys.teku.infrastructure.ssz.SszData; @@ -657,10 +658,10 @@ void shouldUnblindSignedBlindedBeaconBlock() { final CapturingBeaconBlockUnblinder blockUnblinder = new CapturingBeaconBlockUnblinder(spec.getGenesisSchemaDefinitions(), blindedSignedBlock); - when(executionLayer.getUnblindedPayload(blindedSignedBlock)) + when(executionLayer.getUnblindedPayload(blindedSignedBlock, BlockPublishingPerformance.NOOP)) .thenReturn(SafeFuture.completedFuture(randomExecutionPayload)); - factory.createBlockUnblinderSelector().accept(blockUnblinder); + factory.createBlockUnblinderSelector(BlockPublishingPerformance.NOOP).accept(blockUnblinder); assertThat(blockUnblinder.executionPayload).isCompletedWithValue(randomExecutionPayload); } @@ -789,7 +790,9 @@ void shouldCreateBlobSidecarsForBlockContents() { MiscHelpersDeneb.required(spec.atSlot(signedBlockContents.getSlot()).miscHelpers()); final List blobSidecars = - factory.createBlobSidecarsSelector().apply(signedBlockContents); + factory + .createBlobSidecarsSelector(BlockPublishingPerformance.NOOP) + .apply(signedBlockContents); final SszList expectedBlobs = signedBlockContents.getBlobs().orElseThrow(); final SszList expectedProofs = signedBlockContents.getKzgProofs().orElseThrow(); @@ -833,7 +836,11 @@ void shouldFailCreatingBlobSidecarsIfBuilderBlobsBundleCommitmentsRootIsNotConsi dataStructureUtil.randomExecutionPayload(), Optional.of(blobsBundle)); - assertThatThrownBy(() -> factory.createBlobSidecarsSelector().apply(signedBlindedBeaconBlock)) + assertThatThrownBy( + () -> + factory + .createBlobSidecarsSelector(BlockPublishingPerformance.NOOP) + .apply(signedBlindedBeaconBlock)) .isInstanceOf(IllegalStateException.class) .hasMessage( "Commitments in the builder BlobsBundle don't match the commitments in the block"); @@ -854,7 +861,11 @@ void shouldFailCreatingBlobSidecarsIfBuilderBlobsBundleProofsIsNotConsistent() { dataStructureUtil.randomExecutionPayload(), Optional.of(blobsBundle)); - assertThatThrownBy(() -> factory.createBlobSidecarsSelector().apply(signedBlindedBeaconBlock)) + assertThatThrownBy( + () -> + factory + .createBlobSidecarsSelector(BlockPublishingPerformance.NOOP) + .apply(signedBlindedBeaconBlock)) .isInstanceOf(IllegalStateException.class) .hasMessage( "The number of blobs in BlobsBundle doesn't match the number of commitments in the block"); @@ -875,7 +886,11 @@ void shouldFailCreatingBlobSidecarsIfBuilderBlobsBundleBlobsIsNotConsistent() { dataStructureUtil.randomExecutionPayload(), Optional.of(blobsBundle)); - assertThatThrownBy(() -> factory.createBlobSidecarsSelector().apply(signedBlindedBeaconBlock)) + assertThatThrownBy( + () -> + factory + .createBlobSidecarsSelector(BlockPublishingPerformance.NOOP) + .apply(signedBlindedBeaconBlock)) .isInstanceOf(IllegalStateException.class) .hasMessage( "The number of proofs in BlobsBundle doesn't match the number of commitments in the block"); @@ -899,7 +914,9 @@ void shouldCreateBlobSidecarsForBlindedBlock() { Optional.of(blobsBundle)); final List blobSidecars = - factory.createBlobSidecarsSelector().apply(signedBlindedBeaconBlock); + factory + .createBlobSidecarsSelector(BlockPublishingPerformance.NOOP) + .apply(signedBlindedBeaconBlock); final SszList expectedBlobs = blobsBundle.getBlobs(); final SszList expectedProofs = blobsBundle.getProofs(); diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java index aa6587d5e15..40eb7c723fa 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java @@ -66,8 +66,8 @@ import tech.pegasys.teku.ethereum.json.types.validator.ProposerDuties; import tech.pegasys.teku.ethereum.json.types.validator.ProposerDuty; import tech.pegasys.teku.ethereum.json.types.validator.SyncCommitteeDuties; +import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionAndPublishingPerformanceFactory; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; -import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformanceFactory; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.metrics.StubMetricsSystem; import tech.pegasys.teku.infrastructure.metrics.Validator.ValidatorDutyMetricUtils; @@ -172,8 +172,9 @@ class ValidatorApiHandlerTest { private final ArgumentCaptor> blobSidecarsCaptor2 = ArgumentCaptor.forClass(List.class); - private final BlockProductionPerformanceFactory blockProductionPerformanceFactory = - new BlockProductionPerformanceFactory(StubTimeProvider.withTimeInMillis(0), false, 0); + private final BlockProductionAndPublishingPerformanceFactory blockProductionPerformanceFactory = + new BlockProductionAndPublishingPerformanceFactory( + StubTimeProvider.withTimeInMillis(0), __ -> ZERO, false, 0, 0); private Spec spec; private UInt64 epochStartSlot; @@ -220,7 +221,7 @@ public void setUp() { when(chainDataClient.isOptimisticBlock(any())).thenReturn(false); doAnswer(invocation -> SafeFuture.completedFuture(invocation.getArgument(0))) .when(blockFactory) - .unblindSignedBlockIfBlinded(any()); + .unblindSignedBlockIfBlinded(any(), any()); when(proposersDataManager.updateValidatorRegistrations(any(), any())) .thenReturn(SafeFuture.COMPLETE); } @@ -1337,7 +1338,7 @@ private void setupDeneb() { .toList(); }) .when(blockFactory) - .createBlobSidecars(any()); + .createBlobSidecars(any(), any()); } private SafeFuture prepareBlockImportResult( diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisherTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisherTest.java index 9ffde68cde2..7fd4c543dad 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisherTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisherTest.java @@ -23,6 +23,7 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.TestSpecFactory; @@ -59,9 +60,10 @@ public class AbstractBlockPublisherTest { @BeforeEach public void setUp() { - when(blockFactory.unblindSignedBlockIfBlinded(signedBlock)) + when(blockFactory.unblindSignedBlockIfBlinded(signedBlock, BlockPublishingPerformance.NOOP)) .thenReturn(SafeFuture.completedFuture(signedBlock)); - when(blockFactory.createBlobSidecars(signedBlockContents)).thenReturn(blobSidecars); + when(blockFactory.createBlobSidecars(signedBlockContents, BlockPublishingPerformance.NOOP)) + .thenReturn(blobSidecars); } @Test @@ -69,7 +71,10 @@ public void setUp() { sendSignedBlock_shouldPublishImmediatelyAndImportWhenBroadcastValidationIsNotRequired() { when(blockPublisher.importBlockAndBlobSidecars( - signedBlock, blobSidecars, BroadcastValidationLevel.NOT_REQUIRED)) + signedBlock, + blobSidecars, + BroadcastValidationLevel.NOT_REQUIRED, + BlockPublishingPerformance.NOOP)) .thenReturn( SafeFuture.completedFuture( new BlockImportAndBroadcastValidationResults( @@ -77,20 +82,29 @@ public void setUp() { assertThatSafeFuture( blockPublisher.sendSignedBlock( - signedBlockContents, BroadcastValidationLevel.NOT_REQUIRED)) + signedBlockContents, + BroadcastValidationLevel.NOT_REQUIRED, + BlockPublishingPerformance.NOOP)) .isCompletedWithValue(SendSignedBlockResult.success(signedBlockContents.getRoot())); - verify(blockPublisher).publishBlockAndBlobSidecars(signedBlock, blobSidecars); + verify(blockPublisher) + .publishBlockAndBlobSidecars(signedBlock, blobSidecars, BlockPublishingPerformance.NOOP); verify(blockPublisher) .importBlockAndBlobSidecars( - signedBlock, blobSidecars, BroadcastValidationLevel.NOT_REQUIRED); + signedBlock, + blobSidecars, + BroadcastValidationLevel.NOT_REQUIRED, + BlockPublishingPerformance.NOOP); } @Test public void sendSignedBlock_shouldWaitToPublishWhenBroadcastValidationIsSpecified() { final SafeFuture validationResult = new SafeFuture<>(); when(blockPublisher.importBlockAndBlobSidecars( - signedBlock, blobSidecars, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION)) + signedBlock, + blobSidecars, + BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION, + BlockPublishingPerformance.NOOP)) .thenReturn( SafeFuture.completedFuture( new BlockImportAndBroadcastValidationResults( @@ -99,19 +113,26 @@ public void sendSignedBlock_shouldWaitToPublishWhenBroadcastValidationIsSpecifie final SafeFuture sendSignedBlockResult = blockPublisher.sendSignedBlock( - signedBlockContents, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION); + signedBlockContents, + BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION, + BlockPublishingPerformance.NOOP); assertThatSafeFuture(sendSignedBlockResult).isNotCompleted(); verify(blockPublisher) .importBlockAndBlobSidecars( - signedBlock, blobSidecars, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION); + signedBlock, + blobSidecars, + BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION, + BlockPublishingPerformance.NOOP); - verify(blockPublisher, never()).publishBlockAndBlobSidecars(signedBlock, blobSidecars); + verify(blockPublisher, never()) + .publishBlockAndBlobSidecars(signedBlock, blobSidecars, BlockPublishingPerformance.NOOP); validationResult.complete(BroadcastValidationResult.SUCCESS); - verify(blockPublisher).publishBlockAndBlobSidecars(signedBlock, blobSidecars); + verify(blockPublisher) + .publishBlockAndBlobSidecars(signedBlock, blobSidecars, BlockPublishingPerformance.NOOP); assertThatSafeFuture(sendSignedBlockResult) .isCompletedWithValue(SendSignedBlockResult.success(signedBlockContents.getRoot())); } @@ -120,7 +141,10 @@ public void sendSignedBlock_shouldWaitToPublishWhenBroadcastValidationIsSpecifie public void sendSignedBlock_shouldNotPublishWhenBroadcastValidationFails() { final SafeFuture validationResult = new SafeFuture<>(); when(blockPublisher.importBlockAndBlobSidecars( - signedBlock, blobSidecars, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION)) + signedBlock, + blobSidecars, + BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION, + BlockPublishingPerformance.NOOP)) .thenReturn( SafeFuture.completedFuture( new BlockImportAndBroadcastValidationResults( @@ -129,19 +153,26 @@ public void sendSignedBlock_shouldNotPublishWhenBroadcastValidationFails() { final SafeFuture sendSignedBlockResult = blockPublisher.sendSignedBlock( - signedBlockContents, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION); + signedBlockContents, + BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION, + BlockPublishingPerformance.NOOP); assertThatSafeFuture(sendSignedBlockResult).isNotCompleted(); verify(blockPublisher) .importBlockAndBlobSidecars( - signedBlock, blobSidecars, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION); + signedBlock, + blobSidecars, + BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION, + BlockPublishingPerformance.NOOP); - verify(blockPublisher, never()).publishBlockAndBlobSidecars(signedBlock, blobSidecars); + verify(blockPublisher, never()) + .publishBlockAndBlobSidecars(signedBlock, blobSidecars, BlockPublishingPerformance.NOOP); validationResult.complete(BroadcastValidationResult.CONSENSUS_FAILURE); - verify(blockPublisher, never()).publishBlockAndBlobSidecars(signedBlock, blobSidecars); + verify(blockPublisher, never()) + .publishBlockAndBlobSidecars(signedBlock, blobSidecars, BlockPublishingPerformance.NOOP); assertThatSafeFuture(sendSignedBlockResult) .isCompletedWithValue( SendSignedBlockResult.rejected("FAILED_BROADCAST_VALIDATION: CONSENSUS_FAILURE")); @@ -160,12 +191,15 @@ public BlockPublisherTest( SafeFuture importBlockAndBlobSidecars( final SignedBeaconBlock block, final List blobSidecars, - final BroadcastValidationLevel broadcastValidationLevel) { + final BroadcastValidationLevel broadcastValidationLevel, + final BlockPublishingPerformance blockPublishingPerformance) { return null; } @Override void publishBlockAndBlobSidecars( - final SignedBeaconBlock block, final List blobSidecars) {} + final SignedBeaconBlock block, + final List blobSidecars, + final BlockPublishingPerformance blockPublishingPerformance) {} } } diff --git a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImpl.java b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImpl.java index d388e3d86c4..6f047f38d3f 100644 --- a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImpl.java +++ b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImpl.java @@ -19,6 +19,7 @@ import org.apache.tuweni.units.bigints.UInt256; import tech.pegasys.teku.ethereum.events.SlotEventsChannel; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; @@ -136,11 +137,16 @@ public ExecutionPayloadResult initiateBlockAndBlobsProduction( } @Override - public SafeFuture getUnblindedPayload(final SignedBeaconBlock signedBeaconBlock) { + public SafeFuture getUnblindedPayload( + final SignedBeaconBlock signedBeaconBlock, + final BlockPublishingPerformance blockPublishingPerformance) { return executionLayerChannel .builderGetPayload(signedBeaconBlock, this::getCachedPayloadResult) .thenPeek( - builderPayload -> builderResultCache.put(signedBeaconBlock.getSlot(), builderPayload)); + builderPayload -> { + builderResultCache.put(signedBeaconBlock.getSlot(), builderPayload); + blockPublishingPerformance.builderGetPayload(); + }); } @Override diff --git a/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImplTest.java b/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImplTest.java index c2d8928bf74..3a7159eccc2 100644 --- a/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImplTest.java +++ b/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImplTest.java @@ -34,6 +34,7 @@ import tech.pegasys.teku.ethereum.executionclient.schema.Response; import tech.pegasys.teku.ethereum.executionlayer.ExecutionLayerManagerImpl.Source; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.logging.EventLogger; import tech.pegasys.teku.infrastructure.metrics.StubMetricsSystem; @@ -142,13 +143,17 @@ public void preDeneb_builderOffline() throws Exception { final SafeFuture unblindedPayload = blockProductionManager.getUnblindedPayload( - dataStructureUtil.randomSignedBlindedBeaconBlock(slot)); + dataStructureUtil.randomSignedBlindedBeaconBlock(slot), + BlockPublishingPerformance.NOOP); assertThat(unblindedPayload.get()).isEqualTo(localPayload); // wrong slot, we will hit builder client by this call final SignedBeaconBlock signedBlindedBeaconBlock = dataStructureUtil.randomSignedBlindedBeaconBlock(slot.plus(1)); - assertThatThrownBy(() -> blockProductionManager.getUnblindedPayload(signedBlindedBeaconBlock)); + assertThatThrownBy( + () -> + blockProductionManager.getUnblindedPayload( + signedBlindedBeaconBlock, BlockPublishingPerformance.NOOP)); verify(builderClient).getPayload(signedBlindedBeaconBlock); } @@ -195,7 +200,9 @@ public void preDeneb_builderOnline() throws Exception { final ExecutionPayload payload = prepareBuilderGetPayloadResponse(signedBlindedBeaconBlock); // we expect result from the builder - assertThat(blockProductionManager.getUnblindedPayload(signedBlindedBeaconBlock)) + assertThat( + blockProductionManager.getUnblindedPayload( + signedBlindedBeaconBlock, BlockPublishingPerformance.NOOP)) .isCompletedWithValue(payload); // we expect both builder and local engine have been called @@ -241,7 +248,10 @@ public void preDeneb_noBuilder() throws Exception { // we will hit builder client by this call final SignedBeaconBlock signedBlindedBeaconBlock = dataStructureUtil.randomSignedBlindedBeaconBlock(slot); - assertThatThrownBy(() -> blockProductionManager.getUnblindedPayload(signedBlindedBeaconBlock)); + assertThatThrownBy( + () -> + blockProductionManager.getUnblindedPayload( + signedBlindedBeaconBlock, BlockPublishingPerformance.NOOP)); verify(builderClient).getPayload(signedBlindedBeaconBlock); } @@ -304,7 +314,8 @@ public void postDeneb_builderOffline() throws Exception { final SafeFuture unblindedPayload = blockProductionManager.getUnblindedPayload( - dataStructureUtil.randomSignedBlindedBeaconBlock(slot)); + dataStructureUtil.randomSignedBlindedBeaconBlock(slot), + BlockPublishingPerformance.NOOP); assertThat(unblindedPayload.get()).isEqualTo(localPayload); verifyNoMoreInteractions(builderClient); @@ -356,7 +367,9 @@ public void postDeneb_builderOnline() throws Exception { prepareBuilderGetPayloadResponseWithBlobs(signedBlindedBeaconBlock); // we expect result from the builder - assertThat(blockProductionManager.getUnblindedPayload(signedBlindedBeaconBlock)) + assertThat( + blockProductionManager.getUnblindedPayload( + signedBlindedBeaconBlock, BlockPublishingPerformance.NOOP)) .isCompletedWithValue(payloadAndBlobsBundle); // we expect both builder and local engine have been called @@ -407,7 +420,10 @@ public void postDeneb_noBuilder() throws Exception { // we will hit builder client by this call final SignedBeaconBlock signedBlindedBeaconBlock = dataStructureUtil.randomSignedBlindedBeaconBlock(slot); - assertThatThrownBy(() -> blockProductionManager.getUnblindedPayload(signedBlindedBeaconBlock)); + assertThatThrownBy( + () -> + blockProductionManager.getUnblindedPayload( + signedBlindedBeaconBlock, BlockPublishingPerformance.NOOP)); verify(builderClient).getPayload(signedBlindedBeaconBlock); } diff --git a/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionAndPublishingPerformanceFactory.java b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionAndPublishingPerformanceFactory.java new file mode 100644 index 00000000000..7d196542cad --- /dev/null +++ b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionAndPublishingPerformanceFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright Consensys Software Inc., 2023 + * + * 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.ethereum.performance.trackers; + +import java.util.function.Function; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; + +public class BlockProductionAndPublishingPerformanceFactory { + private final TimeProvider timeProvider; + private final boolean enabled; + private final int lateProductionEventThreshold; + private final int latePublishingEventThreshold; + private final Function slotTimeCalculator; + + public BlockProductionAndPublishingPerformanceFactory( + final TimeProvider timeProvider, + final Function slotTimeCalculator, + final boolean enabled, + final int lateProductionEventThreshold, + final int latePublishingEventThreshold) { + this.timeProvider = timeProvider; + this.slotTimeCalculator = slotTimeCalculator; + this.enabled = enabled; + this.lateProductionEventThreshold = lateProductionEventThreshold; + this.latePublishingEventThreshold = latePublishingEventThreshold; + } + + public BlockProductionPerformance createForProduction(final UInt64 slot) { + if (enabled) { + return new BlockProductionPerformanceImpl( + timeProvider, slot, slotTimeCalculator.apply(slot), lateProductionEventThreshold); + } else { + return BlockProductionPerformance.NOOP; + } + } + + public BlockPublishingPerformance createForPublishing(final UInt64 slot) { + if (enabled) { + return new BlockPublishingPerformanceImpl( + timeProvider, slot, slotTimeCalculator.apply(slot), latePublishingEventThreshold); + } else { + return BlockPublishingPerformance.NOOP; + } + } +} diff --git a/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformance.java b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformance.java index ed01ee21774..b0d2bcb5e70 100644 --- a/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformance.java +++ b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformance.java @@ -13,9 +13,6 @@ package tech.pegasys.teku.ethereum.performance.trackers; -import java.util.function.Supplier; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; - /** * This is high level flow, some steps are executed only if builder flow take place * @@ -62,8 +59,6 @@ public interface BlockProductionPerformance { BlockProductionPerformance NOOP = new BlockProductionPerformance() { - @Override - public void slotTime(final Supplier slotTimeSupplier) {} @Override public void complete() {} @@ -102,8 +97,6 @@ public void stateTransition() {} public void stateHashing() {} }; - void slotTime(Supplier slotTimeSupplier); - void complete(); void prepareOnTick(); diff --git a/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformanceFactory.java b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformanceFactory.java deleted file mode 100644 index 5a0b408f033..00000000000 --- a/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformanceFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Consensys Software Inc., 2023 - * - * 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.ethereum.performance.trackers; - -import tech.pegasys.teku.infrastructure.time.TimeProvider; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; - -public class BlockProductionPerformanceFactory { - private final TimeProvider timeProvider; - private final boolean enabled; - private final int lateEventThreshold; - - public BlockProductionPerformanceFactory( - final TimeProvider timeProvider, final boolean enabled, final int lateEventThreshold) { - this.timeProvider = timeProvider; - this.enabled = enabled; - this.lateEventThreshold = lateEventThreshold; - } - - public BlockProductionPerformance create(final UInt64 slot) { - if (enabled) { - return new BlockProductionPerformanceImpl(timeProvider, slot, lateEventThreshold); - } else { - return BlockProductionPerformance.NOOP; - } - } -} diff --git a/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformanceImpl.java b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformanceImpl.java index bd6da142a4f..185d762998e 100644 --- a/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformanceImpl.java +++ b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockProductionPerformanceImpl.java @@ -13,7 +13,6 @@ package tech.pegasys.teku.ethereum.performance.trackers; -import java.util.function.Supplier; import tech.pegasys.teku.infrastructure.logging.EventLogger; import tech.pegasys.teku.infrastructure.time.PerformanceTracker; import tech.pegasys.teku.infrastructure.time.TimeProvider; @@ -22,28 +21,23 @@ public class BlockProductionPerformanceImpl implements BlockProductionPerformance { private final PerformanceTracker performanceTracker; private final UInt64 slot; - private UInt64 slotTime = UInt64.ZERO; + private final UInt64 slotTime; private final int lateThreshold; - public BlockProductionPerformanceImpl( - final TimeProvider timeProvider, final UInt64 slot, final int lateThreshold) { + BlockProductionPerformanceImpl( + final TimeProvider timeProvider, + final UInt64 slot, + final UInt64 slotTime, + final int lateThreshold) { this.performanceTracker = new PerformanceTracker(timeProvider); this.lateThreshold = lateThreshold; this.slot = slot; + this.slotTime = slotTime; performanceTracker.addEvent("start"); } - @Override - public void slotTime(final Supplier slotTimeSupplier) { - this.slotTime = slotTimeSupplier.get(); - } - @Override public void complete() { - if (slotTime.isZero()) { - // we haven't managed to calculate slot time, something wrong happened - return; - } final UInt64 completionTime = performanceTracker.addEvent(COMPLETE_LABEL); final boolean isLateEvent = completionTime.minusMinZero(slotTime).isGreaterThan(lateThreshold); performanceTracker.report( diff --git a/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockPublishingPerformance.java b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockPublishingPerformance.java new file mode 100644 index 00000000000..fe9e468ec08 --- /dev/null +++ b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockPublishingPerformance.java @@ -0,0 +1,52 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * 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.ethereum.performance.trackers; + +public interface BlockPublishingPerformance { + String COMPLETE_LABEL = "complete"; + + BlockPublishingPerformance NOOP = + new BlockPublishingPerformance() { + + @Override + public void complete() {} + + @Override + public void builderGetPayload() {} + + @Override + public void blobSidecarsPrepared() {} + + @Override + public void blockAndBlobSidecarsPublishingInitiated() {} + + @Override + public void blockPublishingInitiated() {} + + @Override + public void blockImportCompleted() {} + }; + + void blockAndBlobSidecarsPublishingInitiated(); + + void blockPublishingInitiated(); + + void builderGetPayload(); + + void blobSidecarsPrepared(); + + void blockImportCompleted(); + + void complete(); +} diff --git a/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockPublishingPerformanceImpl.java b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockPublishingPerformanceImpl.java new file mode 100644 index 00000000000..83367e434ab --- /dev/null +++ b/ethereum/performance-trackers/src/main/java/tech/pegasys/teku/ethereum/performance/trackers/BlockPublishingPerformanceImpl.java @@ -0,0 +1,76 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * 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.ethereum.performance.trackers; + +import tech.pegasys.teku.infrastructure.logging.EventLogger; +import tech.pegasys.teku.infrastructure.time.PerformanceTracker; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; + +public class BlockPublishingPerformanceImpl implements BlockPublishingPerformance { + private final PerformanceTracker performanceTracker; + private final UInt64 slot; + private final UInt64 slotTime; + private final int lateThreshold; + + BlockPublishingPerformanceImpl( + final TimeProvider timeProvider, + final UInt64 slot, + final UInt64 slotTime, + final int lateThreshold) { + this.performanceTracker = new PerformanceTracker(timeProvider); + this.lateThreshold = lateThreshold; + this.slot = slot; + this.slotTime = slotTime; + performanceTracker.addEvent("start"); + } + + @Override + public void complete() { + final UInt64 completionTime = performanceTracker.addEvent(COMPLETE_LABEL); + final boolean isLateEvent = completionTime.minusMinZero(slotTime).isGreaterThan(lateThreshold); + performanceTracker.report( + slotTime, + isLateEvent, + (event, stepDuration) -> {}, + totalDuration -> {}, + (totalDuration, timings) -> + EventLogger.EVENT_LOG.slowBlockPublishingEvent(slot, totalDuration, timings)); + } + + @Override + public void builderGetPayload() { + performanceTracker.addEvent("builder_get_payload"); + } + + @Override + public void blobSidecarsPrepared() { + performanceTracker.addEvent("blob_sidecars_prepared"); + } + + @Override + public void blockAndBlobSidecarsPublishingInitiated() { + performanceTracker.addEvent("block_and_blob_sidecars_publishing_initiated"); + } + + @Override + public void blockPublishingInitiated() { + performanceTracker.addEvent("block_publishing_initiated"); + } + + @Override + public void blockImportCompleted() { + performanceTracker.addEvent("block_import_completed"); + } +} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java index 695442c4cd5..ee2add97ad5 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java @@ -428,8 +428,12 @@ public UInt64 computeEpochAtSlot(final UInt64 slot) { return atSlot(slot).miscHelpers().computeEpochAtSlot(slot); } - public UInt64 computeTimeAtSlot(BeaconState state, UInt64 slot) { - return atSlot(slot).miscHelpers().computeTimeAtSlot(state.getGenesisTime(), slot); + public UInt64 computeTimeAtSlot(final BeaconState state, final UInt64 slot) { + return computeTimeAtSlot(state.getGenesisTime(), slot); + } + + public UInt64 computeTimeAtSlot(final UInt64 genesisTime, final UInt64 slot) { + return atSlot(slot).miscHelpers().computeTimeAtSlot(genesisTime, slot); } public Bytes computeSigningRoot(BeaconBlock block, Bytes32 domain) { diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerBlockProductionManager.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerBlockProductionManager.java index 4b5ce61f8f1..7f52c0dd619 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerBlockProductionManager.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerBlockProductionManager.java @@ -15,6 +15,7 @@ import java.util.Optional; import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance; +import tech.pegasys.teku.ethereum.performance.trackers.BlockPublishingPerformance; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; @@ -59,7 +60,8 @@ public ExecutionPayloadResult initiateBlockAndBlobsProduction( @Override public SafeFuture getUnblindedPayload( - final SignedBeaconBlock signedBeaconBlock) { + final SignedBeaconBlock signedBeaconBlock, + final BlockPublishingPerformance blockPublishingPerformance) { return SafeFuture.completedFuture(null); } @@ -112,11 +114,12 @@ ExecutionPayloadResult initiateBlockAndBlobsProduction( */ Optional getCachedPayloadResult(UInt64 slot); - SafeFuture getUnblindedPayload(SignedBeaconBlock signedBeaconBlock); + SafeFuture getUnblindedPayload( + SignedBeaconBlock signedBeaconBlock, BlockPublishingPerformance blockPublishingPerformance); /** - * Requires {@link #getUnblindedPayload(SignedBeaconBlock)} to have been called first in order for - * a value to be present + * Requires {@link #getUnblindedPayload(SignedBeaconBlock, BlockPublishingPerformance)} to have + * been called first in order for a value to be present */ Optional getCachedUnblindedPayload(UInt64 slot); } diff --git a/infrastructure/logging/src/main/java/tech/pegasys/teku/infrastructure/logging/EventLogger.java b/infrastructure/logging/src/main/java/tech/pegasys/teku/infrastructure/logging/EventLogger.java index c3ec074464e..7935fb12f74 100644 --- a/infrastructure/logging/src/main/java/tech/pegasys/teku/infrastructure/logging/EventLogger.java +++ b/infrastructure/logging/src/main/java/tech/pegasys/teku/infrastructure/logging/EventLogger.java @@ -303,6 +303,15 @@ public void slowBlockProductionEvent( warn(slowBlockProductionLog, Color.YELLOW); } + public void slowBlockPublishingEvent( + final UInt64 slot, final UInt64 totalProcessingDuration, final String timings) { + final String slowBlockPublishingLog = + String.format( + "Slow Block Publishing *** Slot: %s %s total: %sms", + slot, timings, totalProcessingDuration); + warn(slowBlockPublishingLog, Color.YELLOW); + } + public void executionLayerStubEnabled() { error( "Execution Layer Stub has been enabled! This is UNSAFE! You WILL fail to produce blocks and may follow an invalid chain.", diff --git a/infrastructure/metrics/src/main/java/tech/pegasys/teku/infrastructure/metrics/MetricsConfig.java b/infrastructure/metrics/src/main/java/tech/pegasys/teku/infrastructure/metrics/MetricsConfig.java index b98dd1847fc..83d921f596f 100644 --- a/infrastructure/metrics/src/main/java/tech/pegasys/teku/infrastructure/metrics/MetricsConfig.java +++ b/infrastructure/metrics/src/main/java/tech/pegasys/teku/infrastructure/metrics/MetricsConfig.java @@ -40,8 +40,9 @@ public class MetricsConfig { public static final int DEFAULT_METRICS_PUBLICATION_INTERVAL = 60; public static final boolean DEFAULT_BLOCK_PERFORMANCE_ENABLED = true; public static final boolean DEFAULT_TICK_PERFORMANCE_ENABLED = false; - public static final boolean DEFAULT_BLOCK_PRODUCTION_PERFORMANCE_ENABLED = true; + public static final boolean DEFAULT_BLOCK_PRODUCTION_AND_PUBLISHING_PERFORMANCE_ENABLED = true; public static final int DEFAULT_BLOCK_PRODUCTION_PERFORMANCE_WARNING_THRESHOLD = 300; + public static final int DEFAULT_BLOCK_PUBLISHING_PERFORMANCE_WARNING_THRESHOLD = 1000; public static final boolean DEFAULT_BLOB_SIDECARS_STORAGE_COUNTERS_ENABLED = false; private final boolean metricsEnabled; @@ -55,8 +56,9 @@ public class MetricsConfig { private final boolean blockPerformanceEnabled; private final boolean tickPerformanceEnabled; private final boolean blobSidecarsStorageCountersEnabled; - private final boolean blockProductionPerformanceEnabled; + private final boolean blockProductionAndPublishingPerformanceEnabled; private final int blockProductionPerformanceWarningThreshold; + private final int blockPublishingPerformanceWarningThreshold; private MetricsConfig( final boolean metricsEnabled, @@ -70,8 +72,9 @@ private MetricsConfig( final boolean blockPerformanceEnabled, final boolean tickPerformanceEnabled, final boolean blobSidecarsStorageCountersEnabled, - final boolean blockProductionPerformanceEnabled, - final int blockProductionPerformanceWarningThreshold) { + final boolean blockProductionAndPublishingPerformanceEnabled, + final int blockProductionPerformanceWarningThreshold, + final int blockPublishingPerformanceWarningThreshold) { this.metricsEnabled = metricsEnabled; this.metricsPort = metricsPort; this.metricsInterface = metricsInterface; @@ -83,8 +86,10 @@ private MetricsConfig( this.blockPerformanceEnabled = blockPerformanceEnabled; this.tickPerformanceEnabled = tickPerformanceEnabled; this.blobSidecarsStorageCountersEnabled = blobSidecarsStorageCountersEnabled; - this.blockProductionPerformanceEnabled = blockProductionPerformanceEnabled; + this.blockProductionAndPublishingPerformanceEnabled = + blockProductionAndPublishingPerformanceEnabled; this.blockProductionPerformanceWarningThreshold = blockProductionPerformanceWarningThreshold; + this.blockPublishingPerformanceWarningThreshold = blockPublishingPerformanceWarningThreshold; } public static MetricsConfigBuilder builder() { @@ -127,14 +132,18 @@ public boolean isBlockPerformanceEnabled() { return blockPerformanceEnabled; } - public boolean isBlockProductionPerformanceEnabled() { - return blockProductionPerformanceEnabled; + public boolean isBlockProductionAndPublishingPerformanceEnabled() { + return blockProductionAndPublishingPerformanceEnabled; } public int getBlockProductionPerformanceWarningThreshold() { return blockProductionPerformanceWarningThreshold; } + public int getBlockPublishingPerformanceWarningThreshold() { + return blockPublishingPerformanceWarningThreshold; + } + public boolean isTickPerformanceEnabled() { return tickPerformanceEnabled; } @@ -154,10 +163,12 @@ public static final class MetricsConfigBuilder { private int metricsPublishInterval = DEFAULT_METRICS_PUBLICATION_INTERVAL; private int idleTimeoutSeconds = DEFAULT_IDLE_TIMEOUT_SECONDS; private boolean blockPerformanceEnabled = DEFAULT_BLOCK_PERFORMANCE_ENABLED; - private boolean blockProductionPerformanceEnabled = - DEFAULT_BLOCK_PRODUCTION_PERFORMANCE_ENABLED; + private boolean blockProductionAndPublishingPerformanceEnabled = + DEFAULT_BLOCK_PRODUCTION_AND_PUBLISHING_PERFORMANCE_ENABLED; private int blockProductionPerformanceWarningThreshold = DEFAULT_BLOCK_PRODUCTION_PERFORMANCE_WARNING_THRESHOLD; + private int blockPublishingPerformanceWarningThreshold = + DEFAULT_BLOCK_PUBLISHING_PERFORMANCE_WARNING_THRESHOLD; private boolean tickPerformanceEnabled = DEFAULT_TICK_PERFORMANCE_ENABLED; private boolean blobSidecarsStorageCountersEnabled = DEFAULT_BLOB_SIDECARS_STORAGE_COUNTERS_ENABLED; @@ -221,9 +232,10 @@ public MetricsConfigBuilder blockPerformanceEnabled(final boolean blockPerforman return this; } - public MetricsConfigBuilder blockProductionPerformanceEnabled( - final boolean blockProductionPerformanceEnabled) { - this.blockProductionPerformanceEnabled = blockProductionPerformanceEnabled; + public MetricsConfigBuilder blockProductionAndPublishingPerformanceEnabled( + final boolean blockProductionAndPublishingPerformanceEnabled) { + this.blockProductionAndPublishingPerformanceEnabled = + blockProductionAndPublishingPerformanceEnabled; return this; } @@ -233,6 +245,12 @@ public MetricsConfigBuilder blockProductionPerformanceWarningThreshold( return this; } + public MetricsConfigBuilder blockPublishingPerformanceWarningThreshold( + final int blockPublishingPerformanceWarningThreshold) { + this.blockPublishingPerformanceWarningThreshold = blockPublishingPerformanceWarningThreshold; + return this; + } + public MetricsConfigBuilder tickPerformanceEnabled(final boolean tickPerformanceEnabled) { this.tickPerformanceEnabled = tickPerformanceEnabled; return this; @@ -257,8 +275,9 @@ public MetricsConfig build() { blockPerformanceEnabled, tickPerformanceEnabled, blobSidecarsStorageCountersEnabled, - blockProductionPerformanceEnabled, - blockProductionPerformanceWarningThreshold); + blockProductionAndPublishingPerformanceEnabled, + blockProductionPerformanceWarningThreshold, + blockPublishingPerformanceWarningThreshold); } } } diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java index b40a16060f3..243b0e28801 100644 --- a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java @@ -18,6 +18,7 @@ import static tech.pegasys.teku.infrastructure.logging.StatusLogger.STATUS_LOG; import static tech.pegasys.teku.infrastructure.metrics.TekuMetricCategory.BEACON; import static tech.pegasys.teku.infrastructure.time.TimeUtilities.millisToSeconds; +import static tech.pegasys.teku.infrastructure.time.TimeUtilities.secondsToMillis; import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ZERO; import static tech.pegasys.teku.spec.config.SpecConfig.GENESIS_SLOT; import static tech.pegasys.teku.statetransition.attestation.AggregatingAttestationPool.DEFAULT_MAXIMUM_ATTESTATION_COUNT; @@ -55,7 +56,7 @@ import tech.pegasys.teku.ethereum.execution.types.Eth1Address; import tech.pegasys.teku.ethereum.executionclient.ExecutionClientVersionChannel; import tech.pegasys.teku.ethereum.executionclient.ExecutionClientVersionProvider; -import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformanceFactory; +import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionAndPublishingPerformanceFactory; import tech.pegasys.teku.ethereum.pow.api.Eth1EventsChannel; import tech.pegasys.teku.infrastructure.async.AsyncRunner; import tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory; @@ -924,11 +925,15 @@ public void initValidatorApiHandler() { } else { blobSidecarGossipChannel = BlobSidecarGossipChannel.NOOP; } - final BlockProductionPerformanceFactory blockProductionPerformanceFactory = - new BlockProductionPerformanceFactory( + + final BlockProductionAndPublishingPerformanceFactory blockProductionPerformanceFactory = + new BlockProductionAndPublishingPerformanceFactory( timeProvider, - beaconConfig.getMetricsConfig().isBlockProductionPerformanceEnabled(), - beaconConfig.getMetricsConfig().getBlockProductionPerformanceWarningThreshold()); + (slot) -> + secondsToMillis(spec.computeTimeAtSlot(recentChainData.getGenesisTime(), slot)), + beaconConfig.getMetricsConfig().isBlockProductionAndPublishingPerformanceEnabled(), + beaconConfig.getMetricsConfig().getBlockProductionPerformanceWarningThreshold(), + beaconConfig.getMetricsConfig().getBlockPublishingPerformanceWarningThreshold()); final ValidatorApiHandler validatorApiHandler = new ValidatorApiHandler( diff --git a/teku/src/main/java/tech/pegasys/teku/cli/options/MetricsOptions.java b/teku/src/main/java/tech/pegasys/teku/cli/options/MetricsOptions.java index a9954f16286..16c3fbafcf7 100644 --- a/teku/src/main/java/tech/pegasys/teku/cli/options/MetricsOptions.java +++ b/teku/src/main/java/tech/pegasys/teku/cli/options/MetricsOptions.java @@ -113,11 +113,12 @@ public class MetricsOptions { hidden = true, showDefaultValue = Visibility.ALWAYS, paramLabel = "", - description = "Whether block production timing metrics are tracked and reported", + description = + "Whether block production and publishing timing metrics are tracked and reported", fallbackValue = "true", arity = "0..1") - private boolean blockProductionPerformanceEnabled = - MetricsConfig.DEFAULT_BLOCK_PRODUCTION_PERFORMANCE_ENABLED; + private boolean blockProductionAndPublishingPerformanceEnabled = + MetricsConfig.DEFAULT_BLOCK_PRODUCTION_AND_PUBLISHING_PERFORMANCE_ENABLED; @Option( names = {"--Xmetrics-block-production-timing-tracking-warning-threshold"}, @@ -131,6 +132,18 @@ public class MetricsOptions { private int blockProductionPerformanceWarningThreshold = MetricsConfig.DEFAULT_BLOCK_PRODUCTION_PERFORMANCE_WARNING_THRESHOLD; + @Option( + names = {"--Xmetrics-block-publishing-timing-tracking-warning-threshold"}, + hidden = true, + showDefaultValue = Visibility.ALWAYS, + paramLabel = "", + description = + "The time (in ms) at which block publishing is to be considered 'slow'. If set to 100, block publishing taking at least 100ms would raise a warning.", + fallbackValue = "true", + arity = "0..1") + private int blockPublishingPerformanceWarningThreshold = + MetricsConfig.DEFAULT_BLOCK_PUBLISHING_PERFORMANCE_WARNING_THRESHOLD; + @Option( names = {"--Xmetrics-blob-sidecars-storage-enabled"}, hidden = true, @@ -156,9 +169,12 @@ public void configure(TekuConfiguration.Builder builder) { .blockPerformanceEnabled(blockPerformanceEnabled) .tickPerformanceEnabled(tickPerformanceEnabled) .blobSidecarsStorageCountersEnabled(blobSidecarsStorageCountersEnabled) - .blockProductionPerformanceEnabled(blockProductionPerformanceEnabled) + .blockProductionAndPublishingPerformanceEnabled( + blockProductionAndPublishingPerformanceEnabled) .blockProductionPerformanceWarningThreshold( - blockProductionPerformanceWarningThreshold)); + blockProductionPerformanceWarningThreshold) + .blockPublishingPerformanceWarningThreshold( + blockPublishingPerformanceWarningThreshold)); } private URL parseMetricsEndpointUrl() {