Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,11 @@ Fees charge(
* @param fees the fees to be refunded
*/
void refund(@NonNull Context ctx, @NonNull Fees fees);

/**
* Whether the dispatch context should bypass this strategy when charging handler-assessed fees.
*/
default boolean bypassForExtraHandlerCharges() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@
/**
* A fee charging strategy that validates all scenarios and charges no fees.
*/
public enum NoopFeeCharging implements FeeCharging {
NOOP_FEE_CHARGING;
public class NoopFeeCharging implements FeeCharging {
public static final NoopFeeCharging UNIVERSAL_NOOP_FEE_CHARGING = new NoopFeeCharging(false);
public static final NoopFeeCharging DISPATCH_ONLY_NOOP_FEE_CHARGING = new NoopFeeCharging(true);

private final boolean bypassForExtraHandlerCharges;

public NoopFeeCharging(final boolean bypassForExtraHandlerCharges) {
this.bypassForExtraHandlerCharges = bypassForExtraHandlerCharges;
}

@Override
public Validation validate(
Expand Down Expand Up @@ -51,6 +58,11 @@
// No-op
}

@Override
public boolean bypassForExtraHandlerCharges() {
return bypassForExtraHandlerCharges;

Check warning on line 63 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/NoopFeeCharging.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/NoopFeeCharging.java#L63

Added line #L63 was not covered by tests
}

private record PassedValidation(boolean creatorDidDueDiligence, @Nullable ResponseCodeEnum maybeErrorStatus)
implements Validation {
private static final PassedValidation INSTANCE = new PassedValidation(true, null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.node.app.spi.workflows;

import static com.hedera.node.app.spi.fees.NoopFeeCharging.NOOP_FEE_CHARGING;
import static com.hedera.node.app.spi.fees.NoopFeeCharging.UNIVERSAL_NOOP_FEE_CHARGING;
import static com.hedera.node.app.spi.workflows.HandleContext.DispatchMetadata.EMPTY_METADATA;
import static com.hedera.node.app.spi.workflows.HandleContext.DispatchMetadata.Type.CUSTOM_FEE_CHARGING;
import static com.hedera.node.app.spi.workflows.record.StreamBuilder.SignedTxCustomizer.NOOP_SIGNED_TX_CUSTOMIZER;
Expand Down Expand Up @@ -176,7 +176,7 @@ public static <T extends StreamBuilder> DispatchOptions<T> independentDispatch(
ReversingBehavior.IRREVERSIBLE,
NOOP_SIGNED_TX_CUSTOMIZER,
EMPTY_METADATA,
NOOP_FEE_CHARGING);
UNIVERSAL_NOOP_FEE_CHARGING);
}

/**
Expand Down Expand Up @@ -334,7 +334,7 @@ public static <T extends StreamBuilder> DispatchOptions<T> stepDispatch(
reversingBehavior,
signedTxCustomizer,
metadata,
NOOP_FEE_CHARGING);
UNIVERSAL_NOOP_FEE_CHARGING);
}

/**
Expand Down Expand Up @@ -370,7 +370,7 @@ public static <T extends StreamBuilder> DispatchOptions<T> stepDispatch(
ReversingBehavior.REMOVABLE,
signedTxCustomizer,
metaData,
NOOP_FEE_CHARGING);
UNIVERSAL_NOOP_FEE_CHARGING);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.node.app.spi.fees;

import static com.hedera.node.app.spi.fees.NoopFeeCharging.NOOP_FEE_CHARGING;
import static com.hedera.node.app.spi.fees.NoopFeeCharging.UNIVERSAL_NOOP_FEE_CHARGING;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verifyNoInteractions;

Expand All @@ -25,7 +25,7 @@ class NoopFeeChargingTest {

@Test
void validationAlwaysPasses() {
final var validation = NOOP_FEE_CHARGING.validate(
final var validation = UNIVERSAL_NOOP_FEE_CHARGING.validate(
Account.DEFAULT,
AccountID.DEFAULT,
Fees.FREE,
Expand All @@ -39,7 +39,7 @@ void validationAlwaysPasses() {

@Test
void chargingIsNoop() {
NOOP_FEE_CHARGING.charge(ctx, validation, Fees.FREE);
UNIVERSAL_NOOP_FEE_CHARGING.charge(ctx, validation, Fees.FREE);
verifyNoInteractions(ctx, validation);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.node.app.spi.workflows;

import static com.hedera.node.app.spi.fees.NoopFeeCharging.NOOP_FEE_CHARGING;
import static com.hedera.node.app.spi.fees.NoopFeeCharging.UNIVERSAL_NOOP_FEE_CHARGING;
import static com.hedera.node.app.spi.workflows.HandleContext.DispatchMetadata.Type.CUSTOM_FEE_CHARGING;
import static org.junit.jupiter.api.Assertions.*;

Expand All @@ -23,12 +23,12 @@ void propagatesSubDispatchCustomFeeChargingViaExpectedKeyIfRequested() {
StreamBuilder.class,
DispatchOptions.StakingRewards.OFF,
DispatchOptions.UsePresetTxnId.YES,
NOOP_FEE_CHARGING,
UNIVERSAL_NOOP_FEE_CHARGING,
DispatchOptions.PropagateFeeChargingStrategy.YES);

final var maybeFeeCharging = options.dispatchMetadata().getMetadata(CUSTOM_FEE_CHARGING, FeeCharging.class);
assertTrue(maybeFeeCharging.isPresent());
assertSame(NOOP_FEE_CHARGING, maybeFeeCharging.get());
assertSame(UNIVERSAL_NOOP_FEE_CHARGING, maybeFeeCharging.get());
}

@Test
Expand All @@ -41,7 +41,7 @@ void doesNotPropagateSubDispatchCustomFeeChargingViaExpectedKeyIfNotRequested()
StreamBuilder.class,
DispatchOptions.StakingRewards.OFF,
DispatchOptions.UsePresetTxnId.YES,
NOOP_FEE_CHARGING,
UNIVERSAL_NOOP_FEE_CHARGING,
DispatchOptions.PropagateFeeChargingStrategy.NO);

final var maybeFeeCharging = options.dispatchMetadata().getMetadata(CUSTOM_FEE_CHARGING, FeeCharging.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public ValidationResult validate(
@NonNull final AccountID creatorId,
@NonNull final Fees fees,
@NonNull final TransactionBody body,
boolean isDuplicate,
final boolean isDuplicate,
@NonNull final HederaFunctionality function,
@NonNull final HandleContext.TransactionCategory category) {
requireNonNull(payer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static com.hedera.hapi.util.HapiUtils.functionOf;
import static com.hedera.node.app.spi.workflows.HandleContext.DispatchMetadata.Type.INNER_TRANSACTION_BYTES;
import static com.hedera.node.app.spi.workflows.HandleContext.TransactionCategory.BATCH_INNER;
import static com.hedera.node.app.spi.workflows.HandleContext.TransactionCategory.CHILD;
import static com.hedera.node.app.workflows.handle.stack.SavepointStackImpl.castBuilder;
import static java.util.Collections.emptyMap;
import static java.util.Objects.requireNonNull;
Expand All @@ -31,6 +32,7 @@
import com.hedera.node.app.spi.fees.ExchangeRateInfo;
import com.hedera.node.app.spi.fees.FeeCalculator;
import com.hedera.node.app.spi.fees.FeeCalculatorFactory;
import com.hedera.node.app.spi.fees.FeeCharging;
import com.hedera.node.app.spi.fees.FeeContext;
import com.hedera.node.app.spi.fees.Fees;
import com.hedera.node.app.spi.fees.ResourcePriceCalculator;
Expand All @@ -55,6 +57,7 @@
import com.hedera.node.app.workflows.TransactionInfo;
import com.hedera.node.app.workflows.dispatcher.TransactionDispatcher;
import com.hedera.node.app.workflows.handle.dispatch.ChildDispatchFactory;
import com.hedera.node.app.workflows.handle.dispatch.ValidationResult;
import com.hedera.node.app.workflows.handle.stack.SavepointStackImpl;
import com.hedera.node.app.workflows.handle.validation.AttributeValidatorImpl;
import com.hedera.node.app.workflows.handle.validation.ExpiryValidatorImpl;
Expand All @@ -70,11 +73,12 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.ObjLongConsumer;

/**
* The {@link HandleContext} implementation.
*/
public class DispatchHandleContext implements HandleContext, FeeContext {
public class DispatchHandleContext implements HandleContext, FeeContext, FeeCharging.Context {
private final Instant consensusNow;
private final NodeInfo creatorInfo;
private final TransactionInfo txnInfo;
Expand All @@ -83,6 +87,7 @@ public class DispatchHandleContext implements HandleContext, FeeContext {
private final BlockRecordInfo blockRecordInfo;
private final ResourcePriceCalculator resourcePriceCalculator;
private final FeeManager feeManager;
private final FeeCharging feeCharging;
private final StoreFactoryImpl storeFactory;
private final AccountID payerId;
private final AppKeyVerifier verifier;
Expand All @@ -99,11 +104,13 @@ public class DispatchHandleContext implements HandleContext, FeeContext {
private final DispatchProcessor dispatchProcessor;
private final ThrottleAdviser throttleAdviser;
private final FeeAccumulator feeAccumulator;
private Map<AccountID, Long> dispatchPaidRewards;
private final DispatchMetadata dispatchMetaData;
private final TransactionChecker transactionChecker;
private final TransactionCategory transactionCategory;

@Nullable
private Map<AccountID, Long> dispatchPaidRewards;

// This is used to store the pre-handle results for the inner transactions
// in an atomic batch, null otherwise
@Nullable
Expand All @@ -121,6 +128,7 @@ public DispatchHandleContext(
@NonNull final BlockRecordInfo blockRecordInfo,
@NonNull final ResourcePriceCalculator resourcePriceCalculator,
@NonNull final FeeManager feeManager,
@NonNull final FeeCharging feeCharging,
@NonNull final StoreFactoryImpl storeFactory,
@NonNull final AccountID payerId,
@NonNull final AppKeyVerifier verifier,
Expand Down Expand Up @@ -148,6 +156,7 @@ public DispatchHandleContext(
this.blockRecordInfo = requireNonNull(blockRecordInfo);
this.resourcePriceCalculator = requireNonNull(resourcePriceCalculator);
this.feeManager = requireNonNull(feeManager);
this.feeCharging = requireNonNull(feeCharging);
this.storeFactory = requireNonNull(storeFactory);
this.payerId = requireNonNull(payerId);
this.verifier = requireNonNull(verifier);
Expand Down Expand Up @@ -195,16 +204,27 @@ public boolean tryToCharge(@NonNull final AccountID accountId, final long amount
if (amount < 0) {
throw new IllegalArgumentException("Cannot charge negative amount " + amount);
}
return feeAccumulator.chargeFee(accountId, amount, null).networkFee() == amount;
if (feeCharging.bypassForExtraHandlerCharges()) {
return feeAccumulator.chargeFee(accountId, amount, null).networkFee() == amount;
} else {
return feeCharging
.charge(this, ValidationResult.newSuccess(creatorInfo.accountId()), new Fees(0, amount, 0))
.totalFee()
== amount;
}
}

@Override
public void refundBestEffort(@NonNull final AccountID accountId, final long amount) {
requireNonNull(accountId);
if (amount < 0) {
throw new IllegalArgumentException("Cannot charge negative amount " + amount);
throw new IllegalArgumentException("Cannot refund negative amount " + amount);
}
if (feeCharging.bypassForExtraHandlerCharges()) {
feeAccumulator.refundFee(accountId, amount);
} else {
feeCharging.refund(this, new Fees(0, amount, 0));
}
feeAccumulator.refundFee(accountId, amount);
}

@NonNull
Expand Down Expand Up @@ -471,4 +491,48 @@ public NodeInfo creatorInfo() {
public DispatchMetadata dispatchMetadata() {
return dispatchMetaData;
}

@Override
public AccountID payerId() {
return payerId;
}

@Override
public AccountID nodeAccountId() {
return creatorInfo.accountId();
}

@Override
public Fees charge(
@NonNull final AccountID payerId, @NonNull final Fees fees, @Nullable final ObjLongConsumer<AccountID> cb) {
return feeAccumulator.chargeFee(payerId, fees.totalFee(), cb);
}

@Override
public void refund(@NonNull final AccountID receiverId, @NonNull final Fees fees) {
feeAccumulator.refundFee(receiverId, fees.totalFee());
}

@Override
public Fees charge(
@NonNull final AccountID payerId,
@NonNull final Fees fees,
@NonNull final AccountID nodeAccountId,
@Nullable final ObjLongConsumer<AccountID> cb) {
return feeAccumulator.chargeFees(payerId, nodeAccountId, fees, cb);
}

@Override
public void refund(
@NonNull final AccountID payerId, @NonNull final Fees fees, @NonNull final AccountID nodeAccountId) {
feeAccumulator.refundFees(payerId, fees, nodeAccountId);
}

@Override
public TransactionCategory category() {
// When the DispatchHandleContext is used as a fee charging context, always report
// CHILD category to stay backward compatible with the calls made to FeeAccumulator
// when it was invoked directly
return CHILD;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.hedera.hapi.node.transaction.SignedTransaction;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.hapi.util.UnknownHederaFunctionality;
import com.hedera.node.app.fees.AppFeeCharging;
import com.hedera.node.app.fees.ExchangeRateManager;
import com.hedera.node.app.fees.FeeAccumulator;
import com.hedera.node.app.fees.FeeManager;
Expand Down Expand Up @@ -104,6 +105,7 @@ public class ChildDispatchFactory {
private final Authorizer authorizer;
private final NetworkInfo networkInfo;
private final FeeManager feeManager;
private final AppFeeCharging appFeeCharging;
private final DispatchProcessor dispatchProcessor;
private final ServiceScopeLookup serviceScopeLookup;
private final ExchangeRateManager exchangeRateManager;
Expand All @@ -116,6 +118,7 @@ public ChildDispatchFactory(
@NonNull final Authorizer authorizer,
@NonNull final NetworkInfo networkInfo,
@NonNull final FeeManager feeManager,
@NonNull final AppFeeCharging appFeeCharging,
@NonNull final DispatchProcessor dispatchProcessor,
@NonNull final ServiceScopeLookup serviceScopeLookup,
@NonNull final ExchangeRateManager exchangeRateManager,
Expand All @@ -125,6 +128,7 @@ public ChildDispatchFactory(
this.authorizer = requireNonNull(authorizer);
this.networkInfo = requireNonNull(networkInfo);
this.feeManager = requireNonNull(feeManager);
this.appFeeCharging = requireNonNull(appFeeCharging);
this.dispatchProcessor = requireNonNull(dispatchProcessor);
this.serviceScopeLookup = requireNonNull(serviceScopeLookup);
this.exchangeRateManager = requireNonNull(exchangeRateManager);
Expand Down Expand Up @@ -239,7 +243,7 @@ private RecordDispatch newChildDispatch(
@NonNull final Instant consensusNow,
@NonNull final DispatchMetadata dispatchMetadata,
@NonNull final ConsensusThrottling consensusThrottling,
@Nullable FeeCharging customFeeCharging,
@Nullable final FeeCharging customFeeCharging,
// @UserTxnScope
@NonNull final NodeInfo creatorInfo,
@NonNull final Configuration config,
Expand All @@ -266,6 +270,7 @@ private RecordDispatch newChildDispatch(
final var storeFactory = new StoreFactoryImpl(readableStoreFactory, writableStoreFactory, serviceApiFactory);
final var childFeeAccumulator = new FeeAccumulator(
serviceApiFactory.getApi(TokenServiceApi.class), (FeeStreamBuilder) builder, childStack);
final var feeCharging = customFeeCharging != null ? customFeeCharging : appFeeCharging;
final var dispatchHandleContext = new DispatchHandleContext(
consensusNow,
creatorInfo,
Expand All @@ -275,6 +280,7 @@ private RecordDispatch newChildDispatch(
blockRecordInfo,
priceCalculator,
feeManager,
feeCharging,
storeFactory,
payerId,
keyVerifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,17 @@ public static ValidationResult newSuccess(@NonNull final AccountID creatorId, @N
return new ValidationResult(creatorId, null, payer, null, CAN_PAY_SERVICE_FEE, NO_DUPLICATE);
}

/**
* Creates an error report with no error.
* @param creatorId the creator account ID
* @return the error report
*/
@NonNull
public static ValidationResult newSuccess(@NonNull final AccountID creatorId) {
requireNonNull(creatorId);
return new ValidationResult(creatorId, null, null, null, CAN_PAY_SERVICE_FEE, NO_DUPLICATE);
}

/**
* Creates an error report with no error.
* @param creatorId the creator account ID
Expand Down Expand Up @@ -187,15 +198,6 @@ public ResponseCodeEnum creatorErrorOrThrow() {
return requireNonNull(creatorError);
}

/**
* Checks if there is a payer.
* @return payer account if there is a payer. Otherwise, throws an exception.
*/
@NonNull
public Account payerOrThrow() {
return requireNonNull(payer);
}

/**
* Returns the error report with all fees except service fee.
* @return the error report
Expand Down
Loading
Loading