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

com.hedera.node.app.hapi.fees.usage.crypto.CryptoOpsUsage Maven / Gradle / Ivy

There is a newer version: 0.57.2
Show newest version
/*
 * Copyright (C) 2020-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.usage.crypto;

import static com.hedera.node.app.hapi.fees.usage.SingletonEstimatorUtils.ESTIMATOR_UTILS;
import static com.hedera.node.app.hapi.fees.usage.SingletonUsageProperties.USAGE_PROPERTIES;
import static com.hedera.node.app.hapi.fees.usage.crypto.CryptoContextUtils.getChangedCryptoKeys;
import static com.hedera.node.app.hapi.fees.usage.crypto.CryptoContextUtils.getChangedTokenKeys;
import static com.hedera.node.app.hapi.fees.usage.crypto.entities.CryptoEntitySizes.CRYPTO_ENTITY_SIZES;
import static com.hedera.node.app.hapi.fees.usage.token.entities.TokenEntitySizes.TOKEN_ENTITY_SIZES;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.BASIC_ENTITY_ID_SIZE;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.BOOL_SIZE;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.CRYPTO_ALLOWANCE_SIZE;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.INT_SIZE;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.LONG_SIZE;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.NFT_ALLOWANCE_SIZE;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.TOKEN_ALLOWANCE_SIZE;
import static com.hedera.node.app.hapi.utils.fee.FeeBuilder.getAccountKeyStorageSize;

import com.hedera.node.app.hapi.fees.usage.BaseTransactionMeta;
import com.hedera.node.app.hapi.fees.usage.EstimatorFactory;
import com.hedera.node.app.hapi.fees.usage.QueryUsage;
import com.hedera.node.app.hapi.fees.usage.SigUsage;
import com.hedera.node.app.hapi.fees.usage.TxnUsageEstimator;
import com.hedera.node.app.hapi.fees.usage.state.UsageAccumulator;
import com.hederahashgraph.api.proto.java.FeeData;
import com.hederahashgraph.api.proto.java.Query;
import com.hederahashgraph.api.proto.java.ResponseType;
import java.nio.charset.StandardCharsets;
import java.util.function.Function;
import javax.inject.Inject;
import javax.inject.Singleton;

@Singleton
public class CryptoOpsUsage {
    private static final long LONG_BASIC_ENTITY_ID_SIZE = BASIC_ENTITY_ID_SIZE;
    public static final long LONG_ACCOUNT_AMOUNT_BYTES = USAGE_PROPERTIES.accountAmountBytes();

    public static final long CREATE_SLOT_MULTIPLIER = 1228;
    public static final long UPDATE_SLOT_MULTIPLIER = 24000;

    public static EstimatorFactory txnEstimateFactory = TxnUsageEstimator::new;
    static Function queryEstimateFactory = QueryUsage::new;

    @Inject
    public CryptoOpsUsage() {
        // Default constructor
    }

    public void cryptoTransferUsage(
            final SigUsage sigUsage,
            final CryptoTransferMeta xferMeta,
            final BaseTransactionMeta baseMeta,
            final UsageAccumulator accumulator) {
        accumulator.resetForTransaction(baseMeta, sigUsage);

        final int tokenMultiplier = xferMeta.getTokenMultiplier();

        /* BPT calculations shouldn't include any custom fee payment usage */
        int totalXfers = baseMeta.numExplicitTransfers();
        int weightedTokensInvolved = tokenMultiplier * xferMeta.getNumTokensInvolved();
        int weightedTokenXfers = tokenMultiplier * xferMeta.getNumFungibleTokenTransfers();
        long incBpt = weightedTokensInvolved * LONG_BASIC_ENTITY_ID_SIZE;
        incBpt += (weightedTokenXfers + totalXfers) * LONG_ACCOUNT_AMOUNT_BYTES;
        incBpt += TOKEN_ENTITY_SIZES.bytesUsedForUniqueTokenTransfers(xferMeta.getNumNftOwnershipChanges());
        accumulator.addBpt(incBpt);

        totalXfers += xferMeta.getCustomFeeHbarTransfers();
        weightedTokenXfers += tokenMultiplier * xferMeta.getCustomFeeTokenTransfers();
        weightedTokensInvolved += tokenMultiplier * xferMeta.getCustomFeeTokensInvolved();
        long incRb = totalXfers * LONG_ACCOUNT_AMOUNT_BYTES;
        incRb += TOKEN_ENTITY_SIZES.bytesUsedToRecordTokenTransfers(
                weightedTokensInvolved, weightedTokenXfers, xferMeta.getNumNftOwnershipChanges());
        accumulator.addRbs(incRb * USAGE_PROPERTIES.legacyReceiptStorageSecs());
    }

    public FeeData cryptoInfoUsage(final Query cryptoInfoReq, final ExtantCryptoContext ctx) {
        final var op = cryptoInfoReq.getCryptoGetInfo();

        final var estimate = queryEstimateFactory.apply(op.getHeader().getResponseType());
        return getUsage(estimate, ctx);
    }

    public FeeData accountDetailsUsage(final Query accountDetailsReq, final ExtantCryptoContext ctx) {
        final var op = accountDetailsReq.getAccountDetails();

        final var estimate = queryEstimateFactory.apply(op.getHeader().getResponseType());
        return getUsage(estimate, ctx);
    }

    private FeeData getUsage(final QueryUsage estimate, final ExtantCryptoContext ctx) {
        estimate.addTb(BASIC_ENTITY_ID_SIZE);
        long extraRb = 0;
        extraRb += ctx.currentMemo().getBytes(StandardCharsets.UTF_8).length;
        extraRb += getAccountKeyStorageSize(ctx.currentKey());
        if (ctx.currentlyHasProxy()) {
            extraRb += BASIC_ENTITY_ID_SIZE;
        }
        extraRb += ctx.currentNumTokenRels() * TOKEN_ENTITY_SIZES.bytesUsedPerAccountRelationship();
        estimate.addRb(CRYPTO_ENTITY_SIZES.fixedBytesInAccountRepr() + extraRb);

        return estimate.get();
    }

    public long cryptoAutoRenewRb(final ExtantCryptoContext ctx) {
        return CRYPTO_ENTITY_SIZES.fixedBytesInAccountRepr()
                + ctx.currentNonBaseRb()
                + ctx.currentNumTokenRels() * CRYPTO_ENTITY_SIZES.bytesInTokenAssocRepr();
    }

    /**
     * Returns the estimated resource usage for a crypto update transaction.
     *
     * @param sigUsage the already-computed resource usage for signature verification
     * @param baseMeta the already-computed resource usage for the base transaction
     * @param cryptoUpdateMeta metadata summarizing the update transaction
     * @param ctx the current state of the crypto account
     * @param accumulator the resource usage accumulator
     * @param explicitAutoAssocSlotLifetime a minimum lifetime to use for resource usage of new auto-renew slots
     */
    public void cryptoUpdateUsage(
            final SigUsage sigUsage,
            final BaseTransactionMeta baseMeta,
            final CryptoUpdateMeta cryptoUpdateMeta,
            final ExtantCryptoContext ctx,
            final UsageAccumulator accumulator,
            final long explicitAutoAssocSlotLifetime) {
        accumulator.resetForTransaction(baseMeta, sigUsage);

        accumulator.addBpt(cryptoUpdateMeta.getMsgBytesUsed());

        long newVariableBytes = 0;
        final var newMemoSize = cryptoUpdateMeta.getMemoSize();
        newVariableBytes += newMemoSize != 0 ? newMemoSize : ctx.currentMemo().getBytes(StandardCharsets.UTF_8).length;
        final var newKeyBytes = cryptoUpdateMeta.getKeyBytesUsed();
        newVariableBytes += newKeyBytes == 0 ? getAccountKeyStorageSize(ctx.currentKey()) : newKeyBytes;
        newVariableBytes += (cryptoUpdateMeta.hasProxy() || ctx.currentlyHasProxy()) ? BASIC_ENTITY_ID_SIZE : 0;

        final long tokenRelBytes = ctx.currentNumTokenRels() * CRYPTO_ENTITY_SIZES.bytesInTokenAssocRepr();
        final long sharedFixedBytes = CRYPTO_ENTITY_SIZES.fixedBytesInAccountRepr() + tokenRelBytes;
        final long newLifetime =
                ESTIMATOR_UTILS.relativeLifetime(cryptoUpdateMeta.getEffectiveNow(), cryptoUpdateMeta.getExpiry());
        final long oldLifetime =
                ESTIMATOR_UTILS.relativeLifetime(cryptoUpdateMeta.getEffectiveNow(), ctx.currentExpiry());
        final long rbsDelta = ESTIMATOR_UTILS.changeInBsUsage(
                cryptoAutoRenewRb(ctx), oldLifetime, sharedFixedBytes + newVariableBytes, newLifetime);
        if (rbsDelta > 0) {
            accumulator.addRbs(rbsDelta);
        }

        final var oldSlotsUsage = ctx.currentMaxAutomaticAssociations() * UPDATE_SLOT_MULTIPLIER;
        final var newSlotsUsage = cryptoUpdateMeta.hasMaxAutomaticAssociations()
                ? cryptoUpdateMeta.getMaxAutomaticAssociations() * UPDATE_SLOT_MULTIPLIER
                : oldSlotsUsage;
        // If given an explicit auto-assoc slot lifetime, we use it as a lower bound for both old and new lifetimes
        final long slotRbsDelta = ESTIMATOR_UTILS.changeInBsUsage(
                oldSlotsUsage,
                Math.max(explicitAutoAssocSlotLifetime, oldLifetime),
                newSlotsUsage,
                Math.max(explicitAutoAssocSlotLifetime, newLifetime));
        if (slotRbsDelta > 0) {
            accumulator.addRbs(slotRbsDelta);
        }
    }

    public void cryptoCreateUsage(
            final SigUsage sigUsage,
            final BaseTransactionMeta baseMeta,
            final CryptoCreateMeta cryptoCreateMeta,
            final UsageAccumulator accumulator) {
        accumulator.resetForTransaction(baseMeta, sigUsage);

        var baseSize = cryptoCreateMeta.getBaseSize();

        final var maxAutomaticTokenAssociations = cryptoCreateMeta.getMaxAutomaticAssociations();

        final var lifeTime = cryptoCreateMeta.getLifeTime();

        if (maxAutomaticTokenAssociations > 0) {
            baseSize += INT_SIZE;
        }

        /* Variable bytes plus two additional longs for balance and auto-renew period;
        plus a boolean for receiver sig required. */
        accumulator.addBpt(baseSize + 2 * LONG_SIZE + BOOL_SIZE);
        accumulator.addRbs((CRYPTO_ENTITY_SIZES.fixedBytesInAccountRepr() + baseSize) * lifeTime);
        accumulator.addNetworkRbs(BASIC_ENTITY_ID_SIZE * USAGE_PROPERTIES.legacyReceiptStorageSecs());
    }

    public void cryptoApproveAllowanceUsage(
            final SigUsage sigUsage,
            final BaseTransactionMeta baseMeta,
            final CryptoApproveAllowanceMeta cryptoApproveMeta,
            final ExtantCryptoContext ctx,
            final UsageAccumulator accumulator) {
        accumulator.resetForTransaction(baseMeta, sigUsage);
        accumulator.addBpt(cryptoApproveMeta.getMsgBytesUsed());

        final long lifeTime =
                ESTIMATOR_UTILS.relativeLifetime(cryptoApproveMeta.getEffectiveNow(), ctx.currentExpiry());
        // If the value is being adjusted instead of inserting a new entry , the fee charged will be
        // slightly less than
        // the base price
        final var adjustedBytes = getNewBytes(cryptoApproveMeta, ctx);
        if (adjustedBytes > 0) {
            accumulator.addRbs(adjustedBytes * lifeTime);
        }
    }

    public void cryptoDeleteAllowanceUsage(
            final SigUsage sigUsage,
            final BaseTransactionMeta baseMeta,
            final CryptoDeleteAllowanceMeta cryptoDeleteAllowanceMeta,
            final UsageAccumulator accumulator) {

        accumulator.resetForTransaction(baseMeta, sigUsage);
        accumulator.addBpt(cryptoDeleteAllowanceMeta.getMsgBytesUsed());
    }

    private long getNewBytes(final CryptoApproveAllowanceMeta cryptoApproveMeta, final ExtantCryptoContext ctx) {
        long newTotalBytes = 0;
        final var newCryptoKeys = getChangedCryptoKeys(
                cryptoApproveMeta.getCryptoAllowances().keySet(),
                ctx.currentCryptoAllowances().keySet());

        newTotalBytes += newCryptoKeys * CRYPTO_ALLOWANCE_SIZE;

        final var newTokenKeys = getChangedTokenKeys(
                cryptoApproveMeta.getTokenAllowances().keySet(),
                ctx.currentTokenAllowances().keySet());
        newTotalBytes += newTokenKeys * TOKEN_ALLOWANCE_SIZE;

        final var newApproveForAllNfts =
                getChangedTokenKeys(cryptoApproveMeta.getNftAllowances(), ctx.currentNftAllowances());
        newTotalBytes += newApproveForAllNfts * NFT_ALLOWANCE_SIZE;

        return newTotalBytes;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy