diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenFeeScheduleUpdateHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenFeeScheduleUpdateHandler.java index 9c2abf42eda3..8363fc4e08fd 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenFeeScheduleUpdateHandler.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenFeeScheduleUpdateHandler.java @@ -24,6 +24,7 @@ import static com.hedera.node.app.hapi.fees.usage.SingletonEstimatorUtils.ESTIMATOR_UTILS; import static com.hedera.node.app.hapi.fees.usage.token.TokenOpsUsage.LONG_BASIC_ENTITY_ID_SIZE; import static com.hedera.node.app.spi.workflows.HandleException.validateTrue; +import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; import com.hedera.hapi.node.base.AccountID; @@ -192,8 +193,9 @@ public Fees calculateFees(@NonNull final FeeContext feeContext) { final var effConsTime = body.transactionIDOrThrow().transactionValidStartOrThrow().seconds(); final var lifetime = Math.max(0, token == null ? 0 : token.expirationSecond() - effConsTime); + final List customFees = token == null ? emptyList() : token.customFees(); - final var existingFeeReprBytes = currentFeeScheduleSize(token.customFees(), tokenOpsUsage); + final var existingFeeReprBytes = currentFeeScheduleSize(customFees, tokenOpsUsage); final var rbsDelta = ESTIMATOR_UTILS.changeInBsUsage(existingFeeReprBytes, lifetime, newReprBytes, lifetime); return feeContext .feeCalculatorFactory() diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenFeeScheduleUpdateHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenFeeScheduleUpdateHandlerTest.java index 56577ab7b6f7..e22ccbe3c5c8 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenFeeScheduleUpdateHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenFeeScheduleUpdateHandlerTest.java @@ -68,6 +68,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -309,6 +310,48 @@ public void testCalculateFeesHappyPath() { assertEquals(calculateFees, Fees.FREE); } + @Test + @DisplayName("If the requested token ID does not exist, the fee calculation should not throw an exception") + void calculateFeesTokenDoesNotExist() { + TransactionInfo txnInfo = mock(TransactionInfo.class); + FeeManager feeManager = mock(FeeManager.class); + FeeCalculator feeCalculator = mock(FeeCalculator.class); + ReadableStoreFactory storeFactory = mock(ReadableStoreFactory.class); + TransactionBody transactionBody = mock(TransactionBody.class); + TokenFeeScheduleUpdateTransactionBody tokenFeeScheduleUpdateTransactionBody = + mock(TokenFeeScheduleUpdateTransactionBody.class); + TransactionID transactionID = mock(TransactionID.class); + + when(feeManager.createFeeCalculator(any(), any(), any(), anyInt(), anyInt(), any(), any(), anyBoolean(), any())) + .thenReturn(feeCalculator); + when(txnInfo.txBody()).thenReturn(transactionBody); + when(transactionBody.tokenFeeScheduleUpdateOrThrow()).thenReturn(tokenFeeScheduleUpdateTransactionBody); + // Any token ID that doesn't exist: + when(tokenFeeScheduleUpdateTransactionBody.tokenIdOrThrow()) + .thenReturn(TokenID.newBuilder().tokenNum(1500).build()); + when(storeFactory.getStore(ReadableTokenStore.class)).thenReturn(readableTokenStore); + when(transactionBody.transactionIDOrThrow()).thenReturn(transactionID); + when(transactionID.transactionValidStartOrThrow()).thenReturn(consensusTimestamp); + when(txnInfo.signatureMap()).thenReturn(SignatureMap.DEFAULT); + when(feeCalculator.addBytesPerTransaction(anyLong())).thenReturn(feeCalculator); + when(feeCalculator.addRamByteSeconds(anyLong())).thenReturn(feeCalculator); + when(feeCalculator.calculate()).thenReturn(Fees.FREE); + + final var feeContext = new FeeContextImpl( + consensusInstant, + txnInfo, + payerKey, + payerId, + feeManager, + storeFactory, + configuration, + null, + -1, + transactionDispatcher); + + Assertions.assertThatNoException().isThrownBy(() -> subject.calculateFees(feeContext)); + } + private void givenTxn() { txn = TransactionBody.newBuilder() .tokenFeeScheduleUpdate(TokenFeeScheduleUpdateTransactionBody.newBuilder()