diff --git a/build.gradle b/build.gradle index 54e9e617ee6..3259b94673c 100644 --- a/build.gradle +++ b/build.gradle @@ -214,7 +214,7 @@ allprojects { options.errorprone { enabled = !'true'.equalsIgnoreCase(System.getProperty('avt.disableErrorProne')) - disableWarningsInGeneratedCode + disableWarningsInGeneratedCode = true // Our equals need to be symmetric, this checker doesn't respect that check('EqualsGetClass', net.ltgt.gradle.errorprone.CheckSeverity.OFF) diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/OperationsReOrgManager.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/OperationsReOrgManager.java index ab604876478..eb2d37add6e 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/OperationsReOrgManager.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/OperationsReOrgManager.java @@ -43,7 +43,7 @@ public class OperationsReOrgManager implements ChainHeadChannel { private final OperationPool attesterSlashingPool; private final AttestationManager attestationManager; private final AggregatingAttestationPool attestationPool; - private final MappedOperationPool blsToExecutionOperationPool; + private final OperationPool blsToExecutionOperationPool; private final RecentChainData recentChainData; public OperationsReOrgManager( @@ -52,7 +52,7 @@ public OperationsReOrgManager( final OperationPool exitPool, final AggregatingAttestationPool attestationPool, final AttestationManager attestationManager, - final MappedOperationPool blsToExecutionOperationPool, + final OperationPool blsToExecutionOperationPool, final RecentChainData recentChainData) { this.exitPool = exitPool; this.proposerSlashingPool = proposerSlashingPool; diff --git a/infrastructure/events/src/main/java/tech/pegasys/teku/infrastructure/events/EventChannelSubscriber.java b/infrastructure/events/src/main/java/tech/pegasys/teku/infrastructure/events/EventChannelSubscriber.java new file mode 100644 index 00000000000..8242c02def2 --- /dev/null +++ b/infrastructure/events/src/main/java/tech/pegasys/teku/infrastructure/events/EventChannelSubscriber.java @@ -0,0 +1,19 @@ +/* + * 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.infrastructure.events; + +public interface EventChannelSubscriber { + + void subscribe(T listener); +} diff --git a/infrastructure/events/src/main/java/tech/pegasys/teku/infrastructure/events/EventChannels.java b/infrastructure/events/src/main/java/tech/pegasys/teku/infrastructure/events/EventChannels.java index 74f210f1a18..5306b9f6842 100644 --- a/infrastructure/events/src/main/java/tech/pegasys/teku/infrastructure/events/EventChannels.java +++ b/infrastructure/events/src/main/java/tech/pegasys/teku/infrastructure/events/EventChannels.java @@ -84,6 +84,11 @@ public EventChannels subscribe( return subscribeMultithreaded(channelInterface, subscriber, 1); } + public EventChannelSubscriber createSubscriber( + final Class channelInterface) { + return createSubscriberMultithreaded(channelInterface, 1); + } + /** * Adds a subscriber to this channel where events are handled by multiple threads concurrently. * @@ -99,10 +104,16 @@ public EventChannels subscribe( */ public EventChannels subscribeMultithreaded( final Class channelInterface, final T subscriber, final int requestedParallelism) { - getChannel(channelInterface).subscribeMultithreaded(subscriber, requestedParallelism); + createSubscriberMultithreaded(channelInterface, requestedParallelism).subscribe(subscriber); return this; } + public EventChannelSubscriber createSubscriberMultithreaded( + final Class channelInterface, final int requestedParallelism) { + return listener -> + getChannel(channelInterface).subscribeMultithreaded(listener, requestedParallelism); + } + @SuppressWarnings("unchecked") private EventChannel getChannel(final Class channelInterface) { return (EventChannel) channels.computeIfAbsent(channelInterface, eventChannelFactory); diff --git a/services/beaconchain/build.gradle b/services/beaconchain/build.gradle index fc903a0a4ff..f75ae47c03f 100644 --- a/services/beaconchain/build.gradle +++ b/services/beaconchain/build.gradle @@ -34,6 +34,9 @@ dependencies { implementation project(':validator:api') implementation project(':validator:client') + implementation 'com.google.dagger:dagger:2.51.1' + annotationProcessor 'com.google.dagger:dagger-compiler:2.51.1' + testImplementation testFixtures(project(':storage')) testImplementation testFixtures(project(':ethereum:spec')) testImplementation testFixtures(project(':ethereum:statetransition')) @@ -43,4 +46,12 @@ dependencies { testImplementation testFixtures(project(':infrastructure:metrics')) implementation 'io.libp2p:jvm-libp2p' +} + +tasks.withType(JavaCompile) { + options.compilerArgs += [ + '-Adagger.formatGeneratedSource=enabled', + '-Adagger.fullBindingGraphValidation=ERROR', + '-Adagger.ignoreProvisionKeyWildcards=ENABLED' + ] } \ No newline at end of file diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainControllerFacade.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainControllerFacade.java index 5c2b66d009f..eb261eed17c 100644 --- a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainControllerFacade.java +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainControllerFacade.java @@ -19,6 +19,7 @@ import tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory; import tech.pegasys.teku.infrastructure.time.TimeProvider; import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.service.serviceutils.ServiceFacade; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.statetransition.forkchoice.ForkChoice; import tech.pegasys.teku.statetransition.validation.signatures.SignatureVerificationService; @@ -29,7 +30,7 @@ * CAUTION: this API is unstable and primarily intended for debugging and testing purposes this API * might be changed in any version in backward incompatible way */ -public interface BeaconChainControllerFacade { +public interface BeaconChainControllerFacade extends ServiceFacade { Spec getSpec(); diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainControllerFactory.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainControllerFactory.java index 95dc4ba2115..5506f838b7c 100644 --- a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainControllerFactory.java +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainControllerFactory.java @@ -14,6 +14,8 @@ package tech.pegasys.teku.services.beaconchain; import tech.pegasys.teku.service.serviceutils.ServiceConfig; +import tech.pegasys.teku.services.beaconchain.init.DaggerBeaconChainControllerComponent; +import tech.pegasys.teku.services.beaconchain.init.ExternalDependenciesModule; /** * CAUTION: this API is unstable and primarily intended for debugging and testing purposes this API @@ -21,8 +23,17 @@ */ public interface BeaconChainControllerFactory { - BeaconChainControllerFactory DEFAULT = BeaconChainController::new; + BeaconChainControllerFactory DEFAULT = + LateInitDelegateBeaconChainController.createLateInitFactory( + (serviceConfig, beaconConfig) -> + DaggerBeaconChainControllerComponent.builder() + .externalDependenciesModule( + new ExternalDependenciesModule(serviceConfig, beaconConfig)) + .build() + .beaconChainController()); - BeaconChainController create( + BeaconChainControllerFactory OLD = BeaconChainControllerOld::new; + + BeaconChainControllerFacade create( final ServiceConfig serviceConfig, final BeaconChainConfiguration beaconConfig); } 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/BeaconChainControllerOld.java similarity index 99% rename from services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java rename to services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainControllerOld.java index 1782ea9a90d..77bc63222bc 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/BeaconChainControllerOld.java @@ -216,7 +216,7 @@ * initialization behavior (see {@link BeaconChainControllerFactory}} however this class may change * in a backward incompatible manner and either break compilation or runtime behavior */ -public class BeaconChainController extends Service implements BeaconChainControllerFacade { +public class BeaconChainControllerOld extends Service implements BeaconChainControllerFacade { private static final Logger LOG = LogManager.getLogger(); @@ -292,7 +292,7 @@ public class BeaconChainController extends Service implements BeaconChainControl protected IntSupplier rejectedExecutionCountSupplier; protected DebugDataDumper debugDataDumper; - public BeaconChainController( + public BeaconChainControllerOld( final ServiceConfig serviceConfig, final BeaconChainConfiguration beaconConfig) { final Eth2NetworkConfiguration eth2NetworkConfig = beaconConfig.eth2NetworkConfig(); final DataDirLayout dataDirLayout = serviceConfig.getDataDirLayout(); @@ -406,11 +406,13 @@ protected SafeFuture doStop() { } protected SafeFuture initialize() { + + timerService = new TimerService(this::onTick); + final StoreConfig storeConfig = beaconConfig.storeConfig(); coalescingChainHeadChannel = new CoalescingChainHeadChannel( eventChannels.getPublisher(ChainHeadChannel.class), EVENT_LOG); - timerService = new TimerService(this::onTick); final CombinedStorageChannel combinedStorageChannel = eventChannels.getPublisher(CombinedStorageChannel.class, beaconAsyncRunner); @@ -889,7 +891,8 @@ protected void initSubnetSubscriber() { public void initExecutionLayerBlockProductionManager() { LOG.debug("BeaconChainController.initExecutionLayerBlockProductionManager()"); this.executionLayerBlockProductionManager = - ExecutionLayerBlockManagerFactory.create(executionLayer, eventChannels); + ExecutionLayerBlockManagerFactory.create( + executionLayer, eventChannels.createSubscriber(SlotEventsChannel.class)); } public void initRewardCalculator() { diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainService.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainService.java index fd50babc30f..e8fd3a98da7 100644 --- a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainService.java +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainService.java @@ -19,7 +19,7 @@ public class BeaconChainService extends Service implements BeaconChainServiceFacade { - private final BeaconChainController controller; + private final BeaconChainControllerFacade controller; public BeaconChainService( final ServiceConfig serviceConfig, final BeaconChainConfiguration beaconConfig) { @@ -38,7 +38,7 @@ protected SafeFuture doStop() { } @Override - public BeaconChainController getBeaconChainController() { + public BeaconChainControllerFacade getBeaconChainController() { return controller; } } diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/LateInitDelegateBeaconChainController.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/LateInitDelegateBeaconChainController.java new file mode 100644 index 00000000000..c4c2f3caa24 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/LateInitDelegateBeaconChainController.java @@ -0,0 +1,124 @@ +/* + * Copyright Consensys Software Inc., 2022 + * + * 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.services.beaconchain; + +import java.util.Optional; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import tech.pegasys.teku.beacon.sync.SyncService; +import tech.pegasys.teku.beaconrestapi.BeaconRestApi; +import tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.service.serviceutils.Service; +import tech.pegasys.teku.service.serviceutils.ServiceConfig; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoice; +import tech.pegasys.teku.statetransition.validation.signatures.SignatureVerificationService; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.client.RecentChainData; + +public class LateInitDelegateBeaconChainController extends Service + implements BeaconChainControllerFacade { + + public static BeaconChainControllerFactory createLateInitFactory( + final BeaconChainControllerFactory delegateFactory) { + return (serviceConfig, beaconConfig) -> + new LateInitDelegateBeaconChainController(serviceConfig, beaconConfig, delegateFactory); + } + + private static final Logger LOG = LogManager.getLogger(); + + private final ServiceConfig serviceConfig; + private final BeaconChainConfiguration beaconConfig; + private final BeaconChainControllerFactory delegateFactory; + + private volatile BeaconChainControllerFacade delegate; + + public LateInitDelegateBeaconChainController( + final ServiceConfig serviceConfig, + final BeaconChainConfiguration beaconConfig, + final BeaconChainControllerFactory delegateFactory) { + this.serviceConfig = serviceConfig; + this.beaconConfig = beaconConfig; + this.delegateFactory = delegateFactory; + } + + @Override + protected SafeFuture doStart() { + LOG.info("Starting BeaconChainController..."); + this.delegate = delegateFactory.create(serviceConfig, beaconConfig); + + SafeFuture startFuture = this.delegate.start(); + LOG.info("BeaconChainController start complete"); + + return startFuture; + } + + @Override + protected SafeFuture doStop() { + return this.delegate.stop(); + } + + @Override + public Spec getSpec() { + return delegate.getSpec(); + } + + @Override + public TimeProvider getTimeProvider() { + return delegate.getTimeProvider(); + } + + @Override + public AsyncRunnerFactory getAsyncRunnerFactory() { + return delegate.getAsyncRunnerFactory(); + } + + @Override + public SignatureVerificationService getSignatureVerificationService() { + return delegate.getSignatureVerificationService(); + } + + @Override + public RecentChainData getRecentChainData() { + return delegate.getRecentChainData(); + } + + @Override + public CombinedChainDataClient getCombinedChainDataClient() { + return delegate.getCombinedChainDataClient(); + } + + @Override + public Eth2P2PNetwork getP2pNetwork() { + return delegate.getP2pNetwork(); + } + + @Override + public Optional getBeaconRestAPI() { + return delegate.getBeaconRestAPI(); + } + + @Override + public SyncService getSyncService() { + return delegate.getSyncService(); + } + + @Override + public ForkChoice getForkChoice() { + return delegate.getForkChoice(); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/AsyncRunnerModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/AsyncRunnerModule.java new file mode 100644 index 00000000000..f4836f7f929 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/AsyncRunnerModule.java @@ -0,0 +1,102 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import javax.inject.Qualifier; +import javax.inject.Singleton; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory; +import tech.pegasys.teku.infrastructure.async.eventthread.AsyncRunnerEventThread; +import tech.pegasys.teku.networks.Eth2NetworkConfiguration; + +@Module +public interface AsyncRunnerModule { + + @Qualifier + @interface BeaconAsyncRunner {} + + @Qualifier + @interface EventAsyncRunner {} + + @Qualifier + @interface NetworkAsyncRunner {} + + @Qualifier + @interface OperationPoolAsyncRunner {} + + @Qualifier + @interface ForkChoiceExecutor {} + + @Qualifier + @interface ForkChoiceNotifierExecutor {} + + @Provides + @Singleton + @BeaconAsyncRunner + static AsyncRunner beaconAsyncRunner( + final AsyncRunnerFactory asyncRunnerFactory, + final Eth2NetworkConfiguration eth2NetworkConfig) { + return asyncRunnerFactory.create( + "beaconchain", + eth2NetworkConfig.getAsyncBeaconChainMaxThreads(), + eth2NetworkConfig.getAsyncBeaconChainMaxQueue()); + } + + @Provides + @Singleton + @NetworkAsyncRunner + static AsyncRunner networkAsyncRunner( + final AsyncRunnerFactory asyncRunnerFactory, + final Eth2NetworkConfiguration eth2NetworkConfig) { + return asyncRunnerFactory.create( + "p2p", eth2NetworkConfig.getAsyncP2pMaxThreads(), eth2NetworkConfig.getAsyncP2pMaxQueue()); + } + + @Provides + @Singleton + @EventAsyncRunner + static AsyncRunner eventAsyncRunner(final AsyncRunnerFactory asyncRunnerFactory) { + return asyncRunnerFactory.create("events", 10); + } + + @Provides + @Singleton + @OperationPoolAsyncRunner + static AsyncRunner operationPoolAsyncRunner(final AsyncRunnerFactory asyncRunnerFactory) { + return asyncRunnerFactory.create("operationPoolUpdater", 1); + } + + @Provides + @Singleton + @ForkChoiceExecutor + static AsyncRunnerEventThread forkChoiceExecutor(final AsyncRunnerFactory asyncRunnerFactory) { + AsyncRunnerEventThread forkChoiceExecutor = + new AsyncRunnerEventThread("forkchoice", asyncRunnerFactory); + forkChoiceExecutor.start(); + return forkChoiceExecutor; + } + + @Provides + @Singleton + @ForkChoiceNotifierExecutor + static AsyncRunnerEventThread forkChoiceNotifierExecutor( + final AsyncRunnerFactory asyncRunnerFactory) { + AsyncRunnerEventThread forkChoiceNotifierExecutor = + new AsyncRunnerEventThread("forkChoiceNotifier", asyncRunnerFactory); + forkChoiceNotifierExecutor.start(); + return forkChoiceNotifierExecutor; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BeaconChainControllerComponent.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BeaconChainControllerComponent.java new file mode 100644 index 00000000000..62cd58d56eb --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BeaconChainControllerComponent.java @@ -0,0 +1,50 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Component; +import javax.inject.Singleton; +import tech.pegasys.teku.services.beaconchain.BeaconChainControllerFacade; + +@Singleton +@Component( + modules = { + AsyncRunnerModule.class, + BeaconConfigModule.class, + BeaconModule.class, + BlobModule.class, + ChannelsModule.class, + CryptoModule.class, + DataProviderModule.class, + ExternalDependenciesModule.class, + ForkChoiceModule.class, + LoggingModule.class, + MainModule.class, + MetricsModule.class, + NetworkModule.class, + PoolAndCachesModule.class, + PowModule.class, + ServiceConfigModule.class, + SpecModule.class, + StorageModule.class, + SubnetsModule.class, + SyncModule.class, + ValidatorModule.class, + VerifyModule.class, + WSModule.class + }) +public interface BeaconChainControllerComponent { + + BeaconChainControllerFacade beaconChainController(); +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BeaconConfigModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BeaconConfigModule.java new file mode 100644 index 00000000000..0bab99a3fbc --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BeaconConfigModule.java @@ -0,0 +1,82 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import tech.pegasys.teku.beacon.sync.SyncConfig; +import tech.pegasys.teku.beaconrestapi.BeaconRestApiConfig; +import tech.pegasys.teku.infrastructure.metrics.MetricsConfig; +import tech.pegasys.teku.networking.eth2.P2PConfig; +import tech.pegasys.teku.networks.Eth2NetworkConfiguration; +import tech.pegasys.teku.services.beaconchain.BeaconChainConfiguration; +import tech.pegasys.teku.services.powchain.PowchainConfiguration; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.storage.store.StoreConfig; +import tech.pegasys.teku.validator.api.ValidatorConfig; +import tech.pegasys.teku.weaksubjectivity.config.WeakSubjectivityConfig; + +@Module +public interface BeaconConfigModule { + + @Provides + static Spec spec(final BeaconChainConfiguration config) { + return config.getSpec(); + } + + @Provides + static Eth2NetworkConfiguration eth2NetworkConfig(final BeaconChainConfiguration config) { + return config.eth2NetworkConfig(); + } + + @Provides + static StoreConfig storeConfig(final BeaconChainConfiguration config) { + return config.storeConfig(); + } + + @Provides + static PowchainConfiguration powchainConfig(final BeaconChainConfiguration config) { + return config.powchainConfig(); + } + + @Provides + static P2PConfig p2pConfig(final BeaconChainConfiguration config) { + return config.p2pConfig(); + } + + @Provides + static ValidatorConfig validatorConfig(final BeaconChainConfiguration config) { + return config.validatorConfig(); + } + + @Provides + static SyncConfig syncConfig(final BeaconChainConfiguration config) { + return config.syncConfig(); + } + + @Provides + static BeaconRestApiConfig beaconRestApiConfig(final BeaconChainConfiguration config) { + return config.beaconRestApiConfig(); + } + + @Provides + static WeakSubjectivityConfig weakSubjectivityConfig(final BeaconChainConfiguration config) { + return config.weakSubjectivity(); + } + + @Provides + static MetricsConfig metricsConfig(final BeaconChainConfiguration config) { + return config.getMetricsConfig(); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BeaconModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BeaconModule.java new file mode 100644 index 00000000000..8ff97bb8ebf --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BeaconModule.java @@ -0,0 +1,291 @@ +/* + * 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.services.beaconchain.init; + +import static tech.pegasys.teku.infrastructure.time.TimeUtilities.millisToSeconds; + +import dagger.Module; +import dagger.Provides; +import java.util.Map; +import java.util.Optional; +import javax.inject.Singleton; +import org.apache.tuweni.bytes.Bytes32; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.beacon.sync.SyncService; +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.ethereum.execution.types.Eth1Address; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.async.eventthread.AsyncRunnerEventThread; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.infrastructure.logging.EventLogger; +import tech.pegasys.teku.infrastructure.logging.StatusLogger; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.networks.Eth2NetworkConfiguration; +import tech.pegasys.teku.services.beaconchain.SlotProcessor; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.BeaconAsyncRunner; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.ForkChoiceNotifierExecutor; +import tech.pegasys.teku.services.beaconchain.init.MetricsModule.TickProcessingPerformanceRecordFactory; +import tech.pegasys.teku.services.beaconchain.init.PoolAndCachesModule.InvalidBlockRoots; +import tech.pegasys.teku.services.beaconchain.init.PowModule.ProposerDefaultFeeRecipient; +import tech.pegasys.teku.services.timer.TimerService; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; +import tech.pegasys.teku.spec.datastructures.operations.ProposerSlashing; +import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; +import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; +import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel; +import tech.pegasys.teku.spec.logic.common.statetransition.results.BlockImportResult; +import tech.pegasys.teku.statetransition.EpochCachePrimer; +import tech.pegasys.teku.statetransition.OperationPool; +import tech.pegasys.teku.statetransition.OperationsReOrgManager; +import tech.pegasys.teku.statetransition.attestation.AggregatingAttestationPool; +import tech.pegasys.teku.statetransition.attestation.AttestationManager; +import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTrackersPool; +import tech.pegasys.teku.statetransition.block.BlockImportChannel; +import tech.pegasys.teku.statetransition.block.BlockImportMetrics; +import tech.pegasys.teku.statetransition.block.BlockImporter; +import tech.pegasys.teku.statetransition.block.BlockManager; +import tech.pegasys.teku.statetransition.block.ReceivedBlockEventsChannel; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoice; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceNotifier; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceTrigger; +import tech.pegasys.teku.statetransition.forkchoice.ProposersDataManager; +import tech.pegasys.teku.statetransition.forkchoice.TickProcessingPerformance; +import tech.pegasys.teku.statetransition.forkchoice.TickProcessor; +import tech.pegasys.teku.statetransition.util.FutureItems; +import tech.pegasys.teku.statetransition.util.PendingPool; +import tech.pegasys.teku.statetransition.validation.BlockValidator; +import tech.pegasys.teku.storage.api.ChainHeadChannel; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.weaksubjectivity.WeakSubjectivityValidator; + +@Module +public interface BeaconModule { + + @FunctionalInterface + interface TickHandler { + void onTick(); + } + + @FunctionalInterface + interface GenesisTimeTracker { + void update(); + } + + @Provides + @Singleton + static EpochCachePrimer epochCachePrimer( + final Spec spec, + final RecentChainData recentChainData, + @BeaconAsyncRunner final AsyncRunner beaconAsyncRunner) { + return new EpochCachePrimer(spec, recentChainData, beaconAsyncRunner); + } + + @Provides + @Singleton + static SlotProcessor slotProcessor( + final Spec spec, + final RecentChainData recentChainData, + final SyncService syncService, + final ForkChoiceTrigger forkChoiceTrigger, + final ForkChoiceNotifier forkChoiceNotifier, + final Eth2P2PNetwork p2pNetwork, + final SlotEventsChannel slotEventsChannelPublisher, + final EpochCachePrimer epochCachePrimer) { + return new SlotProcessor( + spec, + recentChainData, + syncService, + forkChoiceTrigger, + forkChoiceNotifier, + p2pNetwork, + slotEventsChannelPublisher, + epochCachePrimer); + } + + @Provides + @Singleton + static BlockImporter blockImporter( + final Spec spec, + final RecentChainData recentChainData, + final ReceivedBlockEventsChannel receivedBlockEventsChannelPublisher, + final ForkChoice forkChoice, + final WeakSubjectivityValidator weakSubjectivityValidator, + final ExecutionLayerChannel executionLayer) { + return new BlockImporter( + spec, + receivedBlockEventsChannelPublisher, + recentChainData, + forkChoice, + weakSubjectivityValidator, + executionLayer); + } + + @Provides + @Singleton + static BlockManager blockManager( + final EventLogger eventLogger, + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final BlockValidator blockValidator, + final BlockImporter blockImporter, + final BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool, + final PendingPool pendingBlocks, + @InvalidBlockRoots final Map invalidBlockRoots, + final Optional blockImportMetrics, + final FutureItems futureBlocks, + final EventChannelSubscriber slotEventsChannelSubscriber, + final EventChannelSubscriber blockImportChannelSubscriber, + final EventChannelSubscriber + receivedBlockEventsChannelSubscriber) { + + BlockManager blockManager = + new BlockManager( + recentChainData, + blockImporter, + blockBlobSidecarsTrackersPool, + pendingBlocks, + futureBlocks, + invalidBlockRoots, + blockValidator, + timeProvider, + eventLogger, + blockImportMetrics); + + slotEventsChannelSubscriber.subscribe(blockManager); + blockImportChannelSubscriber.subscribe(blockManager); + receivedBlockEventsChannelSubscriber.subscribe(blockManager); + + return blockManager; + } + + @Provides + @Singleton + static ProposersDataManager proposersDataManager( + final Eth2NetworkConfiguration eth2NetworkConfig, + final Spec spec, + final MetricsSystem metricsSystem, + @ForkChoiceNotifierExecutor final AsyncRunnerEventThread forkChoiceNotifierExecutor, + final ExecutionLayerChannel executionLayer, + final RecentChainData recentChainData, + final EventChannelSubscriber slotEventsChannelSubscriber, + @ProposerDefaultFeeRecipient final Optional proposerDefaultFeeRecipient) { + + ProposersDataManager proposersDataManager = + new ProposersDataManager( + forkChoiceNotifierExecutor, + spec, + metricsSystem, + executionLayer, + recentChainData, + proposerDefaultFeeRecipient, + eth2NetworkConfig.isForkChoiceUpdatedAlwaysSendPayloadAttributes()); + slotEventsChannelSubscriber.subscribe(proposersDataManager); + return proposersDataManager; + } + + @Provides + @Singleton + static OperationsReOrgManager operationsReOrgManager( + final OperationPool attesterSlashingPool, + final OperationPool proposerSlashingPool, + final OperationPool voluntaryExitPool, + final AggregatingAttestationPool attestationPool, + final AttestationManager attestationManager, + final OperationPool blsToExecutionChangePool, + final RecentChainData recentChainData, + final EventChannelSubscriber chainHeadChannelSubscriber) { + + OperationsReOrgManager operationsReOrgManager = + new OperationsReOrgManager( + proposerSlashingPool, + attesterSlashingPool, + voluntaryExitPool, + attestationPool, + attestationManager, + blsToExecutionChangePool, + recentChainData); + chainHeadChannelSubscriber.subscribe(operationsReOrgManager); + return operationsReOrgManager; + } + + @Provides + @Singleton + static TickHandler tickHandler( + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final ForkChoice forkChoice, + final SlotProcessor slotProcessor, + final TickProcessingPerformanceRecordFactory tickProcessingPerformanceRecordFactory, + final GenesisTimeTracker genesisTimeTracker) { + return () -> { + if (recentChainData.isPreGenesis()) { + return; + } + + final UInt64 currentTimeMillis = timeProvider.getTimeInMillis(); + final Optional performanceRecord = + tickProcessingPerformanceRecordFactory.create(); + + forkChoice.onTick(currentTimeMillis, performanceRecord); + + genesisTimeTracker.update(); + + slotProcessor.onTick(currentTimeMillis, performanceRecord); + performanceRecord.ifPresent(TickProcessingPerformance::complete); + }; + } + + @Provides + @Singleton + static TimerService timerService(final TickHandler tickHandler) { + return new TimerService(tickHandler::onTick); + } + + @Provides + @Singleton + static GenesisTimeTracker genesisTimeTracker( + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final Eth2P2PNetwork p2pNetwork, + final StatusLogger statusLogger) { + return new GenesisTimeTracker() { + private UInt64 lastUpdateTime = UInt64.ZERO; + + @Override + public void update() { + final UInt64 genesisTime = recentChainData.getGenesisTime(); + final UInt64 currentTimeMillis = timeProvider.getTimeInMillis(); + final UInt64 currentTimeSeconds = millisToSeconds(currentTimeMillis); + if (genesisTime.isGreaterThan(currentTimeSeconds)) { + // notify every 10 minutes + if (lastUpdateTime.plus(600L).isLessThanOrEqualTo(currentTimeSeconds)) { + lastUpdateTime = currentTimeSeconds; + statusLogger.timeUntilGenesis( + genesisTime.minus(currentTimeSeconds).longValue(), p2pNetwork.getPeerCount()); + } + } + } + }; + } + + @Provides + @Singleton + static TickProcessor tickProcessor(final Spec spec, final RecentChainData recentChainData) { + return new TickProcessor(spec, recentChainData); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BlobModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BlobModule.java new file mode 100644 index 00000000000..ba2e2314353 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/BlobModule.java @@ -0,0 +1,88 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import java.util.Map; +import javax.inject.Singleton; +import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.kzg.KZG; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.BeaconAsyncRunner; +import tech.pegasys.teku.services.beaconchain.init.PoolAndCachesModule.InvalidBlobSidecarRoots; +import tech.pegasys.teku.services.beaconchain.init.PoolAndCachesModule.InvalidBlockRoots; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; +import tech.pegasys.teku.spec.logic.common.statetransition.results.BlockImportResult; +import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb; +import tech.pegasys.teku.statetransition.blobs.BlobSidecarManager; +import tech.pegasys.teku.statetransition.blobs.BlobSidecarManagerImpl; +import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTrackersPool; +import tech.pegasys.teku.statetransition.util.FutureItems; +import tech.pegasys.teku.statetransition.validation.BlobSidecarGossipValidator; +import tech.pegasys.teku.statetransition.validation.GossipValidationHelper; +import tech.pegasys.teku.statetransition.validation.InternalValidationResult; +import tech.pegasys.teku.storage.client.RecentChainData; + +@Module +public interface BlobModule { + + @Provides + @Singleton + static BlobSidecarGossipValidator blobSidecarGossipValidator( + final Spec spec, + final KZG kzg, + @InvalidBlockRoots final Map invalidBlockRoots, + final GossipValidationHelper gossipValidationHelper) { + final MiscHelpersDeneb miscHelpers = + MiscHelpersDeneb.required(spec.forMilestone(SpecMilestone.DENEB).miscHelpers()); + return BlobSidecarGossipValidator.create( + spec, invalidBlockRoots, gossipValidationHelper, miscHelpers, kzg); + } + + @Provides + @Singleton + static BlobSidecarManager blobSidecarManager( + final Spec spec, + final KZG kzg, + @BeaconAsyncRunner final AsyncRunner beaconAsyncRunner, + final RecentChainData recentChainData, + final EventChannelSubscriber slotEventsChannelSubscriber, + final BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool, + final BlobSidecarGossipValidator blobSidecarGossipValidator, + @InvalidBlobSidecarRoots final Map invalidBlobSidecarRoots, + final FutureItems futureBlobSidecars) { + if (spec.isMilestoneSupported(SpecMilestone.DENEB)) { + final BlobSidecarManagerImpl blobSidecarManagerImpl = + new BlobSidecarManagerImpl( + spec, + beaconAsyncRunner, + recentChainData, + blockBlobSidecarsTrackersPool, + blobSidecarGossipValidator, + kzg, + futureBlobSidecars, + invalidBlobSidecarRoots); + slotEventsChannelSubscriber.subscribe(blobSidecarManagerImpl); + + return blobSidecarManagerImpl; + } else { + return BlobSidecarManager.NOOP; + } + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ChannelsModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ChannelsModule.java new file mode 100644 index 00000000000..22a56557629 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ChannelsModule.java @@ -0,0 +1,192 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import tech.pegasys.teku.beaconrestapi.BeaconRestApiConfig; +import tech.pegasys.teku.ethereum.events.ExecutionClientEventsChannel; +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.ethereum.executionclient.ExecutionClientVersionChannel; +import tech.pegasys.teku.ethereum.pow.api.Eth1EventsChannel; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.infrastructure.events.EventChannels; +import tech.pegasys.teku.networking.eth2.gossip.BlobSidecarGossipChannel; +import tech.pegasys.teku.networking.eth2.gossip.BlockGossipChannel; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.BeaconAsyncRunner; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel; +import tech.pegasys.teku.statetransition.block.BlockImportChannel; +import tech.pegasys.teku.statetransition.block.ReceivedBlockEventsChannel; +import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorChannel; +import tech.pegasys.teku.storage.api.ChainHeadChannel; +import tech.pegasys.teku.storage.api.CombinedStorageChannel; +import tech.pegasys.teku.storage.api.Eth1DepositStorageChannel; +import tech.pegasys.teku.storage.api.FinalizedCheckpointChannel; +import tech.pegasys.teku.storage.api.VoteUpdateChannel; +import tech.pegasys.teku.validator.api.ValidatorApiChannel; +import tech.pegasys.teku.validator.api.ValidatorTimingChannel; + +@Module +public interface ChannelsModule { + + // Publishers + + @Provides + static SlotEventsChannel slotEventsChannel(final EventChannels eventChannels) { + return eventChannels.getPublisher(SlotEventsChannel.class); + } + + @Provides + static ExecutionLayerChannel executionLayerChannel( + final EventChannels eventChannels, @BeaconAsyncRunner final AsyncRunner asyncRunner) { + return eventChannels.getPublisher(ExecutionLayerChannel.class, asyncRunner); + } + + @Provides + static BlockImportChannel blockImportChannel( + final EventChannels eventChannels, @BeaconAsyncRunner final AsyncRunner asyncRunner) { + return eventChannels.getPublisher(BlockImportChannel.class, asyncRunner); + } + + @Provides + static CombinedStorageChannel combinedStorageChannel( + final EventChannels eventChannels, @BeaconAsyncRunner final AsyncRunner asyncRunner) { + return eventChannels.getPublisher(CombinedStorageChannel.class, asyncRunner); + } + + @Provides + static ValidatorApiChannel validatorApiChannel( + final EventChannels eventChannels, @BeaconAsyncRunner final AsyncRunner asyncRunner) { + return eventChannels.getPublisher(ValidatorApiChannel.class, asyncRunner); + } + + @Provides + static ActiveValidatorChannel activeValidatorChannel( + final EventChannels eventChannels, @BeaconAsyncRunner final AsyncRunner asyncRunner) { + return eventChannels.getPublisher(ActiveValidatorChannel.class, asyncRunner); + } + + @Provides + static Eth1DepositStorageChannel eth1DepositStorageChannel( + final EventChannels eventChannels, @BeaconAsyncRunner final AsyncRunner asyncRunner) { + return eventChannels.getPublisher(Eth1DepositStorageChannel.class, asyncRunner); + } + + @Provides + static ExecutionClientVersionChannel executionClientVersionChannel( + final EventChannels eventChannels) { + return eventChannels.getPublisher(ExecutionClientVersionChannel.class); + } + + @Provides + static BlockGossipChannel blockGossipChannel(final EventChannels eventChannels) { + return eventChannels.getPublisher(BlockGossipChannel.class); + } + + @Provides + static BlobSidecarGossipChannel blobSidecarGossipChannel( + final EventChannels eventChannels, final Spec spec) { + if (spec.isMilestoneSupported(SpecMilestone.DENEB)) { + return eventChannels.getPublisher(BlobSidecarGossipChannel.class); + } else { + return BlobSidecarGossipChannel.NOOP; + } + } + + @Provides + static ReceivedBlockEventsChannel receivedBlockEventsChannel(final EventChannels eventChannels) { + return eventChannels.getPublisher(ReceivedBlockEventsChannel.class); + } + + @Provides + static ChainHeadChannel chainHeadChannel(final EventChannels eventChannels) { + return eventChannels.getPublisher(ChainHeadChannel.class); + } + + @Provides + static VoteUpdateChannel voteUpdateChannel(final EventChannels eventChannels) { + return eventChannels.getPublisher(VoteUpdateChannel.class); + } + + @Provides + static FinalizedCheckpointChannel finalizedCheckpointChannel( + final EventChannels eventChannels, @BeaconAsyncRunner final AsyncRunner asyncRunner) { + return eventChannels.getPublisher(FinalizedCheckpointChannel.class, asyncRunner); + } + + @Provides + static ValidatorTimingChannel validatorTimingChannel(final EventChannels eventChannels) { + return eventChannels.getPublisher(ValidatorTimingChannel.class); + } + + // Subscribers + + @Provides + static EventChannelSubscriber slotEventsChannelSubscriber( + final EventChannels eventChannels) { + return eventChannels.createSubscriber(SlotEventsChannel.class); + } + + @Provides + static EventChannelSubscriber finalizedCheckpointChannelSubscriber( + final EventChannels eventChannels) { + return eventChannels.createSubscriber(FinalizedCheckpointChannel.class); + } + + @Provides + static EventChannelSubscriber chainHeadChannelSubscriber( + final EventChannels eventChannels) { + return eventChannels.createSubscriber(ChainHeadChannel.class); + } + + @Provides + static EventChannelSubscriber eth1EventsChannelSubscriber( + final EventChannels eventChannels) { + return eventChannels.createSubscriber(Eth1EventsChannel.class); + } + + @Provides + static EventChannelSubscriber + executionClientVersionChannelSubscriber(final EventChannels eventChannels) { + return eventChannels.createSubscriber(ExecutionClientVersionChannel.class); + } + + @Provides + static EventChannelSubscriber + executionClientEventsChannelSubscriber(final EventChannels eventChannels) { + return eventChannels.createSubscriber(ExecutionClientEventsChannel.class); + } + + @Provides + static EventChannelSubscriber receivedBlockEventsChannelSubscriber( + final EventChannels eventChannels) { + return eventChannels.createSubscriber(ReceivedBlockEventsChannel.class); + } + + @Provides + static EventChannelSubscriber blockImportChannelSubscriber( + final EventChannels eventChannels) { + return eventChannels.createSubscriber(BlockImportChannel.class); + } + + @Provides + static EventChannelSubscriber validatorApiChannelSubscriber( + final EventChannels eventChannels, final BeaconRestApiConfig beaconRestApiConfig) { + return eventChannels.createSubscriberMultithreaded( + ValidatorApiChannel.class, beaconRestApiConfig.getValidatorThreads()); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/CryptoModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/CryptoModule.java new file mode 100644 index 00000000000..c5b6c379e23 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/CryptoModule.java @@ -0,0 +1,70 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory; +import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException; +import tech.pegasys.teku.kzg.KZG; +import tech.pegasys.teku.networking.eth2.P2PConfig; +import tech.pegasys.teku.networks.Eth2NetworkConfiguration; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.BeaconAsyncRunner; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.statetransition.validation.signatures.AggregatingSignatureVerificationService; +import tech.pegasys.teku.statetransition.validation.signatures.SignatureVerificationService; + +@Module +public interface CryptoModule { + + @Provides + @Singleton + static SignatureVerificationService signatureVerificationService( + final P2PConfig p2PConfig, + final MetricsSystem metricsSystem, + final AsyncRunnerFactory asyncRunnerFactory, + @BeaconAsyncRunner final AsyncRunner beaconAsyncRunner) { + return new AggregatingSignatureVerificationService( + metricsSystem, + asyncRunnerFactory, + beaconAsyncRunner, + p2PConfig.getBatchVerifyMaxThreads(), + p2PConfig.getBatchVerifyQueueCapacity(), + p2PConfig.getBatchVerifyMaxBatchSize(), + p2PConfig.isBatchVerifyStrictThreadLimitEnabled()); + } + + @Provides + @Singleton + static KZG kzg(final Eth2NetworkConfiguration eth2NetworkConfig, final Spec spec) { + if (spec.isMilestoneSupported(SpecMilestone.DENEB)) { + KZG kzg = KZG.getInstance(); + final String trustedSetupFile = + eth2NetworkConfig + .getTrustedSetup() + .orElseThrow( + () -> + new InvalidConfigurationException( + "Trusted setup should be configured when Deneb is enabled")); + kzg.loadTrustedSetup(trustedSetupFile); + return kzg; + } else { + return KZG.NOOP; + } + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/DataProviderModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/DataProviderModule.java new file mode 100644 index 00000000000..a25f14332d2 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/DataProviderModule.java @@ -0,0 +1,176 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import java.util.Optional; +import java.util.function.IntSupplier; +import javax.inject.Singleton; +import tech.pegasys.teku.api.DataProvider; +import tech.pegasys.teku.api.ExecutionClientDataProvider; +import tech.pegasys.teku.api.RewardCalculator; +import tech.pegasys.teku.beacon.sync.SyncService; +import tech.pegasys.teku.beaconrestapi.BeaconRestApi; +import tech.pegasys.teku.beaconrestapi.BeaconRestApiConfig; +import tech.pegasys.teku.beaconrestapi.JsonTypeDefinitionBeaconRestApi; +import tech.pegasys.teku.ethereum.events.ExecutionClientEventsChannel; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.infrastructure.events.EventChannels; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.services.beaconchain.BeaconChainConfiguration; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.EventAsyncRunner; +import tech.pegasys.teku.services.beaconchain.init.LoggingModule.InitLogger; +import tech.pegasys.teku.services.beaconchain.init.ServiceConfigModule.RejectedExecutionCountSupplier; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; +import tech.pegasys.teku.spec.datastructures.operations.ProposerSlashing; +import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; +import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; +import tech.pegasys.teku.statetransition.OperationPool; +import tech.pegasys.teku.statetransition.attestation.AggregatingAttestationPool; +import tech.pegasys.teku.statetransition.attestation.AttestationManager; +import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTrackersPool; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceNotifier; +import tech.pegasys.teku.statetransition.forkchoice.ProposersDataManager; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; +import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorCache; +import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorChannel; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.validator.api.ValidatorApiChannel; +import tech.pegasys.teku.validator.coordinator.DepositProvider; +import tech.pegasys.teku.validator.coordinator.Eth1DataCache; +import tech.pegasys.teku.validator.coordinator.Eth1DataProvider; + +@Module +public interface DataProviderModule { + + record LivenessTrackingStatus(boolean enabled) {} + + @Provides + @Singleton + static Eth1DataProvider eth1DataProvider( + final Eth1DataCache eth1DataCache, final DepositProvider depositProvider) { + return new Eth1DataProvider(eth1DataCache, depositProvider); + } + + @Provides + @Singleton + static ExecutionClientDataProvider executionClientDataProvider( + final DataProvider dataProvider, + final EventChannelSubscriber + executionClientEventsChannelSubscriber) { + + final ExecutionClientDataProvider executionClientDataProvider = + dataProvider.getExecutionClientDataProvider(); + executionClientEventsChannelSubscriber.subscribe(executionClientDataProvider); + return executionClientDataProvider; + } + + @Provides + @Singleton + static Optional beaconRestApi( + final InitLogger initLogger, + final Spec spec, + final BeaconRestApiConfig beaconRestApiConfig, + @EventAsyncRunner final AsyncRunner eventAsyncRunner, + final TimeProvider timeProvider, + final Eth1DataProvider eth1DataProvider, + final DataProvider dataProvider, + final EventChannels eventChannels, + final LivenessTrackingStatus livenessTrackingStatus) { + if (!beaconRestApiConfig.isRestApiEnabled()) { + initLogger.logger().info("rest-api-enabled is false, not starting rest api."); + return Optional.empty(); + } + + BeaconRestApi beaconRestApi = + new JsonTypeDefinitionBeaconRestApi( + dataProvider, + eth1DataProvider, + beaconRestApiConfig, + eventChannels, + eventAsyncRunner, + timeProvider, + spec); + + if (livenessTrackingStatus.enabled()) { + final int initialValidatorsCount = + spec.getGenesisSpec().getConfig().getMinGenesisActiveValidatorCount(); + eventChannels.subscribe( + ActiveValidatorChannel.class, new ActiveValidatorCache(spec, initialValidatorsCount)); + } + return Optional.of(beaconRestApi); + } + + @Provides + @Singleton + static DataProvider dataProvider( + final Spec spec, + final RecentChainData recentChainData, + final CombinedChainDataClient combinedChainDataClient, + final RewardCalculator rewardCalculator, + final Eth2P2PNetwork p2pNetwork, + final SyncService syncService, + final ValidatorApiChannel validatorApiChannel, + final ActiveValidatorChannel activeValidatorChannel, + final AggregatingAttestationPool attestationPool, + final BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool, + final AttestationManager attestationManager, + final OperationPool attesterSlashingPool, + final OperationPool proposerSlashingPool, + final OperationPool voluntaryExitPool, + final OperationPool blsToExecutionChangePool, + final SyncCommitteeContributionPool syncCommitteeContributionPool, + final ProposersDataManager proposersDataManager, + final ForkChoiceNotifier forkChoiceNotifier, + final LivenessTrackingStatus livenessTrackingStatus, + @RejectedExecutionCountSupplier final IntSupplier rejectedExecutionCountSupplier) { + + // TODO adopt Dagger instead of DataProvider.builder() + return DataProvider.builder() + .spec(spec) + .recentChainData(recentChainData) + .combinedChainDataClient(combinedChainDataClient) + .rewardCalculator(rewardCalculator) + .p2pNetwork(p2pNetwork) + .syncService(syncService) + .validatorApiChannel(validatorApiChannel) + .attestationPool(attestationPool) + .blockBlobSidecarsTrackersPool(blockBlobSidecarsTrackersPool) + .attestationManager(attestationManager) + .isLivenessTrackingEnabled(livenessTrackingStatus.enabled()) + .activeValidatorChannel(activeValidatorChannel) + .attesterSlashingPool(attesterSlashingPool) + .proposerSlashingPool(proposerSlashingPool) + .voluntaryExitPool(voluntaryExitPool) + .blsToExecutionChangePool(blsToExecutionChangePool) + .syncCommitteeContributionPool(syncCommitteeContributionPool) + .proposersDataManager(proposersDataManager) + .forkChoiceNotifier(forkChoiceNotifier) + .rejectedExecutionSupplier(rejectedExecutionCountSupplier) + .build(); + } + + @Provides + static LivenessTrackingStatus livenessTrackingStatus( + final BeaconChainConfiguration beaconConfig) { + return new LivenessTrackingStatus( + beaconConfig.beaconRestApiConfig().isBeaconLivenessTrackingEnabled() + || beaconConfig.validatorConfig().isDoppelgangerDetectionEnabled()); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ExternalDependenciesModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ExternalDependenciesModule.java new file mode 100644 index 00000000000..f8715a00814 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ExternalDependenciesModule.java @@ -0,0 +1,42 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import tech.pegasys.teku.service.serviceutils.ServiceConfig; +import tech.pegasys.teku.services.beaconchain.BeaconChainConfiguration; + +@Module +public class ExternalDependenciesModule { + + private final ServiceConfig serviceConfig; + private final BeaconChainConfiguration beaconConfig; + + public ExternalDependenciesModule( + final ServiceConfig serviceConfig, final BeaconChainConfiguration beaconConfig) { + this.serviceConfig = serviceConfig; + this.beaconConfig = beaconConfig; + } + + @Provides + ServiceConfig provideServiceConfig() { + return serviceConfig; + } + + @Provides + BeaconChainConfiguration provideBeaconChainConfiguration() { + return beaconConfig; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ForkChoiceModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ForkChoiceModule.java new file mode 100644 index 00000000000..2ff5f9d93de --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ForkChoiceModule.java @@ -0,0 +1,104 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.infrastructure.async.eventthread.AsyncRunnerEventThread; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.networks.Eth2NetworkConfiguration; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.ForkChoiceExecutor; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.ForkChoiceNotifierExecutor; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel; +import tech.pegasys.teku.statetransition.blobs.BlobSidecarManager; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoice; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceNotifier; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceNotifierImpl; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceStateProvider; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceTrigger; +import tech.pegasys.teku.statetransition.forkchoice.MergeTransitionBlockValidator; +import tech.pegasys.teku.statetransition.forkchoice.ProposersDataManager; +import tech.pegasys.teku.statetransition.forkchoice.TickProcessor; +import tech.pegasys.teku.statetransition.util.DebugDataDumper; +import tech.pegasys.teku.storage.client.RecentChainData; + +@Module +public interface ForkChoiceModule { + + @Provides + @Singleton + static ForkChoice forkChoice( + final Spec spec, + final Eth2NetworkConfiguration eth2NetworkConfig, + @ForkChoiceExecutor final AsyncRunnerEventThread forkChoiceExecutor, + final MetricsSystem metricsSystem, + final RecentChainData recentChainData, + final BlobSidecarManager blobSidecarManager, + final ForkChoiceNotifier forkChoiceNotifier, + final ForkChoiceStateProvider forkChoiceStateProvider, + final DebugDataDumper debugDataDumper, + final TickProcessor tickProcessor, + final MergeTransitionBlockValidator mergeTransitionBlockValidator) { + return new ForkChoice( + spec, + forkChoiceExecutor, + recentChainData, + blobSidecarManager, + forkChoiceNotifier, + forkChoiceStateProvider, + tickProcessor, + mergeTransitionBlockValidator, + eth2NetworkConfig.isForkChoiceLateBlockReorgEnabled(), + debugDataDumper, + metricsSystem); + } + + @Provides + @Singleton + static ForkChoiceTrigger forkChoiceTrigger(final ForkChoice forkChoice) { + return new ForkChoiceTrigger(forkChoice); + } + + @Provides + @Singleton + static ForkChoiceStateProvider forkChoiceStateProvider( + @ForkChoiceExecutor final AsyncRunnerEventThread forkChoiceExecutor, + final RecentChainData recentChainData) { + return new ForkChoiceStateProvider(forkChoiceExecutor, recentChainData); + } + + @Provides + @Singleton + static ForkChoiceNotifier forkChoiceNotifier( + final Spec spec, + @ForkChoiceNotifierExecutor final AsyncRunnerEventThread forkChoiceNotifierExecutor, + final TimeProvider timeProvider, + final ProposersDataManager proposersDataManager, + final ExecutionLayerChannel executionLayer, + final RecentChainData recentChainData, + final ForkChoiceStateProvider forkChoiceStateProvider) { + + return new ForkChoiceNotifierImpl( + forkChoiceStateProvider, + forkChoiceNotifierExecutor, + timeProvider, + spec, + executionLayer, + recentChainData, + proposersDataManager); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/LoggingModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/LoggingModule.java new file mode 100644 index 00000000000..637a8a02f86 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/LoggingModule.java @@ -0,0 +1,47 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import tech.pegasys.teku.infrastructure.logging.EventLogger; +import tech.pegasys.teku.infrastructure.logging.StatusLogger; + +@Module +public interface LoggingModule { + + @SuppressWarnings("PrivateStaticFinalLoggers") + record InitLogger(Logger logger) {} + + @Provides + @Singleton + static StatusLogger statusLogger() { + return StatusLogger.STATUS_LOG; + } + + @Provides + @Singleton + static EventLogger eventLogger() { + return EventLogger.EVENT_LOG; + } + + @Provides + @Singleton + static InitLogger initLogger() { + return new InitLogger(LogManager.getLogger("BeaconChainController")); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/MainModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/MainModule.java new file mode 100644 index 00000000000..922a340c269 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/MainModule.java @@ -0,0 +1,225 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Binds; +import dagger.Lazy; +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoSet; +import java.util.Optional; +import java.util.Set; +import javax.inject.Singleton; +import tech.pegasys.teku.beacon.sync.SyncService; +import tech.pegasys.teku.beacon.sync.gossip.blobs.RecentBlobSidecarsFetcher; +import tech.pegasys.teku.beacon.sync.gossip.blocks.RecentBlocksFetcher; +import tech.pegasys.teku.beaconrestapi.BeaconRestApi; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.async.eventthread.AsyncRunnerEventThread; +import tech.pegasys.teku.infrastructure.logging.StatusLogger; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.services.beaconchain.BeaconChainControllerFacade; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.ForkChoiceExecutor; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.ForkChoiceNotifierExecutor; +import tech.pegasys.teku.services.beaconchain.init.LoggingModule.InitLogger; +import tech.pegasys.teku.services.powchain.PowchainConfiguration; +import tech.pegasys.teku.services.timer.TimerService; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; +import tech.pegasys.teku.spec.datastructures.operations.ProposerSlashing; +import tech.pegasys.teku.statetransition.OperationPool; +import tech.pegasys.teku.statetransition.OperationsReOrgManager; +import tech.pegasys.teku.statetransition.attestation.AttestationManager; +import tech.pegasys.teku.statetransition.block.BlockManager; +import tech.pegasys.teku.statetransition.block.FailedExecutionPool; +import tech.pegasys.teku.statetransition.forkchoice.TerminalPowBlockMonitor; +import tech.pegasys.teku.statetransition.genesis.GenesisHandler; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.validator.api.ValidatorConfig; +import tech.pegasys.teku.validator.api.ValidatorTimingChannel; +import tech.pegasys.teku.validator.coordinator.ValidatorIndexCacheTracker; + +@Module +public interface MainModule { + + /** Dummy class returned by the dependency which requires just initialization */ + class VoidInitializer {} + + interface ServiceStarter { + SafeFuture start(); + } + + interface ServiceStopper { + SafeFuture stop(); + } + + @Binds + @Singleton + BeaconChainControllerFacade beaconChainController(SimpleBeaconChainController impl); + + @Provides + @IntoSet + static VoidInitializer initSlashingEventsSubscriptions( + final ValidatorConfig validatorConfig, + final ValidatorTimingChannel validatorTimingChannel, + final OperationPool attesterSlashingPool, + final OperationPool proposerSlashingPool) { + if (validatorConfig.isShutdownWhenValidatorSlashedEnabled()) { + attesterSlashingPool.subscribeOperationAdded( + (operation, validationStatus, fromNetwork) -> + validatorTimingChannel.onAttesterSlashing(operation)); + proposerSlashingPool.subscribeOperationAdded( + (operation, validationStatus, fromNetwork) -> + validatorTimingChannel.onProposerSlashing(operation)); + } + return new VoidInitializer(); + } + + @Provides + @IntoSet + static VoidInitializer initGenesisHandler( + final RecentChainData recentChainData, + final Lazy genesisHandler, + final PowchainConfiguration powchainConfig, + final StatusLogger statusLogger) { + if (!recentChainData.isPreGenesis()) { + // We already have a genesis block - no need for a genesis handler + } else if (!powchainConfig.isEnabled()) { + // We're pre-genesis but no eth1 endpoint is set + throw new IllegalStateException("ETH1 is disabled, but no initial state is set."); + } else { + statusLogger.loadingGenesisFromEth1Chain(); + genesisHandler.get(); + } + return new VoidInitializer(); + } + + @Provides + @IntoSet + @SuppressWarnings("UnusedVariable") + static VoidInitializer initOperationsReOrgManager( + final OperationsReOrgManager operationsReOrgManager) { + return new VoidInitializer(); + } + + @Provides + @IntoSet + @SuppressWarnings("UnusedVariable") + static VoidInitializer initValidatorIndexCacheTracker( + final ValidatorIndexCacheTracker validatorIndexCacheTracker) { + return new VoidInitializer(); + } + + @Provides + @IntoSet + @SuppressWarnings("UnusedVariable") + static VoidInitializer initRecentBlocksFetcher(final RecentBlocksFetcher recentBlocksFetcher) { + return new VoidInitializer(); + } + + @Provides + @IntoSet + @SuppressWarnings("UnusedVariable") + static VoidInitializer initRecentBlobSidecarsFetcher( + final RecentBlobSidecarsFetcher recentBlobSidecarsFetcher) { + return new VoidInitializer(); + } + + @Provides + @IntoSet + static VoidInitializer subscribeFailedPayloadExecution( + final Spec spec, + final BlockManager blockManager, + final FailedExecutionPool failedExecutionPool) { + if (spec.isMilestoneSupported(SpecMilestone.BELLATRIX)) { + blockManager.subscribeFailedPayloadExecution(failedExecutionPool::addFailedBlock); + } + return new VoidInitializer(); + } + + @Provides + @IntoSet + static VoidInitializer subscribeOnStoreInitialized( + final RecentChainData recentChainData, + final StorageModule.OnStoreInitializedHandler onStoreInitializedHandler) { + + recentChainData.subscribeStoreInitialized(onStoreInitializedHandler::handle); + return new VoidInitializer(); + } + + @Provides + @Singleton + @SuppressWarnings("UnusedVariable") + static ServiceStarter serviceStarter( + final Set allInitializers, + final Optional beaconRestApi, + final SyncService syncService, + final BlockManager blockManager, + final AttestationManager attestationManager, + final Eth2P2PNetwork p2pNetwork, + final TimerService timerService, + final Optional terminalPowBlockMonitor, + final InitLogger initLogger) { + return () -> + SafeFuture.fromRunnable(() -> initLogger.logger().info("Starting BeaconChain services")) + .thenCompose( + __ -> + SafeFuture.allOf( + syncService.start(), + blockManager.start(), + attestationManager.start(), + p2pNetwork.start(), + SafeFuture.fromRunnable( + () -> + terminalPowBlockMonitor.ifPresent(TerminalPowBlockMonitor::start)))) + .thenCompose(__ -> timerService.start().thenApply(___ -> null)) + .thenCompose( + __ -> + beaconRestApi + .map(BeaconRestApi::start) + .orElse(SafeFuture.completedFuture(null)) + .thenApply(___ -> null)) + .thenRun(() -> initLogger.logger().info("BeaconChain services started")); + } + + @Provides + @Singleton + static ServiceStopper serviceStopper( + final Optional beaconRestApi, + final SyncService syncService, + final BlockManager blockManager, + final AttestationManager attestationManager, + final Eth2P2PNetwork p2pNetwork, + final TimerService timerService, + final Optional terminalPowBlockMonitor, + @ForkChoiceExecutor final AsyncRunnerEventThread forkChoiceExecutor, + @ForkChoiceNotifierExecutor final AsyncRunnerEventThread forkChoiceNotifierExecutor) { + return () -> + SafeFuture.allOf( + beaconRestApi.map(BeaconRestApi::stop).orElse(SafeFuture.completedFuture(null)), + syncService.stop(), + blockManager.stop(), + attestationManager.stop(), + p2pNetwork.stop(), + timerService.stop(), + SafeFuture.fromRunnable( + () -> terminalPowBlockMonitor.ifPresent(TerminalPowBlockMonitor::stop))) + .thenRun( + () -> { + forkChoiceExecutor.stop(); + forkChoiceNotifierExecutor.stop(); + }); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/MetricsModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/MetricsModule.java new file mode 100644 index 00000000000..1b478b68cbd --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/MetricsModule.java @@ -0,0 +1,260 @@ +/* + * 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.services.beaconchain.init; + +import static tech.pegasys.teku.infrastructure.metrics.TekuMetricCategory.BEACON; +import static tech.pegasys.teku.infrastructure.time.TimeUtilities.secondsToMillis; + +import dagger.Module; +import dagger.Provides; +import java.util.Optional; +import javax.inject.Qualifier; +import javax.inject.Singleton; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionAndPublishingPerformanceFactory; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.infrastructure.logging.StatusLogger; +import tech.pegasys.teku.infrastructure.metrics.MetricsConfig; +import tech.pegasys.teku.infrastructure.metrics.SettableGauge; +import tech.pegasys.teku.infrastructure.metrics.SettableLabelledGauge; +import tech.pegasys.teku.infrastructure.metrics.TekuMetricCategory; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.services.beaconchain.BeaconChainMetrics; +import tech.pegasys.teku.services.beaconchain.SlotProcessor; +import tech.pegasys.teku.services.beaconchain.SyncCommitteeMetrics; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.attestation.ValidatableAttestation; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.statetransition.block.BlockImportMetrics; +import tech.pegasys.teku.statetransition.forkchoice.TickProcessingPerformance; +import tech.pegasys.teku.statetransition.util.FutureItems; +import tech.pegasys.teku.storage.api.ChainHeadChannel; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.validator.api.ValidatorConfig; +import tech.pegasys.teku.validator.api.ValidatorPerformanceTrackingMode; +import tech.pegasys.teku.validator.coordinator.ActiveValidatorTracker; +import tech.pegasys.teku.validator.coordinator.DutyMetrics; +import tech.pegasys.teku.validator.coordinator.Eth1DataCache; +import tech.pegasys.teku.validator.coordinator.performance.DefaultPerformanceTracker; +import tech.pegasys.teku.validator.coordinator.performance.NoOpPerformanceTracker; +import tech.pegasys.teku.validator.coordinator.performance.PerformanceTracker; +import tech.pegasys.teku.validator.coordinator.performance.SyncCommitteePerformanceTracker; +import tech.pegasys.teku.validator.coordinator.performance.ValidatorPerformanceMetrics; + +@Module +public interface MetricsModule { + + @FunctionalInterface + interface TickProcessingPerformanceRecordFactory { + Optional create(); + } + + @Qualifier + @interface FutureItemsMetric {} + + @Qualifier + @interface SubnetSubscriptionsMetric {} + + @Qualifier + @interface PerformanceTrackerTimings {} + + @Provides + @Singleton + @FutureItemsMetric + static SettableLabelledGauge futureItemsMetric(final MetricsSystem metricsSystem) { + return SettableLabelledGauge.create( + metricsSystem, + BEACON, + "future_items_size", + "Current number of items held for future slots, labelled by type", + "type"); + } + + @Provides + @Singleton + @SubnetSubscriptionsMetric + static SettableLabelledGauge subnetSubscriptionsMetric(final MetricsSystem metricsSystem) { + return SettableLabelledGauge.create( + metricsSystem, + TekuMetricCategory.NETWORK, + "subnet_subscriptions", + "Tracks attestations subnet subscriptions", + "type"); + } + + @Provides + @Singleton + @PerformanceTrackerTimings + static SettableGauge performanceTrackerTimings(final MetricsSystem metricsSystem) { + return SettableGauge.create( + metricsSystem, + BEACON, + "performance_tracker_timings", + "Tracks how much time (in millis) performance tracker takes to perform calculations"); + } + + @Provides + @Singleton + static FutureItems futureBlobSidecars( + @FutureItemsMetric final SettableLabelledGauge futureItemsMetric) { + return FutureItems.create(BlobSidecar::getSlot, futureItemsMetric, "blob_sidecars"); + } + + @Provides + @Singleton + static ValidatorPerformanceMetrics validatorPerformanceMetrics( + final MetricsSystem metricsSystem) { + return new ValidatorPerformanceMetrics(metricsSystem); + } + + @Provides + @Singleton + static PerformanceTracker performanceTracker( + final Spec spec, + final ValidatorConfig validatorConfig, + final CombinedChainDataClient combinedChainDataClient, + @PerformanceTrackerTimings final SettableGauge performanceTrackerTimings, + final EventChannelSubscriber slotEventsChannelSubscriber, + final ValidatorPerformanceMetrics validatorPerformanceMetrics, + final ActiveValidatorTracker activeValidatorTracker, + final StatusLogger statusLogger) { + ValidatorPerformanceTrackingMode mode = validatorConfig.getValidatorPerformanceTrackingMode(); + if (mode.isEnabled()) { + DefaultPerformanceTracker performanceTracker = + new DefaultPerformanceTracker( + combinedChainDataClient, + statusLogger, + validatorPerformanceMetrics, + validatorConfig.getValidatorPerformanceTrackingMode(), + activeValidatorTracker, + new SyncCommitteePerformanceTracker(spec, combinedChainDataClient), + spec, + performanceTrackerTimings); + slotEventsChannelSubscriber.subscribe(performanceTracker); + return performanceTracker; + } else { + return new NoOpPerformanceTracker(); + } + } + + // TODO not used + @Provides + @Singleton + static SyncCommitteeMetrics syncCommitteeMetrics( + final Spec spec, + final RecentChainData recentChainData, + final MetricsSystem metricsSystem, + final EventChannelSubscriber slotEventsChannelSubscriber, + final EventChannelSubscriber chainHeadChannelSubscriber) { + SyncCommitteeMetrics syncCommitteeMetrics = + new SyncCommitteeMetrics(spec, recentChainData, metricsSystem); + slotEventsChannelSubscriber.subscribe(syncCommitteeMetrics); + chainHeadChannelSubscriber.subscribe(syncCommitteeMetrics); + return syncCommitteeMetrics; + } + + // TODO not used + @Provides + @Singleton + static BeaconChainMetrics beaconChainMetrics( + final Spec spec, + final MetricsSystem metricsSystem, + final RecentChainData recentChainData, + final SlotProcessor slotProcessor, + final Eth2P2PNetwork p2pNetwork, + final Eth1DataCache eth1DataCache, + final EventChannelSubscriber slotEventsChannelSubscriber) { + + final BeaconChainMetrics beaconChainMetrics = + new BeaconChainMetrics( + spec, + recentChainData, + slotProcessor.getNodeSlot(), + metricsSystem, + p2pNetwork, + eth1DataCache); + slotEventsChannelSubscriber.subscribe(beaconChainMetrics); + return beaconChainMetrics; + } + + @Provides + @Singleton + static BlockProductionAndPublishingPerformanceFactory + blockProductionAndPublishingPerformanceFactory( + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final MetricsConfig metricsConfig) { + return new BlockProductionAndPublishingPerformanceFactory( + timeProvider, + (slot) -> secondsToMillis(recentChainData.computeTimeAtSlot(slot)), + metricsConfig.isBlockProductionAndPublishingPerformanceEnabled(), + metricsConfig.getBlockProductionPerformanceWarningLocalThreshold(), + metricsConfig.getBlockProductionPerformanceWarningBuilderThreshold(), + metricsConfig.getBlockPublishingPerformanceWarningLocalThreshold(), + metricsConfig.getBlockPublishingPerformanceWarningBuilderThreshold()); + } + + @Provides + @Singleton + static DutyMetrics dutyMetrics( + final MetricsSystem metricsSystem, + final Spec spec, + final TimeProvider timeProvider, + final RecentChainData recentChainData) { + return DutyMetrics.create(metricsSystem, timeProvider, recentChainData, spec); + } + + @Provides + @Singleton + static FutureItems futureAttestations( + @FutureItemsMetric final SettableLabelledGauge futureItemsMetric) { + return FutureItems.create( + ValidatableAttestation::getEarliestSlotForForkChoiceProcessing, + UInt64.valueOf(3), + futureItemsMetric, + "attestations"); + } + + @Provides + @Singleton + static FutureItems futureBlocks( + @FutureItemsMetric final SettableLabelledGauge futureItemsMetric) { + return FutureItems.create(SignedBeaconBlock::getSlot, futureItemsMetric, "blocks"); + } + + @Provides + @Singleton + static Optional blockImportMetrics( + final MetricsConfig metricsConfig, final MetricsSystem metricsSystem) { + return metricsConfig.isBlockPerformanceEnabled() + ? Optional.of(BlockImportMetrics.create(metricsSystem)) + : Optional.empty(); + } + + @Provides + @Singleton + static TickProcessingPerformanceRecordFactory tickProcessingPerformanceRecordFactory( + final TimeProvider timeProvider, final MetricsConfig metricsConfig) { + return () -> + metricsConfig.isTickPerformanceEnabled() + ? Optional.of( + new TickProcessingPerformance(timeProvider, timeProvider.getTimeInMillis())) + : Optional.empty(); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/NetworkModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/NetworkModule.java new file mode 100644 index 00000000000..1b668c2f8e8 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/NetworkModule.java @@ -0,0 +1,149 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import java.util.Optional; +import javax.inject.Singleton; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.events.EventChannels; +import tech.pegasys.teku.infrastructure.io.PortAvailability; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.kzg.KZG; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetworkBuilder; +import tech.pegasys.teku.networking.eth2.P2PConfig; +import tech.pegasys.teku.networking.eth2.mock.NoOpEth2P2PNetwork; +import tech.pegasys.teku.networking.p2p.discovery.DiscoveryConfig; +import tech.pegasys.teku.service.serviceutils.layout.DataDirLayout; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.NetworkAsyncRunner; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; +import tech.pegasys.teku.spec.datastructures.operations.ProposerSlashing; +import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; +import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; +import tech.pegasys.teku.statetransition.LocalOperationAcceptedFilter; +import tech.pegasys.teku.statetransition.OperationPool; +import tech.pegasys.teku.statetransition.attestation.AttestationManager; +import tech.pegasys.teku.statetransition.blobs.BlobSidecarManager; +import tech.pegasys.teku.statetransition.block.BlockManager; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeMessagePool; +import tech.pegasys.teku.statetransition.util.DebugDataDumper; +import tech.pegasys.teku.statetransition.util.DebugDataFileDumper; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.store.KeyValueStore; +import tech.pegasys.teku.weaksubjectivity.WeakSubjectivityValidator; + +@Module +public interface NetworkModule { + + @Provides + static Eth2P2PNetworkBuilder eth2P2PNetworkBuilder() { + return Eth2P2PNetworkBuilder.create(); + } + + @Provides + @Singleton + static Eth2P2PNetwork eth2P2PNetwork( + final Spec spec, + final P2PConfig p2pConfig, + final MetricsSystem metricsSystem, + @NetworkAsyncRunner final AsyncRunner networkAsyncRunner, + final TimeProvider timeProvider, + final EventChannels eventChannels, + final KeyValueStore keyValueStore, + final Eth2P2PNetworkBuilder eth2P2PNetworkBuilder, + final CombinedChainDataClient combinedChainDataClient, + final BlockManager blockManager, + final BlobSidecarManager blobSidecarManager, + final AttestationManager attestationManager, + final OperationPool attesterSlashingPool, + final OperationPool proposerSlashingPool, + final OperationPool voluntaryExitPool, + final SyncCommitteeContributionPool syncCommitteeContributionPool, + final SyncCommitteeMessagePool syncCommitteeMessagePool, + final OperationPool blsToExecutionChangePool, + final KZG kzg, + final WeakSubjectivityValidator weakSubjectivityValidator, + final DebugDataDumper p2pDebugDataDumper) { + if (!p2pConfig.getNetworkConfig().isEnabled()) { + return new NoOpEth2P2PNetwork(spec); + } + + DiscoveryConfig discoveryConfig = p2pConfig.getDiscoveryConfig(); + final Optional maybeUdpPort = + discoveryConfig.isDiscoveryEnabled() + ? Optional.of(discoveryConfig.getListenUdpPort()) + : Optional.empty(); + + PortAvailability.checkPortsAvailable( + p2pConfig.getNetworkConfig().getListenPort(), maybeUdpPort); + + // TODO adopt Dagger instead of eth2P2PNetworkBuilder() + Eth2P2PNetwork p2pNetwork = + eth2P2PNetworkBuilder + .config(p2pConfig) + .eventChannels(eventChannels) + .combinedChainDataClient(combinedChainDataClient) + .gossipedBlockProcessor(blockManager::validateAndImportBlock) + .gossipedBlobSidecarProcessor(blobSidecarManager::validateAndPrepareForBlockImport) + .gossipedAttestationProcessor(attestationManager::addAttestation) + .gossipedAggregateProcessor(attestationManager::addAggregate) + .gossipedAttesterSlashingProcessor(attesterSlashingPool::addRemote) + .gossipedProposerSlashingProcessor(proposerSlashingPool::addRemote) + .gossipedVoluntaryExitProcessor(voluntaryExitPool::addRemote) + .gossipedSignedContributionAndProofProcessor(syncCommitteeContributionPool::addRemote) + .gossipedSyncCommitteeMessageProcessor(syncCommitteeMessagePool::addRemote) + .gossipedSignedBlsToExecutionChangeProcessor(blsToExecutionChangePool::addRemote) + .processedAttestationSubscriptionProvider( + attestationManager::subscribeToAttestationsToSend) + .metricsSystem(metricsSystem) + .timeProvider(timeProvider) + .asyncRunner(networkAsyncRunner) + .keyValueStore(keyValueStore) + .requiredCheckpoint(weakSubjectivityValidator.getWSCheckpoint()) + .specProvider(spec) + .kzg(kzg) + .recordMessageArrival(true) + .p2pDebugDataDumper(p2pDebugDataDumper) + .build(); + + syncCommitteeMessagePool.subscribeOperationAdded( + new LocalOperationAcceptedFilter<>(p2pNetwork::publishSyncCommitteeMessage)); + syncCommitteeContributionPool.subscribeOperationAdded( + new LocalOperationAcceptedFilter<>(p2pNetwork::publishSyncCommitteeContribution)); + proposerSlashingPool.subscribeOperationAdded( + new LocalOperationAcceptedFilter<>(p2pNetwork::publishProposerSlashing)); + attesterSlashingPool.subscribeOperationAdded( + new LocalOperationAcceptedFilter<>(p2pNetwork::publishAttesterSlashing)); + voluntaryExitPool.subscribeOperationAdded( + new LocalOperationAcceptedFilter<>(p2pNetwork::publishVoluntaryExit)); + blsToExecutionChangePool.subscribeOperationAdded( + new LocalOperationAcceptedFilter<>(p2pNetwork::publishSignedBlsToExecutionChange)); + + return p2pNetwork; + } + + @Provides + @Singleton + static DebugDataDumper debugDataDumper(final DataDirLayout dataDirLayout) { + return dataDirLayout.isDebugDataDumpingEnabled() + ? new DebugDataFileDumper(dataDirLayout.getDebugDataDirectory()) + : DebugDataDumper.NOOP; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/PoolAndCachesModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/PoolAndCachesModule.java new file mode 100644 index 00000000000..8f2d65b7913 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/PoolAndCachesModule.java @@ -0,0 +1,280 @@ +/* + * 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.services.beaconchain.init; + +import static tech.pegasys.teku.statetransition.attestation.AggregatingAttestationPool.DEFAULT_MAXIMUM_ATTESTATION_COUNT; + +import dagger.Module; +import dagger.Provides; +import java.util.Comparator; +import java.util.Map; +import java.util.Optional; +import javax.inject.Qualifier; +import javax.inject.Singleton; +import org.apache.tuweni.bytes.Bytes32; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.collections.LimitedMap; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.BeaconAsyncRunner; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.OperationPoolAsyncRunner; +import tech.pegasys.teku.services.beaconchain.init.SpecModule.SchemaSupplier; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.datastructures.attestation.ValidatableAttestation; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBodySchema; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.capella.BeaconBlockBodySchemaCapella; +import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; +import tech.pegasys.teku.spec.datastructures.operations.ProposerSlashing; +import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; +import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; +import tech.pegasys.teku.spec.logic.common.statetransition.results.BlockImportResult; +import tech.pegasys.teku.statetransition.MappedOperationPool; +import tech.pegasys.teku.statetransition.OperationPool; +import tech.pegasys.teku.statetransition.SimpleOperationPool; +import tech.pegasys.teku.statetransition.attestation.AggregatingAttestationPool; +import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTrackersPool; +import tech.pegasys.teku.statetransition.block.BlockImportChannel; +import tech.pegasys.teku.statetransition.block.BlockImporter; +import tech.pegasys.teku.statetransition.block.BlockManager; +import tech.pegasys.teku.statetransition.block.FailedExecutionPool; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoice; +import tech.pegasys.teku.statetransition.synccommittee.SignedContributionAndProofValidator; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeMessagePool; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeMessageValidator; +import tech.pegasys.teku.statetransition.util.BlockBlobSidecarsTrackersPoolImpl; +import tech.pegasys.teku.statetransition.util.PendingPool; +import tech.pegasys.teku.statetransition.util.PoolFactory; +import tech.pegasys.teku.statetransition.validation.AttesterSlashingValidator; +import tech.pegasys.teku.statetransition.validation.InternalValidationResult; +import tech.pegasys.teku.statetransition.validation.ProposerSlashingValidator; +import tech.pegasys.teku.statetransition.validation.SignedBlsToExecutionChangeValidator; +import tech.pegasys.teku.statetransition.validation.VoluntaryExitValidator; +import tech.pegasys.teku.storage.api.FinalizedCheckpointChannel; +import tech.pegasys.teku.storage.client.RecentChainData; + +@Module +public interface PoolAndCachesModule { + + @Qualifier + @interface InvalidBlockRoots {} + + @Qualifier + @interface InvalidBlobSidecarRoots {} + + @Provides + @Singleton + static PoolFactory poolFactory(final MetricsSystem metricsSystem) { + return new PoolFactory(metricsSystem); + } + + @Provides + @Singleton + static PendingPool pendingBlocksPool( + final Spec spec, + final PoolFactory poolFactory, + final EventChannelSubscriber channelSubscriber) { + PendingPool pool = poolFactory.createPendingPoolForBlocks(spec); + channelSubscriber.subscribe(pool); + return pool; + } + + @Provides + @Singleton + @InvalidBlockRoots + static Map invalidBlockRoots() { + return LimitedMap.createSynchronizedLRU(500); + } + + @Provides + @Singleton + @InvalidBlobSidecarRoots + static Map invalidBlobSidecarRoots() { + return LimitedMap.createSynchronizedLRU(500); + } + + @Provides + @Singleton + static BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool( + final Spec spec, + @BeaconAsyncRunner final AsyncRunner beaconAsyncRunner, + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final BlockImportChannel blockImportChannel, + final PoolFactory poolFactory, + final EventChannelSubscriber + finalizedCheckpointChannelSubscriber) { + if (spec.isMilestoneSupported(SpecMilestone.DENEB)) { + final BlockBlobSidecarsTrackersPoolImpl pool = + poolFactory.createPoolForBlockBlobSidecarsTrackers( + blockImportChannel, spec, timeProvider, beaconAsyncRunner, recentChainData); + finalizedCheckpointChannelSubscriber.subscribe(pool); + return pool; + } else { + return BlockBlobSidecarsTrackersPool.NOOP; + } + } + + @Provides + @Singleton + static OperationPool attesterSlashingPool( + final MetricsSystem metricsSystem, + final SchemaSupplier> beaconBlockSchemaSupplier, + final BlockImporter blockImporter, + final AttesterSlashingValidator attesterSlashingValidator, + final ForkChoice forkChoice) { + OperationPool attesterSlashingPool = + new SimpleOperationPool<>( + "AttesterSlashingPool", + metricsSystem, + beaconBlockSchemaSupplier.andThen(BeaconBlockBodySchema::getAttesterSlashingsSchema), + attesterSlashingValidator, + // Prioritise slashings that include more validators at a time + Comparator.comparingInt( + slashing -> slashing.getIntersectingValidatorIndices().size()) + .reversed()); + blockImporter.subscribeToVerifiedBlockAttesterSlashings(attesterSlashingPool::removeAll); + attesterSlashingPool.subscribeOperationAdded(forkChoice::onAttesterSlashing); + return attesterSlashingPool; + } + + @Provides + @Singleton + static OperationPool proposerSlashingPool( + final MetricsSystem metricsSystem, + final SchemaSupplier> beaconBlockSchemaSupplier, + final BlockImporter blockImporter, + final ProposerSlashingValidator validator) { + SimpleOperationPool proposerSlashingPool = + new SimpleOperationPool<>( + "ProposerSlashingPool", + metricsSystem, + beaconBlockSchemaSupplier.andThen(BeaconBlockBodySchema::getProposerSlashingsSchema), + validator); + blockImporter.subscribeToVerifiedBlockProposerSlashings(proposerSlashingPool::removeAll); + return proposerSlashingPool; + } + + @Provides + @Singleton + static OperationPool voluntaryExitPool( + final MetricsSystem metricsSystem, + @OperationPoolAsyncRunner final AsyncRunner operationPoolAsyncRunner, + final TimeProvider timeProvider, + final SchemaSupplier> beaconBlockSchemaSupplier, + final BlockImporter blockImporter, + final VoluntaryExitValidator validator) { + MappedOperationPool voluntaryExitPool = + new MappedOperationPool<>( + "VoluntaryExitPool", + metricsSystem, + beaconBlockSchemaSupplier.andThen(BeaconBlockBodySchema::getVoluntaryExitsSchema), + validator, + operationPoolAsyncRunner, + timeProvider); + blockImporter.subscribeToVerifiedBlockVoluntaryExits(voluntaryExitPool::removeAll); + return voluntaryExitPool; + } + + @Provides + @Singleton + static OperationPool signedBlsToExecutionChangePool( + final MetricsSystem metricsSystem, + @OperationPoolAsyncRunner final AsyncRunner operationPoolAsyncRunner, + final TimeProvider timeProvider, + final SchemaSupplier> beaconBlockSchemaSupplier, + final BlockImporter blockImporter, + final SignedBlsToExecutionChangeValidator validator) { + OperationPool blsToExecutionChangePool = + new MappedOperationPool<>( + "SignedBlsToExecutionChangePool", + metricsSystem, + beaconBlockSchemaSupplier + .andThen(BeaconBlockBodySchema::toVersionCapella) + .andThen(Optional::orElseThrow) + .andThen(BeaconBlockBodySchemaCapella::getBlsToExecutionChangesSchema), + validator, + operationPoolAsyncRunner, + timeProvider); + blockImporter.subscribeToVerifiedBlockBlsToExecutionChanges( + blsToExecutionChangePool::removeAll); + return blsToExecutionChangePool; + } + + @Provides + @Singleton + static PendingPool pendingAttestationPool( + final Spec spec, + final PoolFactory poolFactory, + final EventChannelSubscriber + finalizedCheckpointChannelSubscriber) { + final PendingPool pendingAttestations = + poolFactory.createPendingPoolForAttestations(spec); + finalizedCheckpointChannelSubscriber.subscribe(pendingAttestations); + return pendingAttestations; + } + + @Provides + @Singleton + static SyncCommitteeContributionPool syncCommitteeContributionPool( + final Spec spec, + final SignedContributionAndProofValidator signedContributionAndProofValidator, + final EventChannelSubscriber slotEventsChannelSubscriber) { + SyncCommitteeContributionPool syncCommitteeContributionPool = + new SyncCommitteeContributionPool(spec, signedContributionAndProofValidator); + + slotEventsChannelSubscriber.subscribe(syncCommitteeContributionPool); + return syncCommitteeContributionPool; + } + + @Provides + @Singleton + static SyncCommitteeMessagePool syncCommitteeMessagePool( + final Spec spec, + final SyncCommitteeMessageValidator syncCommitteeMessageValidator, + final EventChannelSubscriber slotEventsChannelSubscriber) { + SyncCommitteeMessagePool syncCommitteeMessagePool = + new SyncCommitteeMessagePool(spec, syncCommitteeMessageValidator); + slotEventsChannelSubscriber.subscribe(syncCommitteeMessagePool); + return syncCommitteeMessagePool; + } + + @Provides + @Singleton + static AggregatingAttestationPool aggregatingAttestationPool( + final Spec spec, + final RecentChainData recentChainData, + final MetricsSystem metricsSystem, + final BlockImporter blockImporter, + final EventChannelSubscriber slotEventsChannelSubscriber) { + AggregatingAttestationPool attestationPool = + new AggregatingAttestationPool( + spec, recentChainData, metricsSystem, DEFAULT_MAXIMUM_ATTESTATION_COUNT); + slotEventsChannelSubscriber.subscribe(attestationPool); + blockImporter.subscribeToVerifiedBlockAttestations( + attestationPool::onAttestationsIncludedInBlock); + return attestationPool; + } + + @Provides + @Singleton + static FailedExecutionPool failedExecutionPool( + final BlockManager blockManager, @BeaconAsyncRunner final AsyncRunner beaconAsyncRunner) { + return new FailedExecutionPool(blockManager, beaconAsyncRunner); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/PowModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/PowModule.java new file mode 100644 index 00000000000..42ed88b8193 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/PowModule.java @@ -0,0 +1,149 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import java.util.Optional; +import javax.inject.Qualifier; +import javax.inject.Singleton; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.beaconrestapi.BeaconRestApiConfig; +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.ethereum.execution.types.Eth1Address; +import tech.pegasys.teku.ethereum.pow.api.Eth1EventsChannel; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.infrastructure.logging.EventLogger; +import tech.pegasys.teku.infrastructure.logging.StatusLogger; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.BeaconAsyncRunner; +import tech.pegasys.teku.services.powchain.PowchainConfiguration; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceNotifier; +import tech.pegasys.teku.statetransition.forkchoice.TerminalPowBlockMonitor; +import tech.pegasys.teku.statetransition.genesis.GenesisHandler; +import tech.pegasys.teku.storage.api.Eth1DepositStorageChannel; +import tech.pegasys.teku.storage.api.FinalizedCheckpointChannel; +import tech.pegasys.teku.storage.api.StorageUpdateChannel; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.validator.api.ValidatorConfig; +import tech.pegasys.teku.validator.coordinator.DepositProvider; +import tech.pegasys.teku.validator.coordinator.Eth1DataCache; +import tech.pegasys.teku.validator.coordinator.Eth1VotingPeriod; + +@Module +public interface PowModule { + + @Qualifier + @interface ProposerDefaultFeeRecipient {} + + @Provides + @Singleton + static Optional terminalPowBlockMonitor( + final Spec spec, + @BeaconAsyncRunner final AsyncRunner beaconAsyncRunner, + final TimeProvider timeProvider, + final ExecutionLayerChannel executionLayer, + final RecentChainData recentChainData, + final ForkChoiceNotifier forkChoiceNotifier, + final EventLogger eventLogger) { + if (spec.isMilestoneSupported(SpecMilestone.BELLATRIX)) { + return Optional.of( + new TerminalPowBlockMonitor( + executionLayer, + spec, + recentChainData, + forkChoiceNotifier, + beaconAsyncRunner, + eventLogger, + timeProvider)); + } else { + return Optional.empty(); + } + } + + @Provides + @Singleton + static Eth1DataCache eth1DataCache(final Spec spec, final MetricsSystem metricsSystem) { + return new Eth1DataCache(spec, metricsSystem, new Eth1VotingPeriod(spec)); + } + + @Provides + @Singleton + static DepositProvider depositProvider( + final Spec spec, + final PowchainConfiguration powchainConfig, + final MetricsSystem metricsSystem, + final RecentChainData recentChainData, + final Eth1DataCache eth1DataCache, + final StorageUpdateChannel storageUpdateChannel, + final Eth1DepositStorageChannel eth1DepositStorageChannel, + final EventChannelSubscriber eth1EventsChannelSubscriber, + final EventChannelSubscriber finalizedCheckpointChannelSubscriber, + final EventChannelSubscriber slotEventsChannelSubscriber, + final EventLogger eventLogger) { + DepositProvider depositProvider = + new DepositProvider( + metricsSystem, + recentChainData, + eth1DataCache, + storageUpdateChannel, + eth1DepositStorageChannel, + spec, + eventLogger, + powchainConfig.useMissingDepositEventLogging()); + eth1EventsChannelSubscriber.subscribe(depositProvider); + finalizedCheckpointChannelSubscriber.subscribe(depositProvider); + slotEventsChannelSubscriber.subscribe(depositProvider); + + return depositProvider; + } + + @Provides + @Singleton + @ProposerDefaultFeeRecipient + static Optional proposerDefaultFeeRecipient( + final Spec spec, + final ValidatorConfig validatorConfig, + final BeaconRestApiConfig restApiConfig, + final StatusLogger statusLogger) { + if (!spec.isMilestoneSupported(SpecMilestone.BELLATRIX)) { + return Optional.of(Eth1Address.ZERO); + } + + final Optional defaultFeeRecipient = + validatorConfig.getProposerDefaultFeeRecipient(); + + if (defaultFeeRecipient.isEmpty() && restApiConfig.isRestApiEnabled()) { + statusLogger.warnMissingProposerDefaultFeeRecipientWithRestAPIEnabled(); + } + + return defaultFeeRecipient; + } + + @Provides + @Singleton + static GenesisHandler genesisHandler( + final TimeProvider timeProvider, + final Spec spec, + final RecentChainData recentChainData, + final EventChannelSubscriber eth1EventsChannelSubscriber) { + GenesisHandler genesisHandler = new GenesisHandler(recentChainData, timeProvider, spec); + eth1EventsChannelSubscriber.subscribe(genesisHandler); + return genesisHandler; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/RecentChainDataStateInitializer.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/RecentChainDataStateInitializer.java new file mode 100644 index 00000000000..e53382c6c6f --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/RecentChainDataStateInitializer.java @@ -0,0 +1,171 @@ +/* + * 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.services.beaconchain.init; + +import static tech.pegasys.teku.spec.config.SpecConfig.GENESIS_SLOT; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.Optional; +import javax.inject.Inject; +import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException; +import tech.pegasys.teku.infrastructure.logging.EventLogger; +import tech.pegasys.teku.infrastructure.logging.StatusLogger; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.networks.Eth2NetworkConfiguration; +import tech.pegasys.teku.networks.StateBoostrapConfig; +import tech.pegasys.teku.services.beaconchain.BeaconChainConfiguration; +import tech.pegasys.teku.services.beaconchain.WeakSubjectivityInitializer; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadHeader; +import tech.pegasys.teku.spec.datastructures.interop.GenesisStateBuilder; +import tech.pegasys.teku.spec.datastructures.state.AnchorPoint; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.validator.api.InteropConfig; +import tech.pegasys.teku.weaksubjectivity.WeakSubjectivityCalculator; + +public class RecentChainDataStateInitializer { + + @Inject TimeProvider timeProvider; + @Inject BeaconChainConfiguration beaconConfig; + @Inject Spec spec; + @Inject SpecModule.CurrentSlotProvider currentSlotProvider; + @Inject WeakSubjectivityInitializer weakSubjectivityInitializer; + @Inject EventLogger eventLogger; + @Inject StatusLogger statusLogger; + + @Inject + public RecentChainDataStateInitializer() {} + + public void setupInitialState(final RecentChainData recentChainData) { + + final Optional initialAnchor = + tryLoadingAnchorPointFromInitialState(beaconConfig.eth2NetworkConfig()) + .or( + () -> + attemptToLoadAnchorPoint( + beaconConfig + .eth2NetworkConfig() + .getNetworkBoostrapConfig() + .getGenesisState())); + + /* + If flag to allow sync outside of weak subjectivity period has been set, we pass an instance of + WeakSubjectivityPeriodCalculator to the WeakSubjectivityInitializer. Otherwise, we pass an Optional.empty(). + */ + boolean isAllowSyncOutsideWeakSubjectivityPeriod = + beaconConfig + .eth2NetworkConfig() + .getNetworkBoostrapConfig() + .isAllowSyncOutsideWeakSubjectivityPeriod(); + + final Optional maybeWsCalculator; + if (isAllowSyncOutsideWeakSubjectivityPeriod) { + maybeWsCalculator = Optional.empty(); + } else { + maybeWsCalculator = + Optional.of(WeakSubjectivityCalculator.create(beaconConfig.weakSubjectivity())); + } + + // Validate + initialAnchor.ifPresent( + anchor -> { + final UInt64 currentSlot = + currentSlotProvider.getCurrentSlot(anchor.getState().getGenesisTime()); + weakSubjectivityInitializer.validateInitialAnchor( + anchor, currentSlot, spec, maybeWsCalculator); + }); + + if (initialAnchor.isPresent()) { + final AnchorPoint anchor = initialAnchor.get(); + recentChainData.initializeFromAnchorPoint(anchor, timeProvider.getTimeInSeconds()); + if (anchor.isGenesis()) { + eventLogger.genesisEvent( + anchor.getStateRoot(), + recentChainData.getBestBlockRoot().orElseThrow(), + anchor.getState().getGenesisTime()); + } + } else if (beaconConfig.interopConfig().isInteropEnabled()) { + setupInteropState(recentChainData); + } else if (!beaconConfig.powchainConfig().isEnabled()) { + throw new InvalidConfigurationException( + "ETH1 is disabled but initial state is unknown. Enable ETH1 or specify an initial state" + + "."); + } + } + + private Optional tryLoadingAnchorPointFromInitialState( + final Eth2NetworkConfiguration networkConfiguration) { + Optional initialAnchor = Optional.empty(); + + try { + initialAnchor = + attemptToLoadAnchorPoint( + networkConfiguration.getNetworkBoostrapConfig().getInitialState()); + } catch (final InvalidConfigurationException e) { + final StateBoostrapConfig stateBoostrapConfig = + networkConfiguration.getNetworkBoostrapConfig(); + if (stateBoostrapConfig.isUsingCustomInitialState() + && !stateBoostrapConfig.isUsingCheckpointSync()) { + throw e; + } + statusLogger.warnFailedToLoadInitialState(e.getMessage()); + } + + return initialAnchor; + } + + protected Optional attemptToLoadAnchorPoint(final Optional initialState) { + return weakSubjectivityInitializer.loadInitialAnchorPoint(spec, initialState); + } + + protected void setupInteropState(final RecentChainData recentChainData) { + final InteropConfig config = beaconConfig.interopConfig(); + statusLogger.generatingMockStartGenesis( + config.getInteropGenesisTime(), config.getInteropNumberOfValidators()); + + Optional executionPayloadHeader = Optional.empty(); + if (config.getInteropGenesisPayloadHeader().isPresent()) { + try { + executionPayloadHeader = + Optional.of( + spec.deserializeJsonExecutionPayloadHeader( + new ObjectMapper(), + config.getInteropGenesisPayloadHeader().get().toFile(), + GENESIS_SLOT)); + } catch (IOException e) { + throw new RuntimeException( + "Unable to load payload header from " + config.getInteropGenesisPayloadHeader().get(), + e); + } + } + + final BeaconState genesisState = + new GenesisStateBuilder() + .spec(spec) + .genesisTime(config.getInteropGenesisTime()) + .addMockValidators(config.getInteropNumberOfValidators()) + .executionPayloadHeader(executionPayloadHeader) + .build(); + + recentChainData.initializeFromGenesis(genesisState, timeProvider.getTimeInSeconds()); + + eventLogger.genesisEvent( + genesisState.hashTreeRoot(), + recentChainData.getBestBlockRoot().orElseThrow(), + genesisState.getGenesisTime()); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ServiceConfigModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ServiceConfigModule.java new file mode 100644 index 00000000000..415bc44017a --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ServiceConfigModule.java @@ -0,0 +1,65 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import java.util.function.IntSupplier; +import javax.inject.Qualifier; +import javax.inject.Singleton; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory; +import tech.pegasys.teku.infrastructure.events.EventChannels; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.service.serviceutils.ServiceConfig; +import tech.pegasys.teku.service.serviceutils.layout.DataDirLayout; + +@Module +public interface ServiceConfigModule { + + @Qualifier + @interface RejectedExecutionCountSupplier {} + + @Provides + static DataDirLayout dataDirLayout(final ServiceConfig config) { + return config.getDataDirLayout(); + } + + @Provides + static AsyncRunnerFactory asyncRunnerFactory(final ServiceConfig config) { + return config.getAsyncRunnerFactory(); + } + + @Provides + static TimeProvider timeProvider(final ServiceConfig config) { + return config.getTimeProvider(); + } + + @Provides + static EventChannels eventChannels(final ServiceConfig config) { + return config.getEventChannels(); + } + + @Provides + static MetricsSystem metricsSystem(final ServiceConfig config) { + return config.getMetricsSystem(); + } + + @Provides + @Singleton + @RejectedExecutionCountSupplier + static IntSupplier rejectedExecutionCountSupplier(final ServiceConfig config) { + return config.getRejectedExecutionsSupplier(); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SimpleBeaconChainController.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SimpleBeaconChainController.java new file mode 100644 index 00000000000..783a4e5824c --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SimpleBeaconChainController.java @@ -0,0 +1,112 @@ +/* + * 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.services.beaconchain.init; + +import java.util.Optional; +import javax.inject.Inject; +import tech.pegasys.teku.beacon.sync.SyncService; +import tech.pegasys.teku.beaconrestapi.BeaconRestApi; +import tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.services.beaconchain.BeaconChainControllerFacade; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoice; +import tech.pegasys.teku.statetransition.validation.signatures.SignatureVerificationService; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.client.RecentChainData; + +public class SimpleBeaconChainController implements BeaconChainControllerFacade { + @Inject MainModule.ServiceStarter starter; + @Inject MainModule.ServiceStopper stopper; + @Inject Spec spec; + @Inject TimeProvider timeProvider; + @Inject AsyncRunnerFactory asyncRunnerFactory; + @Inject SignatureVerificationService signatureVerificationService; + @Inject RecentChainData recentChainData; + @Inject CombinedChainDataClient combinedChainDataClient; + @Inject Eth2P2PNetwork p2pNetwork; + @Inject Optional beaconRestApi; + @Inject SyncService syncService; + @Inject ForkChoice forkChoice; + + @Inject + public SimpleBeaconChainController() {} + + @Override + public SafeFuture start() { + return starter.start(); + } + + @Override + public SafeFuture stop() { + return stopper.stop(); + } + + @Override + public boolean isRunning() { + throw new UnsupportedOperationException(); + } + + @Override + public Spec getSpec() { + return spec; + } + + @Override + public TimeProvider getTimeProvider() { + return timeProvider; + } + + @Override + public AsyncRunnerFactory getAsyncRunnerFactory() { + return asyncRunnerFactory; + } + + @Override + public SignatureVerificationService getSignatureVerificationService() { + return signatureVerificationService; + } + + @Override + public RecentChainData getRecentChainData() { + return recentChainData; + } + + @Override + public CombinedChainDataClient getCombinedChainDataClient() { + return combinedChainDataClient; + } + + @Override + public Eth2P2PNetwork getP2pNetwork() { + return p2pNetwork; + } + + @Override + public Optional getBeaconRestAPI() { + return beaconRestApi; + } + + @Override + public SyncService getSyncService() { + return syncService; + } + + @Override + public ForkChoice getForkChoice() { + return forkChoice; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SpecModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SpecModule.java new file mode 100644 index 00000000000..a89c555ca73 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SpecModule.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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import java.util.function.Function; +import javax.inject.Singleton; +import tech.pegasys.teku.api.RewardCalculator; +import tech.pegasys.teku.infrastructure.ssz.schema.SszSchema; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBodySchema; +import tech.pegasys.teku.spec.logic.common.util.BlockRewardCalculatorUtil; + +@Module +public interface SpecModule { + + interface CurrentSlotProvider { + + UInt64 getCurrentSlot(UInt64 genesisTime); + + UInt64 getCurrentSlot(UInt64 currentTime, UInt64 genesisTime); + } + + @FunctionalInterface + interface SchemaSupplier> extends Function { + + @Override + default T apply(final UInt64 slot) { + return getSchemaAtSlot(slot); + } + + T getSchemaAtSlot(UInt64 slot); + } + + @Provides + @Singleton + static SchemaSupplier> beaconBlockBodySchemaSupplier(final Spec spec) { + return slot -> spec.atSlot(slot).getSchemaDefinitions().getBeaconBlockBodySchema(); + } + + @Provides + @Singleton + static RewardCalculator rewardCalculator(final Spec spec) { + return new RewardCalculator(spec, new BlockRewardCalculatorUtil(spec)); + } + + @Provides + @Singleton + static CurrentSlotProvider currentSlotProvider(final Spec spec, final TimeProvider timeProvider) { + return new CurrentSlotProvider() { + @Override + public UInt64 getCurrentSlot(final UInt64 genesisTime) { + return getCurrentSlot(timeProvider.getTimeInSeconds(), genesisTime); + } + + @Override + public UInt64 getCurrentSlot(final UInt64 currentTime, final UInt64 genesisTime) { + return spec.getCurrentSlot(currentTime, genesisTime); + } + }; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/StorageModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/StorageModule.java new file mode 100644 index 00000000000..7c5dab4348f --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/StorageModule.java @@ -0,0 +1,193 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Binds; +import dagger.Lazy; +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.logging.StatusLogger; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.networks.Eth2NetworkConfiguration; +import tech.pegasys.teku.service.serviceutils.layout.DataDirLayout; +import tech.pegasys.teku.services.beaconchain.SlotProcessor; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.BeaconAsyncRunner; +import tech.pegasys.teku.services.beaconchain.init.BeaconModule.GenesisTimeTracker; +import tech.pegasys.teku.services.beaconchain.init.SpecModule.CurrentSlotProvider; +import tech.pegasys.teku.services.beaconchain.init.WSModule.WeakSubjectivityFinalizedConfig; +import tech.pegasys.teku.services.beaconchain.init.WSModule.WeakSubjectivityPeriodValidator; +import tech.pegasys.teku.services.beaconchain.init.WSModule.WeakSubjectivityStoreChainValidator; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTrackersPool; +import tech.pegasys.teku.storage.api.ChainHeadChannel; +import tech.pegasys.teku.storage.api.CombinedStorageChannel; +import tech.pegasys.teku.storage.api.FinalizedCheckpointChannel; +import tech.pegasys.teku.storage.api.StorageQueryChannel; +import tech.pegasys.teku.storage.api.StorageUpdateChannel; +import tech.pegasys.teku.storage.api.VoteUpdateChannel; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.client.EarliestAvailableBlockSlot; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.storage.client.StorageBackedRecentChainData; +import tech.pegasys.teku.storage.client.ValidatorIsConnectedProvider; +import tech.pegasys.teku.storage.store.FileKeyValueStore; +import tech.pegasys.teku.storage.store.KeyValueStore; +import tech.pegasys.teku.storage.store.StoreConfig; +import tech.pegasys.teku.validator.coordinator.performance.PerformanceTracker; + +@Module +public interface StorageModule { + + String KEY_VALUE_STORE_SUBDIRECTORY = "kvstore"; + + interface OnStoreInitializedHandler { + void handle(); + } + + @Binds + StorageQueryChannel bindStorageQueryChannel(CombinedStorageChannel combinedStorageChannel); + + @Binds + StorageUpdateChannel bindStorageUpdateChannel(CombinedStorageChannel combinedStorageChannel); + + @Provides + @Singleton + static KeyValueStore keyValueStore(final DataDirLayout dataDirLayout) { + return new FileKeyValueStore( + dataDirLayout.getBeaconDataDirectory().resolve(KEY_VALUE_STORE_SUBDIRECTORY)); + } + + @Provides + @Singleton + static EarliestAvailableBlockSlot earliestAvailableBlockSlot( + final StoreConfig storeConfig, + final TimeProvider timeProvider, + final StorageQueryChannel storageQueryChannel) { + return new EarliestAvailableBlockSlot( + storageQueryChannel, timeProvider, storeConfig.getEarliestAvailableBlockSlotFrequency()); + } + + @Provides + @Singleton + static CombinedChainDataClient combinedChainDataClient( + final Spec spec, + final StorageQueryChannel storageQueryChannel, + final RecentChainData recentChainData, + final EarliestAvailableBlockSlot earliestAvailableBlockSlot) { + return new CombinedChainDataClient( + recentChainData, storageQueryChannel, spec, earliestAvailableBlockSlot); + } + + @Provides + @Singleton + @SuppressWarnings("UnusedVariable") + static SafeFuture recentChainDataFuture( + @BeaconAsyncRunner final AsyncRunner beaconAsyncRunner, + final TimeProvider timeProvider, + final MetricsSystem metricsSystem, + final Spec spec, + final StoreConfig storeConfig, + final StorageQueryChannel storageQueryChannel, + final StorageUpdateChannel storageUpdateChannel, + final Lazy blockBlobSidecarsTrackersPool, + final VoteUpdateChannel voteUpdateChannel, + final FinalizedCheckpointChannel finalizedCheckpointChannel, + final ChainHeadChannel chainHeadChannel, + final ValidatorIsConnectedProvider validatorIsConnectedProvider, + // TODO is there a better option for this dependency ? + final WeakSubjectivityFinalizedConfig __) { + + return StorageBackedRecentChainData.create( + metricsSystem, + storeConfig, + beaconAsyncRunner, + timeProvider, + blockRoot -> blockBlobSidecarsTrackersPool.get().getBlock(blockRoot), + (blockRoot, index) -> blockBlobSidecarsTrackersPool.get().getBlobSidecar(blockRoot, index), + storageQueryChannel, + storageUpdateChannel, + voteUpdateChannel, + finalizedCheckpointChannel, + chainHeadChannel, + validatorIsConnectedProvider, + spec); + } + + @Provides + @Singleton + // TODO producer ? + static RecentChainData recentChainData( + final Eth2NetworkConfiguration eth2NetworkConfig, + final SafeFuture recentChainDataFuture, + final StatusLogger statusLogger, + final Lazy weakSubjectivityPeriodValidator, + final Lazy recentChainDataStateInitializer) { + + RecentChainData recentChainData = recentChainDataFuture.join(); + + boolean isAllowSyncOutsideWeakSubjectivityPeriod = + eth2NetworkConfig.getNetworkBoostrapConfig().isAllowSyncOutsideWeakSubjectivityPeriod(); + boolean isUsingCustomInitialState = + eth2NetworkConfig.getNetworkBoostrapConfig().isUsingCustomInitialState(); + + if (isAllowSyncOutsideWeakSubjectivityPeriod) { + statusLogger.warnIgnoringWeakSubjectivityPeriod(); + } + + // Setup chain storage + if (recentChainData.isPreGenesis()) { + recentChainDataStateInitializer.get().setupInitialState(recentChainData); + } else { + if (isUsingCustomInitialState) { + statusLogger.warnInitialStateIgnored(); + } + if (!isAllowSyncOutsideWeakSubjectivityPeriod) { + weakSubjectivityPeriodValidator.get().validate(recentChainData); + } + } + + return recentChainData; + } + + @Provides + @Singleton + static OnStoreInitializedHandler onStoreInitializedHandler( + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final CurrentSlotProvider currentSlotProvider, + final Lazy weakSubjectivityStoreChainValidator, + final Lazy genesisTimeTracker, + final SlotProcessor slotProcessor, + final PerformanceTracker performanceTracker) { + return () -> { + UInt64 genesisTime = recentChainData.getGenesisTime(); + UInt64 currentTime = timeProvider.getTimeInSeconds(); + final UInt64 currentSlot = currentSlotProvider.getCurrentSlot(currentTime, genesisTime); + if (currentTime.compareTo(genesisTime) >= 0) { + // Validate that we're running within the weak subjectivity period + weakSubjectivityStoreChainValidator.get().validate(currentSlot); + } else { + genesisTimeTracker.get().update(); + } + slotProcessor.setCurrentSlot(currentSlot); + performanceTracker.start(currentSlot); + }; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SubnetsModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SubnetsModule.java new file mode 100644 index 00000000000..6fe747fef3f --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SubnetsModule.java @@ -0,0 +1,99 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.infrastructure.metrics.SettableLabelledGauge; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.networking.eth2.P2PConfig; +import tech.pegasys.teku.networking.eth2.gossip.subnets.AllSubnetsSubscriber; +import tech.pegasys.teku.networking.eth2.gossip.subnets.AllSyncCommitteeSubscriptions; +import tech.pegasys.teku.networking.eth2.gossip.subnets.AttestationTopicSubscriber; +import tech.pegasys.teku.networking.eth2.gossip.subnets.NodeBasedStableSubnetSubscriber; +import tech.pegasys.teku.networking.eth2.gossip.subnets.StableSubnetSubscriber; +import tech.pegasys.teku.networking.eth2.gossip.subnets.SyncCommitteeSubscriptionManager; +import tech.pegasys.teku.spec.Spec; + +@Module +public interface SubnetsModule { + + @Provides + @Singleton + static AttestationTopicSubscriber attestationTopicSubscriber( + @MetricsModule.SubnetSubscriptionsMetric + final SettableLabelledGauge subnetSubscriptionsMetric, + final Spec spec, + final P2PConfig p2pConfig, + final EventChannelSubscriber slotEventsChannelSubscriber, + final Eth2P2PNetwork p2pNetwork) { + AttestationTopicSubscriber attestationTopicSubscriber = + new AttestationTopicSubscriber(spec, p2pNetwork, subnetSubscriptionsMetric); + if (p2pConfig.isSubscribeAllSubnetsEnabled()) { + slotEventsChannelSubscriber.subscribe(attestationTopicSubscriber); + } + return attestationTopicSubscriber; + } + + @Provides + @Singleton + static StableSubnetSubscriber stableSubnetSubscriber( + final Spec spec, + final P2PConfig p2pConfig, + final Eth2P2PNetwork p2pNetwork, + final AttestationTopicSubscriber attestationTopicSubscriber, + final EventChannelSubscriber slotEventsChannelSubscriber, + final LoggingModule.InitLogger logger) { + final StableSubnetSubscriber stableSubnetSubscriber; + if (p2pConfig.isSubscribeAllSubnetsEnabled()) { + logger.logger().info("Subscribing to all attestation subnets"); + stableSubnetSubscriber = + AllSubnetsSubscriber.create(attestationTopicSubscriber, spec.getNetworkingConfig()); + } else { + if (p2pNetwork.getDiscoveryNodeId().isPresent()) { + stableSubnetSubscriber = + new NodeBasedStableSubnetSubscriber( + attestationTopicSubscriber, spec, p2pNetwork.getDiscoveryNodeId().get()); + } else { + logger + .logger() + .warn("Discovery nodeId is not defined, disabling stable subnet subscriptions"); + stableSubnetSubscriber = StableSubnetSubscriber.NOOP; + } + } + slotEventsChannelSubscriber.subscribe(stableSubnetSubscriber); + return stableSubnetSubscriber; + } + + @Provides + @Singleton + static SyncCommitteeSubscriptionManager syncCommitteeSubscriptionManager( + final Spec spec, + final P2PConfig p2pConfig, + final EventChannelSubscriber slotEventsChannelSubscriber, + final Eth2P2PNetwork p2pNetwork) { + + final SyncCommitteeSubscriptionManager syncCommitteeSubscriptionManager; + if (p2pConfig.isSubscribeAllSubnetsEnabled()) { + syncCommitteeSubscriptionManager = new AllSyncCommitteeSubscriptions(p2pNetwork, spec); + slotEventsChannelSubscriber.subscribe(syncCommitteeSubscriptionManager); + } else { + syncCommitteeSubscriptionManager = new SyncCommitteeSubscriptionManager(p2pNetwork); + } + return syncCommitteeSubscriptionManager; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SyncModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SyncModule.java new file mode 100644 index 00000000000..80e1ad614bd --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/SyncModule.java @@ -0,0 +1,187 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import java.time.Duration; +import java.util.Optional; +import javax.inject.Singleton; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.beacon.sync.DefaultSyncServiceFactory; +import tech.pegasys.teku.beacon.sync.SyncConfig; +import tech.pegasys.teku.beacon.sync.SyncService; +import tech.pegasys.teku.beacon.sync.SyncServiceFactory; +import tech.pegasys.teku.beacon.sync.events.CoalescingChainHeadChannel; +import tech.pegasys.teku.beacon.sync.gossip.blobs.RecentBlobSidecarsFetcher; +import tech.pegasys.teku.beacon.sync.gossip.blocks.RecentBlocksFetcher; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.infrastructure.events.EventChannels; +import tech.pegasys.teku.infrastructure.logging.EventLogger; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; +import tech.pegasys.teku.networks.Eth2NetworkConfiguration; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule.BeaconAsyncRunner; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobIdentifier; +import tech.pegasys.teku.spec.datastructures.validator.BroadcastValidationLevel; +import tech.pegasys.teku.statetransition.blobs.BlobSidecarManager; +import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTrackersPool; +import tech.pegasys.teku.statetransition.block.BlockImportChannel; +import tech.pegasys.teku.statetransition.block.BlockImporter; +import tech.pegasys.teku.statetransition.block.BlockManager; +import tech.pegasys.teku.statetransition.block.ReceivedBlockEventsChannel; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoice; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceNotifier; +import tech.pegasys.teku.statetransition.forkchoice.TerminalPowBlockMonitor; +import tech.pegasys.teku.statetransition.util.PendingPool; +import tech.pegasys.teku.statetransition.validation.signatures.SignatureVerificationService; +import tech.pegasys.teku.storage.api.ChainHeadChannel; +import tech.pegasys.teku.storage.api.StorageUpdateChannel; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.validator.coordinator.DepositProvider; + +@Module +public interface SyncModule { + + @Provides + @Singleton + static SyncServiceFactory syncServiceFactory( + final Spec spec, + final SyncConfig syncConfig, + final Eth2NetworkConfiguration eth2NetworkConfig, + final MetricsSystem metricsSystem, + final AsyncRunnerFactory asyncRunnerFactory, + @BeaconAsyncRunner final AsyncRunner beaconAsyncRunner, + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final CombinedChainDataClient combinedChainDataClient, + final StorageUpdateChannel storageUpdateChannel, + final Eth2P2PNetwork p2pNetwork, + final BlockImporter blockImporter, + final BlobSidecarManager blobSidecarManager, + final PendingPool pendingBlocksPool, + final BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool, + final SignatureVerificationService signatureVerificationService) { + return new DefaultSyncServiceFactory( + syncConfig, + eth2NetworkConfig.getNetworkBoostrapConfig().getGenesisState(), + metricsSystem, + asyncRunnerFactory, + beaconAsyncRunner, + timeProvider, + recentChainData, + combinedChainDataClient, + storageUpdateChannel, + p2pNetwork, + blockImporter, + blobSidecarManager, + pendingBlocksPool, + blockBlobSidecarsTrackersPool, + eth2NetworkConfig.getStartupTargetPeerCount(), + signatureVerificationService, + Duration.ofSeconds(eth2NetworkConfig.getStartupTimeoutSeconds()), + spec); + } + + @Provides + @Singleton + static SyncService syncService( + final SyncServiceFactory syncServiceFactory, + final EventChannels eventChannels, + final ForkChoice forkChoice, + final ForkChoiceNotifier forkChoiceNotifier, + final DepositProvider depositProvider, + final Optional terminalPowBlockMonitor, + final Eth2P2PNetwork p2pNetwork, + final ChainHeadChannel chainHeadChannelPublisher, + final EventLogger eventLogger) { + SyncService syncService = syncServiceFactory.create(eventChannels); + + // chainHeadChannel subscription + CoalescingChainHeadChannel coalescingChainHeadChannel = + new CoalescingChainHeadChannel(chainHeadChannelPublisher, eventLogger); + syncService.getForwardSync().subscribeToSyncChanges(coalescingChainHeadChannel); + + // forkChoiceNotifier subscription + syncService.subscribeToSyncStateChangesAndUpdate( + syncState -> forkChoiceNotifier.onSyncingStatusChanged(syncState.isInSync())); + + // depositProvider subscription + syncService.subscribeToSyncStateChangesAndUpdate( + syncState -> depositProvider.onSyncingStatusChanged(syncState.isInSync())); + + // forkChoice subscription + forkChoice.subscribeToOptimisticHeadChangesAndUpdate(syncService.getOptimisticSyncSubscriber()); + + // terminalPowBlockMonitor subscription + terminalPowBlockMonitor.ifPresent( + monitor -> + syncService.subscribeToSyncStateChangesAndUpdate( + syncState -> monitor.onNodeSyncStateChanged(syncState.isInSync()))); + + // p2pNetwork subscription so gossip can be enabled and disabled appropriately + syncService.subscribeToSyncStateChangesAndUpdate( + state -> p2pNetwork.onSyncStateChanged(state.isInSync(), state.isOptimistic())); + + return syncService; + } + + @Provides + @Singleton + static RecentBlocksFetcher recentBlocksFetcher( + final SyncService syncService, + final BlockManager blockManager, + final EventChannelSubscriber receivedBlockEventsChannelSubscriber, + final LoggingModule.InitLogger initLogger) { + final RecentBlocksFetcher recentBlocksFetcher = syncService.getRecentBlocksFetcher(); + recentBlocksFetcher.subscribeBlockFetched( + (block) -> + blockManager + .importBlock( + block, + BroadcastValidationLevel.NOT_REQUIRED, + Optional.of(BlobSidecarManager.RemoteOrigin.RPC)) + .thenCompose( + BlockImportChannel.BlockImportAndBroadcastValidationResults::blockImportResult) + .finish( + err -> + initLogger + .logger() + .error("Failed to process recently fetched block.", err))); + receivedBlockEventsChannelSubscriber.subscribe(recentBlocksFetcher); + return recentBlocksFetcher; + } + + @Provides + @Singleton + static RecentBlobSidecarsFetcher recentBlobSidecarsFetcher( + final SyncService syncService, final BlobSidecarManager blobSidecarManager) { + final RecentBlobSidecarsFetcher recentBlobSidecarsFetcher = + syncService.getRecentBlobSidecarsFetcher(); + recentBlobSidecarsFetcher.subscribeBlobSidecarFetched( + (blobSidecar) -> + blobSidecarManager.prepareForBlockImport( + blobSidecar, BlobSidecarManager.RemoteOrigin.RPC)); + blobSidecarManager.subscribeToReceivedBlobSidecar( + blobSidecar -> + recentBlobSidecarsFetcher.cancelRecentBlobSidecarRequest( + new BlobIdentifier(blobSidecar.getBlockRoot(), blobSidecar.getIndex()))); + return recentBlobSidecarsFetcher; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ValidatorModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ValidatorModule.java new file mode 100644 index 00000000000..363bae37aaf --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/ValidatorModule.java @@ -0,0 +1,297 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Lazy; +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; +import tech.pegasys.teku.api.ChainDataProvider; +import tech.pegasys.teku.api.DataProvider; +import tech.pegasys.teku.api.RewardCalculator; +import tech.pegasys.teku.beacon.sync.SyncService; +import tech.pegasys.teku.ethereum.events.ExecutionClientEventsChannel; +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.ethereum.executionclient.ExecutionClientVersionChannel; +import tech.pegasys.teku.ethereum.executionclient.ExecutionClientVersionProvider; +import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionAndPublishingPerformanceFactory; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; +import tech.pegasys.teku.networking.eth2.gossip.BlobSidecarGossipChannel; +import tech.pegasys.teku.networking.eth2.gossip.BlockGossipChannel; +import tech.pegasys.teku.networking.eth2.gossip.subnets.AttestationTopicSubscriber; +import tech.pegasys.teku.networking.eth2.gossip.subnets.SyncCommitteeSubscriptionManager; +import tech.pegasys.teku.services.beaconchain.ValidatorIsConnectedProviderImpl; +import tech.pegasys.teku.services.executionlayer.ExecutionLayerBlockManagerFactory; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.attestation.ValidatableAttestation; +import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; +import tech.pegasys.teku.spec.datastructures.operations.ProposerSlashing; +import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; +import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; +import tech.pegasys.teku.spec.executionlayer.ExecutionLayerBlockProductionManager; +import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel; +import tech.pegasys.teku.statetransition.OperationPool; +import tech.pegasys.teku.statetransition.attestation.AggregatingAttestationPool; +import tech.pegasys.teku.statetransition.attestation.AttestationManager; +import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTrackersPool; +import tech.pegasys.teku.statetransition.block.BlockImportChannel; +import tech.pegasys.teku.statetransition.block.BlockImporter; +import tech.pegasys.teku.statetransition.block.ReceivedBlockEventsChannel; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoice; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceNotifier; +import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceTrigger; +import tech.pegasys.teku.statetransition.forkchoice.ProposersDataManager; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeMessagePool; +import tech.pegasys.teku.statetransition.util.FutureItems; +import tech.pegasys.teku.statetransition.util.PendingPool; +import tech.pegasys.teku.statetransition.validation.AggregateAttestationValidator; +import tech.pegasys.teku.statetransition.validation.AttestationValidator; +import tech.pegasys.teku.statetransition.validation.signatures.SignatureVerificationService; +import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorChannel; +import tech.pegasys.teku.storage.api.FinalizedCheckpointChannel; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.storage.client.ValidatorIsConnectedProvider; +import tech.pegasys.teku.validator.api.ValidatorApiChannel; +import tech.pegasys.teku.validator.api.ValidatorConfig; +import tech.pegasys.teku.validator.coordinator.ActiveValidatorTracker; +import tech.pegasys.teku.validator.coordinator.BlockFactory; +import tech.pegasys.teku.validator.coordinator.BlockOperationSelectorFactory; +import tech.pegasys.teku.validator.coordinator.DepositProvider; +import tech.pegasys.teku.validator.coordinator.DutyMetrics; +import tech.pegasys.teku.validator.coordinator.Eth1DataCache; +import tech.pegasys.teku.validator.coordinator.GraffitiBuilder; +import tech.pegasys.teku.validator.coordinator.MilestoneBasedBlockFactory; +import tech.pegasys.teku.validator.coordinator.ValidatorApiHandler; +import tech.pegasys.teku.validator.coordinator.ValidatorIndexCacheTracker; +import tech.pegasys.teku.validator.coordinator.performance.PerformanceTracker; + +@Module +public interface ValidatorModule { + + @Provides + @Singleton + static ActiveValidatorTracker activeValidatorTracker( + final Spec spec, + final EventChannelSubscriber slotEventsChannelSubscriber) { + ActiveValidatorTracker activeValidatorTracker = new ActiveValidatorTracker(spec); + slotEventsChannelSubscriber.subscribe(activeValidatorTracker); + return activeValidatorTracker; + } + + @Provides + @Singleton + static ExecutionLayerBlockProductionManager executionLayerBlockProductionManager( + final ExecutionLayerChannel executionLayer, + final EventChannelSubscriber slotEventsChannelSubscriber) { + + return ExecutionLayerBlockManagerFactory.create(executionLayer, slotEventsChannelSubscriber); + } + + @Provides + @Singleton + static GraffitiBuilder graffitiBuilder( + final ValidatorConfig validatorConfig, + final EventChannelSubscriber + executionClientVersionChannelSubscriber) { + GraffitiBuilder graffitiBuilder = + new GraffitiBuilder(validatorConfig.getClientGraffitiAppendFormat()); + executionClientVersionChannelSubscriber.subscribe(graffitiBuilder); + return graffitiBuilder; + } + + @Provides + @Singleton + static ExecutionClientVersionProvider executionClientVersionProvider( + final GraffitiBuilder graffitiBuilder, + final ExecutionLayerChannel executionLayer, + final ExecutionClientVersionChannel executionClientVersionChannelPublisher, + final EventChannelSubscriber + executionClientEventsChannelSubscriber) { + + final ExecutionClientVersionProvider executionClientVersionProvider = + new ExecutionClientVersionProvider( + executionLayer, + executionClientVersionChannelPublisher, + graffitiBuilder.getConsensusClientVersion()); + executionClientEventsChannelSubscriber.subscribe(executionClientVersionProvider); + return executionClientVersionProvider; + } + + @Provides + @Singleton + static BlockOperationSelectorFactory blockOperationSelectorFactory( + final Spec spec, + final AggregatingAttestationPool attestationPool, + final OperationPool attesterSlashingPool, + final OperationPool proposerSlashingPool, + final OperationPool voluntaryExitPool, + final OperationPool blsToExecutionChangePool, + final SyncCommitteeContributionPool syncCommitteeContributionPool, + final DepositProvider depositProvider, + final Eth1DataCache eth1DataCache, + final ForkChoiceNotifier forkChoiceNotifier, + final ExecutionLayerBlockProductionManager executionLayerBlockProductionManager, + final GraffitiBuilder graffitiBuilder) { + + return new BlockOperationSelectorFactory( + spec, + attestationPool, + attesterSlashingPool, + proposerSlashingPool, + voluntaryExitPool, + blsToExecutionChangePool, + syncCommitteeContributionPool, + depositProvider, + eth1DataCache, + graffitiBuilder, + forkChoiceNotifier, + executionLayerBlockProductionManager); + } + + @Provides + @Singleton + static BlockFactory blockFactory( + final Spec spec, final BlockOperationSelectorFactory blockOperationSelectorFactory) { + return new MilestoneBasedBlockFactory(spec, blockOperationSelectorFactory); + } + + @Provides + @Singleton + static ChainDataProvider chainDataProvider( + final Spec spec, + final RecentChainData recentChainData, + final CombinedChainDataClient combinedChainDataClient, + final RewardCalculator rewardCalculator) { + return new ChainDataProvider(spec, recentChainData, combinedChainDataClient, rewardCalculator); + } + + @Provides + @Singleton + static ValidatorApiHandler validatorApiHandler( + final Spec spec, + final ChainDataProvider chainDataProvider, + final DataProvider dataProvider, + final CombinedChainDataClient combinedChainDataClient, + final SyncService syncService, + final BlockFactory blockFactory, + final BlockImportChannel blockImportChannel, + final BlockGossipChannel blockGossipChannel, + final BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool, + final BlobSidecarGossipChannel blobSidecarGossipChannel, + final AggregatingAttestationPool attestationPool, + final AttestationManager attestationManager, + final AttestationTopicSubscriber attestationTopicSubscriber, + final ActiveValidatorTracker activeValidatorTracker, + final DutyMetrics dutyMetrics, + final PerformanceTracker performanceTracker, + final ForkChoiceTrigger forkChoiceTrigger, + final ProposersDataManager proposersDataManager, + final SyncCommitteeMessagePool syncCommitteeMessagePool, + final SyncCommitteeContributionPool syncCommitteeContributionPool, + final SyncCommitteeSubscriptionManager syncCommitteeSubscriptionManager, + final BlockProductionAndPublishingPerformanceFactory blockProductionPerformanceFactory, + final EventChannelSubscriber validatorApiChannelSubscriber) { + ValidatorApiHandler validatorApiHandler = + new ValidatorApiHandler( + chainDataProvider, + dataProvider.getNodeDataProvider(), + combinedChainDataClient, + syncService, + blockFactory, + blockImportChannel, + blockGossipChannel, + blockBlobSidecarsTrackersPool, + blobSidecarGossipChannel, + attestationPool, + attestationManager, + attestationTopicSubscriber, + activeValidatorTracker, + dutyMetrics, + performanceTracker, + spec, + forkChoiceTrigger, + proposersDataManager, + syncCommitteeMessagePool, + syncCommitteeContributionPool, + syncCommitteeSubscriptionManager, + blockProductionPerformanceFactory); + + validatorApiChannelSubscriber.subscribe(validatorApiHandler); + + return validatorApiHandler; + } + + @Provides + @Singleton + static AttestationManager attestationManager( + final Spec spec, + final BlockImporter blockImporter, + final AggregateAttestationValidator aggregateValidator, + final PendingPool pendingAttestations, + final FutureItems futureAttestations, + final ForkChoice forkChoice, + final AggregatingAttestationPool attestationPool, + final AttestationValidator attestationValidator, + final SignatureVerificationService signatureVerificationService, + final ActiveValidatorChannel activeValidatorChannel, + final EventChannelSubscriber slotEventsChannelSubscriber, + final EventChannelSubscriber + receivedBlockEventsChannelSubscriber) { + + // TODO + blockImporter.subscribeToVerifiedBlockAttestations( + (slot, attestations) -> + attestations.forEach( + attestation -> + aggregateValidator.addSeenAggregate( + ValidatableAttestation.from(spec, attestation)))); + + AttestationManager attestationManager = + AttestationManager.create( + pendingAttestations, + futureAttestations, + forkChoice, + attestationPool, + attestationValidator, + aggregateValidator, + signatureVerificationService, + activeValidatorChannel); + + slotEventsChannelSubscriber.subscribe(attestationManager); + receivedBlockEventsChannelSubscriber.subscribe(attestationManager); + + return attestationManager; + } + + @Provides + @Singleton + static ValidatorIsConnectedProvider validatorIsConnectedProvider( + final Lazy forkChoiceNotifier) { + return new ValidatorIsConnectedProviderImpl(() -> forkChoiceNotifier.get()); + } + + @Provides + @Singleton + static ValidatorIndexCacheTracker validatorIndexCacheTracker( + final RecentChainData recentChainData, + final EventChannelSubscriber + finalizedCheckpointChannelSubscriber) { + final ValidatorIndexCacheTracker validatorIndexCacheTracker = + new ValidatorIndexCacheTracker(recentChainData); + finalizedCheckpointChannelSubscriber.subscribe(validatorIndexCacheTracker); + return validatorIndexCacheTracker; + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/VerifyModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/VerifyModule.java new file mode 100644 index 00000000000..27571c37b9e --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/VerifyModule.java @@ -0,0 +1,157 @@ +/* + * 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.services.beaconchain.init; + +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel; +import tech.pegasys.teku.statetransition.block.ReceivedBlockEventsChannel; +import tech.pegasys.teku.statetransition.forkchoice.MergeTransitionBlockValidator; +import tech.pegasys.teku.statetransition.synccommittee.SignedContributionAndProofValidator; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeMessageValidator; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeStateUtils; +import tech.pegasys.teku.statetransition.validation.AggregateAttestationValidator; +import tech.pegasys.teku.statetransition.validation.AttestationValidator; +import tech.pegasys.teku.statetransition.validation.AttesterSlashingValidator; +import tech.pegasys.teku.statetransition.validation.BlockGossipValidator; +import tech.pegasys.teku.statetransition.validation.BlockValidator; +import tech.pegasys.teku.statetransition.validation.GossipValidationHelper; +import tech.pegasys.teku.statetransition.validation.ProposerSlashingValidator; +import tech.pegasys.teku.statetransition.validation.SignedBlsToExecutionChangeValidator; +import tech.pegasys.teku.statetransition.validation.VoluntaryExitValidator; +import tech.pegasys.teku.statetransition.validation.signatures.SignatureVerificationService; +import tech.pegasys.teku.storage.client.RecentChainData; + +@Module +public interface VerifyModule { + @Provides + @Singleton + static GossipValidationHelper gossipValidationHelper( + final Spec spec, final RecentChainData recentChainData) { + return new GossipValidationHelper(spec, recentChainData); + } + + @Provides + @Singleton + static AttesterSlashingValidator attesterSlashingValidator( + final Spec spec, final RecentChainData recentChainData) { + return new AttesterSlashingValidator(recentChainData, spec); + } + + @Provides + @Singleton + static ProposerSlashingValidator proposerSlashingValidator( + final Spec spec, final RecentChainData recentChainData) { + return new ProposerSlashingValidator(spec, recentChainData); + } + + @Provides + @Singleton + static VoluntaryExitValidator voluntaryExitValidator( + final Spec spec, final RecentChainData recentChainData) { + return new VoluntaryExitValidator(spec, recentChainData); + } + + @Provides + @Singleton + static SignedBlsToExecutionChangeValidator signedBlsToExecutionChangeValidator( + final Spec spec, + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final SignatureVerificationService signatureVerificationService) { + return new SignedBlsToExecutionChangeValidator( + spec, timeProvider, recentChainData, signatureVerificationService); + } + + @Provides + @Singleton + static MergeTransitionBlockValidator mergeTransitionBlockValidator( + final Spec spec, + final RecentChainData recentChainData, + final ExecutionLayerChannel executionLayer) { + return new MergeTransitionBlockValidator(spec, recentChainData, executionLayer); + } + + @Provides + @Singleton + static AttestationValidator attestationValidator( + final Spec spec, + final RecentChainData recentChainData, + final SignatureVerificationService signatureVerificationService, + final MetricsSystem metricsSystem) { + return new AttestationValidator( + spec, recentChainData, signatureVerificationService, metricsSystem); + } + + @Provides + @Singleton + static AggregateAttestationValidator aggregateAttestationValidator( + final Spec spec, + final AttestationValidator attestationValidator, + final SignatureVerificationService signatureVerificationService) { + return new AggregateAttestationValidator( + spec, attestationValidator, signatureVerificationService); + } + + @Provides + @Singleton + static SyncCommitteeStateUtils syncCommitteeStateUtils( + final Spec spec, final RecentChainData recentChainData) { + return new SyncCommitteeStateUtils(spec, recentChainData); + } + + @Provides + @Singleton + static SignedContributionAndProofValidator signedContributionAndProofValidator( + final Spec spec, + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final SyncCommitteeStateUtils syncCommitteeStateUtils, + final SignatureVerificationService signatureVerificationService) { + return new SignedContributionAndProofValidator( + spec, recentChainData, syncCommitteeStateUtils, timeProvider, signatureVerificationService); + } + + @Provides + @Singleton + static SyncCommitteeMessageValidator syncCommitteeMessageValidator( + final Spec spec, + final TimeProvider timeProvider, + final RecentChainData recentChainData, + final SyncCommitteeStateUtils syncCommitteeStateUtils, + final SignatureVerificationService signatureVerificationService) { + return new SyncCommitteeMessageValidator( + spec, recentChainData, syncCommitteeStateUtils, signatureVerificationService, timeProvider); + } + + @Provides + @Singleton + static BlockGossipValidator blockGossipValidator( + final Spec spec, + final GossipValidationHelper gossipValidationHelper, + final ReceivedBlockEventsChannel receivedBlockEventsChannelPublisher) { + return new BlockGossipValidator( + spec, gossipValidationHelper, receivedBlockEventsChannelPublisher); + } + + @Provides + @Singleton + static BlockValidator blockValidator(final BlockGossipValidator blockGossipValidator) { + return new BlockValidator(blockGossipValidator); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/WSModule.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/WSModule.java new file mode 100644 index 00000000000..56de99d304e --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/init/WSModule.java @@ -0,0 +1,127 @@ +/* + * 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.services.beaconchain.init; + +import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ZERO; + +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.services.beaconchain.WeakSubjectivityInitializer; +import tech.pegasys.teku.services.beaconchain.init.SpecModule.CurrentSlotProvider; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.state.AnchorPoint; +import tech.pegasys.teku.storage.api.StorageQueryChannel; +import tech.pegasys.teku.storage.api.StorageUpdateChannel; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.client.RecentChainData; +import tech.pegasys.teku.weaksubjectivity.WeakSubjectivityCalculator; +import tech.pegasys.teku.weaksubjectivity.WeakSubjectivityValidator; +import tech.pegasys.teku.weaksubjectivity.config.WeakSubjectivityConfig; + +@Module +public interface WSModule { + + record WeakSubjectivityFinalizedConfig(WeakSubjectivityConfig config) {} + + interface WeakSubjectivityPeriodValidator { + void validate(RecentChainData recentChainData); + } + + interface WeakSubjectivityStoreChainValidator { + void validate(UInt64 currentSlot); + } + + @Provides + @Singleton + static WeakSubjectivityInitializer weakSubjectivityInitializer() { + return new WeakSubjectivityInitializer(); + } + + @Provides + @Singleton + static SafeFuture weakSubjectivityFinalizedConfigFuture( + final WeakSubjectivityInitializer weakSubjectivityInitializer, + final WeakSubjectivityConfig weakSubjectivityConfig, + final StorageQueryChannel storageQueryChannel, + final StorageUpdateChannel storageUpdateChannel) { + SafeFuture finalizedConfig = + weakSubjectivityInitializer.finalizeAndStoreConfig( + weakSubjectivityConfig, storageQueryChannel, storageUpdateChannel); + return finalizedConfig.thenApply(WeakSubjectivityFinalizedConfig::new); + } + + @Provides + @Singleton + // TODO producer ? + static WeakSubjectivityFinalizedConfig weakSubjectivityConfig( + final SafeFuture weakSubjectivityConfigFuture) { + return weakSubjectivityConfigFuture.join(); + } + + @Provides + @Singleton + static WeakSubjectivityValidator weakSubjectivityValidator( + final WeakSubjectivityFinalizedConfig weakSubjectivityConfig) { + return WeakSubjectivityValidator.moderate(weakSubjectivityConfig.config()); + } + + @Provides + @Singleton + static WeakSubjectivityPeriodValidator weakSubjectivityPeriodValidator( + final Spec spec, + final CurrentSlotProvider currentSlotProvider, + final WeakSubjectivityConfig weakSubjectivityConfig, + final WeakSubjectivityInitializer weakSubjectivityInitializer) { + return client -> { + final AnchorPoint latestFinalizedAnchor = client.getStore().getLatestFinalized(); + final UInt64 currentSlot = currentSlotProvider.getCurrentSlot(client.getGenesisTime()); + final WeakSubjectivityCalculator wsCalculator = + WeakSubjectivityCalculator.create(weakSubjectivityConfig); + weakSubjectivityInitializer.validateAnchorIsWithinWeakSubjectivityPeriod( + latestFinalizedAnchor, currentSlot, spec, wsCalculator); + }; + } + + @Provides + @Singleton + static WeakSubjectivityStoreChainValidator weakSubjectivityStoreChainValidator( + final WeakSubjectivityValidator weakSubjectivityValidator, + final CombinedChainDataClient combinedChainDataClient, + final RecentChainData recentChainData) { + return currentSlot -> { + weakSubjectivityValidator + .validateChainIsConsistentWithWSCheckpoint(combinedChainDataClient) + .thenCompose( + __ -> + SafeFuture.of( + () -> recentChainData.getStore().retrieveFinalizedCheckpointAndState())) + .thenAccept( + finalizedCheckpointState -> { + final UInt64 slot = currentSlot.max(recentChainData.getCurrentSlot().orElse(ZERO)); + weakSubjectivityValidator.validateLatestFinalizedCheckpoint( + finalizedCheckpointState, slot); + }) + .finish( + err -> { + weakSubjectivityValidator.handleValidationFailure( + "Encountered an error while trying to validate latest finalized checkpoint", + err); + throw new RuntimeException(err); + }); + }; + } +} diff --git a/services/executionlayer/src/main/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerBlockManagerFactory.java b/services/executionlayer/src/main/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerBlockManagerFactory.java index 6030dc5f6d4..7f110f854d8 100644 --- a/services/executionlayer/src/main/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerBlockManagerFactory.java +++ b/services/executionlayer/src/main/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerBlockManagerFactory.java @@ -15,16 +15,17 @@ import tech.pegasys.teku.ethereum.events.SlotEventsChannel; import tech.pegasys.teku.ethereum.executionlayer.ExecutionLayerBlockProductionManagerImpl; -import tech.pegasys.teku.infrastructure.events.EventChannels; +import tech.pegasys.teku.infrastructure.events.EventChannelSubscriber; import tech.pegasys.teku.spec.executionlayer.ExecutionLayerBlockProductionManager; import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel; public class ExecutionLayerBlockManagerFactory { public static ExecutionLayerBlockProductionManager create( - final ExecutionLayerChannel executionLayerManager, final EventChannels eventChannels) { + final ExecutionLayerChannel executionLayerManager, + final EventChannelSubscriber slotEventsChannelSubscriber) { final ExecutionLayerBlockProductionManagerImpl executionLayerBlockProductionManager = new ExecutionLayerBlockProductionManagerImpl(executionLayerManager); - eventChannels.subscribe(SlotEventsChannel.class, executionLayerBlockProductionManager); + slotEventsChannelSubscriber.subscribe(executionLayerBlockProductionManager); return executionLayerBlockProductionManager; } diff --git a/teku/build.gradle b/teku/build.gradle index 475d195a664..69a90417d0c 100644 --- a/teku/build.gradle +++ b/teku/build.gradle @@ -71,4 +71,9 @@ dependencies { testImplementation project(':data:provider') testImplementation 'org.awaitility:awaitility' + + testImplementation 'com.google.dagger:dagger:2.51.1' + testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.51.1' + + testImplementation project(':ethereum:performance-trackers') } diff --git a/teku/src/test/java/tech/pegasys/teku/config/TekuConfigurationTest.java b/teku/src/test/java/tech/pegasys/teku/config/TekuConfigurationTest.java index 8f7aebcc234..69a4a6a683a 100644 --- a/teku/src/test/java/tech/pegasys/teku/config/TekuConfigurationTest.java +++ b/teku/src/test/java/tech/pegasys/teku/config/TekuConfigurationTest.java @@ -15,14 +15,26 @@ import static org.assertj.core.api.Assertions.assertThat; +import dagger.Component; +import dagger.Module; +import dagger.Provides; import java.nio.file.Path; import java.util.concurrent.atomic.AtomicBoolean; +import javax.inject.Singleton; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.plugin.services.MetricsSystem; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import tech.pegasys.teku.BeaconNodeFacade; import tech.pegasys.teku.TekuFacade; import tech.pegasys.teku.cli.TempDirUtils; +import tech.pegasys.teku.infrastructure.async.AsyncRunner; +import tech.pegasys.teku.infrastructure.events.EventChannels; +import tech.pegasys.teku.infrastructure.time.TimeProvider; +import tech.pegasys.teku.kzg.KZG; +import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork; import tech.pegasys.teku.networking.eth2.Eth2P2PNetworkBuilder; +import tech.pegasys.teku.networking.eth2.P2PConfig; import tech.pegasys.teku.networking.p2p.discovery.DiscoveryNetwork; import tech.pegasys.teku.networking.p2p.discovery.DiscoveryNetworkBuilder; import tech.pegasys.teku.networking.p2p.libp2p.LibP2PNetworkBuilder; @@ -30,8 +42,48 @@ import tech.pegasys.teku.networking.p2p.libp2p.gossip.LibP2PGossipNetworkBuilder; import tech.pegasys.teku.networking.p2p.network.P2PNetwork; import tech.pegasys.teku.networking.p2p.peer.Peer; -import tech.pegasys.teku.services.beaconchain.BeaconChainController; +import tech.pegasys.teku.service.serviceutils.layout.DataDirLayout; +import tech.pegasys.teku.services.beaconchain.BeaconChainControllerFacade; import tech.pegasys.teku.services.beaconchain.BeaconChainControllerFactory; +import tech.pegasys.teku.services.beaconchain.LateInitDelegateBeaconChainController; +import tech.pegasys.teku.services.beaconchain.init.AsyncRunnerModule; +import tech.pegasys.teku.services.beaconchain.init.BeaconConfigModule; +import tech.pegasys.teku.services.beaconchain.init.BeaconModule; +import tech.pegasys.teku.services.beaconchain.init.BlobModule; +import tech.pegasys.teku.services.beaconchain.init.ChannelsModule; +import tech.pegasys.teku.services.beaconchain.init.CryptoModule; +import tech.pegasys.teku.services.beaconchain.init.DataProviderModule; +import tech.pegasys.teku.services.beaconchain.init.ExternalDependenciesModule; +import tech.pegasys.teku.services.beaconchain.init.ForkChoiceModule; +import tech.pegasys.teku.services.beaconchain.init.LoggingModule; +import tech.pegasys.teku.services.beaconchain.init.MainModule; +import tech.pegasys.teku.services.beaconchain.init.MetricsModule; +import tech.pegasys.teku.services.beaconchain.init.NetworkModule; +import tech.pegasys.teku.services.beaconchain.init.PoolAndCachesModule; +import tech.pegasys.teku.services.beaconchain.init.PowModule; +import tech.pegasys.teku.services.beaconchain.init.ServiceConfigModule; +import tech.pegasys.teku.services.beaconchain.init.SpecModule; +import tech.pegasys.teku.services.beaconchain.init.StorageModule; +import tech.pegasys.teku.services.beaconchain.init.SubnetsModule; +import tech.pegasys.teku.services.beaconchain.init.SyncModule; +import tech.pegasys.teku.services.beaconchain.init.ValidatorModule; +import tech.pegasys.teku.services.beaconchain.init.VerifyModule; +import tech.pegasys.teku.services.beaconchain.init.WSModule; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; +import tech.pegasys.teku.spec.datastructures.operations.ProposerSlashing; +import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; +import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; +import tech.pegasys.teku.statetransition.OperationPool; +import tech.pegasys.teku.statetransition.attestation.AttestationManager; +import tech.pegasys.teku.statetransition.blobs.BlobSidecarManager; +import tech.pegasys.teku.statetransition.block.BlockManager; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; +import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeMessagePool; +import tech.pegasys.teku.statetransition.util.DebugDataDumper; +import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.store.KeyValueStore; +import tech.pegasys.teku.weaksubjectivity.WeakSubjectivityValidator; public class TekuConfigurationTest { @@ -94,13 +146,14 @@ protected LibP2PNetworkBuilder createLibP2PNetworkBuilder() { }; BeaconChainControllerFactory customControllerFactory = - (serviceConfig, beaconConfig) -> - new BeaconChainController(serviceConfig, beaconConfig) { - @Override - protected Eth2P2PNetworkBuilder createEth2P2PNetworkBuilder() { - return customEth2P2PNetworkBuilder; - } - }; + LateInitDelegateBeaconChainController.createLateInitFactory( + (serviceConfig, beaconConfig) -> + DaggerTekuConfigurationTest_TestBeaconChainControllerComponent.builder() + .externalDependenciesModule( + new ExternalDependenciesModule(serviceConfig, beaconConfig)) + .testNetworkModule(new TestNetworkModule(customEth2P2PNetworkBuilder)) + .build() + .beaconChainController()); TekuConfiguration tekuConfiguration = TekuConfiguration.builder() @@ -117,4 +170,106 @@ protected Eth2P2PNetworkBuilder createEth2P2PNetworkBuilder() { assertThat(customGossipNetworkBuilderCalled).isTrue(); } } + + @Singleton + @Component( + modules = { + AsyncRunnerModule.class, + BeaconConfigModule.class, + BeaconModule.class, + BlobModule.class, + ChannelsModule.class, + CryptoModule.class, + DataProviderModule.class, + ExternalDependenciesModule.class, + ForkChoiceModule.class, + LoggingModule.class, + MainModule.class, + MetricsModule.class, + // NetworkModule.class, + TestNetworkModule.class, + PoolAndCachesModule.class, + PowModule.class, + ServiceConfigModule.class, + SpecModule.class, + StorageModule.class, + SubnetsModule.class, + SyncModule.class, + ValidatorModule.class, + VerifyModule.class, + WSModule.class + }) + public interface TestBeaconChainControllerComponent { + + BeaconChainControllerFacade beaconChainController(); + } + + @Module + public static class TestNetworkModule { + private final Eth2P2PNetworkBuilder eth2P2PNetworkBuilder; + + public TestNetworkModule(final Eth2P2PNetworkBuilder eth2P2PNetworkBuilder) { + this.eth2P2PNetworkBuilder = eth2P2PNetworkBuilder; + } + + @Provides + Eth2P2PNetworkBuilder eth2P2PNetworkBuilder() { + return eth2P2PNetworkBuilder; + } + + @Provides + @Singleton + static Eth2P2PNetwork eth2P2PNetwork( + final Spec spec, + final P2PConfig p2pConfig, + final MetricsSystem metricsSystem, + @AsyncRunnerModule.NetworkAsyncRunner final AsyncRunner networkAsyncRunner, + final TimeProvider timeProvider, + final EventChannels eventChannels, + final KeyValueStore keyValueStore, + final Eth2P2PNetworkBuilder eth2P2PNetworkBuilder, + final CombinedChainDataClient combinedChainDataClient, + final BlockManager blockManager, + final BlobSidecarManager blobSidecarManager, + final AttestationManager attestationManager, + final OperationPool attesterSlashingPool, + final OperationPool proposerSlashingPool, + final OperationPool voluntaryExitPool, + final SyncCommitteeContributionPool syncCommitteeContributionPool, + final SyncCommitteeMessagePool syncCommitteeMessagePool, + final OperationPool blsToExecutionChangePool, + final KZG kzg, + final WeakSubjectivityValidator weakSubjectivityValidator, + final DebugDataDumper p2pDebugDataDumper) { + + return NetworkModule.eth2P2PNetwork( + spec, + p2pConfig, + metricsSystem, + networkAsyncRunner, + timeProvider, + eventChannels, + keyValueStore, + eth2P2PNetworkBuilder, + combinedChainDataClient, + blockManager, + blobSidecarManager, + attestationManager, + attesterSlashingPool, + proposerSlashingPool, + voluntaryExitPool, + syncCommitteeContributionPool, + syncCommitteeMessagePool, + blsToExecutionChangePool, + kzg, + weakSubjectivityValidator, + p2pDebugDataDumper); + } + + @Provides + @Singleton + static DebugDataDumper p2pDebugDataDumper(final DataDirLayout dataDirLayout) { + return NetworkModule.debugDataDumper(dataDirLayout); + } + } }