Skip to content

Commit

Permalink
Add missing logics and validations from services p1 (#8157)
Browse files Browse the repository at this point in the history
**AccessorBasedUsages#estimateCryptoTransfer - token multiplier**
Add `MirrorNodeEvmProperties` in `AccessorBasedUsages`, `ServiceConfiguration`. Add `feesTransferUsageMultiplier` with default value 380 in `MirrorNodeEvmProperties`. Set token multiplier value from `mirrorNodeEvmProperties` in `AccessorBasedUsages`. Modification in `AccessorBasedUsagesTest`. 

**HederaSelfDestructOperationV038 - reasonToHalt missing if statements**
Add `contractIsTokenTreasury`, `contractHasAnyBalance`, `contractOwnsNfts` in` HederaEvmStackedWorldStateUpdater`, because they are needed in `reasonToHalt` statements. Add more tests in `HederaSelfDestructOperationV038 `- `rejectsSelfDestructIfTreasury()`, `rejectsSelfDestructIfContractHasAnyTokenBalance()`, `rejectsSelfDestructIfContractHasAnyNfts()`.

**AbstractAutoCreationLogic#create - missing analyzeTokenTransferCreations part**
Add `tokenAliasMap`, `analyzeTokenTransferCreations` and `reset` in `AbstractAutoCreationLogic`. Add `getTokenAliasMap` in `AutoCreationLogic`. Add needed modifications in `MirrorEvmMessageCallProcessorTest`, `TransferLogicTest`, `SyntheticTxnFactoryTest`, `TransferPrecompileTest`. Add new tests in `AutoCreationLogicTest`.

**SyntethicTxnFactory#createHollowAccount - setMaxAutomaticTokenAssociations**
Add `maxAutoAssociations`in `createHollowAccount`.

---------

Signed-off-by: Zhivko Petkov <zhpetkov9@gmail.com>
Signed-off-by: Zhivko Petkov <119669063+zhpetkov@users.noreply.github.com>
  • Loading branch information
zhpetkov committed May 7, 2024
1 parent e2d14ac commit 13d3ede
Show file tree
Hide file tree
Showing 37 changed files with 880 additions and 321 deletions.
3 changes: 2 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ The following table lists the available properties along with their default valu
value, it is recommended to only populate overridden properties in the custom `application.yml`.

| Name | Default | Description |
| ------------------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|---------------------------------------------------------------|---------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `hedera.mirror.web3.cache.contractState` | expireAfterWrite=1s,maximumSize=10000,recordStats | Cache configuration for contract state |
| `hedera.mirror.web3.cache.entity ` | expireAfterWrite=1s,maximumSize=10000,recordStats | Cache configuration for entity |
| `hedera.mirror.web3.cache.fee` | expireAfterWrite=10m,maximumSize=20,recordStats | Cache configuration for fee related info |
Expand Down Expand Up @@ -697,6 +697,7 @@ value, it is recommended to only populate overridden properties in the custom `a
| `hedera.mirror.web3.evm.minAutoRenewDuration` | 2592000 | Minimum duration for auto-renew account |
| `hedera.mirror.web3.evm.network` | TESTNET | Which Hedera network to use. Can be either `MAINNET`, `PREVIEWNET`, `TESTNET` or `OTHER` |
| `hedera.mirror.web3.evm.rateLimit` | 500 | Maximum RPS limit |
| `hedera.mirror.web3.evm.feesTokenTransferUsageMultiplier` | 380 | Used to calculate token transfer fees |
| `hedera.mirror.web3.evm.trace.enabled` | false | Flag enabling tracer |
| `hedera.mirror.web3.evm.trace.contract` | [] | A set with contract addresses to filter. By default it is empty to indicate it will trace all contract addresses. |
| `hedera.mirror.web3.evm.trace.status` | [] | A set with frame statuses to filter. By default it is empty to indicate it will trace all frames regardless of status. |
Original file line number Diff line number Diff line change
Expand Up @@ -2028,7 +2028,7 @@ enum ContractMethods implements ContractMethodInterface {
TRANSFER_NFT("transferNFTExternal", 53751),
TRANSFER_NFTS("transferNFTsExternal", 57015),
TRANSFER_TOKEN("transferTokenExternal", 39666),
TRANSFER_TOKENS("transferTokensExternal", 42480),
TRANSFER_TOKENS("transferTokensExternal", 43314),
UNFREEZE_TOKEN("unfreezeTokenExternal", 39323),
WIPE_TOKEN_ACCOUNT("wipeTokenAccountExternal", 39496),
WIPE_NFT_ACCOUNT("wipeTokenAccountNFTExternal", 40394),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,9 @@ OpUsageCtxHelper opUsageCtxHelper(final Store store, final HederaEvmContractAlia
AccessorBasedUsages accessorBasedUsages(
final TokenOpsUsage tokenOpsUsage,
final CryptoOpsUsage cryptoOpsUsage,
final OpUsageCtxHelper opUsageCtxHelper) {
return new AccessorBasedUsages(tokenOpsUsage, cryptoOpsUsage, opUsageCtxHelper);
final OpUsageCtxHelper opUsageCtxHelper,
final MirrorNodeEvmProperties mirrorNodeEvmProperties) {
return new AccessorBasedUsages(tokenOpsUsage, cryptoOpsUsage, opUsageCtxHelper, mirrorNodeEvmProperties);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ public class MirrorNodeEvmProperties implements EvmProperties {
@Min(100)
private long rateLimit = 500;

@Getter
@Min(1)
private int feesTokenTransferUsageMultiplier = 380;

public boolean shouldAutoRenewAccounts() {
return autoRenewTargetTypes.contains(EntityType.ACCOUNT);
}
Expand Down Expand Up @@ -300,6 +304,10 @@ SemanticVersion getEvmVersionForBlock(long blockNumber) {
}
}

public int feesTokenTransferUsageMultiplier() {
return feesTokenTransferUsageMultiplier;
}

@Getter
@RequiredArgsConstructor
public enum HederaNetwork {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@
import com.hedera.services.store.models.TokenRelationship;
import com.hedera.services.store.models.UniqueToken;
import com.hederahashgraph.api.proto.java.AccountID;
import com.hederahashgraph.api.proto.java.ResponseCodeEnum;
import com.hederahashgraph.api.proto.java.TokenID;
import org.hyperledger.besu.datatypes.Address;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;
import org.hyperledger.besu.datatypes.Address;

/**
* An interface which serves as a facade over the mirror-node specific in-memory state. This interface is used by components
Expand Down Expand Up @@ -96,6 +99,8 @@ public interface Store {

Optional<Long> getHistoricalTimestamp();

Account loadAccountOrFailWith(final Address evmAddress, @Nullable final ResponseCodeEnum responseCodeEnum);

enum OnMissing {
THROW,
DONT_THROW
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@

package com.hedera.mirror.web3.evm.store;

import static com.hedera.node.app.service.evm.utils.ValidationUtils.validateFalse;
import static com.hedera.node.app.service.evm.utils.ValidationUtils.validateTrue;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.FAIL_INVALID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_WAS_DELETED;

import com.google.protobuf.ByteString;
import com.hedera.mirror.web3.evm.store.CachingStateFrame.CacheAccessIncorrectTypeException;
import com.hedera.mirror.web3.evm.store.UpdatableReferenceCache.UpdatableCacheUsageException;
Expand All @@ -34,23 +28,40 @@
import com.hedera.services.store.models.Token;
import com.hedera.services.store.models.TokenRelationship;
import com.hedera.services.store.models.UniqueToken;
import com.hedera.services.txns.validation.OptionValidator;
import com.hedera.services.utils.EntityIdUtils;
import com.hederahashgraph.api.proto.java.AccountID;
import com.hederahashgraph.api.proto.java.ResponseCodeEnum;
import com.hederahashgraph.api.proto.java.TokenID;
import jakarta.inject.Named;
import org.hyperledger.besu.datatypes.Address;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.hyperledger.besu.datatypes.Address;

import static com.hedera.node.app.service.evm.utils.ValidationUtils.validateFalse;
import static com.hedera.node.app.service.evm.utils.ValidationUtils.validateTrue;
import static com.hedera.services.utils.EntityIdUtils.accountIdFromEvmAddress;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.ACCOUNT_DELETED;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.FAIL_INVALID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ACCOUNT_ID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.OK;
import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_WAS_DELETED;

@Named
public class StoreImpl implements Store {

private final StackedStateFrames stackedStateFrames;

public StoreImpl(final StackedStateFrames stackedStateFrames) {
private final OptionValidator validator;

public StoreImpl(final StackedStateFrames stackedStateFrames, final OptionValidator validator) {
this.stackedStateFrames = stackedStateFrames;
this.validator = validator;
}

@Override
Expand Down Expand Up @@ -288,6 +299,31 @@ public Optional<Long> getHistoricalTimestamp() {
.flatMap(databaseBackedStateFrame -> databaseBackedStateFrame.timestamp);
}

/**
* Returns a {@link Account} model with loaded account from the state and throws the given code if an exception occurs due
* to an invalid account.
*
* @param evmAddress of the account to load
* @param code the {@link ResponseCodeEnum} to fail with if the account is deleted/missing
* @return a usable model of the account if available
*/
@Override
public Account loadAccountOrFailWith(final Address evmAddress, @Nullable ResponseCodeEnum code) {
final var account = getAccount(evmAddress, OnMissing.DONT_THROW);
validateUsable(account, code, INVALID_ACCOUNT_ID, ACCOUNT_DELETED);
return account;
}

private void validateUsable(final Account account,
@Nullable final ResponseCodeEnum explicitResponse,
final ResponseCodeEnum nonExistingCode,
final ResponseCodeEnum deletedCode) {
validateTrue(account != null, explicitResponse != null ? explicitResponse : nonExistingCode);
validateFalse(account.isDeleted(), explicitResponse != null ? explicitResponse : deletedCode);
final var expiryStatus = validator.expiryStatusGiven(this, accountIdFromEvmAddress(account.getAccountAddress()));
validateTrue(expiryStatus == OK, expiryStatus);
}

/**
* Returns a {@link Token} model with loaded unique tokens
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,22 @@ private MutableAccount createGhostAccount(Address address) {
store.updateAccount(ghostAcc);
return createAccount(address, 0, Wei.ZERO);
}

public boolean contractIsTokenTreasury(final Address addressOrAlias) {
final var address = mirrorEvmContractAliases.resolveForEvm(addressOrAlias);
com.hedera.services.store.models.Account account = store.getAccount(address, OnMissing.THROW);
return account.getNumTreasuryTitles() > 0;
}

public boolean contractHasAnyBalance(final Address addressOrAlias) {
final var address = mirrorEvmContractAliases.resolveForEvm(addressOrAlias);
com.hedera.services.store.models.Account account = store.getAccount(address, OnMissing.THROW);
return account.getNumPositiveBalances() > 0;
}

public boolean contractOwnsNfts(final Address addressOrAlias) {
final var address = mirrorEvmContractAliases.resolveForEvm(addressOrAlias);
com.hedera.services.store.models.Account account = store.getAccount(address, OnMissing.THROW);
return account.getOwnedNfts() > 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,15 @@

import com.hedera.mirror.web3.evm.store.contract.HederaEvmStackedWorldStateUpdater;
import com.hedera.node.app.service.evm.contracts.operations.HederaExceptionalHaltReason;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.function.BiPredicate;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.operation.SelfDestructOperation;

import java.util.function.BiPredicate;

/**
* Hedera adapted version of the {@link SelfDestructOperation}.
*
Expand All @@ -42,7 +39,7 @@
* address being destructed
* This class is a copy of HederaSelfDestructOperation from hedera-services mono
*/
public class HederaSelfDestructOperation extends SelfDestructOperation {
public class HederaSelfDestructOperation extends HederaSelfDestructOperationBase {

private final BiPredicate<Address, MessageFrame> addressValidator;

Expand All @@ -63,24 +60,11 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) {
}
final var beneficiary = updater.get(beneficiaryAddress);

final var exceptionalHaltReason = reasonToHalt(toBeDeleted, beneficiaryAddress);
final var exceptionalHaltReason = reasonToHalt(toBeDeleted, beneficiaryAddress, updater);
if (exceptionalHaltReason != null) {
return reversionWith(beneficiary, exceptionalHaltReason);
}

return super.execute(frame, evm);
}

@Nullable
private ExceptionalHaltReason reasonToHalt(final Address toBeDeleted, final Address beneficiaryAddress) {
if (toBeDeleted.equals(beneficiaryAddress)) {
return HederaExceptionalHaltReason.SELF_DESTRUCT_TO_SELF;
}
return null;
}

private OperationResult reversionWith(final Account beneficiary, final ExceptionalHaltReason reason) {
final long cost = gasCalculator().selfDestructOperationGasCost(beneficiary, Wei.ONE);
return new OperationResult(cost, reason);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.hedera.services.evm.contracts.operations;

import com.hedera.mirror.web3.evm.store.contract.HederaEvmStackedWorldStateUpdater;
import com.hedera.node.app.service.evm.contracts.operations.HederaExceptionalHaltReason;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.operation.SelfDestructOperation;

public class HederaSelfDestructOperationBase extends SelfDestructOperation {

public HederaSelfDestructOperationBase(final GasCalculator gasCalculator) {
super(gasCalculator);
}

@Nullable
protected ExceptionalHaltReason reasonToHalt(final Address toBeDeleted,
final Address beneficiaryAddress,
final HederaEvmStackedWorldStateUpdater updater) {
if (toBeDeleted.equals(beneficiaryAddress)) {
return HederaExceptionalHaltReason.SELF_DESTRUCT_TO_SELF;
}

if (updater.contractIsTokenTreasury(toBeDeleted)) {
return HederaExceptionalHaltReason.CONTRACT_IS_TREASURY;
}

if (updater.contractHasAnyBalance(toBeDeleted)) {
return HederaExceptionalHaltReason.TRANSACTION_REQUIRES_ZERO_TOKEN_BALANCES;
}

if (updater.contractOwnsNfts(toBeDeleted)) {
return HederaExceptionalHaltReason.CONTRACT_STILL_OWNS_NFTS;
}
return null;
}

protected OperationResult reversionWith(final Account beneficiary, final ExceptionalHaltReason reason) {
final long cost = gasCalculator().selfDestructOperationGasCost(beneficiary, Wei.ONE);
return new OperationResult(cost, reason);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,16 @@

import com.hedera.mirror.web3.evm.store.contract.HederaEvmStackedWorldStateUpdater;
import com.hedera.node.app.service.evm.contracts.operations.HederaExceptionalHaltReason;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.operation.SelfDestructOperation;

import java.util.function.BiPredicate;
import java.util.function.Predicate;

/**
* Hedera adapted version of the {@link SelfDestructOperation}.
*
Expand All @@ -43,7 +40,7 @@
* address being destructed
* This class is a copy of HederaSelfDestructOperationV038 from hedera-services mono
*/
public class HederaSelfDestructOperationV038 extends SelfDestructOperation {
public class HederaSelfDestructOperationV038 extends HederaSelfDestructOperationBase {

private final BiPredicate<Address, MessageFrame> addressValidator;

Expand All @@ -69,24 +66,11 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) {
}
final var beneficiary = updater.get(beneficiaryAddress);

final var exceptionalHaltReason = reasonToHalt(toBeDeleted, beneficiaryAddress);
final var exceptionalHaltReason = reasonToHalt(toBeDeleted, beneficiaryAddress, updater);
if (exceptionalHaltReason != null) {
return reversionWith(beneficiary, exceptionalHaltReason);
}

return super.execute(frame, evm);
}

@Nullable
private ExceptionalHaltReason reasonToHalt(final Address toBeDeleted, final Address beneficiaryAddress) {
if (toBeDeleted.equals(beneficiaryAddress)) {
return HederaExceptionalHaltReason.SELF_DESTRUCT_TO_SELF;
}
return null;
}

private OperationResult reversionWith(final Account beneficiary, final ExceptionalHaltReason reason) {
final long cost = gasCalculator().selfDestructOperationGasCost(beneficiary, Wei.ONE);
return new OperationResult(cost, reason);
}
}
}

0 comments on commit 13d3ede

Please sign in to comment.