Skip to content

Commit

Permalink
Smart contract service metrics, P2 priority.
Browse files Browse the repository at this point in the history
These are the metrics that are interesting now and we can collect in the consensus node, but later we'll
collect in other ways. E.g., from block nodes. (All these metrics are for post-consensus
transactions and will show up in the record/block stream.)

Does _not_ include metrics for ExchangeRate and PNRG system contracts:  Those system contracts
are coded differently (don't use the same base (abstract) classes as HAS/HSS/HTS) and so will need
a bit more work.

Related issue(s):

Fixes #16088

Reviewer notes:

Changes a boatload of files.  Each of the ~60 FooTranslator classes (about one for each system
contract method) needs a small change, but that's a change to the construtor, and so it had
effects on all the unit tests for those classes as well.  You'll quickly see the pattern ...

Signed-off-by: David S Bakin <117694041+david-bakin-sl@users.noreply.github.com>
  • Loading branch information
david-bakin-sl committed Dec 11, 2024
1 parent 0665700 commit d0294ab
Show file tree
Hide file tree
Showing 142 changed files with 2,844 additions and 551 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,14 @@ public interface RpcService extends Service {
*/
@NonNull
Set<RpcServiceDefinition> rpcDefinitions();

/**
* Services may have initialization to be done which can't be done in the constructor (too soon)
* but should/must be done before the system starts processing transactions. This is the hook
* for that.
*
* Called on each Service when `Hedera.onStateInitialized() is called for `InitTrigger.GENESIS`.
* Services module is still single-threaded when this happens.
*/
default void onStateInitializedForGenesis() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@
import com.hedera.node.app.services.AppContextImpl;
import com.hedera.node.app.services.ServiceMigrator;
import com.hedera.node.app.services.ServicesRegistry;
import com.hedera.node.app.services.ServicesRegistry.Registration;
import com.hedera.node.app.signature.AppSignatureVerifier;
import com.hedera.node.app.signature.impl.SignatureExpanderImpl;
import com.hedera.node.app.signature.impl.SignatureVerifierImpl;
import com.hedera.node.app.spi.AppContext;
import com.hedera.node.app.spi.RpcService;
import com.hedera.node.app.spi.workflows.PreCheckException;
import com.hedera.node.app.state.MerkleStateLifecyclesImpl;
import com.hedera.node.app.state.recordcache.RecordCacheService;
Expand Down Expand Up @@ -606,7 +608,16 @@ public void onStateInitialized(
}
// With the States API grounded in the working state, we can create the object graph from it
initializeDagger(state, trigger);
contractServiceImpl.registerMetrics();

// Tell each service it can do its final initialization (if needed) before the system starts
// processing transactions.
if (trigger == GENESIS) {
servicesRegistry.registrations().stream()
.map(Registration::service)
.filter(RpcService.class::isInstance)
.map(RpcService.class::cast)
.forEach(RpcService::onStateInitializedForGenesis);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@

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.exec.systemcontracts.common.CallTranslator;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.HasCallAttempt;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hss.HssCallAttempt;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt;
import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethodRegistry;
import com.hedera.node.app.service.contract.impl.handlers.ContractHandlers;
import com.hedera.node.app.spi.signatures.SignatureVerifier;
import dagger.BindsInstance;
Expand All @@ -26,6 +31,8 @@
import java.time.InstantSource;
import java.util.List;
import java.util.function.Supplier;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.hyperledger.besu.evm.tracing.OperationTracer;

Expand All @@ -52,7 +59,8 @@ ContractServiceComponent create(
@BindsInstance SignatureVerifier signatureVerifier,
@BindsInstance VerificationStrategies verificationStrategies,
@BindsInstance @Nullable Supplier<List<OperationTracer>> addOnTracers,
@BindsInstance ContractMetrics contractMetrics);
@BindsInstance ContractMetrics contractMetrics,
@BindsInstance SystemContractMethodRegistry systemContractMethodRegistry);
}

/**
Expand All @@ -64,4 +72,18 @@ ContractServiceComponent create(
* @return contract metrics collection, instance
*/
ContractMetrics contractMetrics();

/**
* @return method registry for system contracts
*/
SystemContractMethodRegistry systemContractMethodRegistry();

@Named("HasTranslators")
Provider<List<CallTranslator<HasCallAttempt>>> hasCallTranslators();

@Named("HssTranslators")
Provider<List<CallTranslator<HssCallAttempt>>> hssCallTranslators();

@Named("HtsTranslators")
Provider<List<CallTranslator<HtsCallAttempt>>> htsCallTranslators();
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
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.exec.systemcontracts.common.AbstractCallAttempt;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractCallTranslator;
import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.CallTranslator;
import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethodRegistry;
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;
Expand All @@ -30,15 +34,23 @@
import com.swirlds.state.lifecycle.SchemaRegistry;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.inject.Singleton;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hyperledger.besu.evm.tracing.OperationTracer;

/**
* Implementation of the {@link ContractService}.
*/
public class ContractServiceImpl implements ContractService {

private static final Logger log = LogManager.getLogger(ContractServiceImpl.class);

/**
* Minimum gas required for contract operations.
*/
Expand Down Expand Up @@ -66,7 +78,10 @@ public ContractServiceImpl(
final var metricsSupplier = requireNonNull(appContext.metricsSupplier());
final Supplier<ContractsConfig> contractsConfigSupplier =
() -> appContext.configSupplier().get().getConfigData(ContractsConfig.class);
final var contractMetrics = new ContractMetrics(metricsSupplier, contractsConfigSupplier);
final var systemContractMethodRegistry = new SystemContractMethodRegistry();
final var contractMetrics =
new ContractMetrics(metricsSupplier, contractsConfigSupplier, systemContractMethodRegistry);

this.component = DaggerContractServiceComponent.factory()
.create(
appContext.instantSource(),
Expand All @@ -75,7 +90,8 @@ public ContractServiceImpl(
appContext.signatureVerifier(),
Optional.ofNullable(verificationStrategies).orElseGet(DefaultVerificationStrategies::new),
addOnTracers,
contractMetrics);
contractMetrics,
systemContractMethodRegistry);
}

@Override
Expand All @@ -84,12 +100,19 @@ 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();
@Override
public void onStateInitializedForGenesis() {
// Force call translators to be instantiated now, so that all the system contract methods
// will be registered, so the secondary metrics can be created. (Left to its own devices
// Dagger would delay instantiating them until transactions started flowing.)
final var allTranslators = allCallTranslators();

// TESTING
final var msg = "Known call translators:\n" + allTranslatorNames(allTranslators);
// END TESTING

component.contractMetrics().createContractPrimaryMetrics();
component.contractMetrics().createContractSecondaryMetrics();
}

/**
Expand All @@ -98,4 +121,35 @@ public void registerMetrics() {
public ContractHandlers handlers() {
return component.handlers();
}

private @NonNull List<CallTranslator<? extends AbstractCallAttempt<?>>> allCallTranslators() {
final var allCallTranslators = new ArrayList<CallTranslator<? extends AbstractCallAttempt<?>>>();
allCallTranslators.addAll(component.hasCallTranslators().get());
allCallTranslators.addAll(component.hssCallTranslators().get());
allCallTranslators.addAll(component.htsCallTranslators().get());
return allCallTranslators;
}

// -----------------
// For testing only:

private @NonNull String allTranslatorNames(
@NonNull List<CallTranslator<? extends AbstractCallAttempt<?>>> translators) {
return translators.stream().map(this::translatorName).sorted().collect(Collectors.joining("\n"));
}

private @NonNull String translatorName(@NonNull final CallTranslator<? extends AbstractCallAttempt<?>> translator) {
final var simpleName = translator.getClass().getSimpleName();
final var isSingleton = isSingleton(translator.getClass());
final var contractName = translator instanceof AbstractCallTranslator<?> act ? act.kind() : "<UNKNOWN-KIND>";

var name = contractName + "." + simpleName;
if (!isSingleton) name += "(NOT-SINGLETON)";

return name;
}

private boolean isSingleton(Class<?> klass) {
return klass.getDeclaredAnnotation(Singleton.class) != null;
}
}
Loading

0 comments on commit d0294ab

Please sign in to comment.