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

Add missing logics and validations from services #8157

Merged
merged 17 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 14 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
Original file line number Diff line number Diff line change
Expand Up @@ -2024,7 +2024,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 @@ -521,8 +521,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 @@ -33,6 +33,7 @@
import com.hederahashgraph.api.proto.java.ResponseCodeEnum;
import com.hederahashgraph.api.proto.java.Timestamp;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.List;
import java.util.Optional;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
Expand Down Expand Up @@ -72,7 +73,7 @@ protected void executeLazyCreate(final MessageFrame frame, final OperationTracer
.setSeconds(frame.getBlockValues().getTimestamp())
.build();
final var lazyCreateResult =
autoCreationLogic.create(syntheticBalanceChange, timestamp, updater.getStore(), entityAddressSequencer);
autoCreationLogic.create(syntheticBalanceChange, timestamp, updater.getStore(), entityAddressSequencer, List.of(syntheticBalanceChange));
if (lazyCreateResult.getLeft() != ResponseCodeEnum.OK) {
haltFrameAndTraceCreationResult(frame, operationTracer, FAILURE_DURING_LAZY_ACCOUNT_CREATE);
} else {
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;
zhpetkov marked this conversation as resolved.
Show resolved Hide resolved

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);
}
}
}