All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hedera.node.app.hapi.fees.pricing.FeeSchedules Maven / Gradle / Ivy

There is a newer version: 0.57.2
Show newest version
/*
 * Copyright (C) 2021-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.hapi.fees.pricing;

import static java.math.MathContext.DECIMAL128;
import static java.math.RoundingMode.HALF_UP;

import com.hedera.node.app.hapi.fees.usage.state.UsageAccumulator;
import com.hederahashgraph.api.proto.java.HederaFunctionality;
import com.hederahashgraph.api.proto.java.SubType;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.EnumMap;
import java.util.Map;

/**
 * Given an operation and type, generates the "fee schedule" entry of the node, network, and service
 * resource prices for that operation.
 */
public class FeeSchedules {
    public static final BigDecimal USD_TO_TINYCENTS = BigDecimal.valueOf(100 * 100_000_000L);
    static final BigDecimal FEE_SCHEDULE_MULTIPLIER = BigDecimal.valueOf(1_000L);

    private static final AssetsLoader ASSETS_LOADER = new AssetsLoader();
    private static final BaseOperationUsage CANONICAL_OPS = new BaseOperationUsage();

    /**
     * Computes the node, network, and service prices to charge for each resource type, given a
     * particular type of a particular Hedera operation.
     *
     * @param function the operation of interest
     * @param type the type of that operation to use
     * @return the node, network, and service resource prices
     * @throws IOException if a backing JSON resource cannot be loaded
     */
    Map> canonicalPricesFor(
            final HederaFunctionality function, final SubType type) throws IOException {
        final var canonicalUsage = CANONICAL_OPS.baseUsageFor(function, type);
        final var genericPrices = genericPricesFor(function);
        final var genericPrice = computeGenericGiven(canonicalUsage, genericPrices);
        final var canonicalPrice =
                ASSETS_LOADER.loadCanonicalPrices().get(function).get(type);

        final var normalizingFactor = FEE_SCHEDULE_MULTIPLIER
                .multiply(canonicalPrice)
                .divide(genericPrice, DECIMAL128)
                .multiply(USD_TO_TINYCENTS);

        return canonicalPricesGiven(normalizingFactor, genericPrices);
    }

    private Map> canonicalPricesGiven(
            final BigDecimal normalizingFactor,
            final Map> genericPrices) {
        final Map> canonicalPrices = new EnumMap<>(ResourceProvider.class);
        for (final var provider : ResourceProvider.class.getEnumConstants()) {
            final Map providerPrices = new EnumMap<>(UsableResource.class);
            final var providerGenerics = genericPrices.get(provider);
            for (final var resource : UsableResource.class.getEnumConstants()) {
                final var genericPrice = providerGenerics.get(resource);
                final var exactCanonicalPrice = normalizingFactor.multiply(genericPrice);
                final var canonicalPrice =
                        exactCanonicalPrice.setScale(0, HALF_UP).longValueExact();
                providerPrices.put(resource, canonicalPrice);
            }
            canonicalPrices.put(provider, providerPrices);
        }
        return canonicalPrices;
    }

    private BigDecimal computeGenericGiven(
            final UsageAccumulator canonicalUsage,
            final Map> genericPrices) {
        var sum = BigDecimal.ZERO;
        for (final var provider : ResourceProvider.class.getEnumConstants()) {
            final var providerGenerics = genericPrices.get(provider);
            for (final var resource : UsableResource.class.getEnumConstants()) {
                final var bdUsage = BigDecimal.valueOf(canonicalUsage.get(provider, resource));
                sum = sum.add(providerGenerics.get(resource).multiply(bdUsage));
            }
        }
        return sum;
    }

    private Map> genericPricesFor(final HederaFunctionality function)
            throws IOException {
        final var capacities = ASSETS_LOADER.loadCapacities();
        final var constW = ASSETS_LOADER.loadConstWeights().get(function);
        final var oneMinusConstW = BigDecimal.ONE.subtract(constW);

        final Map> generics = new EnumMap<>(ResourceProvider.class);
        for (final var provider : ResourceProvider.class.getEnumConstants()) {
            final Map providerGenerics = new EnumMap<>(UsableResource.class);

            var nonConstantGenerics = BigDecimal.ZERO;
            for (final var resource : UsableResource.class.getEnumConstants()) {
                final var scale = BigDecimal.valueOf(provider.relativeWeight());
                final var capacity = capacities.get(resource);
                final var generic = BigDecimal.ONE.divide(capacity, DECIMAL128).multiply(scale);
                providerGenerics.put(resource, generic);
                if (resource != UsableResource.CONSTANT) {
                    nonConstantGenerics = nonConstantGenerics.add(generic);
                }
            }
            providerGenerics.put(
                    UsableResource.CONSTANT,
                    nonConstantGenerics.multiply(constW).divide(oneMinusConstW, DECIMAL128));

            generics.put(provider, providerGenerics);
        }
        return generics;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy