diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/MirrorNodeState.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/MirrorNodeState.java index 5ef545378e..fd08f2622c 100644 --- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/MirrorNodeState.java +++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/MirrorNodeState.java @@ -76,6 +76,7 @@ import jakarta.inject.Named; import java.time.InstantSource; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -127,8 +128,8 @@ private void init() { networkInfo, new MetricsImpl()); - final var writableStates = this.getWritableStates(FileService.NAME); - final var files = writableStates.get(V0490FileSchema.BLOBS_KEY); + final var fileServiceStates = this.getWritableStates(FileService.NAME); + final var files = fileServiceStates.get(V0490FileSchema.BLOBS_KEY); genesisContentProviders(networkInfo, bootstrapConfig).forEach((fileNum, provider) -> { final var fileId = createFileID(fileNum, bootstrapConfig); files.put( @@ -139,7 +140,7 @@ private void init() { .contents(provider.apply(bootstrapConfig)) .build()); }); - ((CommittableWritableStates) writableStates).commit(); + ((CommittableWritableStates) fileServiceStates).commit(); } public MirrorNodeState addService(@NonNull final String serviceName, @NonNull final Map dataSources) { @@ -227,7 +228,7 @@ public WritableStates getWritableStates(@Nonnull String serviceName) { data.put( stateName, withAnyRegisteredListeners(serviceName, new ListWritableQueueState<>(stateName, queue))); - } else if (state instanceof Map map) { + } else if (state instanceof Map) { data.put( stateName, withAnyRegisteredListeners( @@ -361,6 +362,11 @@ void setWritableStates(final Map writableStates) { this.writableStates.putAll(writableStates); } + @VisibleForTesting + Map> getStates() { + return Collections.unmodifiableMap(states); + } + private void registerServices(ServicesRegistry servicesRegistry) { // Register all service schema RuntimeConstructable factories before platform init final var appContext = new AppContextImpl(InstantSource.system(), signatureVerifier()); diff --git a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/components/MetricsImpl.java b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/components/MetricsImpl.java index 412b84e577..bacb907116 100644 --- a/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/components/MetricsImpl.java +++ b/hedera-mirror-web3/src/main/java/com/hedera/mirror/web3/state/components/MetricsImpl.java @@ -28,42 +28,54 @@ public class MetricsImpl implements Metrics { @Nullable @Override public Metric getMetric(@Nonnull String category, @Nonnull String name) { - return null; + throw new UnsupportedOperationException(); } @Nonnull @Override public Collection findMetricsByCategory(@Nonnull String category) { - return null; + throw new UnsupportedOperationException(); } @Nonnull @Override public Collection getAll() { - return null; + throw new UnsupportedOperationException(); } @Nonnull @Override public T getOrCreate(@Nonnull MetricConfig config) { - return null; + throw new UnsupportedOperationException(); } @Override - public void remove(@Nonnull String category, @Nonnull String name) {} + public void remove(@Nonnull String category, @Nonnull String name) { + throw new UnsupportedOperationException(); + } @Override - public void remove(@Nonnull Metric metric) {} + public void remove(@Nonnull Metric metric) { + throw new UnsupportedOperationException(); + } @Override - public void remove(@Nonnull MetricConfig config) {} + public void remove(@Nonnull MetricConfig config) { + throw new UnsupportedOperationException(); + } @Override - public void addUpdater(@Nonnull Runnable updater) {} + public void addUpdater(@Nonnull Runnable updater) { + throw new UnsupportedOperationException(); + } @Override - public void removeUpdater(@Nonnull Runnable updater) {} + public void removeUpdater(@Nonnull Runnable updater) { + throw new UnsupportedOperationException(); + } @Override - public void start() {} + public void start() { + throw new UnsupportedOperationException(); + } } diff --git a/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateIntegrationTest.java b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateIntegrationTest.java new file mode 100644 index 0000000000..f215a49b05 --- /dev/null +++ b/hedera-mirror-web3/src/test/java/com/hedera/mirror/web3/state/MirrorNodeStateIntegrationTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * 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 com.hedera.mirror.web3.state; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import com.hedera.mirror.web3.Web3IntegrationTest; +import com.hedera.node.app.fees.FeeService; +import com.hedera.node.app.ids.EntityIdService; +import com.hedera.node.app.records.BlockRecordService; +import com.hedera.node.app.service.contract.ContractService; +import com.hedera.node.app.service.contract.impl.ContractServiceImpl; +import com.hedera.node.app.service.file.FileService; +import com.hedera.node.app.service.file.impl.FileServiceImpl; +import com.hedera.node.app.service.token.TokenService; +import com.hedera.node.app.service.token.impl.TokenServiceImpl; +import com.hedera.node.app.services.ServicesRegistry; +import com.hedera.node.app.state.recordcache.RecordCacheService; +import com.hedera.node.app.throttle.CongestionThrottleService; +import com.swirlds.state.spi.Service; +import java.util.Deque; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; + +@RequiredArgsConstructor +public class MirrorNodeStateIntegrationTest extends Web3IntegrationTest { + + private final MirrorNodeState mirrorNodeState; + private final ServicesRegistry servicesRegistry; + + @Test + void verifyMirrorNodeStateHasRegisteredServices() { + Set> expectedServices = new HashSet<>(List.of( + EntityIdService.class, + TokenServiceImpl.class, + FileServiceImpl.class, + ContractServiceImpl.class, + BlockRecordService.class, + FeeService.class, + CongestionThrottleService.class, + RecordCacheService.class)); + + final var registeredServices = servicesRegistry.registrations(); + assertThat(registeredServices.size()).isEqualTo(expectedServices.size()); + + for (var expectedService : expectedServices) { + assertThat(registeredServices.stream() + .anyMatch(registration -> + registration.service().getClass().equals(expectedService))) + .isTrue(); + } + } + + @Test + void verifyServicesHaveAssignedDataSources() { + final var states = mirrorNodeState.getStates(); + + // BlockRecordService + Map> blockRecordServiceDataSources = Map.of( + "BLOCKS", AtomicReference.class, + "RUNNING_HASHES", AtomicReference.class); + verifyServiceDataSources(states, BlockRecordService.NAME, blockRecordServiceDataSources); + + // FileService + Map> fileServiceDataSources = Map.of("FILES", Map.class); + verifyServiceDataSources(states, FileService.NAME, fileServiceDataSources); + + // CongestionThrottleService + Map> congestionThrottleServiceDataSources = Map.of( + "THROTTLE_USAGE_SNAPSHOTS", AtomicReference.class, + "CONGESTION_LEVEL_STARTS", AtomicReference.class); + verifyServiceDataSources(states, CongestionThrottleService.NAME, congestionThrottleServiceDataSources); + + // FeeService + Map> feeServiceDataSources = Map.of("MIDNIGHT_RATES", AtomicReference.class); + verifyServiceDataSources(states, FeeService.NAME, feeServiceDataSources); + + // ContractService + Map> contractServiceDataSources = Map.of( + "BYTECODE", Map.class, + "STORAGE", Map.class); + verifyServiceDataSources(states, ContractService.NAME, contractServiceDataSources); + + // RecordCacheService + Map> recordCacheServiceDataSources = Map.of("TransactionReceiptQueue", Deque.class); + verifyServiceDataSources(states, RecordCacheService.NAME, recordCacheServiceDataSources); + + // EntityIdService + Map> entityIdServiceDataSources = Map.of("ENTITY_ID", AtomicReference.class); + verifyServiceDataSources(states, EntityIdService.NAME, entityIdServiceDataSources); + + // TokenService + Map> tokenServiceDataSources = Map.of( + "ACCOUNTS", Map.class, + "PENDING_AIRDROPS", Map.class, + "ALIASES", Map.class, + "NFTS", Map.class, + "TOKENS", Map.class, + "TOKEN_RELS", Map.class, + "STAKING_NETWORK_REWARDS", AtomicReference.class); + verifyServiceDataSources(states, TokenService.NAME, tokenServiceDataSources); + } + + private void verifyServiceDataSources( + Map> states, String serviceName, Map> expectedDataSources) { + final var serviceState = states.get(serviceName); + assertThat(serviceState).isNotNull(); + expectedDataSources.forEach((key, type) -> { + assertThat(serviceState.containsKey(key)).isTrue(); + assertThat(serviceState.get(key)).isInstanceOf(type); + }); + } +}