-
Notifications
You must be signed in to change notification settings - Fork 181
feat: Update CryptoCreate/CryptoDelete to use new simple fees #21901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 43 commits
df68997
b21e8d6
d713ee2
b2a87cd
31b6cf3
95d1987
96676c5
9c70a6c
ea625cb
498085f
f7197f6
7ada0df
8cf90bf
182e806
6ac8c83
828151d
636c859
7824a07
750075d
9856c22
f6548c3
a4da07a
c11e6c7
e221be2
fc2d7c7
aaf7c4a
4ca01b2
c66a9ab
a73fa33
e289a2a
60ea61b
5e324f4
a40f433
d74ac10
59fbc50
9ee5558
303e4de
4c2f38e
0a76237
fb94b6c
3126115
5f80dd4
0c1db1e
0c14289
caaf245
1ecfb14
da6fe20
1c48896
bf94994
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| package com.hedera.node.app.spi.fees; | ||
|
|
||
| import static org.hiero.hapi.fees.FeeScheduleUtils.lookupExtraFee; | ||
|
|
||
| import edu.umd.cs.findbugs.annotations.NonNull; | ||
| import java.util.List; | ||
| import java.util.function.Function; | ||
| import org.hiero.hapi.support.fees.Extra; | ||
| import org.hiero.hapi.support.fees.ExtraFeeReference; | ||
| import org.hiero.hapi.support.fees.FeeSchedule; | ||
|
|
||
| /** | ||
| * Base class for simple fee calculators. Provides reusable utility methods for common fee | ||
| * calculation patterns per HIP-1261. | ||
| * | ||
| * <p>Subclasses implement {@link SimpleFeeCalculator} directly and can use the static utility | ||
| * methods provided here to avoid code duplication. | ||
| */ | ||
| public abstract class AbstractSimpleFeeCalculator implements SimpleFeeCalculator { | ||
|
Check warning on line 20 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
|
|
||
| /** | ||
| * Utility: Calculates extra fees from a list of ExtraFeeReferences. | ||
| * Subclasses provide a lambda/method reference to map Extra enum to usage count. | ||
| * | ||
| * <p>This implements the common pattern: base + sum(overage * feePerUnit) where overage = | ||
| * max(0, used - included). | ||
| * | ||
| * @param extras The list of extra fee references to process | ||
| * @param feeSchedule The fee schedule to lookup extra fee costs | ||
| * @param usageMapper Function that returns usage count for each Extra type | ||
| * @return Total extra fees | ||
| */ | ||
| protected static long calculateExtraFees( | ||
| @NonNull final List<ExtraFeeReference> extras, | ||
| @NonNull final FeeSchedule feeSchedule, | ||
| @NonNull final Function<Extra, Long> usageMapper) { | ||
| long total = 0; | ||
|
Check warning on line 38 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| for (final var ref : extras) { | ||
| final long used = usageMapper.apply(ref.name()); | ||
|
Check warning on line 40 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| if (used > ref.includedCount()) { | ||
| final long overage = used - ref.includedCount(); | ||
| total += overage * lookupExtraFee(feeSchedule, ref).fee(); | ||
|
Check warning on line 43 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
| } | ||
| return total; | ||
|
Check warning on line 46 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| /** | ||
| * Utility: Calculates network fee from node fee and multiplier. | ||
| * @param nodeFee The calculated node fee | ||
| * @param multiplier The network multiplier from fee schedule | ||
| * @return Network fee | ||
| */ | ||
| protected static long calculateNetworkFee(long nodeFee, int multiplier) { | ||
| return nodeFee * multiplier; | ||
|
Check warning on line 56 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| /** | ||
| * Creates a builder for constructing usage mappers. | ||
| * Usage example: | ||
| * <pre> | ||
| * var usageMapper = usageBuilder() | ||
| * .withSignatures(context.numTxnSignatures()) | ||
| * .withKeys(keyCount) | ||
| * .build(); | ||
| * </pre> | ||
| * | ||
| * @return a new UsageBuilder instance | ||
| */ | ||
| protected static UsageBuilder usageBuilder() { | ||
| return new UsageBuilder(); | ||
|
Check warning on line 72 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| /** | ||
| * Fluent builder for creating Extra usage mappers per HIP-1261. | ||
| * Provides named methods for each Extra type to clearly document which extras | ||
| * a transaction uses. | ||
| */ | ||
| protected static final class UsageBuilder { | ||
| private final long[] counts = new long[Extra.values().length]; | ||
|
Check warning on line 81 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
|
|
||
| private UsageBuilder() {} | ||
|
Check warning on line 83 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
|
|
||
| public UsageBuilder withSignatures(long count) { | ||
| counts[Extra.SIGNATURES.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 87 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withBytes(long count) { | ||
| counts[Extra.BYTES.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 92 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withKeys(long count) { | ||
| counts[Extra.KEYS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 97 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withTokenTypes(long count) { | ||
| counts[Extra.TOKEN_TYPES.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 102 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withNftSerials(long count) { | ||
| counts[Extra.NFT_SERIALS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 107 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withAccounts(long count) { | ||
| counts[Extra.ACCOUNTS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 112 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withStandardFungibleTokens(long count) { | ||
| counts[Extra.STANDARD_FUNGIBLE_TOKENS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 117 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withStandardNonFungibleTokens(long count) { | ||
| counts[Extra.STANDARD_NON_FUNGIBLE_TOKENS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 122 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withCustomFeeFungibleTokens(long count) { | ||
| counts[Extra.CUSTOM_FEE_FUNGIBLE_TOKENS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 127 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withCustomFeeNonFungibleTokens(long count) { | ||
| counts[Extra.CUSTOM_FEE_NON_FUNGIBLE_TOKENS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 132 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withCreatedAutoAssociations(long count) { | ||
| counts[Extra.CREATED_AUTO_ASSOCIATIONS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 137 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withCreatedAccounts(long count) { | ||
| counts[Extra.CREATED_ACCOUNTS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 142 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withCustomFee(long count) { | ||
| counts[Extra.CUSTOM_FEE.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 147 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withGas(long count) { | ||
| counts[Extra.GAS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 152 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withAllowances(long count) { | ||
| counts[Extra.ALLOWANCES.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 157 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| public UsageBuilder withAirdrops(long count) { | ||
| counts[Extra.AIRDROPS.ordinal()] = count; | ||
| return this; | ||
|
Check warning on line 162 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
|
|
||
| /** | ||
| * Builds the usage mapper function. | ||
| * @return Function that maps Extra types to their usage counts | ||
| */ | ||
| public Function<Extra, Long> build() { | ||
| return extra -> counts[extra.ordinal()]; | ||
|
Check warning on line 170 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java
|
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| /* | ||
| * Copyright (C) 2025 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.spi.fees; | ||
|
|
||
| import com.hedera.hapi.node.base.AccountID; | ||
| import com.hedera.hapi.node.base.TokenID; | ||
| import com.hedera.hapi.node.state.token.Account; | ||
| import com.hedera.hapi.node.state.token.Token; | ||
| import com.hedera.hapi.node.transaction.Query; | ||
| import com.hedera.hapi.node.transaction.TransactionBody; | ||
| import com.hedera.pbj.runtime.io.buffer.Bytes; | ||
| import com.swirlds.config.api.Configuration; | ||
| import edu.umd.cs.findbugs.annotations.NonNull; | ||
| import edu.umd.cs.findbugs.annotations.Nullable; | ||
| import java.util.Optional; | ||
| import java.util.OptionalInt; | ||
| import org.hiero.hapi.fees.FeeResult; | ||
|
|
||
| /** Calculates transaction and query fees. Null context = approximate, non-null = exact using state. */ | ||
| public interface SimpleFeeCalculator { | ||
|
|
||
| /** Provides state access for exact fee calculation. */ | ||
| interface TxContext { | ||
| int cryptoVerificationsRequired(); | ||
|
|
||
| boolean existsAccountWith(@NonNull Bytes alias); | ||
|
|
||
| boolean existsAccount(@NonNull AccountID accountId); | ||
|
||
|
|
||
| @NonNull | ||
| Optional<Account> getAccount(@NonNull AccountID accountId); | ||
|
|
||
| boolean tokenHasCustomFees(@NonNull TokenID tokenId); | ||
|
|
||
| @NonNull | ||
| OptionalInt customFeeCount(@NonNull TokenID tokenId); | ||
|
|
||
| @NonNull | ||
| Optional<Token> getToken(@NonNull TokenID tokenId); | ||
|
|
||
| boolean existsTokenRelation(@NonNull AccountID accountId, @NonNull TokenID tokenId); | ||
|
|
||
| @NonNull | ||
| Configuration configuration(); | ||
|
|
||
| int numTxnSignatures(); | ||
|
|
||
| @NonNull | ||
| FeeCalculatorFactory feeCalculatorFactory(); | ||
| } | ||
|
|
||
| /** Provides state access for exact query fee calculation. */ | ||
| interface QueryContext { | ||
| int cryptoVerificationsRequired(); | ||
| } | ||
|
|
||
| @NonNull | ||
| FeeResult calculateTxFee(@NonNull TransactionBody body, @Nullable TxContext context); | ||
|
|
||
| @NonNull | ||
| FeeResult calculateQueryFee(@NonNull Query query, @Nullable QueryContext context); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| package com.hedera.node.app.service.token.impl.handlers; | ||
|
|
||
| import static java.util.Objects.requireNonNull; | ||
| import static org.hiero.hapi.fees.FeeScheduleUtils.lookupServiceFee; | ||
|
|
||
| import com.hedera.hapi.node.base.HederaFunctionality; | ||
| import com.hedera.hapi.node.base.Key; | ||
| import com.hedera.hapi.node.base.SubType; | ||
| import com.hedera.hapi.node.transaction.Query; | ||
| import com.hedera.hapi.node.transaction.TransactionBody; | ||
| import com.hedera.node.app.spi.fees.AbstractSimpleFeeCalculator; | ||
| import edu.umd.cs.findbugs.annotations.NonNull; | ||
| import edu.umd.cs.findbugs.annotations.Nullable; | ||
| import org.hiero.hapi.fees.FeeResult; | ||
|
|
||
| /** Calculates CryptoCreate fees. Per HIP-1261, uses SIGNATURES and KEYS extras. */ | ||
| public class CryptoCreateFeeCalculator extends AbstractSimpleFeeCalculator { | ||
|
Check warning on line 18 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
|
|
||
| @Override | ||
| @NonNull | ||
| public FeeResult calculateTxFee(@NonNull final TransactionBody body, @Nullable final TxContext context) { | ||
| requireNonNull(body, "body"); | ||
| final var op = body.cryptoCreateAccountOrThrow(); | ||
|
Check warning on line 24 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
| // Count keys in the account being created | ||
| final long keyCount = op.hasKey() ? countKeys(op.key()) : 0L; | ||
|
|
||
| // Build usage mapper - fluent API makes fee structure explicit | ||
| final var usageMapper = usageBuilder() | ||
| .withSignatures(context.numTxnSignatures()) | ||
| .withKeys(keyCount) | ||
| .build(); | ||
|
Check warning on line 32 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
|
|
||
| // Get fee schedule | ||
| final var feeCalculator = context.feeCalculatorFactory().feeCalculator(SubType.DEFAULT); | ||
| final var feeSchedule = feeCalculator.getSimpleFeesSchedule(); | ||
| final var serviceDef = lookupServiceFee(feeSchedule, HederaFunctionality.CRYPTO_CREATE); | ||
|
Check warning on line 37 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
|
|
||
| // Calculate fees | ||
| long nodeFee = feeSchedule.node().baseFee() | ||
| + calculateExtraFees(feeSchedule.node().extras(), feeSchedule, usageMapper); | ||
|
Check warning on line 41 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
|
|
||
| long serviceFee = serviceDef.baseFee() + calculateExtraFees(serviceDef.extras(), feeSchedule, usageMapper); | ||
|
Check warning on line 43 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
|
|
||
| long networkFee = calculateNetworkFee(nodeFee, feeSchedule.network().multiplier()); | ||
|
Check warning on line 45 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
|
|
||
| // Return result | ||
| final var result = new FeeResult(); | ||
| result.node = nodeFee; | ||
| result.network = networkFee; | ||
| result.service = serviceFee; | ||
| return result; | ||
|
Check warning on line 52 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
|
||
| } | ||
|
|
||
| /** | ||
| * Counts all keys including nested ones in threshold/key lists. | ||
| */ | ||
| private long countKeys(Key key) { | ||
aderevets marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return switch (key.key().kind()) { | ||
| case ED25519, ECDSA_SECP256K1, ECDSA_384 -> 1L; | ||
|
Check warning on line 60 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
| case THRESHOLD_KEY -> | ||
|
||
| key.thresholdKeyOrThrow().keys().keys().stream() | ||
| .mapToLong(this::countKeys) | ||
| .sum(); | ||
|
Check warning on line 64 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
| case KEY_LIST -> | ||
| key.keyListOrThrow().keys().stream().mapToLong(this::countKeys).sum(); | ||
| default -> 0L; | ||
|
Check warning on line 67 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
| }; | ||
| } | ||
|
|
||
| @Override | ||
| @NonNull | ||
| public FeeResult calculateQueryFee(@NonNull final Query query, @Nullable final QueryContext context) { | ||
aderevets marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| throw new UnsupportedOperationException("Query fee calculation not implemented for CRYPTO_CREATE"); | ||
|
Check warning on line 74 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java
|
||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.