Skip to content

Commit

Permalink
chore: Cherry pick 13520 (#13528)
Browse files Browse the repository at this point in the history
Signed-off-by: lukelee-sl <[email protected]>
  • Loading branch information
lukelee-sl authored May 28, 2024
1 parent b7f1590 commit 3d97a2b
Show file tree
Hide file tree
Showing 21 changed files with 403 additions and 224 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,21 @@

package com.hedera.node.app.workflows.ingest;

import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CALL;
import static com.hedera.hapi.node.base.HederaFunctionality.CRYPTO_ADD_LIVE_HASH;
import static com.hedera.hapi.node.base.HederaFunctionality.CRYPTO_DELETE_LIVE_HASH;
import static com.hedera.hapi.node.base.HederaFunctionality.ETHEREUM_TRANSACTION;
import static com.hedera.hapi.node.base.HederaFunctionality.FREEZE;
import static com.hedera.hapi.node.base.HederaFunctionality.SYSTEM_DELETE;
import static com.hedera.hapi.node.base.HederaFunctionality.SYSTEM_UNDELETE;
import static com.hedera.hapi.node.base.ResponseCodeEnum.BUSY;
import static com.hedera.hapi.node.base.ResponseCodeEnum.DUPLICATE_TRANSACTION;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_GAS;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ETHEREUM_TRANSACTION;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_NODE_ACCOUNT;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_SIGNATURE;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_SOLIDITY_ADDRESS;
import static com.hedera.hapi.node.base.ResponseCodeEnum.NOT_SUPPORTED;
import static com.hedera.hapi.node.base.ResponseCodeEnum.PLATFORM_NOT_ACTIVE;
import static com.hedera.hapi.node.base.ResponseCodeEnum.UNAUTHORIZED;
import static com.hedera.node.app.hapi.utils.ethereum.EthTxData.populateEthTxData;
import static com.hedera.node.app.service.contract.impl.ContractServiceImpl.INTRINSIC_GAS_LOWER_BOUND;
import static com.hedera.node.app.spi.HapiUtils.isHollow;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateFalsePreCheck;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateTruePreCheck;
import static com.swirlds.platform.system.status.PlatformStatus.ACTIVE;
import static java.util.Objects.nonNull;
import static java.util.Objects.requireNonNull;

import com.hedera.hapi.node.base.AccountID;
Expand Down Expand Up @@ -74,9 +65,7 @@
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.swirlds.config.api.Configuration;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.math.BigInteger;
import java.time.Instant;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -178,31 +167,6 @@ public TransactionInfo runAllChecks(
final var txBody = txInfo.txBody();
final var functionality = txInfo.functionality();

// Temporary ingest checks needed for specifically ContractCall and EthereumTransaction
// as long as it is being charged exclusively in gas
// We cannot submit transactions that offer no gas or the work done gossiping
// and reaching consensus on the transaction will be completely uncompensated; the
// minimum threshold here is chosen for mono-service compatibility
if (functionality == CONTRACT_CALL) {
validateTruePreCheck(txBody.contractCallOrThrow().gas() >= INTRINSIC_GAS_LOWER_BOUND, INSUFFICIENT_GAS);

// If the fee offered does not cover the gas cost of the transaction, then
// we would again end up with uncompensated work
// final var gasCost = solvencyPreCheck.estimateAdditionalCosts(txBody, CONTRACT_CALL, consensusTime)
// - txBody.contractCallOrThrow().amount();
// validateTruePreCheck(txBody.transactionFee() >= gasCost, INSUFFICIENT_TX_FEE);
} else if (functionality == ETHEREUM_TRANSACTION) {
final var ethTxData = populateEthTxData(
requireNonNull(txBody.ethereumTransactionOrThrow().ethereumData())
.toByteArray());
validateTruePreCheck(nonNull(ethTxData), INVALID_ETHEREUM_TRANSACTION);
validateTruePreCheck(ethTxData.gasLimit() >= INTRINSIC_GAS_LOWER_BOUND, INSUFFICIENT_GAS);
// Do not allow sending HBars to Burn Address
if (ethTxData.value().compareTo(BigInteger.ZERO) > 0) {
validateFalsePreCheck(Arrays.equals(ethTxData.to(), new byte[20]), INVALID_SOLIDITY_ADDRESS);
}
}

// 1a. Verify the transaction has been sent to *this* node
if (!nodeAccount.equals(txBody.nodeAccountID())) {
throw new PreCheckException(INVALID_NODE_ACCOUNT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
package com.hedera.node.app.service.contract.impl.handlers;

import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CALL;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_GAS;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_CONTRACT_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.MAX_GAS_LIMIT_EXCEEDED;
import static com.hedera.node.app.service.contract.impl.handlers.ContractHandlers.MAX_GAS_LIMIT;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.throwIfUnsuccessful;
import static com.hedera.node.app.service.mono.pbj.PbjConverter.fromPbj;
import static com.hedera.node.app.spi.validation.Validations.mustExist;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateTruePreCheck;
import static java.util.Objects.requireNonNull;

import com.hedera.hapi.node.base.HederaFunctionality;
Expand All @@ -42,17 +46,23 @@
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

/**
* This class contains all workflow-related functionality regarding {@link HederaFunctionality#CONTRACT_CALL}.
*/
@Singleton
public class ContractCallHandler implements TransactionHandler {
private final Provider<TransactionComponent.Factory> provider;
private final GasCalculator gasCalculator;

@Inject
public ContractCallHandler(@NonNull final Provider<TransactionComponent.Factory> provider) {
public ContractCallHandler(
@NonNull final Provider<TransactionComponent.Factory> provider,
@NonNull final GasCalculator gasCalculator) {
this.provider = requireNonNull(provider);
this.gasCalculator = requireNonNull(gasCalculator);
}

@Override
Expand Down Expand Up @@ -80,6 +90,11 @@ public void preHandle(@NonNull final PreHandleContext context) {
public void pureChecks(@NonNull TransactionBody txn) throws PreCheckException {
final var op = txn.contractCallOrThrow();
mustExist(op.contractID(), INVALID_CONTRACT_ID);

validateTruePreCheck(op.gas() <= MAX_GAS_LIMIT, MAX_GAS_LIMIT_EXCEEDED);
final var intrinsicGas = gasCalculator.transactionIntrinsicGasCost(
Bytes.wrap(op.functionParameters().toByteArray()), false);
validateTruePreCheck(op.gas() >= intrinsicGas, INSUFFICIENT_GAS);
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CALL_LOCAL;
import static com.hedera.hapi.node.base.ResponseCodeEnum.CONTRACT_NEGATIVE_GAS;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_GAS;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_CONTRACT_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.MAX_GAS_LIMIT_EXCEEDED;
import static com.hedera.node.app.service.mono.pbj.PbjConverter.fromPbj;
Expand Down Expand Up @@ -52,17 +53,21 @@
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

/**
* This class contains all workflow-related functionality regarding {@link HederaFunctionality#CONTRACT_CALL_LOCAL}.
*/
@Singleton
public class ContractCallLocalHandler extends PaidQueryHandler {
private final Provider<QueryComponent.Factory> provider;
private final GasCalculator gasCalculator;

@Inject
public ContractCallLocalHandler(@NonNull final Provider<Factory> provider) {
public ContractCallLocalHandler(
@NonNull final Provider<Factory> provider, @NonNull final GasCalculator gasCalculator) {
this.provider = provider;
this.gasCalculator = gasCalculator;
}

@Override
Expand All @@ -88,6 +93,10 @@ public void validate(@NonNull final QueryContext context) throws PreCheckExcepti
final var maxGasLimit =
context.configuration().getConfigData(ContractsConfig.class).maxGasPerSec();
validateTruePreCheck(requestedGas <= maxGasLimit, MAX_GAS_LIMIT_EXCEEDED);
final var intrinsicGas = gasCalculator.transactionIntrinsicGasCost(
org.apache.tuweni.bytes.Bytes.wrap(op.functionParameters().toByteArray()), false);
validateTruePreCheck(op.gas() >= intrinsicGas, INSUFFICIENT_GAS);

final var contractID = op.contractID();
mustExist(contractID, INVALID_CONTRACT_ID);
// A contract or token contract corresponding to that contract ID must exist in state (otherwise we have
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
package com.hedera.node.app.service.contract.impl.handlers;

import static com.hedera.hapi.node.base.HederaFunctionality.CONTRACT_CREATE;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_GAS;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_AUTORENEW_ACCOUNT;
import static com.hedera.hapi.node.base.ResponseCodeEnum.MAX_GAS_LIMIT_EXCEEDED;
import static com.hedera.node.app.service.contract.impl.handlers.ContractHandlers.MAX_GAS_LIMIT;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.throwIfUnsuccessful;
import static com.hedera.node.app.service.mono.pbj.PbjConverter.fromPbj;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateTruePreCheck;
import static java.util.Objects.requireNonNull;

import com.hedera.hapi.node.base.AccountID;
Expand All @@ -42,6 +46,8 @@
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

/**
* This class contains all workflow-related functionality regarding {@link HederaFunctionality#CONTRACT_CREATE}.
Expand All @@ -51,10 +57,14 @@ public class ContractCreateHandler implements TransactionHandler {
private static final AccountID REMOVE_AUTO_RENEW_ACCOUNT_SENTINEL =
AccountID.newBuilder().shardNum(0).realmNum(0).accountNum(0).build();
private final Provider<TransactionComponent.Factory> provider;
private final GasCalculator gasCalculator;

@Inject
public ContractCreateHandler(@NonNull final Provider<TransactionComponent.Factory> provider) {
public ContractCreateHandler(
@NonNull final Provider<TransactionComponent.Factory> provider,
@NonNull final GasCalculator gasCalculator) {
this.provider = requireNonNull(provider);
this.gasCalculator = requireNonNull(gasCalculator);
}

@Override
Expand All @@ -73,7 +83,12 @@ public void handle(@NonNull final HandleContext context) throws HandleException

@Override
public void pureChecks(@NonNull final TransactionBody txn) throws PreCheckException {
// nothing to do
final var op = txn.contractCreateInstanceOrThrow();

validateTruePreCheck(op.gas() <= MAX_GAS_LIMIT, MAX_GAS_LIMIT_EXCEEDED);
// FUTURE:Consider reading the init code from a file but this may be too much to do for pure checks
final var intrinsicGas = gasCalculator.transactionIntrinsicGasCost(Bytes.wrap(new byte[0]), true);
validateTruePreCheck(op.gas() >= intrinsicGas, INSUFFICIENT_GAS);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public class ContractHandlers {

private final EthereumTransactionHandler ethereumTransactionHandler;

// Approximate gas limit for contract transactions. Used for pure checks.
public static final long MAX_GAS_LIMIT = 15_000_000L;

@Inject
public ContractHandlers(
@NonNull final ContractCallHandler contractCallHandler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@
package com.hedera.node.app.service.contract.impl.handlers;

import static com.hedera.hapi.node.base.HederaFunctionality.ETHEREUM_TRANSACTION;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_GAS;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ETHEREUM_TRANSACTION;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_SOLIDITY_ADDRESS;
import static com.hedera.hapi.node.base.ResponseCodeEnum.MAX_GAS_LIMIT_EXCEEDED;
import static com.hedera.hapi.node.base.ResponseCodeEnum.OK;
import static com.hedera.node.app.hapi.utils.ethereum.EthTxData.populateEthTxData;
import static com.hedera.node.app.service.contract.impl.handlers.ContractHandlers.MAX_GAS_LIMIT;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.throwIfUnsuccessful;
import static com.hedera.node.app.service.mono.pbj.PbjConverter.fromPbj;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateFalsePreCheck;
import static com.hedera.node.app.spi.workflows.PreCheckException.validateTruePreCheck;
import static java.util.Objects.nonNull;
import static java.util.Objects.requireNonNull;

import com.hedera.hapi.node.base.HederaFunctionality;
Expand Down Expand Up @@ -51,9 +58,12 @@
import com.swirlds.config.api.Configuration;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.math.BigInteger;
import java.util.Arrays;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

/**
* This class contains all workflow-related functionality regarding {@link
Expand All @@ -64,15 +74,18 @@ public class EthereumTransactionHandler implements TransactionHandler {
private final EthTxSigsCache ethereumSignatures;
private final EthereumCallDataHydration callDataHydration;
private final Provider<TransactionComponent.Factory> provider;
private final GasCalculator gasCalculator;

@Inject
public EthereumTransactionHandler(
@NonNull final EthTxSigsCache ethereumSignatures,
@NonNull final EthereumCallDataHydration callDataHydration,
@NonNull final Provider<TransactionComponent.Factory> provider) {
@NonNull final Provider<TransactionComponent.Factory> provider,
@NonNull final GasCalculator gasCalculator) {
this.ethereumSignatures = requireNonNull(ethereumSignatures);
this.callDataHydration = requireNonNull(callDataHydration);
this.provider = requireNonNull(provider);
this.gasCalculator = requireNonNull(gasCalculator);
}

@Override
Expand All @@ -87,7 +100,19 @@ public void preHandle(@NonNull final PreHandleContext context) throws PreCheckEx

@Override
public void pureChecks(@NonNull final TransactionBody txn) throws PreCheckException {
// nothing to do
final var ethTxData = populateEthTxData(
requireNonNull(txn.ethereumTransactionOrThrow().ethereumData()).toByteArray());
validateTruePreCheck(nonNull(ethTxData), INVALID_ETHEREUM_TRANSACTION);
validateTruePreCheck(ethTxData.gasLimit() <= MAX_GAS_LIMIT, MAX_GAS_LIMIT_EXCEEDED);
final byte[] callData = ethTxData.hasCallData() ? ethTxData.callData() : new byte[0];
final var intrinsicGas =
gasCalculator.transactionIntrinsicGasCost(org.apache.tuweni.bytes.Bytes.wrap(callData), false);
validateTruePreCheck(ethTxData.gasLimit() >= intrinsicGas, INSUFFICIENT_GAS);
// FUTURE: This was copied over from IngestChecker. Investigate if it's still needed.
// Do not allow sending HBars to Burn Address
if (ethTxData.value().compareTo(BigInteger.ZERO) > 0) {
validateFalsePreCheck(Arrays.equals(ethTxData.to(), new byte[20]), INVALID_SOLIDITY_ADDRESS);
}
}

/**
Expand Down
Loading

0 comments on commit 3d97a2b

Please sign in to comment.