Skip to content
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

CryptoDelete handle implementation #6694

Merged
merged 21 commits into from May 26, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -16,6 +16,8 @@

package com.hedera.node.app.spi.validation;

import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.hapi.node.state.token.Account;
import com.hedera.node.app.spi.workflows.HandleException;
import com.hedera.node.app.spi.workflows.TransactionHandler;

Expand Down Expand Up @@ -45,4 +47,28 @@ public interface ExpiryValidator {
* @throws HandleException if the metadata is invalid
*/
ExpiryMeta resolveUpdateAttempt(ExpiryMeta currentMetadata, ExpiryMeta updateMetadata);

/**
* Gets the expiration status of an account
* @param account the account to check
* @param isAutoRenewEnabled whether auto-renew is enabled for the account
* @param expireContracts whether to expire contracts
* @param expireAccounts whether to expire accounts
* @return OK if the account is not expired, otherwise the appropriate error code
*/
ResponseCodeEnum expirationStatus(
Neeharika-Sompalli marked this conversation as resolved.
Show resolved Hide resolved
Account account, boolean isAutoRenewEnabled, boolean expireContracts, boolean expireAccounts);
Neeharika-Sompalli marked this conversation as resolved.
Show resolved Hide resolved

/**
* Gets the expiration status of an account and returns if the account is detached
* @param account the account to check
* @param isAutoRenewEnabled whether auto-renew is enabled for the account
* @param expireContracts whether to expire contracts
* @param expireAccounts whether to expire accounts
* @return true if the account is detached, otherwise false
*/
default boolean isDetached(
Account account, boolean isAutoRenewEnabled, boolean expireContracts, boolean expireAccounts) {
return expirationStatus(account, isAutoRenewEnabled, expireContracts, expireAccounts) != ResponseCodeEnum.OK;
}
}
Expand Up @@ -94,6 +94,11 @@ protected void finishCryptoCreate(
accountStore.commit();
}

@Override
protected void finishCryptoDelete(@NonNull final WritableAccountStore accountStore) {
accountStore.commit();
}

@Override
protected void finishConsensusUpdateTopic(@NonNull WritableTopicStore topicStore) {
topicStore.commit();
Expand Down
Expand Up @@ -110,6 +110,7 @@
case TOKEN_FEE_SCHEDULE_UPDATE -> dispatchTokenFeeScheduleUpdate(
txn, writableStoreFactory.createTokenStore());
case CRYPTO_CREATE -> dispatchCryptoCreate(txn, writableStoreFactory.createAccountStore());
case CRYPTO_DELETE -> dispatchCryptoDelete(txn, writableStoreFactory.createAccountStore());
case UTIL_PRNG -> dispatchPrng(txn);
default -> throw new IllegalArgumentException(TYPE_NOT_SUPPORTED);
}
Expand Down Expand Up @@ -431,6 +432,18 @@
finishCryptoCreate(recordBuilder, accountStore);
}

/**
* Dispatches the crypto delete transaction to the appropriate handler.
* @param cryptoDelete the crypto delete transaction body
* @param accountStore the writable account store
*/
private void dispatchCryptoDelete(
@NonNull final TransactionBody cryptoDelete, @NonNull final WritableAccountStore accountStore) {
final var handler = handlers.cryptoDeleteHandler();
handler.handle(handleContext, cryptoDelete, accountStore);
finishCryptoDelete(accountStore);
}

/**
* A temporary hook to isolate logic that we expect to move to a workflow, but
* is currently needed when running with facility implementations that are adapters
Expand All @@ -444,6 +457,17 @@
// No-op by default
}

/**
* A temporary hook to isolate logic that we expect to move to a workflow, but
* is currently needed when running with facility implementations that are adapters
* for either {@code mono-service} logic or integration tests.
*
* @param accountStore the account store used for the creation
*/
protected void finishCryptoDelete(@NonNull final WritableAccountStore accountStore) {
// No-op by default
}

Check warning on line 469 in hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionDispatcher.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionDispatcher.java#L469

Added line #L469 was not covered by tests

/**
* Dispatches the token fee schedule update transaction to the appropriate handler.
*
Expand Down
Expand Up @@ -16,11 +16,16 @@

package com.hedera.node.app.workflows.handle.validation;

import static com.hedera.hapi.node.base.ResponseCodeEnum.ACCOUNT_EXPIRED_AND_PENDING_REMOVAL;
import static com.hedera.hapi.node.base.ResponseCodeEnum.CONTRACT_EXPIRED_AND_PENDING_REMOVAL;
import static com.hedera.hapi.node.base.ResponseCodeEnum.EXPIRATION_REDUCTION_NOT_ALLOWED;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_AUTORENEW_ACCOUNT;
import static com.hedera.hapi.node.base.ResponseCodeEnum.OK;
import static com.hedera.node.app.spi.workflows.HandleException.validateFalse;
import static com.hedera.node.app.spi.workflows.HandleException.validateTrue;

import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.hapi.node.state.token.Account;
import com.hedera.node.app.service.mono.config.HederaNumbers;
import com.hedera.node.app.service.mono.store.models.Id;
import com.hedera.node.app.spi.validation.AttributeValidator;
Expand Down Expand Up @@ -115,6 +120,30 @@
return new ExpiryMeta(resolvedExpiry, resolvedAutoRenewPeriod, resolvedAutoRenewNum);
}

/**
* {@inheritDoc}
*/
@Override
public ResponseCodeEnum expirationStatus(
@NonNull final Account account,
final boolean isAutoRenewEnabled,
final boolean expireAccounts,
final boolean expireContracts) {
if (!isAutoRenewEnabled) {
jsync-swirlds marked this conversation as resolved.
Show resolved Hide resolved
return OK;
}
if (account.tinybarBalance() > 0) {
return OK;

Check warning on line 136 in hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/validation/StandardizedExpiryValidator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/validation/StandardizedExpiryValidator.java#L136

Added line #L136 was not covered by tests
}
if (!account.expiredAndPendingRemoval()) {
return OK;
}
if (isExpiryDisabled(account.smartContract(), expireAccounts, expireContracts)) {
return OK;

Check warning on line 142 in hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/validation/StandardizedExpiryValidator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/validation/StandardizedExpiryValidator.java#L142

Added line #L142 was not covered by tests
}
return account.smartContract() ? CONTRACT_EXPIRED_AND_PENDING_REMOVAL : ACCOUNT_EXPIRED_AND_PENDING_REMOVAL;
}

/**
* Helper to check if an entity with the given metadata has a completely specified
* auto-renew configuration. This is true if either the {@link ExpiryMeta} includes
Expand Down Expand Up @@ -148,4 +177,8 @@
final var autoRenewId = new Id(numbers.shard(), numbers.realm(), num);
idValidator.accept(autoRenewId);
}

private boolean isExpiryDisabled(boolean smartContract, boolean expireAccounts, boolean expireContracts) {
return (smartContract && !expireContracts) || (!smartContract && !expireAccounts);
}
}
Expand Up @@ -613,6 +613,14 @@ void dispatchesCryptoCreateAsExpected() {
verify(writableAccountStore).commit();
}

@Test
void dispatchesCryptoDeleteAsExpected() {
given(writableStoreFactory.createAccountStore()).willReturn(writableAccountStore);

dispatcher.dispatchHandle(HederaFunctionality.CRYPTO_DELETE, transactionBody, writableStoreFactory);
verify(writableAccountStore).commit();
}

@Test
void doesntCommitWhenUsageLimitsExceeded() {
final var createBuilder = mock(CreateAccountRecordBuilder.class);
Expand Down
Expand Up @@ -16,16 +16,22 @@

package com.hedera.node.app.workflows.handle.validation;

import static com.hedera.hapi.node.base.ResponseCodeEnum.ACCOUNT_EXPIRED_AND_PENDING_REMOVAL;
import static com.hedera.hapi.node.base.ResponseCodeEnum.CONTRACT_EXPIRED_AND_PENDING_REMOVAL;
import static com.hedera.hapi.node.base.ResponseCodeEnum.OK;
import static com.hedera.node.app.spi.validation.ExpiryMeta.NA;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_AUTORENEW_ACCOUNT;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willThrow;

import com.hedera.hapi.node.base.ResponseCodeEnum;
import com.hedera.hapi.node.state.token.Account;
import com.hedera.node.app.service.evm.exceptions.InvalidTransactionException;
import com.hedera.node.app.service.mono.config.HederaNumbers;
import com.hedera.node.app.service.mono.store.AccountStore;
Expand Down Expand Up @@ -61,6 +67,9 @@ class MonoExpiryValidatorTest {
@Mock
private HederaNumbers numbers;

@Mock
private Account account;

private MonoExpiryValidator subject;

@BeforeEach
Expand Down Expand Up @@ -279,6 +288,40 @@ void canUseWildcardForRemovingAutoRenewAccount() {
assertEquals(update, subject.resolveUpdateAttempt(current, update));
}

@Test
void checksIfAccountIsDetachedIfBalanceZero() {
given(account.tinybarBalance()).willReturn(0L);

assertEquals(OK, subject.expirationStatus(account, true, true, true));
assertFalse(subject.isDetached(account, true, true, true));
}

@Test
void failsIfAccountExpiredAndPendingRemoval() {
given(account.expiredAndPendingRemoval()).willReturn(true);

assertEquals(ACCOUNT_EXPIRED_AND_PENDING_REMOVAL, subject.expirationStatus(account, true, true, true));
assertTrue(subject.isDetached(account, true, true, true));

given(account.smartContract()).willReturn(true);
assertEquals(CONTRACT_EXPIRED_AND_PENDING_REMOVAL, subject.expirationStatus(account, true, true, true));
assertTrue(subject.isDetached(account, true, true, true));
}

@Test
void notDetachedIfAccountNotExpired() {
given(account.expiredAndPendingRemoval()).willReturn(false);

assertEquals(OK, subject.expirationStatus(account, true, true, true));
assertFalse(subject.isDetached(account, true, true, true));
}

@Test
void notDetachedIfAutoRenewDisabled() {
assertEquals(OK, subject.expirationStatus(account, false, false, false));
assertFalse(subject.isDetached(account, false, false, false));
}

private static void assertFailsWith(final ResponseCodeEnum expected, final Runnable runnable) {
final var e = assertThrows(HandleException.class, runnable::run);
assertEquals(expected, e.getStatus());
Expand Down
Expand Up @@ -17,7 +17,22 @@
package com.hedera.node.config.data;

import com.swirlds.config.api.ConfigData;
import com.swirlds.config.api.ConfigProperty;
import java.util.Set;

@ConfigData("autoRenew")
public record AutoRenewConfig( // @ConfigProperty(defaultValue = "") Set<EntityType> targetTypes
) {}
public record AutoRenewConfig(

Check warning on line 24 in hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/AutoRenewConfig.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/AutoRenewConfig.java#L24

Added line #L24 was not covered by tests
// @ConfigProperty(defaultValue = "") Set<EntityType> targetTypes
@ConfigProperty(defaultValue = "CONTRACT") Set<String> targetTypes) {
public boolean expireContracts() {
return targetTypes.contains("CONTRACT");

Check warning on line 28 in hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/AutoRenewConfig.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/AutoRenewConfig.java#L28

Added line #L28 was not covered by tests
}

public boolean expireAccounts() {
return targetTypes.contains("ACCOUNT");

Check warning on line 32 in hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/AutoRenewConfig.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/AutoRenewConfig.java#L32

Added line #L32 was not covered by tests
}

public boolean isAutoRenewEnabled() {
return !targetTypes.isEmpty();
}
}
1 change: 1 addition & 0 deletions hedera-node/hedera-token-service-impl/build.gradle.kts
Expand Up @@ -30,6 +30,7 @@ configurations.all {
dependencies {
implementation(project(":hedera-node:hapi"))
implementation(project(":hedera-node:hedera-config"))
implementation(project(mapOf("path" to ":hedera-node:hedera-config")))
Neeharika-Sompalli marked this conversation as resolved.
Show resolved Hide resolved
testImplementation(project(mapOf("path" to ":hedera-node:hedera-app")))
annotationProcessor(libs.dagger.compiler)
api(project(":hedera-node:hedera-token-service"))
Expand Down
Expand Up @@ -127,6 +127,15 @@ public Optional<Account> getForModify(final AccountID id) {
return Optional.ofNullable(accountState.getForModify(EntityNumVirtualKey.fromLong(accountNum)));
}

/**
* Removes the {@link Account} with the given {@link AccountID} from the state.
* This will add value of the accountId to num in the modifications in state.
* @param accountID - the account id of the account to be removed.
*/
public void remove(@NonNull final AccountID accountID) {
accountState.remove(EntityNumVirtualKey.fromLong(accountID.accountNum()));
}

/**
* Returns the number of accounts in the state. It also includes modifications in the {@link
* WritableKVState}.
Expand Down

This file was deleted.