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 0248cc876d7c..2712503cd48e 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 @@ -22,6 +22,7 @@ import com.hedera.node.app.spi.throttle.Throttle; 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; @@ -105,6 +106,12 @@ public Signature sign(final byte[] ledgerId) { */ Supplier selfNodeInfoSupplier(); + /** + * The supplier of (platform) metrics + * @return the supplier + */ + Supplier metricsSupplier(); + /** * The application's strategy for creating {@link Throttle} instances. * @return the throttle factory 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 66aeda46fda1..471acf95e398 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 @@ -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; @@ -104,6 +105,7 @@ public class BlockStreamManagerBenchmark { private static final String SAMPLE_BLOCK = "sample.blk.gz"; private static final Instant FAKE_CONSENSUS_NOW = Instant.ofEpochSecond(1_234_567L, 890); private static final Timestamp FAKE_CONSENSUS_TIME = new Timestamp(1_234_567L, 890); + private static final Metrics NO_OP_METRICS = new NoOpMetrics(); private static final SemanticVersion VERSION = new SemanticVersion(0, 56, 0, "", ""); public static void main(String... args) throws Exception { @@ -124,6 +126,7 @@ public static void main(String... args) throws Exception { UNAVAILABLE_GOSSIP, configProvider::getConfiguration, () -> DEFAULT_NODE_INFO, + () -> NO_OP_METRICS, (split, snapshots) -> { throw new UnsupportedOperationException(); }); 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 58231f0f0fc9..a7b0e0fddb3e 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 @@ -56,6 +56,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; @@ -84,6 +85,7 @@ public class StandaloneRoundManagement { private static final String SAMPLE_BLOCK = "sample.blk.gz"; private static final Instant FAKE_CONSENSUS_NOW = Instant.ofEpochSecond(1_234_567L, 890); private static final Timestamp FAKE_CONSENSUS_TIME = new Timestamp(1_234_567L, 890); + private static final Metrics NO_OP_METRICS = new NoOpMetrics(); private static final SemanticVersion VERSION = new SemanticVersion(0, 56, 0, "", ""); private static final int NUM_ROUNDS = 10000; @@ -100,6 +102,7 @@ public class StandaloneRoundManagement { UNAVAILABLE_GOSSIP, configProvider::getConfiguration, () -> DEFAULT_NODE_INFO, + () -> NO_OP_METRICS, (split, snapshots) -> { throw new UnsupportedOperationException(); }); 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 5b6d54bc9670..8eb512776b59 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 @@ -429,6 +429,7 @@ public Hedera( this, configSupplier, () -> daggerApp.networkInfo().selfNodeInfo(), + () -> metrics, new AppThrottleFactory( configSupplier, () -> daggerApp.workingStateAccessor().getState(), @@ -602,6 +603,8 @@ public void onStateInitialized( trigger, RosterUtils.buildAddressBook(platform.getRoster()), platform.getContext().getConfiguration()); + + contractServiceImpl.registerMetrics(); } // With the States API grounded in the working state, we can create the object graph from it initializeDagger(state, trigger); 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 1c9c9c92e6e9..c74b7210bc0d 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 @@ -20,6 +20,7 @@ import com.hedera.node.app.spi.signatures.SignatureVerifier; import com.hedera.node.app.spi.throttle.Throttle; 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; @@ -39,5 +40,6 @@ public record AppContextImpl( @NonNull Gossip gossip, @NonNull Supplier configSupplier, @NonNull Supplier selfNodeInfoSupplier, + @NonNull Supplier metricsSupplier, @NonNull Throttle.Factory throttleFactory) 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 cd68c02bf164..043b8f76929d 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 @@ -39,6 +39,7 @@ import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.metrics.noop.NoOpMetrics; +import com.swirlds.metrics.api.Metrics; import com.swirlds.state.State; import com.swirlds.state.lifecycle.info.NodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; @@ -111,6 +112,7 @@ private ExecutorComponent newExecutorComponent( UNAVAILABLE_GOSSIP, bootstrapConfigProvider::getConfiguration, () -> DEFAULT_NODE_INFO, + () -> NO_OP_METRICS, new AppThrottleFactory( configProvider::getConfiguration, () -> state, @@ -122,7 +124,7 @@ private ExecutorComponent newExecutorComponent( ForkJoinPool.commonPool(), new TssLibraryImpl(appContext), ForkJoinPool.commonPool(), - new NoOpMetrics()); + NO_OP_METRICS); final var contractService = new ContractServiceImpl(appContext, NOOP_VERIFICATION_STRATEGIES, tracerBinding); final var fileService = new FileServiceImpl(); final var scheduleService = new ScheduleServiceImpl(); @@ -133,7 +135,7 @@ private ExecutorComponent newExecutorComponent( .fileServiceImpl(fileService) .contractServiceImpl(contractService) .scheduleServiceImpl(scheduleService) - .metrics(new NoOpMetrics()) + .metrics(NO_OP_METRICS) .throttleFactory(appContext.throttleFactory()) .build(); componentRef.set(component); @@ -159,4 +161,6 @@ public List get() { return OPERATION_TRACERS.get(); } } + + private static final Metrics NO_OP_METRICS = new NoOpMetrics(); } 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 e2c261bb0f5d..34b298a96c9d 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 @@ -101,6 +101,8 @@ class IngestComponentTest { private HederaInjectionComponent app; + private static final Metrics NO_OP_METRICS = new NoOpMetrics(); + @BeforeEach void setUp() { final Configuration configuration = HederaTestConfigBuilder.createConfig(); @@ -125,6 +127,7 @@ void setUp() { UNAVAILABLE_GOSSIP, () -> configuration, () -> DEFAULT_NODE_INFO, + () -> NO_OP_METRICS, throttleFactory); given(tssBaseService.tssHandlers()) .willReturn(new TssHandlers(tssMessageHandler, tssVoteHandler, tssShareSignatureHandler)); 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 75ade645a7e4..fa945f5cd98c 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 @@ -250,6 +250,7 @@ private State genesisState(@NonNull final Map overrides) { UNAVAILABLE_GOSSIP, () -> config, () -> DEFAULT_NODE_INFO, + () -> NO_OP_METRICS, new AppThrottleFactory( () -> config, () -> state, () -> ThrottleDefinitions.DEFAULT, ThrottleAccumulator::new)); registerServices(appContext, config, servicesRegistry); 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..16ebea137121 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,6 +16,7 @@ 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; @@ -50,11 +51,17 @@ ContractServiceComponent create( @BindsInstance InstantSource instantSource, @BindsInstance SignatureVerifier signatureVerifier, @BindsInstance VerificationStrategies verificationStrategies, - @BindsInstance @Nullable Supplier> addOnTracers); + @BindsInstance @Nullable Supplier> addOnTracers, + @BindsInstance ContractMetrics contractMetrics); } /** * @return all contract transaction handlers */ ContractHandlers handlers(); + + /** + * @return contract metrics collection, instance + */ + 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..4e873b6eddb5 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,6 +19,7 @@ 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; @@ -61,6 +62,8 @@ public ContractServiceImpl( @Nullable final VerificationStrategies verificationStrategies, @Nullable final Supplier> addOnTracers) { requireNonNull(appContext); + final var metricsSupplier = requireNonNull(appContext.metricsSupplier()); + final var contractMetrics = new ContractMetrics(metricsSupplier); this.component = DaggerContractServiceComponent.factory() .create( appContext.instantSource(), @@ -68,7 +71,8 @@ public ContractServiceImpl( // C.f. https://github.com/hashgraph/hedera-services/issues/14248 appContext.signatureVerifier(), Optional.ofNullable(verificationStrategies).orElseGet(DefaultVerificationStrategies::new), - addOnTracers); + addOnTracers, + contractMetrics); } @Override @@ -77,10 +81,25 @@ public void registerSchemas(@NonNull final SchemaRegistry registry) { registry.register(new V0500ContractSchema()); } + /** + * Create the metrics for the smart contracts service. This needs to be delayed until _after_ + * the metrics are available - which happens after `Hedera.initializeStatesApi`. + */ + public void registerMetrics() { + component.contractMetrics().createContractMetrics(); + } + /** * @return all contract transaction handlers */ public ContractHandlers handlers() { return component.handlers(); } + + /** + * @return the contract metrics collector + */ + public ContractMetrics contractMetrics() { + 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..41b200ab21fb --- /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,214 @@ +/* + * 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.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Metrics collection management for Smart Contracts service + */ +@Singleton +public class ContractMetrics { + + // Rejected transactions counters: Failed `pureCheck` for one reason or another + + private static final Logger log = LogManager.getLogger(ContractMetrics.class); + + private final Supplier metricsSupplier; + + 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"; + + @Inject + public ContractMetrics(@NonNull final Supplier metricsSupplier) { + this.metricsSupplier = requireNonNull( + metricsSupplier, "metrics supplier (from platform via ServicesMain/Hedera must not be null"); + } + + public void createContractMetrics() { + + final var metrics = requireNonNull(metricsSupplier.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; + } + } + + 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 8a8eeed6eb8a..69034b1ef021 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.hevm.HederaEvmTransaction; import com.hedera.node.app.service.contract.impl.infra.EthTxSigsCache; @@ -73,6 +75,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(); /** @@ -86,11 +89,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 @@ -105,16 +110,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 969f78233dd8..67f84fa6aea4 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 @@ -12,6 +12,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; @@ -68,6 +69,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..6c8a131f9de0 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 @@ -55,6 +55,7 @@ void setUp() { // given when(appContext.instantSource()).thenReturn(instantSource); when(appContext.signatureVerifier()).thenReturn(signatureVerifier); + when(appContext.metricsSupplier()).thenReturn(() -> null); 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..0e588354d50d --- /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,109 @@ +/* + * 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 java.util.function.Supplier; +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 Supplier metricsSupplier = () -> fakeMetrics(); + + public @NonNull ContractMetrics getSubject() { + final var contractMetrics = new ContractMetrics(metricsSupplier); + contractMetrics.createContractMetrics(); + return contractMetrics; + } + + @Test + public void rejectedTxCountersGetBumped() { + final var subject = getSubject(); + + subject.incrementRejectedTx(HederaFunctionality.CONTRACT_CALL); + subject.bumpRejectedTx(HederaFunctionality.CONTRACT_CREATE, 2); + subject.bumpRejectedTx(HederaFunctionality.ETHEREUM_TRANSACTION, 4); + + subject.bumpRejectedForGasTx(HederaFunctionality.CONTRACT_CALL, 10); + subject.bumpRejectedForGasTx(HederaFunctionality.CONTRACT_CREATE, 12); + subject.bumpRejectedForGasTx(HederaFunctionality.ETHEREUM_TRANSACTION, 14); + + subject.bumpRejectedType3EthTx(20); + + assertThat(subject.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(subject.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, () -> { + subject.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..cc1d1724e7de 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(() -> metrics); + private ContractCallHandler subject; @BeforeEach void setUp() { - subject = new ContractCallHandler(() -> factory, gasCalculator); + contractMetrics.createContractMetrics(); + 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..f7dc77fecc0a 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(() -> metrics); + private ContractCreateHandler subject; @BeforeEach void setUp() { - subject = new ContractCreateHandler(() -> factory, gasCalculator); + contractMetrics.createContractMetrics(); + 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 056586e0d673..02a08b5f03e9 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 @@ -42,11 +42,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; @@ -71,7 +73,9 @@ 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.config.api.Configuration; +import com.swirlds.metrics.api.Metrics; import java.util.List; import java.util.function.Supplier; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -79,6 +83,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; @@ -150,6 +155,12 @@ 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(() -> metrics); + @Mock private Configuration configuration; @@ -158,7 +169,10 @@ class EthereumTransactionHandlerTest { @BeforeEach void setUp() { - subject = new EthereumTransactionHandler(ethereumSignatures, callDataHydration, () -> factory, gasCalculator); + contractMetrics.createContractMetrics(); + given(contractServiceComponent.contractMetrics()).willReturn(contractMetrics); + subject = new EthereumTransactionHandler( + ethereumSignatures, callDataHydration, () -> factory, gasCalculator, contractServiceComponent); } void setUpTransactionProcessing() {