diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/AppContext.java b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/AppContext.java index 8725c57a7134..c84e77f8b124 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/AppContext.java +++ b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/AppContext.java @@ -21,6 +21,7 @@ import com.hedera.node.app.spi.signatures.SignatureVerifier; import com.swirlds.common.crypto.Signature; import com.swirlds.config.api.Configuration; +import com.swirlds.metrics.api.Metrics; import com.swirlds.state.lifecycle.Service; import com.swirlds.state.lifecycle.info.NodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; @@ -92,14 +93,23 @@ public Signature sign(final byte[] ledgerId) { */ Gossip gossip(); + /** + * The metrics provider for the system + * + * @return the metrics provider + */ + Supplier metricsSupplier(); + /** * The active configuration of the application. + * * @return the configuration */ Supplier configSupplier(); /** * The supplier of the self node info. + * * @return the supplier */ Supplier selfNodeInfoSupplier(); diff --git a/hedera-node/hedera-app-spi/src/main/java/module-info.java b/hedera-node/hedera-app-spi/src/main/java/module-info.java index ca84080f4df9..8e08eb99c651 100644 --- a/hedera-node/hedera-app-spi/src/main/java/module-info.java +++ b/hedera-node/hedera-app-spi/src/main/java/module-info.java @@ -3,6 +3,7 @@ requires transitive com.hedera.node.hapi; requires transitive com.swirlds.common; requires transitive com.swirlds.config.api; + requires transitive com.swirlds.metrics.api; requires transitive com.swirlds.state.api; requires transitive com.hedera.pbj.runtime; requires static com.github.spotbugs.annotations; diff --git a/hedera-node/hedera-app/src/jmh/java/com/hedera/node/app/blocks/BlockStreamManagerBenchmark.java b/hedera-node/hedera-app/src/jmh/java/com/hedera/node/app/blocks/BlockStreamManagerBenchmark.java index b18c6d8250bc..b7879f76b944 100644 --- a/hedera-node/hedera-app/src/jmh/java/com/hedera/node/app/blocks/BlockStreamManagerBenchmark.java +++ b/hedera-node/hedera-app/src/jmh/java/com/hedera/node/app/blocks/BlockStreamManagerBenchmark.java @@ -54,6 +54,7 @@ import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.crypto.Hash; import com.swirlds.common.metrics.noop.NoOpMetrics; +import com.swirlds.metrics.api.Metrics; import com.swirlds.platform.state.service.PlatformStateService; import com.swirlds.platform.state.service.schemas.V0540PlatformStateSchema; import com.swirlds.platform.system.Round; @@ -117,18 +118,20 @@ public static void main(String... args) throws Exception { "blockStream.hashCombineBatchSize", "64", "blockStream.serializationBatchSize", "32")); private final List roundItems = new ArrayList<>(); + private final Metrics noopMetrics = new NoOpMetrics(); private final TssBaseServiceImpl tssBaseService = new TssBaseServiceImpl( new AppContextImpl( Instant::now, fakeSignatureVerifier(), UNAVAILABLE_GOSSIP, + () -> noopMetrics, () -> configProvider.getConfiguration(), () -> DEFAULT_NODE_INFO), ForkJoinPool.commonPool(), ForkJoinPool.commonPool(), new PlaceholderTssLibrary(), ForkJoinPool.commonPool(), - new NoOpMetrics()); + noopMetrics); private final BlockStreamManagerImpl subject = new BlockStreamManagerImpl( NoopBlockItemWriter::new, // BaosBlockItemWriter::new, diff --git a/hedera-node/hedera-app/src/jmh/java/com/hedera/node/app/blocks/StandaloneRoundManagement.java b/hedera-node/hedera-app/src/jmh/java/com/hedera/node/app/blocks/StandaloneRoundManagement.java index 3f770009f24d..fafa467e2c78 100644 --- a/hedera-node/hedera-app/src/jmh/java/com/hedera/node/app/blocks/StandaloneRoundManagement.java +++ b/hedera-node/hedera-app/src/jmh/java/com/hedera/node/app/blocks/StandaloneRoundManagement.java @@ -55,6 +55,7 @@ import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.crypto.Hash; import com.swirlds.common.metrics.noop.NoOpMetrics; +import com.swirlds.metrics.api.Metrics; import com.swirlds.platform.state.service.PlatformStateService; import com.swirlds.platform.state.service.schemas.V0540PlatformStateSchema; import com.swirlds.platform.system.Round; @@ -93,18 +94,20 @@ public class StandaloneRoundManagement { private final ConfigProvider configProvider = new ConfigProviderImpl(false, null, Map.of("blockStream.serializationBatchSize", "32")); private final List roundItems = new ArrayList<>(); + private final Metrics noopMetrics = new NoOpMetrics(); private final TssBaseServiceImpl tssBaseService = new TssBaseServiceImpl( new AppContextImpl( Instant::now, fakeSignatureVerifier(), UNAVAILABLE_GOSSIP, + () -> noopMetrics, () -> configProvider.getConfiguration(), () -> DEFAULT_NODE_INFO), ForkJoinPool.commonPool(), ForkJoinPool.commonPool(), new PlaceholderTssLibrary(), ForkJoinPool.commonPool(), - new NoOpMetrics()); + noopMetrics); private final BlockStreamManagerImpl subject = new BlockStreamManagerImpl( NoopBlockItemWriter::new, ForkJoinPool.commonPool(), diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java index 337168e3a98f..4140cf5667be 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java @@ -384,6 +384,7 @@ public Hedera( new SignatureExpanderImpl(), new SignatureVerifierImpl(CryptographyHolder.get())), this, + () -> metrics /* DOES THIS WORK TO DELAY; DOES IT NEED TO BE A REFERENCE? */, bootstrapConfigProvider::getConfiguration, () -> daggerApp.networkInfo().selfNodeInfo()); tssBaseService = tssBaseServiceFactory.apply(appContext); diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/AppContextImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/AppContextImpl.java index f8fda60ab8a2..49a1891db0ae 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/AppContextImpl.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/AppContextImpl.java @@ -19,6 +19,7 @@ import com.hedera.node.app.spi.AppContext; import com.hedera.node.app.spi.signatures.SignatureVerifier; import com.swirlds.config.api.Configuration; +import com.swirlds.metrics.api.Metrics; import com.swirlds.state.lifecycle.info.NodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.time.InstantSource; @@ -36,6 +37,7 @@ public record AppContextImpl( @NonNull InstantSource instantSource, @NonNull SignatureVerifier signatureVerifier, @NonNull Gossip gossip, + @NonNull Supplier metricsSupplier, @NonNull Supplier configSupplier, @NonNull Supplier selfNodeInfoSupplier) implements AppContext {} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/TransactionExecutors.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/TransactionExecutors.java index 385feb14129f..02704b9e71dd 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/TransactionExecutors.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/TransactionExecutors.java @@ -93,6 +93,7 @@ public TransactionExecutor newExecutor( private ExecutorComponent newExecutorComponent( @NonNull final Map properties, @NonNull final TracerBinding tracerBinding) { final var bootstrapConfigProvider = new BootstrapConfigProviderImpl(); + final var noopMetrics = new NoOpMetrics(); final var appContext = new AppContextImpl( InstantSource.system(), new AppSignatureVerifier( @@ -100,6 +101,7 @@ private ExecutorComponent newExecutorComponent( new SignatureExpanderImpl(), new SignatureVerifierImpl(CryptographyHolder.get())), UNAVAILABLE_GOSSIP, + () -> noopMetrics, bootstrapConfigProvider::getConfiguration, () -> DEFAULT_NODE_INFO); final var tssBaseService = new TssBaseServiceImpl( @@ -108,7 +110,7 @@ private ExecutorComponent newExecutorComponent( ForkJoinPool.commonPool(), new PlaceholderTssLibrary(), ForkJoinPool.commonPool(), - new NoOpMetrics()); + noopMetrics); final var contractService = new ContractServiceImpl(appContext, NOOP_VERIFICATION_STRATEGIES, tracerBinding); final var fileService = new FileServiceImpl(); final var configProvider = new ConfigProviderImpl(false, null, properties); @@ -118,7 +120,7 @@ private ExecutorComponent newExecutorComponent( .tssBaseService(tssBaseService) .fileServiceImpl(fileService) .contractServiceImpl(contractService) - .metrics(new NoOpMetrics()) + .metrics(noopMetrics) .build(); } diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java index f339bed2ed15..4e7287dc59a7 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java @@ -107,6 +107,7 @@ void setUp() { Bytes.wrap("cert7")); final var configProvider = new ConfigProviderImpl(false); + final var noopMetrics = new NoOpMetrics(); final var appContext = new AppContextImpl( InstantSource.system(), new AppSignatureVerifier( @@ -114,6 +115,7 @@ void setUp() { new SignatureExpanderImpl(), new SignatureVerifierImpl(CryptographyHolder.get())), UNAVAILABLE_GOSSIP, + () -> noopMetrics, () -> configuration, () -> DEFAULT_NODE_INFO); given(tssBaseService.tssHandlers()) diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/standalone/TransactionExecutorsTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/standalone/TransactionExecutorsTest.java index 5f2f69dbbfc4..89823e67b9d0 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/standalone/TransactionExecutorsTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/standalone/TransactionExecutorsTest.java @@ -275,6 +275,7 @@ private Map> genesisContentProviders( private void registerServices(@NonNull final ServicesRegistry servicesRegistry) { // Register all service schema RuntimeConstructable factories before platform init + final var noopMetrics = new NoOpMetrics(); Set.of( new EntityIdService(), new ConsensusServiceImpl(), @@ -282,6 +283,7 @@ private void registerServices(@NonNull final ServicesRegistry servicesRegistry) InstantSource.system(), signatureVerifier, UNAVAILABLE_GOSSIP, + () -> noopMetrics, () -> HederaTestConfigBuilder.createConfig(), () -> DEFAULT_NODE_INFO)), new FileServiceImpl(), diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceComponent.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceComponent.java index 546b8da5765b..92a5c53afab0 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceComponent.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceComponent.java @@ -16,9 +16,11 @@ package com.hedera.node.app.service.contract.impl; +import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics; import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategies; import com.hedera.node.app.service.contract.impl.handlers.ContractHandlers; import com.hedera.node.app.spi.signatures.SignatureVerifier; +import com.swirlds.metrics.api.Metrics; import dagger.BindsInstance; import dagger.Component; import edu.umd.cs.findbugs.annotations.Nullable; @@ -50,11 +52,17 @@ ContractServiceComponent create( @BindsInstance InstantSource instantSource, @BindsInstance SignatureVerifier signatureVerifier, @BindsInstance VerificationStrategies verificationStrategies, - @BindsInstance @Nullable Supplier> addOnTracers); + @BindsInstance @Nullable Supplier> addOnTracers, + @BindsInstance Supplier metricsSupplier, + @BindsInstance ContractMetrics contractMetrics); } /** * @return all contract transaction handlers */ ContractHandlers handlers(); + + Supplier metricsSupplier(); + + ContractMetrics contractMetrics(); } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceImpl.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceImpl.java index 0f8d403c6003..f74b0f32fd15 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceImpl.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceImpl.java @@ -19,12 +19,14 @@ import static java.util.Objects.requireNonNull; import com.hedera.node.app.service.contract.ContractService; +import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics; import com.hedera.node.app.service.contract.impl.exec.scope.DefaultVerificationStrategies; import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategies; import com.hedera.node.app.service.contract.impl.handlers.ContractHandlers; import com.hedera.node.app.service.contract.impl.schemas.V0490ContractSchema; import com.hedera.node.app.service.contract.impl.schemas.V0500ContractSchema; import com.hedera.node.app.spi.AppContext; +import com.swirlds.metrics.api.Metrics; import com.swirlds.state.lifecycle.SchemaRegistry; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -68,7 +70,9 @@ public ContractServiceImpl( // C.f. https://github.com/hashgraph/hedera-services/issues/14248 appContext.signatureVerifier(), Optional.ofNullable(verificationStrategies).orElseGet(DefaultVerificationStrategies::new), - addOnTracers); + addOnTracers, + appContext.metricsSupplier(), + new ContractMetrics()); } @Override @@ -83,4 +87,13 @@ public void registerSchemas(@NonNull final SchemaRegistry registry) { public ContractHandlers handlers() { return component.handlers(); } + + public Supplier metricsSupplier() { + return component.metricsSupplier(); + } + + public ContractMetrics contractMetrics() { + component.contractMetrics().init(metricsSupplier().get()); + return component.contractMetrics(); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/metrics/ContractMetrics.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/metrics/ContractMetrics.java new file mode 100644 index 000000000000..cc12aedd3ece --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/metrics/ContractMetrics.java @@ -0,0 +1,217 @@ +/* + * 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.node.app.service.contract.impl.exec.metrics; + +import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CALL; +import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CREATE; +import static com.hedera.hapi.node.base.HederaFunctionality.ETHEREUM_TRANSACTION; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toMap; + +import com.hedera.hapi.node.base.HederaFunctionality; +import com.swirlds.metrics.api.Counter; +import com.swirlds.metrics.api.Metric; +import com.swirlds.metrics.api.Metrics; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Metrics collection management for Smart Contracts service + */ +public class ContractMetrics { + + // Rejected transactions counters: Failed `pureCheck` for one reason or another + + private static final Logger log = LogManager.getLogger(ContractMetrics.class); + + private final HashMap rejectedTxsCounters = new HashMap<>(); + private final HashMap rejectedTxsLackingIntrinsicGas = new HashMap<>(); + private Counter rejectedEthType3Counter; + + private static final Map POSSIBLE_FAILING_TX_TYPES = Map.of( + CONTRACT_CALL, "contractCall", CONTRACT_CREATE, "createContract", ETHEREUM_TRANSACTION, "callEthereum"); + + private static final String METRIC_CATEGORY = "app"; + private static final String METRIC_SERVICE = "SmartContractService"; + private static final String METRIC_TXN_UNIT = "txs"; + + // Templates: %1$s - HederaFunctionality name + // %2$s - METRIC_SERVICE + // %3$s - short specific metric description + + private static final String REJECTED_NAME_TEMPLATE = "%2$s:Rejected-%1$s_total"; + private static final String REJECTED_DESCR_TEMPLATE = "submitted %1$s %3$s rejected by pureChecks"; + + private static final String REJECTED_TXN_SHORT_DESCR = "txns"; + private static final String REJECTED_TYPE3_SHORT_DESCR = "Ethereum Type 3 txns"; + private static final String REJECTED_FOR_GAS_SHORT_DESCR = "txns with not even intrinsic gas"; + + private static final String REJECTED_TYPE3_FUNCTIONALITY = "ethType3BlobTransaction"; + + public ContractMetrics() {} + + private final AtomicBoolean initialized = new AtomicBoolean(false); + + public void init(@NonNull final Metrics metrics) { + + // This homegrown singleton initialization is (apparently) needed because 1) the Metrics + // instance is created too late in the app - _after_ the AppContext is created, and 2) Services + // do _not_ get a call after the app is "up" but before processing starts. So we have to + // lazy initialize - once only. + + if (initialized.get()) return; + + synchronized (initialized) { + if (!initialized.get()) { + // Rejected transactions counters + for (final var txKind : POSSIBLE_FAILING_TX_TYPES.keySet()) { + final var name = toRejectedName(txKind, REJECTED_TXN_SHORT_DESCR); + final var descr = toRejectedDescr(txKind, REJECTED_TXN_SHORT_DESCR); + final var config = new Counter.Config(METRIC_CATEGORY, name) + .withDescription(descr) + .withUnit(METRIC_TXN_UNIT); + final var metric = newCounter(metrics, config); + rejectedTxsCounters.put(txKind, metric); + } + + // Rejected transactions because they don't even have intrinsic gas + for (final var txKind : POSSIBLE_FAILING_TX_TYPES.keySet()) { + final var functionalityName = POSSIBLE_FAILING_TX_TYPES.get(txKind) + "DueToIntrinsicGas"; + final var name = toRejectedName(functionalityName, REJECTED_FOR_GAS_SHORT_DESCR); + final var descr = toRejectedDescr(functionalityName, REJECTED_FOR_GAS_SHORT_DESCR); + final var config = new Counter.Config(METRIC_CATEGORY, name) + .withDescription(descr) + .withUnit(METRIC_TXN_UNIT); + final var metric = newCounter(metrics, config); + rejectedTxsLackingIntrinsicGas.put(txKind, metric); + } + + // Rejected transactions for ethereum calls that are in type 3 blob transaction format + { + final var name = toRejectedName(REJECTED_TYPE3_FUNCTIONALITY, REJECTED_TYPE3_SHORT_DESCR); + final var descr = toRejectedDescr(REJECTED_TYPE3_FUNCTIONALITY, REJECTED_TYPE3_SHORT_DESCR); + final var config = new Counter.Config(METRIC_CATEGORY, name) + .withDescription(descr) + .withUnit(METRIC_TXN_UNIT); + final var metric = newCounter(metrics, config); + rejectedEthType3Counter = metric; + } + } + initialized.set(true); + } + } + + public void incrementRejectedTx(@NonNull final HederaFunctionality txKind) { + bumpRejectedTx(txKind, 1); + } + + public void bumpRejectedTx(@NonNull final HederaFunctionality txKind, final long bumpBy) { + requireNonNull(rejectedTxsCounters.get(txKind)).add(bumpBy); + } + + public void incrementRejectedForGasTx(@NonNull final HederaFunctionality txKind) { + bumpRejectedForGasTx(txKind, 1); + } + + public void bumpRejectedForGasTx(@NonNull final HederaFunctionality txKind, final long bumpBy) { + requireNonNull(rejectedTxsLackingIntrinsicGas.get(txKind)).add(bumpBy); + } + + public void incrementRejectedType3EthTx() { + bumpRejectedType3EthTx(1); + } + + public void bumpRejectedType3EthTx(final long bumpBy) { + rejectedEthType3Counter.add(bumpBy); + } + + public @NonNull Map getAllCounters() { + return Stream.concat( + Stream.concat( + rejectedTxsCounters.values().stream(), + rejectedTxsLackingIntrinsicGas.values().stream()), + Stream.of(rejectedEthType3Counter)) + .collect(toMap(Counter::getName, Counter::get)); + } + + public @NonNull List getAllCounterNames() { + return Stream.concat( + Stream.concat( + rejectedTxsCounters.values().stream(), + rejectedTxsLackingIntrinsicGas.values().stream()), + Stream.of(rejectedEthType3Counter)) + .map(Metric::getName) + .sorted() + .toList(); + } + + public @NonNull List getAllCounterDescriptions() { + return Stream.concat( + Stream.concat( + rejectedTxsCounters.values().stream(), + rejectedTxsLackingIntrinsicGas.values().stream()), + Stream.of(rejectedEthType3Counter)) + .map(Metric::getDescription) + .sorted() + .toList(); + } + + public @NonNull String allCountersToString() { + return getAllCounters().entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(e -> e.getKey() + ": " + e.getValue()) + .collect(Collectors.joining(", ", "{", "}")); + } + + private @NonNull Counter newCounter(@NonNull final Metrics metrics, @NonNull final Counter.Config config) { + return metrics.getOrCreate(config); + } + + private static @NonNull String toRejectedName( + @NonNull final HederaFunctionality functionality, @NonNull final String shortDescription) { + return toRejectedName(POSSIBLE_FAILING_TX_TYPES.get(functionality), shortDescription); + } + + private static @NonNull String toRejectedName( + @NonNull final String functionality, @NonNull final String shortDescription) { + return toString(REJECTED_NAME_TEMPLATE, functionality, shortDescription); + } + + private static @NonNull String toRejectedDescr( + @NonNull final HederaFunctionality functionality, @NonNull final String shortDescription) { + return toString(REJECTED_DESCR_TEMPLATE, POSSIBLE_FAILING_TX_TYPES.get(functionality), shortDescription); + } + + private static @NonNull String toRejectedDescr( + @NonNull final String functionality, @NonNull final String shortDescription) { + return toString(REJECTED_DESCR_TEMPLATE, functionality, shortDescription); + } + + private static @NonNull String toString( + @NonNull final String template, + @NonNull final String functionality, + @NonNull final String shortDescription) { + return template.formatted(functionality, METRIC_SERVICE, shortDescription); + } +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCallHandler.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCallHandler.java index 9756c422210a..15eb50e7d3e0 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCallHandler.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCallHandler.java @@ -29,6 +29,7 @@ import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.hapi.utils.CommonPbjConverters; import com.hedera.node.app.hapi.utils.fee.SmartContractFeeBuilder; +import com.hedera.node.app.service.contract.impl.ContractServiceComponent; import com.hedera.node.app.service.contract.impl.exec.TransactionComponent; import com.hedera.node.app.service.contract.impl.records.ContractCallStreamBuilder; import com.hedera.node.app.spi.fees.FeeContext; @@ -52,6 +53,7 @@ public class ContractCallHandler implements TransactionHandler { private final Provider provider; private final GasCalculator gasCalculator; + private final ContractServiceComponent component; private final SmartContractFeeBuilder usageEstimator = new SmartContractFeeBuilder(); /** @@ -63,9 +65,11 @@ public class ContractCallHandler implements TransactionHandler { @Inject public ContractCallHandler( @NonNull final Provider provider, - @NonNull final GasCalculator gasCalculator) { + @NonNull final GasCalculator gasCalculator, + @NonNull final ContractServiceComponent component) { this.provider = requireNonNull(provider); this.gasCalculator = requireNonNull(gasCalculator); + this.component = requireNonNull(component); } @Override @@ -89,12 +93,21 @@ public void preHandle(@NonNull final PreHandleContext context) { @Override public void pureChecks(@NonNull TransactionBody txn) throws PreCheckException { - final var op = txn.contractCallOrThrow(); - mustExist(op.contractID(), INVALID_CONTRACT_ID); + try { + final var op = txn.contractCallOrThrow(); + mustExist(op.contractID(), INVALID_CONTRACT_ID); - final var intrinsicGas = gasCalculator.transactionIntrinsicGasCost( - Bytes.wrap(op.functionParameters().toByteArray()), false); - validateTruePreCheck(op.gas() >= intrinsicGas, INSUFFICIENT_GAS); + final var intrinsicGas = gasCalculator.transactionIntrinsicGasCost( + Bytes.wrap(op.functionParameters().toByteArray()), false); + validateTruePreCheck(op.gas() >= intrinsicGas, INSUFFICIENT_GAS); + } catch (@NonNull final Exception e) { + final var contractMetrics = component.contractMetrics(); + contractMetrics.incrementRejectedTx(CONTRACT_CALL); + if (e instanceof PreCheckException pce && pce.responseCode() == INSUFFICIENT_GAS) { + contractMetrics.incrementRejectedForGasTx(CONTRACT_CALL); + } + throw e; + } } @NonNull diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCreateHandler.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCreateHandler.java index 6d2991a2c0c3..8330fcbc00c7 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCreateHandler.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCreateHandler.java @@ -16,6 +16,7 @@ package com.hedera.node.app.service.contract.impl.handlers; +import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CALL; import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CREATE; import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_GAS; import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_AUTORENEW_ACCOUNT; @@ -29,6 +30,7 @@ import com.hedera.hapi.node.base.SubType; import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.hapi.utils.fee.SmartContractFeeBuilder; +import com.hedera.node.app.service.contract.impl.ContractServiceComponent; import com.hedera.node.app.service.contract.impl.exec.TransactionComponent; import com.hedera.node.app.service.contract.impl.records.ContractCreateStreamBuilder; import com.hedera.node.app.spi.fees.FeeContext; @@ -53,6 +55,7 @@ public class ContractCreateHandler implements TransactionHandler { private static final AccountID REMOVE_AUTO_RENEW_ACCOUNT_SENTINEL = AccountID.newBuilder().shardNum(0).realmNum(0).accountNum(0).build(); private final Provider provider; + private final ContractServiceComponent component; private final SmartContractFeeBuilder usageEstimator = new SmartContractFeeBuilder(); private final GasCalculator gasCalculator; @@ -65,9 +68,11 @@ public class ContractCreateHandler implements TransactionHandler { @Inject public ContractCreateHandler( @NonNull final Provider provider, - @NonNull final GasCalculator gasCalculator) { + @NonNull final GasCalculator gasCalculator, + @NonNull final ContractServiceComponent component) { this.provider = requireNonNull(provider); this.gasCalculator = requireNonNull(gasCalculator); + this.component = requireNonNull(component); } @Override @@ -86,10 +91,19 @@ public void handle(@NonNull final HandleContext context) throws HandleException @Override public void pureChecks(@NonNull final TransactionBody txn) throws PreCheckException { - final var op = txn.contractCreateInstanceOrThrow(); + try { + final var op = txn.contractCreateInstanceOrThrow(); - final var intrinsicGas = gasCalculator.transactionIntrinsicGasCost(Bytes.wrap(new byte[0]), true); - validateTruePreCheck(op.gas() >= intrinsicGas, INSUFFICIENT_GAS); + final var intrinsicGas = gasCalculator.transactionIntrinsicGasCost(Bytes.wrap(new byte[0]), true); + validateTruePreCheck(op.gas() >= intrinsicGas, INSUFFICIENT_GAS); + } catch (@NonNull final Exception e) { + final var contractMetrics = component.contractMetrics(); + contractMetrics.incrementRejectedTx(CONTRACT_CREATE); + if (e instanceof PreCheckException pce && pce.responseCode() == INSUFFICIENT_GAS) { + contractMetrics.incrementRejectedForGasTx(CONTRACT_CALL); + } + throw e; + } } @Override diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/EthereumTransactionHandler.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/EthereumTransactionHandler.java index c7d8a7e0db23..4b18cc8b6435 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/EthereumTransactionHandler.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/EthereumTransactionHandler.java @@ -16,6 +16,7 @@ package com.hedera.node.app.service.contract.impl.handlers; +import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CALL; import static com.hedera.hapi.node.base.HederaFunctionality.ETHEREUM_TRANSACTION; import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_GAS; import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ETHEREUM_TRANSACTION; @@ -35,6 +36,7 @@ import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.hapi.utils.ethereum.EthTxSigs; import com.hedera.node.app.hapi.utils.fee.SmartContractFeeBuilder; +import com.hedera.node.app.service.contract.impl.ContractServiceComponent; import com.hedera.node.app.service.contract.impl.exec.TransactionComponent; import com.hedera.node.app.service.contract.impl.infra.EthTxSigsCache; import com.hedera.node.app.service.contract.impl.infra.EthereumCallDataHydration; @@ -71,6 +73,7 @@ public class EthereumTransactionHandler implements TransactionHandler { private final EthereumCallDataHydration callDataHydration; private final Provider provider; private final GasCalculator gasCalculator; + private final ContractServiceComponent component; private final SmartContractFeeBuilder usageEstimator = new SmartContractFeeBuilder(); /** @@ -84,11 +87,13 @@ public EthereumTransactionHandler( @NonNull final EthTxSigsCache ethereumSignatures, @NonNull final EthereumCallDataHydration callDataHydration, @NonNull final Provider provider, - @NonNull final GasCalculator gasCalculator) { + @NonNull final GasCalculator gasCalculator, + @NonNull final ContractServiceComponent component) { this.ethereumSignatures = requireNonNull(ethereumSignatures); this.callDataHydration = requireNonNull(callDataHydration); this.provider = requireNonNull(provider); this.gasCalculator = requireNonNull(gasCalculator); + this.component = requireNonNull(component); } @Override @@ -103,16 +108,29 @@ public void preHandle(@NonNull final PreHandleContext context) throws PreCheckEx @Override public void pureChecks(@NonNull final TransactionBody txn) throws PreCheckException { - final var ethTxData = populateEthTxData( - requireNonNull(txn.ethereumTransactionOrThrow().ethereumData()).toByteArray()); - validateTruePreCheck(nonNull(ethTxData), INVALID_ETHEREUM_TRANSACTION); - final byte[] callData = ethTxData.hasCallData() ? ethTxData.callData() : new byte[0]; - final var intrinsicGas = - gasCalculator.transactionIntrinsicGasCost(org.apache.tuweni.bytes.Bytes.wrap(callData), false); - validateTruePreCheck(ethTxData.gasLimit() >= intrinsicGas, INSUFFICIENT_GAS); - // Do not allow sending HBars to Burn Address - if (ethTxData.value().compareTo(BigInteger.ZERO) > 0) { - validateFalsePreCheck(Arrays.equals(ethTxData.to(), EMPTY_ADDRESS), INVALID_SOLIDITY_ADDRESS); + try { + final var ethTxData = populateEthTxData( + requireNonNull(txn.ethereumTransactionOrThrow().ethereumData()) + .toByteArray()); + validateTruePreCheck(nonNull(ethTxData), INVALID_ETHEREUM_TRANSACTION); + final byte[] callData = ethTxData.hasCallData() ? ethTxData.callData() : new byte[0]; + final var intrinsicGas = + gasCalculator.transactionIntrinsicGasCost(org.apache.tuweni.bytes.Bytes.wrap(callData), false); + validateTruePreCheck(ethTxData.gasLimit() >= intrinsicGas, INSUFFICIENT_GAS); + // Do not allow sending HBars to Burn Address + if (ethTxData.value().compareTo(BigInteger.ZERO) > 0) { + validateFalsePreCheck(Arrays.equals(ethTxData.to(), EMPTY_ADDRESS), INVALID_SOLIDITY_ADDRESS); + } + } catch (@NonNull final Exception e) { + final var contractMetrics = component.contractMetrics(); + contractMetrics.incrementRejectedTx(CONTRACT_CALL); + if (e instanceof PreCheckException pce && pce.responseCode() == INSUFFICIENT_GAS) { + contractMetrics.incrementRejectedForGasTx(CONTRACT_CALL); + } + if (e instanceof NullPointerException) { + contractMetrics.incrementRejectedType3EthTx(); + } + throw e; } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java index 6e20e9a82460..17b98b8a8b09 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java @@ -11,6 +11,7 @@ requires transitive com.hedera.node.config; requires transitive com.hedera.node.hapi; requires transitive com.swirlds.config.api; + requires transitive com.swirlds.metrics.api; requires transitive com.swirlds.state.api; requires transitive com.hedera.pbj.runtime; requires transitive dagger; @@ -67,6 +68,7 @@ exports com.hedera.node.app.service.contract.impl.exec.v038 to com.hedera.node.app.service.contract.impl.test; exports com.hedera.node.app.service.contract.impl.utils; + exports com.hedera.node.app.service.contract.impl.exec.metrics; exports com.hedera.node.app.service.contract.impl.exec.utils; opens com.hedera.node.app.service.contract.impl.exec to diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/ContractServiceImplTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/ContractServiceImplTest.java index 659fd39c53ca..9e46924eaa24 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/ContractServiceImplTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/ContractServiceImplTest.java @@ -28,6 +28,8 @@ import com.hedera.node.app.service.contract.impl.schemas.V0500ContractSchema; import com.hedera.node.app.spi.AppContext; import com.hedera.node.app.spi.signatures.SignatureVerifier; +import com.swirlds.common.metrics.noop.NoOpMetrics; +import com.swirlds.metrics.api.Metrics; import com.swirlds.state.lifecycle.Schema; import com.swirlds.state.lifecycle.SchemaRegistry; import java.time.InstantSource; @@ -48,6 +50,8 @@ class ContractServiceImplTest { @Mock private SignatureVerifier signatureVerifier; + private final Metrics noopMetrics = new NoOpMetrics(); + private ContractServiceImpl subject; @BeforeEach @@ -55,6 +59,7 @@ void setUp() { // given when(appContext.instantSource()).thenReturn(instantSource); when(appContext.signatureVerifier()).thenReturn(signatureVerifier); + when(appContext.metricsSupplier()).thenReturn(() -> noopMetrics); subject = new ContractServiceImpl(appContext); } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/metrics/ContractMetricsTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/metrics/ContractMetricsTest.java new file mode 100644 index 000000000000..0b0c872a731e --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/metrics/ContractMetricsTest.java @@ -0,0 +1,111 @@ +/* + * 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.node.app.service.contract.impl.test.exec.metrics; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.hedera.hapi.node.base.HederaFunctionality; +import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics; +import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; +import com.swirlds.common.metrics.config.MetricsConfig; +import com.swirlds.common.metrics.platform.DefaultPlatformMetrics; +import com.swirlds.common.metrics.platform.MetricKeyRegistry; +import com.swirlds.common.metrics.platform.PlatformMetricsFactoryImpl; +import com.swirlds.common.platform.NodeId; +import com.swirlds.metrics.api.Metrics; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Map; +import java.util.concurrent.Executors; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class ContractMetricsTest { + + private final Metrics metrics = fakeMetrics(); + + public @NonNull ContractMetrics getSut() { + final var contractMetrics = new ContractMetrics(); + contractMetrics.init(metrics); + return contractMetrics; + } + + @Test + public void rejectedTxCountersGetBumped() { + final var sut = getSut(); + + sut.init(metrics); + + sut.incrementRejectedTx(HederaFunctionality.CONTRACT_CALL); + sut.bumpRejectedTx(HederaFunctionality.CONTRACT_CREATE, 2); + sut.bumpRejectedTx(HederaFunctionality.ETHEREUM_TRANSACTION, 4); + + sut.bumpRejectedForGasTx(HederaFunctionality.CONTRACT_CALL, 10); + sut.bumpRejectedForGasTx(HederaFunctionality.CONTRACT_CREATE, 12); + sut.bumpRejectedForGasTx(HederaFunctionality.ETHEREUM_TRANSACTION, 14); + + sut.bumpRejectedType3EthTx(20); + + assertThat(sut.getAllCounterNames()) + .containsExactlyInAnyOrder( + "SmartContractService:Rejected-callEthereumDueToIntrinsicGas_total", + "SmartContractService:Rejected-callEthereum_total", + "SmartContractService:Rejected-contractCallDueToIntrinsicGas_total", + "SmartContractService:Rejected-contractCall_total", + "SmartContractService:Rejected-createContractDueToIntrinsicGas_total", + "SmartContractService:Rejected-createContract_total", + "SmartContractService:Rejected-ethType3BlobTransaction_total"); + // assertThat(sut.getAllCounterDescriptions()).containsExactlyInAnyOrder(); + assertThat(sut.getAllCounters()) + .containsExactlyInAnyOrderEntriesOf(Map.of( + "SmartContractService:Rejected-callEthereumDueToIntrinsicGas_total", + 14L, + "SmartContractService:Rejected-callEthereum_total", + 4L, + "SmartContractService:Rejected-contractCallDueToIntrinsicGas_total", + 10L, + "SmartContractService:Rejected-contractCall_total", + 1L, + "SmartContractService:Rejected-createContractDueToIntrinsicGas_total", + 12L, + "SmartContractService:Rejected-createContract_total", + 2L, + "SmartContractService:Rejected-ethType3BlobTransaction_total", + 20L)); + + // And there is no counter for this functionality + assertThrows(NullPointerException.class, () -> { + sut.bumpRejectedTx(HederaFunctionality.CRYPTO_APPROVE_ALLOWANCE, 22); + }); + } + + private static final long DEFAULT_NODE_ID = 3; + + public static Metrics fakeMetrics() { + final MetricsConfig metricsConfig = + HederaTestConfigBuilder.createConfig().getConfigData(MetricsConfig.class); + + return new DefaultPlatformMetrics( + NodeId.of(DEFAULT_NODE_ID), + new MetricKeyRegistry(), + Executors.newSingleThreadScheduledExecutor(), + new PlatformMetricsFactoryImpl(metricsConfig), + metricsConfig); + } +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractCallHandlerTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractCallHandlerTest.java index d88040b65486..2442f5bc0faa 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractCallHandlerTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractCallHandlerTest.java @@ -32,9 +32,11 @@ import com.hedera.hapi.node.base.TransactionID; import com.hedera.hapi.node.contract.ContractCallTransactionBody; import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.contract.impl.ContractServiceComponent; import com.hedera.node.app.service.contract.impl.exec.CallOutcome; import com.hedera.node.app.service.contract.impl.exec.ContextTransactionProcessor; import com.hedera.node.app.service.contract.impl.exec.TransactionComponent; +import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics; import com.hedera.node.app.service.contract.impl.handlers.ContractCallHandler; import com.hedera.node.app.service.contract.impl.records.ContractCallStreamBuilder; import com.hedera.node.app.service.contract.impl.state.RootProxyWorldUpdater; @@ -44,12 +46,15 @@ import com.hedera.node.app.spi.fixtures.workflows.FakePreHandleContext; import com.hedera.node.app.spi.workflows.HandleContext; import com.hedera.node.app.spi.workflows.PreCheckException; +import com.swirlds.common.metrics.noop.NoOpMetrics; +import com.swirlds.metrics.api.Metrics; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mock.Strictness; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) @@ -80,11 +85,19 @@ class ContractCallHandlerTest extends ContractHandlerTestBase { @Mock private GasCalculator gasCalculator; + @Mock(strictness = Strictness.LENIENT) + private ContractServiceComponent contractServiceComponent; + + private final Metrics metrics = new NoOpMetrics(); + private final ContractMetrics contractMetrics = new ContractMetrics(); + private ContractCallHandler subject; @BeforeEach void setUp() { - subject = new ContractCallHandler(() -> factory, gasCalculator); + contractMetrics.init(metrics); + given(contractServiceComponent.contractMetrics()).willReturn(contractMetrics); + subject = new ContractCallHandler(() -> factory, gasCalculator, contractServiceComponent); } @Test diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractCreateHandlerTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractCreateHandlerTest.java index f0fc7e152f7c..bb942c642943 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractCreateHandlerTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractCreateHandlerTest.java @@ -36,9 +36,11 @@ import com.hedera.hapi.node.contract.ContractCreateTransactionBody; import com.hedera.hapi.node.state.token.Account; import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.contract.impl.ContractServiceComponent; import com.hedera.node.app.service.contract.impl.exec.CallOutcome; import com.hedera.node.app.service.contract.impl.exec.ContextTransactionProcessor; import com.hedera.node.app.service.contract.impl.exec.TransactionComponent; +import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics; import com.hedera.node.app.service.contract.impl.handlers.ContractCreateHandler; import com.hedera.node.app.service.contract.impl.records.ContractCreateStreamBuilder; import com.hedera.node.app.service.contract.impl.state.RootProxyWorldUpdater; @@ -48,12 +50,15 @@ import com.hedera.node.app.spi.fixtures.workflows.FakePreHandleContext; import com.hedera.node.app.spi.workflows.HandleContext; import com.hedera.node.app.spi.workflows.PreCheckException; +import com.swirlds.common.metrics.noop.NoOpMetrics; +import com.swirlds.metrics.api.Metrics; import java.util.List; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import org.mockito.Mock.Strictness; class ContractCreateHandlerTest extends ContractHandlerTestBase { @@ -86,11 +91,19 @@ class ContractCreateHandlerTest extends ContractHandlerTestBase { @Mock private GasCalculator gasCalculator; + @Mock(strictness = Strictness.LENIENT) + private ContractServiceComponent contractServiceComponent; + + private final Metrics metrics = new NoOpMetrics(); + private final ContractMetrics contractMetrics = new ContractMetrics(); + private ContractCreateHandler subject; @BeforeEach void setUp() { - subject = new ContractCreateHandler(() -> factory, gasCalculator); + contractMetrics.init(metrics); + given(contractServiceComponent.contractMetrics()).willReturn(contractMetrics); + subject = new ContractCreateHandler(() -> factory, gasCalculator, contractServiceComponent); } @Test diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/EthereumTransactionHandlerTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/EthereumTransactionHandlerTest.java index ab4b646d27f9..f39363e5d466 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/EthereumTransactionHandlerTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/EthereumTransactionHandlerTest.java @@ -39,11 +39,13 @@ import com.hedera.hapi.node.contract.EthereumTransactionBody; import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.hapi.utils.ethereum.EthTxData; +import com.hedera.node.app.service.contract.impl.ContractServiceComponent; import com.hedera.node.app.service.contract.impl.exec.CallOutcome; import com.hedera.node.app.service.contract.impl.exec.ContextTransactionProcessor; import com.hedera.node.app.service.contract.impl.exec.TransactionComponent; import com.hedera.node.app.service.contract.impl.exec.TransactionProcessor; import com.hedera.node.app.service.contract.impl.exec.gas.CustomGasCharging; +import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics; import com.hedera.node.app.service.contract.impl.exec.tracers.EvmActionTracer; import com.hedera.node.app.service.contract.impl.handlers.EthereumTransactionHandler; import com.hedera.node.app.service.contract.impl.hevm.HederaEvmContext; @@ -67,6 +69,8 @@ import com.hedera.node.app.spi.workflows.PreHandleContext; import com.hedera.node.config.data.ContractsConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.common.metrics.noop.NoOpMetrics; +import com.swirlds.metrics.api.Metrics; import java.util.List; import java.util.function.Supplier; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -74,6 +78,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mock.Strictness; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; @@ -145,9 +150,18 @@ class EthereumTransactionHandlerTest { @Mock private EthTxData ethTxDataReturned; + @Mock(strictness = Strictness.LENIENT) + private ContractServiceComponent contractServiceComponent; + + private final Metrics metrics = new NoOpMetrics(); + private final ContractMetrics contractMetrics = new ContractMetrics(); + @BeforeEach void setUp() { - subject = new EthereumTransactionHandler(ethereumSignatures, callDataHydration, () -> factory, gasCalculator); + contractMetrics.init(metrics); + given(contractServiceComponent.contractMetrics()).willReturn(contractMetrics); + subject = new EthereumTransactionHandler( + ethereumSignatures, callDataHydration, () -> factory, gasCalculator, contractServiceComponent); } void setUpTransactionProcessing() {