diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/BaseTokenHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/BaseTokenHandler.java
index d346996d1ccc..8e477646ee35 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/BaseTokenHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/BaseTokenHandler.java
@@ -42,7 +42,9 @@
import com.hedera.node.app.service.token.impl.WritableAccountStore;
import com.hedera.node.app.service.token.impl.WritableTokenRelationStore;
import com.hedera.node.app.service.token.impl.WritableTokenStore;
+import com.hedera.node.app.service.token.impl.util.TokenHandlerHelper;
import com.hedera.node.app.service.token.impl.util.TokenKey;
+import com.hedera.node.app.spi.validation.ExpiryValidator;
import com.hedera.node.config.data.EntitiesConfig;
import com.hedera.node.config.data.TokensConfig;
import com.swirlds.config.api.Configuration;
@@ -77,12 +79,14 @@ public class BaseTokenHandler {
/**
* Mints fungible tokens. This method is called in both token create and mint.
- * @param token the new or existing token to mint
- * @param treasuryRel the treasury relation for the token
- * @param amount the amount to mint
- * @param accountStore the account store
- * @param tokenStore the token store
+ *
+ * @param token the new or existing token to mint
+ * @param treasuryRel the treasury relation for the token
+ * @param amount the amount to mint
+ * @param accountStore the account store
+ * @param tokenStore the token store
* @param tokenRelationStore the token relation store
+ * @param expiryValidator the expiry validator
*/
protected long mintFungible(
@NonNull final Token token,
@@ -90,12 +94,20 @@ protected long mintFungible(
final long amount,
@NonNull final WritableAccountStore accountStore,
@NonNull final WritableTokenStore tokenStore,
- @NonNull final WritableTokenRelationStore tokenRelationStore) {
+ @NonNull final WritableTokenRelationStore tokenRelationStore,
+ @NonNull final ExpiryValidator expiryValidator) {
requireNonNull(token);
requireNonNull(treasuryRel);
return changeSupply(
- token, treasuryRel, +amount, INVALID_TOKEN_MINT_AMOUNT, accountStore, tokenStore, tokenRelationStore);
+ token,
+ treasuryRel,
+ +amount,
+ INVALID_TOKEN_MINT_AMOUNT,
+ accountStore,
+ tokenStore,
+ tokenRelationStore,
+ expiryValidator);
}
/**
@@ -105,13 +117,14 @@ protected long mintFungible(
*
* Note: This method assumes the given token has a non-null supply key!
*
- * @param token the token that is minted or burned
- * @param treasuryRel the treasury relation for the token
- * @param amount the amount to mint or burn
- * @param invalidSupplyCode the invalid supply code to use if the supply is invalid
- * @param accountStore the account store
- * @param tokenStore the token store
+ * @param token the token that is minted or burned
+ * @param treasuryRel the treasury relation for the token
+ * @param amount the amount to mint or burn
+ * @param invalidSupplyCode the invalid supply code to use if the supply is invalid
+ * @param accountStore the account store
+ * @param tokenStore the token store
* @param tokenRelationStore the token relation store
+ * @param expiryValidator the expiry validator
*/
protected long changeSupply(
@NonNull final Token token,
@@ -120,7 +133,8 @@ protected long changeSupply(
@NonNull final ResponseCodeEnum invalidSupplyCode,
@NonNull final WritableAccountStore accountStore,
@NonNull final WritableTokenStore tokenStore,
- @NonNull final WritableTokenRelationStore tokenRelationStore) {
+ @NonNull final WritableTokenRelationStore tokenRelationStore,
+ @NonNull final ExpiryValidator expiryValidator) {
requireNonNull(token);
requireNonNull(treasuryRel);
requireNonNull(invalidSupplyCode);
@@ -140,8 +154,8 @@ protected long changeSupply(
validateTrue(token.maxSupply() >= newTotalSupply, TOKEN_MAX_SUPPLY_REACHED);
}
- final var treasuryAccount = accountStore.get(treasuryRel.accountId());
- validateTrue(treasuryAccount != null, INVALID_TREASURY_ACCOUNT_FOR_TOKEN);
+ final var treasuryAccount = TokenHandlerHelper.getIfUsable(
+ treasuryRel.accountId(), accountStore, expiryValidator, INVALID_TREASURY_ACCOUNT_FOR_TOKEN);
final long newTreasuryBalance = treasuryRel.balance() + amount;
validateTrue(newTreasuryBalance >= 0, INSUFFICIENT_TOKEN_BALANCE);
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoApproveAllowanceHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoApproveAllowanceHandler.java
index c93d986638e3..51a843f38b09 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoApproveAllowanceHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoApproveAllowanceHandler.java
@@ -41,6 +41,7 @@
import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.HederaFunctionality;
+import com.hedera.hapi.node.base.NftID;
import com.hedera.hapi.node.base.SubType;
import com.hedera.hapi.node.base.TokenID;
import com.hedera.hapi.node.state.token.Account;
@@ -56,9 +57,11 @@
import com.hedera.node.app.service.token.impl.WritableAccountStore;
import com.hedera.node.app.service.token.impl.WritableNftStore;
import com.hedera.node.app.service.token.impl.WritableTokenStore;
+import com.hedera.node.app.service.token.impl.util.TokenHandlerHelper;
import com.hedera.node.app.service.token.impl.validators.ApproveAllowanceValidator;
import com.hedera.node.app.spi.fees.FeeContext;
import com.hedera.node.app.spi.fees.Fees;
+import com.hedera.node.app.spi.validation.ExpiryValidator;
import com.hedera.node.app.spi.workflows.HandleContext;
import com.hedera.node.app.spi.workflows.HandleException;
import com.hedera.node.app.spi.workflows.PreCheckException;
@@ -186,8 +189,8 @@ public void handle(@NonNull final HandleContext context) throws HandleException
final var accountStore = context.storeFactory().writableStore(WritableAccountStore.class);
// Validate payer account exists
- final var payerAccount = accountStore.getAccountById(payer);
- validateTrue(payerAccount != null, INVALID_PAYER_ACCOUNT_ID);
+ final var payerAccount = TokenHandlerHelper.getIfUsable(
+ payer, accountStore, context.expiryValidator(), INVALID_PAYER_ACCOUNT_ID);
// Validate the transaction body fields that include state or configuration.
validateSemantics(context, payerAccount, accountStore);
@@ -241,26 +244,36 @@ private void approveAllowance(
/* --- Apply changes to state --- */
final var allowanceMaxAccountLimit = hederaConfig.allowancesMaxAccountLimit();
- applyCryptoAllowances(cryptoAllowances, payerId, accountStore, allowanceMaxAccountLimit);
- applyFungibleTokenAllowances(tokenAllowances, payerId, accountStore, allowanceMaxAccountLimit);
+ final var expiryValidator = context.expiryValidator();
+ applyCryptoAllowances(cryptoAllowances, payerId, accountStore, allowanceMaxAccountLimit, expiryValidator);
+ applyFungibleTokenAllowances(tokenAllowances, payerId, accountStore, allowanceMaxAccountLimit, expiryValidator);
applyNftAllowances(
- nftAllowances, payerId, accountStore, tokenStore, uniqueTokenStore, allowanceMaxAccountLimit);
+ nftAllowances,
+ payerId,
+ accountStore,
+ tokenStore,
+ uniqueTokenStore,
+ allowanceMaxAccountLimit,
+ expiryValidator);
}
/**
* Applies all changes needed for Crypto allowances from the transaction.
* If the spender already has an allowance, the allowance value will be replaced with values
* from transaction. If the amount specified is 0, the allowance will be removed.
- * @param cryptoAllowances the list of crypto allowances
- * @param payerId the payer account id
- * @param accountStore the account store
+ *
+ * @param cryptoAllowances the list of crypto allowances
+ * @param payerId the payer account id
+ * @param accountStore the account store
* @param allowanceMaxAccountLimit the {@link HederaConfig}
+ * @param expiryValidator the expiry validator
*/
private void applyCryptoAllowances(
@NonNull final List cryptoAllowances,
@NonNull final AccountID payerId,
@NonNull final WritableAccountStore accountStore,
- final int allowanceMaxAccountLimit) {
+ final int allowanceMaxAccountLimit,
+ @NonNull final ExpiryValidator expiryValidator) {
requireNonNull(cryptoAllowances);
requireNonNull(payerId);
requireNonNull(accountStore);
@@ -268,7 +281,7 @@ private void applyCryptoAllowances(
for (final var allowance : cryptoAllowances) {
final var owner = allowance.owner();
final var spender = allowance.spenderOrThrow();
- final var effectiveOwner = getEffectiveOwnerAccount(owner, payerId, accountStore);
+ final var effectiveOwner = getEffectiveOwnerAccount(owner, payerId, accountStore, expiryValidator);
final var mutableAllowances = new ArrayList<>(effectiveOwner.cryptoAllowances());
final var amount = allowance.amount();
@@ -318,23 +331,25 @@ private void updateCryptoAllowance(
* Applies all changes needed for fungible token allowances from the transaction. If the key
* {token, spender} already has an allowance, the allowance value will be replaced with values
* from transaction.
- * @param tokenAllowances the list of token allowances
- * @param payerId the payer account id
- * @param accountStore the account store
+ * @param tokenAllowances the list of token allowances
+ * @param payerId the payer account id
+ * @param accountStore the account store
* @param allowanceMaxAccountLimit the {@link HederaConfig} allowance max account limit
+ * @param expiryValidator the expiry validator
*/
private void applyFungibleTokenAllowances(
@NonNull final List tokenAllowances,
@NonNull final AccountID payerId,
@NonNull final WritableAccountStore accountStore,
- final int allowanceMaxAccountLimit) {
+ final int allowanceMaxAccountLimit,
+ @NonNull final ExpiryValidator expiryValidator) {
for (final var allowance : tokenAllowances) {
final var owner = allowance.owner();
final var amount = allowance.amount();
final var tokenId = allowance.tokenIdOrThrow();
final var spender = allowance.spenderOrThrow();
- final var effectiveOwner = getEffectiveOwnerAccount(owner, payerId, accountStore);
+ final var effectiveOwner = getEffectiveOwnerAccount(owner, payerId, accountStore, expiryValidator);
final var mutableTokenAllowances = new ArrayList<>(effectiveOwner.tokenAllowances());
updateTokenAllowance(mutableTokenAllowances, amount, spender, tokenId);
@@ -387,12 +402,13 @@ private void updateTokenAllowance(
* spenderNum} doesn't exist in the map the allowance will be inserted. If the key exists,
* existing allowance values will be replaced with new allowances given in operation
*
- * @param nftAllowances the list of nft allowances
- * @param payerId the payer account id
- * @param accountStore the account store
- * @param tokenStore the token store
- * @param uniqueTokenStore the unique token store
+ * @param nftAllowances the list of nft allowances
+ * @param payerId the payer account id
+ * @param accountStore the account store
+ * @param tokenStore the token store
+ * @param uniqueTokenStore the unique token store
* @param allowanceMaxAccountLimit the {@link HederaConfig} config allowance max account limit
+ * @param expiryValidator the expiry validator
*/
protected void applyNftAllowances(
final List nftAllowances,
@@ -400,13 +416,14 @@ protected void applyNftAllowances(
@NonNull final WritableAccountStore accountStore,
@NonNull final WritableTokenStore tokenStore,
@NonNull final WritableNftStore uniqueTokenStore,
- final int allowanceMaxAccountLimit) {
+ final int allowanceMaxAccountLimit,
+ @NonNull final ExpiryValidator expiryValidator) {
for (final var allowance : nftAllowances) {
final var owner = allowance.owner();
final var tokenId = allowance.tokenIdOrThrow();
final var spender = allowance.spenderOrThrow();
- final var effectiveOwner = getEffectiveOwnerAccount(owner, payerId, accountStore);
+ final var effectiveOwner = getEffectiveOwnerAccount(owner, payerId, accountStore, expiryValidator);
final var mutableNftAllowances = new ArrayList<>(effectiveOwner.approveForAllNftAllowances());
if (allowance.hasApprovedForAll()) {
@@ -456,8 +473,11 @@ public void updateSpender(
final var serialsSet = new HashSet<>(serialNums);
for (final var serialNum : serialsSet) {
- final var nft = uniqueTokenStore.get(tokenId, serialNum);
- final var token = tokenStore.get(tokenId);
+ final var nftId =
+ NftID.newBuilder().serialNumber(serialNum).tokenId(tokenId).build();
+ final var nft = TokenHandlerHelper.getIfUsable(nftId, uniqueTokenStore);
+ final var token = TokenHandlerHelper.getIfUsable(
+ tokenId, tokenStore, TokenHandlerHelper.TokenValidations.PERMIT_PAUSED);
final AccountID accountOwner = owner.accountId();
validateTrue(isValidOwner(nft, accountOwner, token), SENDER_DOES_NOT_OWN_NFT_SERIAL_NO);
@@ -508,15 +528,18 @@ private int lookupSpenderAndToken(
* Returns the effective owner account. If the owner is not present or owner is same as payer.
* Since we are modifying the payer account in the same transaction for each allowance if owner is not specified,
* we need to get the payer account each time from the modifications map.
- * @param owner owner of the allowance
- * @param payerId payer of the transaction
- * @param accountStore account store
+ *
+ * @param owner owner of the allowance
+ * @param payerId payer of the transaction
+ * @param accountStore account store
+ * @param expiryValidator the expiry validator
* @return effective owner account
*/
private static Account getEffectiveOwnerAccount(
@Nullable final AccountID owner,
@NonNull final AccountID payerId,
- @NonNull final ReadableAccountStore accountStore) {
+ @NonNull final ReadableAccountStore accountStore,
+ @NonNull final ExpiryValidator expiryValidator) {
final var ownerNum = owner != null ? owner.accountNumOrElse(0L) : 0L;
if (ownerNum == 0 || ownerNum == payerId.accountNumOrThrow()) {
// The payer would have been modified in the same transaction for previous allowances
@@ -524,8 +547,8 @@ private static Account getEffectiveOwnerAccount(
return accountStore.getAccountById(payerId);
} else {
// If owner is in modifications get the modified account from state
- final var ownerAccount = accountStore.getAccountById(owner);
- validateTrue(ownerAccount != null, INVALID_ALLOWANCE_OWNER_ID);
+ final var ownerAccount =
+ TokenHandlerHelper.getIfUsable(owner, accountStore, expiryValidator, INVALID_ALLOWANCE_OWNER_ID);
return ownerAccount;
}
}
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java
index 14684156873c..78812d9b856c 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java
@@ -18,7 +18,6 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.EMPTY_ALLOWANCES;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ALLOWANCE_OWNER_ID;
-import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_NFT_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_PAYER_ACCOUNT_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.SENDER_DOES_NOT_OWN_NFT_SERIAL_NO;
@@ -33,6 +32,7 @@
import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.HederaFunctionality;
+import com.hedera.hapi.node.base.NftID;
import com.hedera.hapi.node.base.SubType;
import com.hedera.hapi.node.base.TokenID;
import com.hedera.hapi.node.state.token.Account;
@@ -42,6 +42,7 @@
import com.hedera.node.app.service.token.impl.WritableAccountStore;
import com.hedera.node.app.service.token.impl.WritableNftStore;
import com.hedera.node.app.service.token.impl.WritableTokenStore;
+import com.hedera.node.app.service.token.impl.util.TokenHandlerHelper;
import com.hedera.node.app.service.token.impl.validators.DeleteAllowanceValidator;
import com.hedera.node.app.spi.fees.FeeContext;
import com.hedera.node.app.spi.fees.Fees;
@@ -113,8 +114,8 @@ public void handle(@NonNull final HandleContext context) throws HandleException
final var accountStore = context.storeFactory().writableStore(WritableAccountStore.class);
// validate payer account exists
- final var payerAccount = accountStore.getAccountById(payer);
- validateTrue(payerAccount != null, INVALID_PAYER_ACCOUNT_ID);
+ final var payerAccount = TokenHandlerHelper.getIfUsable(
+ payer, accountStore, context.expiryValidator(), INVALID_PAYER_ACCOUNT_ID);
// validate the transaction body fields that include state or configuration
// We can use payerAccount for validations since it's not mutated in validateSemantics
@@ -180,9 +181,10 @@ private void deleteNftSerials(
final var owner = getEffectiveOwner(allowance.owner(), payerAccount, accountStore, expiryValidator);
final var token = tokenStore.get(tokenId);
for (final var serial : serialNums) {
- final var nft = nftStore.get(tokenId, serial);
+ final var nftId =
+ NftID.newBuilder().serialNumber(serial).tokenId(tokenId).build();
+ final var nft = TokenHandlerHelper.getIfUsable(nftId, nftStore);
- validateTrue(nft != null, INVALID_NFT_ID);
final AccountID accountOwner = owner.accountId();
validateTrue(isValidOwner(nft, accountOwner, token), SENDER_DOES_NOT_OWN_NFT_SERIAL_NO);
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoUpdateHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoUpdateHandler.java
index ea67624613a8..4cd8e94db1f7 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoUpdateHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoUpdateHandler.java
@@ -55,6 +55,7 @@
import com.hedera.node.app.service.token.CryptoSignatureWaivers;
import com.hedera.node.app.service.token.ReadableAccountStore;
import com.hedera.node.app.service.token.impl.WritableAccountStore;
+import com.hedera.node.app.service.token.impl.util.TokenHandlerHelper;
import com.hedera.node.app.service.token.impl.validators.StakingValidator;
import com.hedera.node.app.service.token.records.CryptoUpdateStreamBuilder;
import com.hedera.node.app.spi.fees.FeeCalculator;
@@ -147,8 +148,8 @@ public void handle(@NonNull final HandleContext context) {
// validate update account exists
final var accountStore = context.storeFactory().writableStore(WritableAccountStore.class);
- final var targetAccount = accountStore.get(target);
- validateTrue(targetAccount != null, INVALID_ACCOUNT_ID);
+ final var targetAccount =
+ TokenHandlerHelper.getIfUsable(target, accountStore, context.expiryValidator(), INVALID_ACCOUNT_ID);
context.attributeValidator().validateMemo(op.memo());
// Customize the account based on fields set in transaction body
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenAccountWipeHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenAccountWipeHandler.java
index eca905121322..63c9074cff55 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenAccountWipeHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenAccountWipeHandler.java
@@ -19,7 +19,6 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.ACCOUNT_DOES_NOT_OWN_WIPED_NFT;
import static com.hedera.hapi.node.base.ResponseCodeEnum.ACCOUNT_KYC_NOT_GRANTED_FOR_TOKEN;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ACCOUNT_ID;
-import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_NFT_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_WIPING_AMOUNT;
import static com.hedera.node.app.hapi.fees.usage.SingletonUsageProperties.USAGE_PROPERTIES;
@@ -169,8 +168,11 @@ public void handle(@NonNull final HandleContext context) throws HandleException
// Load and validate the nfts
for (final Long nftSerial : nftSerialNums) {
- final var nft = nftStore.get(tokenId, nftSerial);
- validateTrue(nft != null, INVALID_NFT_ID);
+ final var nftId = NftID.newBuilder()
+ .serialNumber(nftSerial)
+ .tokenId(tokenId)
+ .build();
+ final var nft = TokenHandlerHelper.getIfUsable(nftId, nftStore);
final var nftOwner = nft.ownerId();
validateTrue(Objects.equals(nftOwner, unaliasedId), ACCOUNT_DOES_NOT_OWN_WIPED_NFT);
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenAssociateToAccountHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenAssociateToAccountHandler.java
index 1a51c5521298..288a1577a1b2 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenAssociateToAccountHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenAssociateToAccountHandler.java
@@ -16,7 +16,6 @@
package com.hedera.node.app.service.token.impl.handlers;
-import static com.hedera.hapi.node.base.ResponseCodeEnum.ACCOUNT_DELETED;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ACCOUNT_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.MAX_ENTITIES_IN_PRICE_REGIME_HAVE_BEEN_CREATED;
@@ -29,7 +28,6 @@
import static com.hedera.node.app.service.token.impl.handlers.BaseCryptoHandler.hasAccountNumOrAlias;
import static com.hedera.node.app.service.token.impl.util.TokenHandlerHelper.getIfUsable;
import static com.hedera.node.app.spi.fees.Fees.CONSTANT_FEE_DATA;
-import static com.hedera.node.app.spi.workflows.HandleException.validateFalse;
import static com.hedera.node.app.spi.workflows.HandleException.validateTrue;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateFalsePreCheck;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateTruePreCheck;
@@ -50,9 +48,11 @@
import com.hedera.node.app.service.token.ReadableTokenStore;
import com.hedera.node.app.service.token.impl.WritableAccountStore;
import com.hedera.node.app.service.token.impl.WritableTokenRelationStore;
+import com.hedera.node.app.service.token.impl.util.TokenHandlerHelper;
import com.hedera.node.app.service.token.impl.validators.TokenListChecks;
import com.hedera.node.app.spi.fees.FeeContext;
import com.hedera.node.app.spi.fees.Fees;
+import com.hedera.node.app.spi.validation.ExpiryValidator;
import com.hedera.node.app.spi.workflows.HandleContext;
import com.hedera.node.app.spi.workflows.HandleException;
import com.hedera.node.app.spi.workflows.PreCheckException;
@@ -102,7 +102,14 @@ public void handle(@NonNull final HandleContext context) throws HandleException
final var accountStore = storeFactory.writableStore(WritableAccountStore.class);
final var tokenRelStore = storeFactory.writableStore(WritableTokenRelationStore.class);
final var validated = validateSemantics(
- tokenIds, op.accountOrThrow(), tokensConfig, entitiesConfig, accountStore, tokenStore, tokenRelStore);
+ tokenIds,
+ op.accountOrThrow(),
+ tokensConfig,
+ entitiesConfig,
+ accountStore,
+ tokenStore,
+ tokenRelStore,
+ context.expiryValidator());
// Now that we've validated we can link all the new token IDs to the account,
// create the corresponding token relations and update the account
@@ -133,7 +140,8 @@ private Validated validateSemantics(
@NonNull final EntitiesConfig entitiesConfig,
@NonNull final WritableAccountStore accountStore,
@NonNull final ReadableTokenStore tokenStore,
- @NonNull final WritableTokenRelationStore tokenRelStore) {
+ @NonNull final WritableTokenRelationStore tokenRelStore,
+ @NonNull final ExpiryValidator expiryValidator) {
requireNonNull(tokenConfig);
requireNonNull(entitiesConfig);
@@ -142,10 +150,9 @@ private Validated validateSemantics(
isTotalNumTokenRelsWithinMax(tokenIds.size(), tokenRelStore, tokenConfig.maxAggregateRels()),
MAX_ENTITIES_IN_PRICE_REGIME_HAVE_BEEN_CREATED);
- // Check that the account exists
- final var account = accountStore.get(accountId);
- validateTrue(account != null, INVALID_ACCOUNT_ID);
- validateFalse(account.deleted(), ACCOUNT_DELETED);
+ // Check that the account is usable
+ final var account =
+ TokenHandlerHelper.getIfUsable(accountId, accountStore, expiryValidator, INVALID_ACCOUNT_ID);
// Check that the given tokens exist and are usable
final var tokens = new ArrayList();
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenBurnHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenBurnHandler.java
index 4942d0fce953..2c8ad612ef74 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenBurnHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenBurnHandler.java
@@ -136,7 +136,8 @@ public void handle(@NonNull final HandleContext context) throws HandleException
INVALID_TOKEN_BURN_AMOUNT,
accountStore,
tokenStore,
- tokenRelStore);
+ tokenRelStore,
+ context.expiryValidator());
record.newTotalSupply(newTotalSupply);
} else {
validateTrue(!nftSerialNums.isEmpty(), INVALID_TOKEN_BURN_METADATA);
@@ -152,7 +153,14 @@ public void handle(@NonNull final HandleContext context) throws HandleException
// Update counts for accounts and token rels
final var newTotalSupply = changeSupply(
- token, treasuryRel, -nftSerialNums.size(), FAIL_INVALID, accountStore, tokenStore, tokenRelStore);
+ token,
+ treasuryRel,
+ -nftSerialNums.size(),
+ FAIL_INVALID,
+ accountStore,
+ tokenStore,
+ tokenRelStore,
+ context.expiryValidator());
// Update treasury's NFT count
final var treasuryAcct = accountStore.get(token.treasuryAccountIdOrThrow());
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenCreateHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenCreateHandler.java
index 280cee2a2fc8..7f464a7c8cc1 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenCreateHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenCreateHandler.java
@@ -163,7 +163,14 @@ public void handle(@NonNull final HandleContext context) {
final var treasuryRel = requireNonNull(tokenRelationStore.get(op.treasuryOrThrow(), newTokenId));
if (op.initialSupply() > 0) {
// This keeps modified token with minted balance into modifications in token store
- mintFungible(newToken, treasuryRel, op.initialSupply(), accountStore, tokenStore, tokenRelationStore);
+ mintFungible(
+ newToken,
+ treasuryRel,
+ op.initialSupply(),
+ accountStore,
+ tokenStore,
+ tokenRelationStore,
+ context.expiryValidator());
}
// Increment treasury's title count
final var treasuryAccount = requireNonNull(accountStore.get(treasuryRel.accountIdOrThrow()));
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenFreezeAccountHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenFreezeAccountHandler.java
index dc83931d7d05..cf0f278d802d 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenFreezeAccountHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenFreezeAccountHandler.java
@@ -19,7 +19,6 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ACCOUNT_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_HAS_NO_FREEZE_KEY;
-import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT;
import static com.hedera.node.app.hapi.fees.usage.token.TokenOpsUsageUtils.TOKEN_OPS_USAGE_UTILS;
import static com.hedera.node.app.spi.workflows.HandleException.validateTrue;
import static java.util.Objects.requireNonNull;
@@ -143,12 +142,8 @@ private TokenRelation validateSemantics(
// Check that token exists
TokenHandlerHelper.getIfUsable(tokenId, tokenStore);
- // Check that the token is associated to the account
- final var tokenRel = tokenRelStore.getForModify(accountId, tokenId);
- validateTrue(tokenRel != null, TOKEN_NOT_ASSOCIATED_TO_ACCOUNT);
-
- // Return the token relation
- return tokenRel;
+ // Check that the token is associated to the account, and return it
+ return TokenHandlerHelper.getIfUsable(accountId, tokenId, tokenRelStore);
}
@NonNull
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenMintHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenMintHandler.java
index 43f6f7d83b04..75c467ff1324 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenMintHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenMintHandler.java
@@ -57,6 +57,7 @@
import com.hedera.node.app.service.token.records.TokenMintStreamBuilder;
import com.hedera.node.app.spi.fees.FeeContext;
import com.hedera.node.app.spi.fees.Fees;
+import com.hedera.node.app.spi.validation.ExpiryValidator;
import com.hedera.node.app.spi.workflows.HandleContext;
import com.hedera.node.app.spi.workflows.HandleException;
import com.hedera.node.app.spi.workflows.PreCheckException;
@@ -138,8 +139,14 @@ public void handle(@NonNull final HandleContext context) throws HandleException
if (token.tokenType() == TokenType.FUNGIBLE_COMMON) {
validateTrue(op.amount() >= 0, INVALID_TOKEN_MINT_AMOUNT);
// we need to know if treasury mint while creation to ignore supply key exist or not.
- long newTotalSupply =
- mintFungible(token, treasuryRel, op.amount(), accountStore, tokenStore, tokenRelStore);
+ long newTotalSupply = mintFungible(
+ token,
+ treasuryRel,
+ op.amount(),
+ accountStore,
+ tokenStore,
+ tokenRelStore,
+ context.expiryValidator());
recordBuilder.newTotalSupply(newTotalSupply);
} else {
// get the config needed for validation
@@ -159,7 +166,8 @@ public void handle(@NonNull final HandleContext context) throws HandleException
accountStore,
tokenStore,
tokenRelStore,
- nftStore);
+ nftStore,
+ context.expiryValidator());
recordBuilder.newTotalSupply(tokenStore.get(tokenId).totalSupply());
recordBuilder.serialNumbers(mintedSerials);
}
@@ -182,14 +190,15 @@ private void validateSemantics(final HandleContext context) {
* serial number of the given base unique token, and increments total owned nfts of the
* non-fungible token.
*
- * @param token - the token to mint nfts for
- * @param treasuryRel - the treasury relation of the token
- * @param metadata - the metadata of the nft to be minted
- * @param consensusTime - the consensus time of the transaction
- * @param accountStore - the account store
- * @param tokenStore - the token store
- * @param tokenRelStore - the token relation store
- * @param nftStore - the nft store
+ * @param token - the token to mint nfts for
+ * @param treasuryRel - the treasury relation of the token
+ * @param metadata - the metadata of the nft to be minted
+ * @param consensusTime - the consensus time of the transaction
+ * @param accountStore - the account store
+ * @param tokenStore - the token store
+ * @param tokenRelStore - the token relation store
+ * @param nftStore - the nft store
+ * @param expiryValidator - the expiry validator
*/
private List mintNonFungible(
final Token token,
@@ -199,7 +208,8 @@ private List mintNonFungible(
@NonNull final WritableAccountStore accountStore,
@NonNull final WritableTokenStore tokenStore,
@NonNull final WritableTokenRelationStore tokenRelStore,
- @NonNull final WritableNftStore nftStore) {
+ @NonNull final WritableNftStore nftStore,
+ @NonNull final ExpiryValidator expiryValidator) {
final var metadataCount = metadata.size();
validateFalse(metadata.isEmpty(), INVALID_TOKEN_MINT_METADATA);
@@ -207,15 +217,23 @@ private List mintNonFungible(
final var tokenId = treasuryRel.tokenId();
// get the treasury account
- var treasuryAccount = accountStore.get(treasuryRel.accountIdOrThrow());
- validateTrue(treasuryAccount != null, INVALID_TREASURY_ACCOUNT_FOR_TOKEN);
+ var treasuryAccount = TokenHandlerHelper.getIfUsable(
+ treasuryRel.accountIdOrThrow(), accountStore, expiryValidator, INVALID_TREASURY_ACCOUNT_FOR_TOKEN);
// get the latest serial number minted for the token
var currentSerialNumber = token.lastUsedSerialNumber();
validateTrue((currentSerialNumber + metadataCount) <= MAX_SERIAL_NO_ALLOWED, SERIAL_NUMBER_LIMIT_REACHED);
// Change the supply on token
- changeSupply(token, treasuryRel, metadataCount, FAIL_INVALID, accountStore, tokenStore, tokenRelStore);
+ changeSupply(
+ token,
+ treasuryRel,
+ metadataCount,
+ FAIL_INVALID,
+ accountStore,
+ tokenStore,
+ tokenRelStore,
+ expiryValidator);
// Since changeSupply call above modifies the treasuryAccount, we need to get the modified treasuryAccount
treasuryAccount = accountStore.get(treasuryRel.accountIdOrThrow());
// The token is modified in previous step, so we need to get the modified token
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenUpdateHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenUpdateHandler.java
index 2752d16a73ed..8977c733730a 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenUpdateHandler.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenUpdateHandler.java
@@ -58,6 +58,7 @@
import com.hedera.node.app.service.token.impl.WritableAccountStore;
import com.hedera.node.app.service.token.impl.WritableTokenRelationStore;
import com.hedera.node.app.service.token.impl.WritableTokenStore;
+import com.hedera.node.app.service.token.impl.util.TokenHandlerHelper;
import com.hedera.node.app.service.token.impl.util.TokenKey;
import com.hedera.node.app.service.token.impl.validators.TokenUpdateValidator;
import com.hedera.node.app.service.token.records.TokenUpdateStreamBuilder;
@@ -170,8 +171,8 @@ public void handle(@NonNull final HandleContext context) throws HandleException
// Treasury can be modified when it owns NFTs when the property "tokens.nfts.useTreasuryWildcards"
// is enabled.
if (!tokensConfig.nftsUseTreasuryWildcards() && token.tokenType().equals(NON_FUNGIBLE_UNIQUE)) {
- final var existingTreasuryRel = tokenRelStore.get(existingTreasury, tokenId);
- validateTrue(existingTreasuryRel != null, INVALID_TREASURY_ACCOUNT_FOR_TOKEN);
+ final var existingTreasuryRel =
+ TokenHandlerHelper.getIfUsable(existingTreasury, tokenId, tokenRelStore);
final var tokenRelBalance = existingTreasuryRel.balance();
validateTrue(tokenRelBalance == 0, CURRENT_TREASURY_STILL_OWNS_NFTS);
}
diff --git a/hedera-node/hedera-token-service-impl/src/main/java/module-info.java b/hedera-node/hedera-token-service-impl/src/main/java/module-info.java
index 8b2c1e55c8b1..e5ba7bd85c1e 100644
--- a/hedera-node/hedera-token-service-impl/src/main/java/module-info.java
+++ b/hedera-node/hedera-token-service-impl/src/main/java/module-info.java
@@ -28,7 +28,8 @@
exports com.hedera.node.app.service.token.impl.handlers to
com.hedera.node.app,
- com.hedera.node.app.service.token.impl.test;
+ com.hedera.node.app.service.token.impl.test,
+ com.hedera.node.test.clients;
exports com.hedera.node.app.service.token.impl;
exports com.hedera.node.app.service.token.impl.api to
com.hedera.node.app,
diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java
index 5536c049a51d..fe206a575965 100644
--- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java
+++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java
@@ -71,6 +71,7 @@ public void setUp() {
given(handleContext.configuration()).willReturn(configuration);
given(handleContext.expiryValidator()).willReturn(expiryValidator);
+ given(expiryValidator.expirationStatus(any(), anyBoolean(), anyLong())).willReturn(OK);
}
@Test
diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoUpdateHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoUpdateHandlerTest.java
index fff176cb6b2a..f29e8e781472 100644
--- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoUpdateHandlerTest.java
+++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoUpdateHandlerTest.java
@@ -27,6 +27,7 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_EXPIRATION_TIME;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_STAKING_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.MEMO_TOO_LONG;
+import static com.hedera.hapi.node.base.ResponseCodeEnum.OK;
import static com.hedera.hapi.node.base.ResponseCodeEnum.PROXY_ACCOUNT_ID_FIELD_IS_DEPRECATED;
import static com.hedera.hapi.node.base.ResponseCodeEnum.REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT;
import static com.hedera.hapi.node.base.ResponseCodeEnum.STAKING_NOT_ENABLED;
@@ -146,6 +147,9 @@ public void setUp() {
updateReadableAccountStore(Map.of(updateAccountId.accountNum(), updateAccount, accountNum, account));
lenient().when(handleContext.savepointStack()).thenReturn(stack);
lenient().when(stack.getBaseBuilder(CryptoUpdateStreamBuilder.class)).thenReturn(streamBuilder);
+ lenient()
+ .when(expiryValidator.expirationStatus(any(), anyBoolean(), anyLong()))
+ .thenReturn(OK);
subject = new CryptoUpdateHandler(waivers);
}
diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenAssociateToAccountHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenAssociateToAccountHandlerTest.java
index a13e38f7a1fa..22bcb3d20355 100644
--- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenAssociateToAccountHandlerTest.java
+++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenAssociateToAccountHandlerTest.java
@@ -18,6 +18,7 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ACCOUNT_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
+import static com.hedera.hapi.node.base.ResponseCodeEnum.OK;
import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKENS_PER_ACCOUNT_LIMIT_EXCEEDED;
import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_ID_REPEATED_IN_TOKEN_LIST;
import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_IS_PAUSED;
@@ -33,7 +34,11 @@
import static com.hedera.node.app.service.token.impl.test.keys.KeysAndIds.MISC_ACCOUNT;
import static com.hedera.node.app.spi.fixtures.workflows.ExceptionConditions.responseCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;
+import static org.mockito.Mock.Strictness.LENIENT;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
@@ -53,6 +58,7 @@
import com.hedera.node.app.service.token.impl.test.handlers.util.ParityTestBase;
import com.hedera.node.app.spi.fixtures.workflows.FakePreHandleContext;
import com.hedera.node.app.spi.store.StoreFactory;
+import com.hedera.node.app.spi.validation.ExpiryValidator;
import com.hedera.node.app.spi.workflows.HandleContext;
import com.hedera.node.app.spi.workflows.HandleException;
import com.hedera.node.app.spi.workflows.PreCheckException;
@@ -86,8 +92,14 @@ class TokenAssociateToAccountHandlerTest {
@Mock
private StoreFactory storeFactory;
+ @Mock(strictness = LENIENT)
+ private ExpiryValidator expiryValidator;
+
@BeforeEach
void setUp() {
+ lenient().when(context.expiryValidator()).thenReturn(expiryValidator);
+ given(expiryValidator.expirationStatus(any(), anyBoolean(), anyLong())).willReturn(OK);
+
subject = new TokenAssociateToAccountHandler();
}
diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenBurnHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenBurnHandlerTest.java
index 8ea4083b7937..42e7614403c9 100644
--- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenBurnHandlerTest.java
+++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenBurnHandlerTest.java
@@ -26,6 +26,7 @@
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TRANSACTION_BODY;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TREASURY_ACCOUNT_FOR_TOKEN;
import static com.hedera.hapi.node.base.ResponseCodeEnum.NOT_SUPPORTED;
+import static com.hedera.hapi.node.base.ResponseCodeEnum.OK;
import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_HAS_NO_SUPPLY_KEY;
import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_IS_PAUSED;
import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT;
@@ -42,6 +43,9 @@
import static com.hedera.node.app.spi.workflows.record.StreamBuilder.ReversingBehavior.REVERSIBLE;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
@@ -72,6 +76,7 @@
import com.hedera.node.app.spi.fixtures.state.MapWritableStates;
import com.hedera.node.app.spi.metrics.StoreMetricsService;
import com.hedera.node.app.spi.store.StoreFactory;
+import com.hedera.node.app.spi.validation.ExpiryValidator;
import com.hedera.node.app.spi.workflows.HandleContext;
import com.hedera.node.app.spi.workflows.HandleException;
import com.hedera.node.app.spi.workflows.PreCheckException;
@@ -868,6 +873,11 @@ private HandleContext mockContext(TransactionBody txn) {
given(storeFactory.writableStore(WritableNftStore.class)).willReturn(writableNftStore);
given(context.configuration()).willReturn(configuration);
lenient().when(context.savepointStack()).thenReturn(stack);
+ final var expiryValidator = mock(ExpiryValidator.class);
+ lenient().when(context.expiryValidator()).thenReturn(expiryValidator);
+ lenient()
+ .when(expiryValidator.expirationStatus(any(), anyBoolean(), anyLong()))
+ .thenReturn(OK);
return context;
}
diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenFreezeAccountHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenFreezeAccountHandlerTest.java
index ec9a9964c522..17eaed27a331 100644
--- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenFreezeAccountHandlerTest.java
+++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenFreezeAccountHandlerTest.java
@@ -253,7 +253,7 @@ void tokenRelNotFound() throws HandleException {
given(readableTokenStore.getTokenMeta(token)).willReturn(tokenMetaWithFreezeKey());
given(readableAccountStore.getAccountById(ACCOUNT_13257))
.willReturn(Account.newBuilder().accountId(ACCOUNT_13257).build());
- given(tokenRelStore.getForModify(ACCOUNT_13257, token)).willReturn(null);
+ given(tokenRelStore.get(ACCOUNT_13257, token)).willReturn(null);
given(expiryValidator.expirationStatus(EntityType.ACCOUNT, false, 0))
.willReturn(OK);
final var txn = newFreezeTxn(token);
@@ -273,7 +273,7 @@ void tokenRelFreezeSuccessful() {
given(readableTokenStore.getTokenMeta(token)).willReturn(tokenMetaWithFreezeKey());
given(readableAccountStore.getAccountById(ACCOUNT_13257))
.willReturn(Account.newBuilder().accountId(ACCOUNT_13257).build());
- given(tokenRelStore.getForModify(ACCOUNT_13257, token))
+ given(tokenRelStore.get(ACCOUNT_13257, token))
.willReturn(TokenRelation.newBuilder()
.tokenId(token)
.accountId(ACCOUNT_13257)
diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/transfer/AssociateTokenRecipientsStepTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/transfer/AssociateTokenRecipientsStepTest.java
index 213b502b6bec..a27935c60d8a 100644
--- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/transfer/AssociateTokenRecipientsStepTest.java
+++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/transfer/AssociateTokenRecipientsStepTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.lenient;
import com.hedera.hapi.node.base.AccountAmount;
import com.hedera.hapi.node.base.AccountID;
@@ -115,7 +116,9 @@ void givenValidTxn() {
.build();
given(handleContext.configuration()).willReturn(configuration);
given(handleContext.expiryValidator()).willReturn(expiryValidator);
- given(expiryValidator.expirationStatus(any(), anyBoolean(), anyLong())).willReturn(ResponseCodeEnum.OK);
+ lenient()
+ .when(expiryValidator.expirationStatus(any(), anyBoolean(), anyLong()))
+ .thenReturn(ResponseCodeEnum.OK);
given(handleContext.savepointStack()).willReturn(stack);
}
diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/util/CryptoTokenHandlerTestBase.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/util/CryptoTokenHandlerTestBase.java
index f98ce345e12d..f573577db529 100644
--- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/util/CryptoTokenHandlerTestBase.java
+++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/util/CryptoTokenHandlerTestBase.java
@@ -26,8 +26,12 @@
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.ContractID;
@@ -37,6 +41,7 @@
import com.hedera.hapi.node.base.NftID;
import com.hedera.hapi.node.base.PendingAirdropId;
import com.hedera.hapi.node.base.PendingAirdropValue;
+import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.hapi.node.base.Timestamp;
import com.hedera.hapi.node.base.TokenID;
import com.hedera.hapi.node.base.TokenSupplyType;
@@ -86,6 +91,7 @@
import com.hedera.node.app.spi.fees.Fees;
import com.hedera.node.app.spi.metrics.StoreMetricsService;
import com.hedera.node.app.spi.store.StoreFactory;
+import com.hedera.node.app.spi.validation.ExpiryValidator;
import com.hedera.node.app.spi.workflows.HandleContext;
import com.hedera.node.app.spi.workflows.PreHandleContext;
import com.hedera.node.config.VersionedConfigImpl;
@@ -1173,7 +1179,13 @@ protected void givenStoresAndConfig(final HandleContext context) {
.willReturn(readableRewardsStore);
given(storeFactory.writableStore(WritableNetworkStakingRewardsStore.class))
.willReturn(writableRewardsStore);
- given(context.dispatchComputeFees(any(), any(), any())).willReturn(new Fees(1l, 2l, 3l));
+ given(context.dispatchComputeFees(any(), any(), any())).willReturn(new Fees(1L, 2L, 3L));
+
+ final var expiryValidator = mock(ExpiryValidator.class);
+ lenient().when(context.expiryValidator()).thenReturn(expiryValidator);
+ lenient()
+ .when(expiryValidator.expirationStatus(any(), anyBoolean(), anyLong()))
+ .thenReturn(ResponseCodeEnum.OK);
}
protected void givenStoresAndConfig(final FinalizeContext context) {
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoUpdateSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoUpdateSuite.java
index 04c5f8d0271e..4b038e755376 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoUpdateSuite.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/CryptoUpdateSuite.java
@@ -35,6 +35,7 @@
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractCreate;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractUpdate;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoCreate;
+import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoDelete;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoTransfer;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoUpdate;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenAssociate;
@@ -50,6 +51,7 @@
import static com.hedera.services.bdd.spec.utilops.mod.ModificationUtils.withSuccessivelyVariedBodyIds;
import static com.hedera.services.bdd.spec.utilops.records.SnapshotMatchMode.EXPECT_STREAMLINED_INGEST_RECORDS;
import static com.hedera.services.bdd.spec.utilops.records.SnapshotMatchMode.NONDETERMINISTIC_TRANSACTION_FEES;
+import static com.hedera.services.bdd.suites.HapiSuite.DEFAULT_PAYER;
import static com.hedera.services.bdd.suites.HapiSuite.GENESIS;
import static com.hedera.services.bdd.suites.HapiSuite.ONE_HBAR;
import static com.hedera.services.bdd.suites.HapiSuite.ONE_HUNDRED_HBARS;
@@ -57,6 +59,7 @@
import static com.hedera.services.bdd.suites.HapiSuite.ZERO_BYTE_MEMO;
import static com.hedera.services.bdd.suites.contract.hapi.ContractUpdateSuite.ADMIN_KEY;
import static com.hederahashgraph.api.proto.java.HederaFunctionality.CryptoUpdate;
+import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.ACCOUNT_DELETED;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.EXISTING_AUTOMATIC_ASSOCIATIONS_EXCEED_GIVEN_LIMIT;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ADMIN_KEY;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_EXPIRATION_TIME;
@@ -549,4 +552,16 @@ final Stream updateMaxAutoAssociationsWorks() {
contractUpdate(CONTRACT).newMaxAutomaticAssociations(-1).hasKnownStatus(SUCCESS),
getContractInfo(CONTRACT).has(contractWith().maxAutoAssociations(-1)));
}
+
+ @HapiTest
+ final Stream deletedAccountCannotBeUpdated() {
+ final var accountToDelete = "accountToDelete";
+ return hapiTest(
+ cryptoCreate(accountToDelete).declinedReward(false),
+ cryptoDelete(accountToDelete),
+ cryptoUpdate(accountToDelete)
+ .payingWith(DEFAULT_PAYER)
+ .newDeclinedReward(true)
+ .hasKnownStatus(ACCOUNT_DELETED));
+ }
}
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenAssociationSpecs.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenAssociationSpecs.java
index 5a7201250925..135dcb515a0c 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenAssociationSpecs.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenAssociationSpecs.java
@@ -71,6 +71,7 @@
import static com.hederahashgraph.api.proto.java.TokenFreezeStatus.Unfrozen;
import static com.hederahashgraph.api.proto.java.TokenKycStatus.Granted;
import static com.hederahashgraph.api.proto.java.TokenKycStatus.KycNotApplicable;
+import static com.hederahashgraph.api.proto.java.TokenType.FUNGIBLE_COMMON;
import static com.hederahashgraph.api.proto.java.TokenType.NON_FUNGIBLE_UNIQUE;
import com.google.protobuf.ByteString;
@@ -583,4 +584,22 @@ public static HapiSpecOperation[] basicKeysAndTokens() {
tokenCreate(VANILLA_TOKEN).treasury(TOKEN_TREASURY)
};
}
+
+ @HapiTest
+ final Stream deletedAccountCannotBeAssociatedToToken() {
+ final var accountToDelete = "accountToDelete";
+ final var token = "anyToken";
+ final var supplyKey = "supplyKey";
+ return hapiTest(
+ newKeyNamed(supplyKey),
+ cryptoCreate(accountToDelete),
+ cryptoDelete(accountToDelete),
+ tokenCreate(token)
+ .treasury(DEFAULT_PAYER)
+ .tokenType(FUNGIBLE_COMMON)
+ .initialSupply(1000L)
+ .supplyKey(supplyKey)
+ .hasKnownStatus(SUCCESS),
+ tokenAssociate(accountToDelete, token).hasKnownStatus(ACCOUNT_DELETED));
+ }
}
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenTransactSpecs.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenTransactSpecs.java
index 08e6edd9fd18..d304c11f5383 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenTransactSpecs.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenTransactSpecs.java
@@ -20,6 +20,7 @@
import static com.hedera.services.bdd.junit.TestTags.ADHOC;
import static com.hedera.services.bdd.junit.TestTags.TOKEN;
import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec;
+import static com.hedera.services.bdd.spec.HapiSpec.hapiTest;
import static com.hedera.services.bdd.spec.assertions.AccountInfoAsserts.accountWith;
import static com.hedera.services.bdd.spec.assertions.AccountInfoAsserts.changeFromSnapshot;
import static com.hedera.services.bdd.spec.assertions.AutoAssocAsserts.accountTokenPairs;
@@ -44,6 +45,7 @@
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenCreate;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenDelete;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenDissociate;
+import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenFreeze;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenUpdate;
import static com.hedera.services.bdd.spec.transactions.token.CustomFeeSpecs.fixedHbarFee;
import static com.hedera.services.bdd.spec.transactions.token.CustomFeeSpecs.fixedHbarFeeInheritingRoyaltyCollector;
@@ -2038,6 +2040,25 @@ final Stream collectorIsChargedRoyaltyFallbackFeeUnlessExempt() {
.logged());
}
+ @HapiTest
+ final Stream tokenFrozenOnTreasuryCannotBeFrozenAgain() {
+ final var alice = "alice";
+ final var token = "token";
+ final var freezeKey = "freezeKey";
+ return hapiTest(
+ newKeyNamed(freezeKey),
+ cryptoCreate(alice),
+ tokenCreate(token)
+ .treasury(DEFAULT_PAYER)
+ .tokenType(FUNGIBLE_COMMON)
+ .initialSupply(1000L)
+ .freezeKey(freezeKey)
+ .hasKnownStatus(SUCCESS),
+ tokenAssociate(alice, token),
+ tokenFreeze(token, alice).hasKnownStatus(SUCCESS),
+ tokenFreeze(token, alice).hasKnownStatus(ACCOUNT_FROZEN_FOR_TOKEN));
+ }
+
private static final String TXN_TRIGGERING_COLLECTOR_EXEMPT_FEE = "collectorExempt";
private static final String TXN_TRIGGERING_COLLECTOR_NON_EXEMPT_FEE = "collectorNonExempt";
diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenUpdateSpecs.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenUpdateSpecs.java
index 45335e53072d..0d00678b0d0c 100644
--- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenUpdateSpecs.java
+++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/TokenUpdateSpecs.java
@@ -34,6 +34,7 @@
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenCreate;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenDelete;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenFeeScheduleUpdate;
+import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenFreeze;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenUnfreeze;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenUpdate;
import static com.hedera.services.bdd.spec.transactions.TxnVerbs.wipeTokenAccount;
@@ -47,6 +48,7 @@
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.doSeveralWithStartupConfigNow;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.doWithStartupConfigNow;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.newKeyNamed;
+import static com.hedera.services.bdd.spec.utilops.UtilVerbs.overriding;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.specOps;
import static com.hedera.services.bdd.spec.utilops.UtilVerbs.withOpContext;
import static com.hedera.services.bdd.suites.HapiSuite.ADDRESS_BOOK_CONTROL;
@@ -59,6 +61,7 @@
import static com.hedera.services.bdd.suites.HapiSuite.ZERO_BYTE_MEMO;
import static com.hedera.services.bdd.suites.HapiSuite.salted;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.ACCOUNT_DELETED;
+import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.ACCOUNT_FROZEN_FOR_TOKEN;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ADMIN_KEY;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_AUTORENEW_ACCOUNT;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_CUSTOM_FEE_SCHEDULE_KEY;
@@ -68,6 +71,7 @@
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ZERO_BYTE_IN_STRING;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.NO_REMAINING_AUTOMATIC_ASSOCIATIONS;
+import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_FEE_SCHEDULE_KEY;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_FREEZE_KEY;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_KYC_KEY;
@@ -85,6 +89,7 @@
import com.google.protobuf.ByteString;
import com.hedera.services.bdd.junit.HapiTest;
+import com.hedera.services.bdd.junit.LeakyHapiTest;
import com.hedera.services.bdd.spec.queries.crypto.ExpectedTokenRel;
import com.hedera.services.bdd.spec.transactions.TxnUtils;
import com.hederahashgraph.api.proto.java.TokenFreezeStatus;
@@ -1014,4 +1019,29 @@ final Stream updateUniqueTreasuryWithNfts() {
getAccountInfo("newTreasury").logged(),
getTokenInfo("tbu").hasTreasury("newTreasury"));
}
+
+ @LeakyHapiTest(overrides = {"tokens.nfts.useTreasuryWildcards"})
+ final Stream tokenFrozenOnTreasuryCannotBeUpdated() {
+ final var accountToFreeze = "account";
+ final var adminKey = "adminKey";
+ final var tokenToFreeze = "token";
+ return hapiTest(
+ overriding("tokens.nfts.useTreasuryWildcards", "false"),
+ newKeyNamed(adminKey),
+ cryptoCreate(accountToFreeze),
+ tokenCreate(tokenToFreeze)
+ .treasury(accountToFreeze)
+ .tokenType(NON_FUNGIBLE_UNIQUE)
+ .initialSupply(0)
+ .supplyKey(adminKey)
+ .freezeKey(adminKey)
+ .adminKey(adminKey)
+ .hasKnownStatus(SUCCESS),
+ tokenFreeze(tokenToFreeze, accountToFreeze),
+ tokenAssociate(DEFAULT_PAYER, tokenToFreeze),
+ tokenUpdate(tokenToFreeze)
+ .treasury(DEFAULT_PAYER)
+ .signedBy(DEFAULT_PAYER, adminKey)
+ .hasKnownStatus(ACCOUNT_FROZEN_FOR_TOKEN));
+ }
}