diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/TransactionProcessor.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/TransactionProcessor.java index bd4185478dbf..22064a678bf7 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/TransactionProcessor.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/TransactionProcessor.java @@ -37,6 +37,7 @@ import edu.umd.cs.findbugs.annotations.Nullable; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.evm.code.CodeFactory; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.processor.ContractCreationProcessor; /** @@ -52,6 +53,7 @@ public class TransactionProcessor { private final ContractCreationProcessor contractCreation; private final FeatureFlags featureFlags; private final CodeFactory codeFactory; + private final GasCalculator gasCalculator; public TransactionProcessor( @NonNull final FrameBuilder frameBuilder, @@ -60,14 +62,16 @@ public TransactionProcessor( @NonNull final CustomMessageCallProcessor messageCall, @NonNull final ContractCreationProcessor contractCreation, @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { this.frameBuilder = requireNonNull(frameBuilder); this.frameRunner = requireNonNull(frameRunner); this.gasCharging = requireNonNull(gasCharging); this.messageCall = requireNonNull(messageCall); this.contractCreation = requireNonNull(contractCreation); this.featureFlags = requireNonNull(featureFlags); - this.codeFactory = codeFactory; + this.codeFactory = requireNonNull(codeFactory); + this.gasCalculator = requireNonNull(gasCalculator); } /** @@ -129,6 +133,7 @@ private HederaEvmTransactionResult processTransactionWithParties( final var gasCharges = transaction.hookOwnerAddress() != null ? GasCharges.NONE : gasCharging.chargeForGas(parties.sender(), parties.relayer(), context, updater, transaction); + final var initialFrame = frameBuilder.buildInitialFrameWith( transaction, updater, @@ -139,7 +144,8 @@ private HederaEvmTransactionResult processTransactionWithParties( parties.sender().getAddress(), parties.receiverAddress(), gasCharges.intrinsicGas(), - codeFactory); + codeFactory, + gasCalculator); // Compute the result of running the frame to completion final var result = frameRunner.runToCompletion( diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/delegation/CodeDelegationProcessor.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/delegation/CodeDelegationProcessor.java index 1999f75a8786..416871a1627c 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/delegation/CodeDelegationProcessor.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/delegation/CodeDelegationProcessor.java @@ -2,6 +2,7 @@ package com.hedera.node.app.service.contract.impl.exec.delegation; import static org.hyperledger.besu.evm.account.Account.MAX_NONCE; +import static org.hyperledger.besu.evm.worldstate.CodeDelegationHelper.CODE_DELEGATION_PREFIX; import com.hedera.node.app.hapi.utils.ethereum.CodeDelegation; import com.hedera.node.app.hapi.utils.ethereum.EthTxSigs; @@ -28,8 +29,6 @@ public record CodeDelegationProcessor(long chainId) { private static final int MAX_Y_PARITY = 2 ^ 8; - public static final Bytes CODE_DELEGATION_PREFIX = Bytes.fromHexString("ef0100"); - /** The size of the delegated code */ public static final int DELEGATED_CODE_SIZE = CODE_DELEGATION_PREFIX.size() + Address.SIZE; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/operations/CustomDelegateCallOperation.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/operations/CustomDelegateCallOperation.java index b352c14410fb..e7ccf9b2f205 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/operations/CustomDelegateCallOperation.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/operations/CustomDelegateCallOperation.java @@ -79,6 +79,8 @@ public OperationResult execute(@NonNull final MessageFrame frame, @NonNull final private boolean isRedirectFromNativeEntity(@NonNull final MessageFrame frame) { final var updater = (ProxyWorldUpdater) frame.getWorldUpdater(); final var recipient = requireNonNull(updater.getHederaAccount(frame.getRecipientAddress())); + // TODO(Pectra): update the condition below. Specifically recipient.isRegularAccount() no longer holds. + // Consider adding a frame variable (e.g. isFacadeExecution) or a different mechanism. return recipient.isTokenFacade() || recipient.isScheduleTxnFacade() || recipient.isRegularAccount(); } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/CustomMessageCallProcessor.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/CustomMessageCallProcessor.java index e0102df43c35..e3b2251c10a5 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/CustomMessageCallProcessor.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/CustomMessageCallProcessor.java @@ -4,6 +4,7 @@ import static com.hedera.hapi.streams.ContractActionType.PRECOMPILE; import static com.hedera.hapi.streams.ContractActionType.SYSTEM; import static com.hedera.node.app.service.contract.impl.exec.failure.CustomExceptionalHaltReason.*; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HasSystemContract.HAS_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_HOOKS_CONTRACT_ADDRESS; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateCommons.createMethodsSet; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.*; @@ -19,12 +20,14 @@ import com.hedera.node.app.service.contract.impl.exec.AddressChecks; import com.hedera.node.app.service.contract.impl.exec.FeatureFlags; import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.HasSystemContract; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.HederaSystemContract; import com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils; import com.hedera.node.app.service.contract.impl.state.ProxyEvmContract; import com.hedera.node.app.service.contract.impl.state.ProxyWorldUpdater; +import com.hedera.node.app.service.contract.impl.state.ScheduleEvmAccount; +import com.hedera.node.app.service.contract.impl.state.TokenEvmAccount; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; import java.util.Arrays; import java.util.Map; import java.util.Objects; @@ -32,6 +35,7 @@ import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Address; 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.operation.Operation; @@ -40,11 +44,12 @@ import org.hyperledger.besu.evm.precompile.PrecompiledContract.PrecompileContractResult; import org.hyperledger.besu.evm.processor.MessageCallProcessor; import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.evm.worldstate.CodeDelegationHelper; /** * A {@link MessageCallProcessor} customized to, *
    - *
  1. Call Hedera-specific precompiles.
  2. + *
  3. Call Hedera-specific System Contracts and precompiles.
  4. *
  5. Impose Hedera restrictions in the system account range.
  6. *
  7. Do lazy creation when appropriate.
  8. *
@@ -52,6 +57,57 @@ * and the core {@link MessageCallProcessor#process(MessageFrame, OperationTracer)} logic we inherit. */ public class CustomMessageCallProcessor extends MessageCallProcessor { + + private record CustomMessageCallContext( + @NonNull MessageFrame frame, + @NonNull OperationTracer tracer, + @NonNull Optional contractAccount, + // The address whose code is to be executed: either frame.getContractAddress(), + // EIP-7702 delegation target or HAS address (for account proxy redirect) + @NonNull Address executableCodeAddress, + boolean isCodeDelegation, + boolean transfersValue) { + + static CustomMessageCallContext create(MessageFrame frame, OperationTracer tracer) { + final Account contractAccount = frame.getWorldUpdater().get(frame.getContractAddress()); + + final Address executableCodeAddress; + final boolean isCodeDelegation; + if (contractAccount != null) { + final var hasCodeDelegation = CodeDelegationHelper.hasCodeDelegation(contractAccount.getCode()); + final var isEoa = contractAccount.getCode().isEmpty() || hasCodeDelegation; + final var isEligibleForHasRedirect = + HasSystemContract.isPayloadEligibleForHasProxyRedirect(frame.getInputData()); + + if (isEoa && isEligibleForHasRedirect) { + // HAS proxy calls have priority, even if code delegation is set + // - this is a built-in redirect functionality, and it isn't considered code delegation. + executableCodeAddress = Address.fromHexString(HAS_EVM_ADDRESS); + isCodeDelegation = false; + } else if (hasCodeDelegation) { + executableCodeAddress = Address.wrap( + contractAccount.getCode().slice(CodeDelegationHelper.CODE_DELEGATION_PREFIX.size())); + isCodeDelegation = true; + } else { + executableCodeAddress = frame.getContractAddress(); + isCodeDelegation = false; + } + } else { + // Call target account doesn't exist + executableCodeAddress = frame.getContractAddress(); + isCodeDelegation = false; + } + + return new CustomMessageCallContext( + frame, + tracer, + Optional.ofNullable(contractAccount), + executableCodeAddress, + isCodeDelegation, + FrameUtils.transfersValue(frame)); + } + } + private final FeatureFlags featureFlags; private final AddressChecks addressChecks; private final PrecompileContractRegistry precompiles; @@ -105,140 +161,59 @@ public CustomMessageCallProcessor( */ @Override public void start(@NonNull final MessageFrame frame, @NonNull final OperationTracer tracer) { - final var codeAddress = frame.getContractAddress(); + final var context = CustomMessageCallContext.create(frame, tracer); + // This must be done first as the system contract address range overlaps with system // accounts. Note that unlike EVM precompiles, we do allow sending value "to" Hedera // system contracts because they sometimes require fees greater than be reasonably // paid using gas; for example, when creating a new token. But the system contract // only diverts this value to the network's fee collection accounts, instead of // actually receiving it. - // We do not allow sending value to Hedera system contracts except in the case of token creation. - if (systemContracts.containsKey(codeAddress)) { - if (!isTokenCreation(frame)) { - doHaltIfInvalidSystemCall(frame, tracer); - if (alreadyHalted(frame)) { - return; - } - } - doExecuteSystemContract(systemContracts.get(codeAddress), codeAddress, frame, tracer); - return; - } - var evmPrecompile = precompiles.get(codeAddress); - if (evmPrecompile != null && !isPrecompileEnabled(codeAddress, frame)) { - // disable precompile if so configured. - evmPrecompile = null; - } - // Check to see if the code address is a system account and possibly halt - // Note that we allow calls to the allowance hook address(0x16d) if the call is part of - // a hook dispatch; in that case, the allowance hook is being treated as a normal - // contract, not as a system account. - if (addressChecks.isSystemAccount(codeAddress) && isNotAllowanceHook(frame, codeAddress)) { - doHaltIfInvalidSystemCall(frame, tracer); - if (alreadyHalted(frame)) { - return; - } - if (evmPrecompile == null) { - handleNonExtantSystemAccount(frame, tracer); - return; - } - } - // Handle evm precompiles - if (evmPrecompile != null) { - doExecutePrecompile(evmPrecompile, frame, tracer); - return; - } - // Transfer value to the contract if required and possibly halt - if (transfersValue(frame)) { - doTransferValueOrHalt(frame, tracer); - if (alreadyHalted(frame)) { - return; - } - } - // For mono-service fidelity, we need to consider called contracts - // as a special case eligible for staking rewards - if (isTopLevelTransaction(frame)) { - final var maybeCalledContract = proxyUpdaterFor(frame).get(codeAddress); - if (maybeCalledContract instanceof ProxyEvmContract a) { - recordBuilderFor(frame).trackExplicitRewardSituation(a.hederaId()); - } - } - - frame.setState(MessageFrame.State.CODE_EXECUTING); - } - /** - * Checks if the message frame is not executing a hook dispatch and if the contract address is not - * the allowance hook address - * - * @param codeAddress the address of the precompile to check - * @param frame the current message frame - * @return true if the frame is not executing a hook dispatch or the code address is not the allowance hook - * address, false otherwise - */ - private static boolean isNotAllowanceHook(final @NonNull MessageFrame frame, final Address codeAddress) { - return !FrameUtils.isHookExecution(frame) || !HTS_HOOKS_CONTRACT_ADDRESS.equals(codeAddress); - } - - /** - * Checks if the given message frame is a token creation scenario. - * - *

This method inspects the first four bytes of the input data of the message frame - * to determine if it matches any of the known selectors for creating fungible or non-fungible tokens. - * - * @param frame the message frame to check - * @return true if the input data matches any of the known create selectors, false otherwise - */ - private boolean isTokenCreation(MessageFrame frame) { - if (frame.getInputData().isEmpty()) { - return false; + if (isSystemContractCall(context)) { + handleSystemContractCall(context); + } else if (isPrecompileCall(context)) { + handlePrecompileCall(context); + } else if (addressChecks.isSystemAccount(context.executableCodeAddress)) { + // Handle System Account that is neither System Contract nor Precompile + handleNonExtantSystemAccountCall(context); + } else { + handleRegularCall(context); } - var selector = frame.getInputData().slice(0, 4).toArray(); - return createMethodsSet.stream().anyMatch(s -> Arrays.equals(s.selector(), selector)); } - /** - * @return whether the implicit creation is currently enabled - */ - public boolean isImplicitCreationEnabled() { - return featureFlags.isImplicitCreationEnabled(); - } - - private void handleNonExtantSystemAccount( - @NonNull final MessageFrame frame, @NonNull final OperationTracer tracer) { - final PrecompileContractResult result = PrecompileContractResult.success(Bytes.EMPTY); - frame.clearGasRemaining(); - finishPrecompileExecution(frame, result, PRECOMPILE, (ActionSidecarContentTracer) tracer); + private boolean isSystemContractCall(@NonNull final CustomMessageCallContext context) { + return systemContracts.containsKey(context.executableCodeAddress); } - private void doExecutePrecompile( - @NonNull final PrecompiledContract precompile, - @NonNull final MessageFrame frame, - @NonNull final OperationTracer tracer) { - final var gasRequirement = precompile.gasRequirement(frame.getInputData()); - final PrecompileContractResult result; - if (frame.getRemainingGas() < gasRequirement) { - result = PrecompileContractResult.halt(Bytes.EMPTY, Optional.of(INSUFFICIENT_GAS)); - } else { - frame.decrementRemainingGas(gasRequirement); + private void handleSystemContractCall(@NonNull final CustomMessageCallContext context) { + if (context.isCodeDelegation) { + final var recipientIsTokenOrScheduleAccount = context.contractAccount.stream() + .anyMatch(a -> a instanceof TokenEvmAccount || a instanceof ScheduleEvmAccount); - final var opsDurationCounter = FrameUtils.opsDurationCounter(frame); - final var opsDurationSchedule = opsDurationCounter.schedule(); - final var opsDurationCost = gasRequirement - * opsDurationSchedule.precompileGasBasedDurationMultiplier() - / opsDurationSchedule.multipliersDenominator(); - opsDurationCounter.recordOpsDurationUnitsConsumed(opsDurationCost); - contractMetrics.opsDurationMetrics().recordPrecompileOpsDuration(precompile.getName(), opsDurationCost); + if (recipientIsTokenOrScheduleAccount) { + // Token and Schedule accounts are allowed to delegate code to an actual System Contract call, + // but value transfer isn't allowed. + if (context.transfersValue) { + doHalt(context, INVALID_CONTRACT_ID); + } else { + doExecuteSystemContract(context, systemContracts.get(context.executableCodeAddress)); + } + } else { + // For any other Hedera accounts: code delegation to System Contracts is a no-op - so just succeed. + context.frame.setState(MessageFrame.State.CODE_SUCCESS); - result = precompile.computePrecompile(frame.getInputData(), frame); - if (result.isRefundGas()) { - frame.incrementRemainingGas(gasRequirement); + // Even though execution is no-op, the call may still carry value transfer. + if (context.transfersValue) { + doTransferValueOrHalt(context); + } } + } else if (context.transfersValue && !isTokenCreation(context.frame)) { + // System Contract calls that transfer value aren't allowed, unless they're token creation. + doHalt(context, INVALID_CONTRACT_ID); + } else { + doExecuteSystemContract(context, systemContracts.get(context.executableCodeAddress)); } - // We must always call tracePrecompileResult() to ensure the tracer is in a consistent - // state, because AbstractMessageProcessor.process() will not invoke the tracer's - // tracePostExecution() method unless start() returns with a state of CODE_EXECUTING; - // but for a precompile call this never happens. - finishPrecompileExecution(frame, result, PRECOMPILE, (ActionSidecarContentTracer) tracer); } /** @@ -246,15 +221,13 @@ private void doExecutePrecompile( * the call to computePrecompile. Thus, the logic for checking for sufficient gas must be done in a different * order vs normal precompiles. * + * @param context the current call context * @param systemContract the system contract to execute - * @param frame the current frame - * @param tracer the operation tracer */ private void doExecuteSystemContract( - @NonNull final HederaSystemContract systemContract, - @NonNull final Address systemContractAddress, - @NonNull final MessageFrame frame, - @NonNull final OperationTracer tracer) { + @NonNull final CustomMessageCallContext context, @NonNull final HederaSystemContract systemContract) { + final var frame = context.frame; + final var systemContractAddress = context.executableCodeAddress; final var fullResult = systemContract.computeFully( ContractID.newBuilder() .contractNum(numberOfLongZero(systemContractAddress)) @@ -283,14 +256,14 @@ private void doExecuteSystemContract( result = fullResult.result(); } - finishPrecompileExecution(frame, result, SYSTEM, (ActionSidecarContentTracer) tracer); + finishPrecompileExecution(context, result, SYSTEM); } private void finishPrecompileExecution( - @NonNull final MessageFrame frame, + @NonNull final CustomMessageCallContext context, @NonNull final PrecompileContractResult result, - @NonNull final ContractActionType type, - @NonNull final ActionSidecarContentTracer tracer) { + @NonNull final ContractActionType type) { + final var frame = context.frame; if (result.state() == MessageFrame.State.REVERT) { frame.setRevertReason(result.output()); } else { @@ -298,16 +271,147 @@ private void finishPrecompileExecution( } frame.setState(result.state()); frame.setExceptionalHaltReason(result.haltReason()); - tracer.tracePrecompileResult(frame, type); + ((ActionSidecarContentTracer) context.tracer).tracePrecompileResult(frame, type); } - private void doTransferValueOrHalt( - @NonNull final MessageFrame frame, @NonNull final OperationTracer operationTracer) { + private boolean isPrecompileCall(@NonNull final CustomMessageCallContext context) { + final var evmPrecompile = precompiles.get(context.executableCodeAddress); + return evmPrecompile != null && isPrecompileEnabled(context.executableCodeAddress, context.frame); + } + + private void handlePrecompileCall(@NonNull final CustomMessageCallContext context) { + final var evmPrecompile = precompiles.get(context.executableCodeAddress); + + if (context.isCodeDelegation) { + // Code delegation to Precompile is a no-op - so just succeed. + context.frame.setState(MessageFrame.State.CODE_SUCCESS); + // Even though execution is no-op, the call may still carry value transfer. + if (context.transfersValue) { + doTransferValueOrHalt(context); + } + } else if (context.transfersValue) { + // Value transfer isn't allowed for precompile calls. + doHalt(context, INVALID_CONTRACT_ID); + } else { + doExecutePrecompile(context, evmPrecompile); + } + } + + private void doExecutePrecompile( + @NonNull final CustomMessageCallContext context, @NonNull final PrecompiledContract precompile) { + final var frame = context.frame; + final var gasRequirement = precompile.gasRequirement(frame.getInputData()); + final PrecompileContractResult result; + if (frame.getRemainingGas() < gasRequirement) { + result = PrecompileContractResult.halt(Bytes.EMPTY, Optional.of(INSUFFICIENT_GAS)); + } else { + frame.decrementRemainingGas(gasRequirement); + + final var opsDurationCounter = FrameUtils.opsDurationCounter(frame); + final var opsDurationSchedule = opsDurationCounter.schedule(); + final var opsDurationCost = gasRequirement + * opsDurationSchedule.precompileGasBasedDurationMultiplier() + / opsDurationSchedule.multipliersDenominator(); + opsDurationCounter.recordOpsDurationUnitsConsumed(opsDurationCost); + contractMetrics.opsDurationMetrics().recordPrecompileOpsDuration(precompile.getName(), opsDurationCost); + + result = precompile.computePrecompile(frame.getInputData(), frame); + if (result.isRefundGas()) { + frame.incrementRemainingGas(gasRequirement); + } + } + // We must always call tracePrecompileResult() to ensure the tracer is in a consistent + // state, because AbstractMessageProcessor.process() will not invoke the tracer's + // tracePostExecution() method unless start() returns with a state of CODE_EXECUTING; + // but for a precompile call this never happens. + finishPrecompileExecution(context, result, PRECOMPILE); + } + + private void handleNonExtantSystemAccountCall(CustomMessageCallContext context) { + if (context.isCodeDelegation) { + // Code delegation is a no-op - so just succeed. + context.frame.setState(MessageFrame.State.CODE_SUCCESS); + // Even though execution is no-op, the call may still carry value transfer. + if (context.transfersValue) { + doTransferValueOrHalt(context); + } + } else if (isAllowanceHook(context.frame, context.executableCodeAddress)) { + // Allowance hook execution is explicitly allowed + context.frame.setState(MessageFrame.State.CODE_EXECUTING); + } else if (context.transfersValue) { + doHalt(context, INVALID_CONTRACT_ID); + } else { + final var result = PrecompileContractResult.success(Bytes.EMPTY); + context.frame.clearGasRemaining(); + finishPrecompileExecution(context, result, PRECOMPILE); + } + } + + private void handleRegularCall(@NonNull final CustomMessageCallContext context) { + if (context.transfersValue) { + doTransferValueOrHalt(context); + if (alreadyHalted(context.frame)) { + return; + } + } + + // For mono-service fidelity, we need to consider called contracts + // as a special case eligible for staking rewards + if (isTopLevelTransaction(context.frame)) { + context.contractAccount + .filter(ProxyEvmContract.class::isInstance) + .map(ProxyEvmContract.class::cast) + .ifPresent(contract -> + recordBuilderFor(context.frame).trackExplicitRewardSituation(contract.hederaId())); + } + + context.frame.setState(MessageFrame.State.CODE_EXECUTING); + } + + /** + * Checks if the message frame is executing a hook dispatch and if the contract address is + * the allowance hook address + * + * @param codeAddress the address of the precompile to check + * @param frame the current message frame + * @return true if the frame is executing a hook dispatch and the code address is the allowance hook + * address, false otherwise + */ + private static boolean isAllowanceHook(final @NonNull MessageFrame frame, final Address codeAddress) { + return FrameUtils.isHookExecution(frame) && HTS_HOOKS_CONTRACT_ADDRESS.equals(codeAddress); + } + + /** + * Checks if the given message frame is a token creation scenario. + * + *

This method inspects the first four bytes of the input data of the message frame + * to determine if it matches any of the known selectors for creating fungible or non-fungible tokens. + * + * @param frame the message frame to check + * @return true if the input data matches any of the known create selectors, false otherwise + */ + private boolean isTokenCreation(MessageFrame frame) { + if (frame.getInputData().isEmpty()) { + return false; + } + final var selector = frame.getInputData().slice(0, 4).toArray(); + return createMethodsSet.stream().anyMatch(s -> Arrays.equals(s.selector(), selector)); + } + + /** + * @return whether the implicit creation is currently enabled + */ + public boolean isImplicitCreationEnabled() { + return featureFlags.isImplicitCreationEnabled(); + } + + private void doTransferValueOrHalt(@NonNull final CustomMessageCallContext context) { + final var frame = context.frame; final var proxyWorldUpdater = (ProxyWorldUpdater) frame.getWorldUpdater(); // Try to lazy-create the recipient address if it doesn't exist if (!addressChecks.isPresent(frame.getRecipientAddress(), frame)) { final var maybeReasonToHalt = proxyWorldUpdater.tryLazyCreation(frame.getRecipientAddress(), frame); - maybeReasonToHalt.ifPresent(reason -> doHaltOnFailedLazyCreation(frame, reason, operationTracer)); + maybeReasonToHalt.ifPresent(reason -> doHaltOnFailedLazyCreation(context, reason)); } if (!alreadyHalted(frame)) { final var maybeReasonToHalt = proxyWorldUpdater.tryTransfer( @@ -319,37 +423,25 @@ private void doTransferValueOrHalt( if (reason == INVALID_SIGNATURE) { setPropagatedCallFailure(frame, MISSING_RECEIVER_SIGNATURE); } - doHalt(frame, reason, operationTracer); + doHalt(context, reason); }); } } - private void doHaltIfInvalidSystemCall( - @NonNull final MessageFrame frame, @NonNull final OperationTracer operationTracer) { - if (transfersValue(frame)) { - doHalt(frame, INVALID_CONTRACT_ID, operationTracer); - } - } - private void doHaltOnFailedLazyCreation( - @NonNull final MessageFrame frame, - @NonNull final ExceptionalHaltReason reason, - @NonNull final OperationTracer tracer) { - doHalt(frame, reason, tracer, ForLazyCreation.YES); + @NonNull final CustomMessageCallContext context, @NonNull final ExceptionalHaltReason reason) { + doHalt(context, reason, ForLazyCreation.YES); } - private void doHalt( - @NonNull final MessageFrame frame, - @NonNull final ExceptionalHaltReason reason, - @NonNull final OperationTracer tracer) { - doHalt(frame, reason, tracer, ForLazyCreation.NO); + private void doHalt(@NonNull final CustomMessageCallContext context, @NonNull final ExceptionalHaltReason reason) { + doHalt(context, reason, ForLazyCreation.NO); } private void doHalt( - @NonNull final MessageFrame frame, + @NonNull final CustomMessageCallContext context, @NonNull final ExceptionalHaltReason reason, - @Nullable final OperationTracer operationTracer, @NonNull final ForLazyCreation forLazyCreation) { + final var frame = context.frame; frame.setState(EXCEPTIONAL_HALT); frame.setExceptionalHaltReason(Optional.of(reason)); if (forLazyCreation == ForLazyCreation.YES) { @@ -358,13 +450,10 @@ private void doHalt( setPropagatedCallFailure(frame, RESULT_CANNOT_BE_EXTERNALIZED); } } - if (operationTracer != null) { - if (forLazyCreation == ForLazyCreation.YES) { - operationTracer.traceAccountCreationResult(frame, Optional.of(reason)); - } else { - operationTracer.tracePostExecution( - frame, new Operation.OperationResult(frame.getRemainingGas(), reason)); - } + if (forLazyCreation == ForLazyCreation.YES) { + context.tracer.traceAccountCreationResult(frame, Optional.of(reason)); + } else { + context.tracer.tracePostExecution(frame, new Operation.OperationResult(frame.getRemainingGas(), reason)); } } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HasSystemContract.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HasSystemContract.java index 63b1522b3d32..8701918de6c8 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HasSystemContract.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HasSystemContract.java @@ -14,6 +14,7 @@ import com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils; import com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.EntityType; import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; import org.apache.tuweni.bytes.Bytes; @@ -30,6 +31,20 @@ public class HasSystemContract extends AbstractNativeSystemContract implements H .contractNum(numberOfLongZero(Address.fromHexString(HAS_EVM_ADDRESS))) .build(); + /** + * A set of call data prefixes (i.e. function selectors in the realm of Solidity) + * that are eligible for proxy redirection to the Hedera Account Service system contract. + */ + private static final Set HAS_PROXY_ELIGIBLE_CALL_DATA_PREFIXES = Set.of( + 0xbbee989e, // hbarAllowance(address spender) + 0x86aff07c, // hbarApprove(address spender, int256 amount) + 0xf5677e99); // setUnlimitedAutomaticAssociations(bool enableAutoAssociations) + + public static boolean isPayloadEligibleForHasProxyRedirect(Bytes payload) { + final int prefix = payload.size() >= FUNCTION_SELECTOR_LENGTH ? payload.getInt(0) : 0; + return HAS_PROXY_ELIGIBLE_CALL_DATA_PREFIXES.contains(prefix); + } + @Inject public HasSystemContract( @NonNull final GasCalculator gasCalculator, diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/common/AbstractCallAttempt.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/common/AbstractCallAttempt.java index e6a9a898f796..84159ca68a18 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/common/AbstractCallAttempt.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/common/AbstractCallAttempt.java @@ -12,7 +12,6 @@ import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategy; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AddressIdConverter; import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethod; -import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethod.SystemContract; import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethodRegistry; import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater; import com.swirlds.config.api.Configuration; @@ -21,6 +20,7 @@ import java.nio.BufferUnderflowException; import java.util.Arrays; import java.util.Optional; +import java.util.Set; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Address; @@ -35,51 +35,58 @@ public abstract class AbstractCallAttempt> { protected final AccountID senderId; protected final Bytes input; protected final byte[] selector; - // If non-null, the address of a non-contract entity (e.g., account or token) whose - // "bytecode" redirects all calls to a system contract address, and was determined - // to be the redirecting entity for this call attempt - protected @Nullable final Address redirectAddress; + protected final Optional

maybeRedirectAddress; + + private boolean matchesFnSelector(Function fn, Bytes input) { + final var selector = fn.selector(); + return Arrays.equals(input.toArrayUnsafe(), 0, selector.length, selector, 0, selector.length); + } /** * @param input the input in bytes * @param options the AbstractCallAttempt parameters and options - * @param redirectFunction the redirect function */ public AbstractCallAttempt( // we are keeping the 'input' out of the 'options' for not duplicate and keep close to related params @NonNull final Bytes input, @NonNull final CallAttemptOptions options, - @NonNull final Function redirectFunction) { + Set
systemContractAddresses, + Function legacyRedirectFunction) { requireNonNull(input); - requireNonNull(redirectFunction); this.options = requireNonNull(options); this.senderId = options.addressIdConverter().convertSender(options.senderAddress()); - if (isRedirectSelector(redirectFunction.selector(), input.toArrayUnsafe())) { + // If the recipient address of this call doesn't match the system contract address + // it means we're running an EIP-7702 delegation (i.e. a facade/redirect call). + final var isDelegationRedirect = !systemContractAddresses.contains(options.recipientAddress()); + if (isDelegationRedirect) { + this.maybeRedirectAddress = Optional.of(options.recipientAddress()); + this.input = input; + } else if (matchesFnSelector(legacyRedirectFunction, input)) { Tuple abiCall = null; try { - // First try to decode the redirect with standard ABI encoding using a 32-byte address - abiCall = redirectFunction.decodeCall(input.toArrayUnsafe()); + abiCall = legacyRedirectFunction.decodeCall(input.toArrayUnsafe()); } catch (IllegalArgumentException | BufferUnderflowException | IndexOutOfBoundsException ignore) { - // Otherwise use the "packed" encoding with a 20-byte address + // no-op } if (abiCall != null) { - this.redirectAddress = Address.fromHexString(abiCall.get(0).toString()); + this.maybeRedirectAddress = + Optional.of(Address.fromHexString(abiCall.get(0).toString())); this.input = Bytes.wrap((byte[]) abiCall.get(1)); } else { - this.redirectAddress = Address.wrap(input.slice(4, 20)); + // TODO(Pectra): consider dropping support for proxy calls that don't confirm to ABI + this.maybeRedirectAddress = Optional.of(Address.wrap(input.slice(4, 20))); this.input = input.slice(24); } } else { - this.redirectAddress = null; + // A regular call; neither EIP-7702 redirect nor legacy redirect function. Process as-is. + this.maybeRedirectAddress = Optional.empty(); this.input = input; } this.selector = this.input.slice(0, 4).toArrayUnsafe(); } - protected abstract SystemContract systemContractKind(); - protected abstract T self(); /** @@ -216,7 +223,7 @@ public boolean isStaticCall() { * @return whether the current call attempt is redirected to a system contract address */ public boolean isRedirect() { - return redirectAddress != null; + return this.maybeRedirectAddress.isPresent(); } /** @@ -262,16 +269,6 @@ public boolean isSelectorIfConfigEnabled( return configEnabled && isSelector(methods); } - /** - * Returns whether this call attempt is a selector for any of the given functions. - * @param functionSelector bytes of the function selector - * @param input input bytes - * @return true if the function selector at the start of the input bytes - */ - private boolean isRedirectSelector(@NonNull final byte[] functionSelector, @NonNull final byte[] input) { - return Arrays.equals(input, 0, functionSelector.length, functionSelector, 0, functionSelector.length); - } - /** * Returns whether only delegate contract keys are active. * diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/common/CallAttemptOptions.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/common/CallAttemptOptions.java index 355076bb7b74..651cc1ee15e6 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/common/CallAttemptOptions.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/common/CallAttemptOptions.java @@ -18,6 +18,7 @@ public record CallAttemptOptions>( @NonNull ContractID contractID, @NonNull Address senderAddress, + @NonNull Address recipientAddress, @NonNull Address authorizingAddress, boolean onlyDelegatableContractKeysActive, @NonNull HederaWorldUpdater.Enhancement enhancement, @@ -32,6 +33,7 @@ public record CallAttemptOptions>( /** * @param contractID the target system contract ID * @param senderAddress the address of the sender of this call + * @param recipientAddress the recipient address of this call * @param authorizingAddress the contract whose keys are to be activated * @param onlyDelegatableContractKeysActive whether the strategy should require a delegatable contract id key * @param enhancement the enhancement to get the native operations to look up the contract's number @@ -46,6 +48,7 @@ public record CallAttemptOptions>( public CallAttemptOptions( @NonNull final ContractID contractID, @NonNull final Address senderAddress, + @NonNull final Address recipientAddress, @NonNull final Address authorizingAddress, final boolean onlyDelegatableContractKeysActive, @NonNull final Enhancement enhancement, @@ -58,6 +61,7 @@ public CallAttemptOptions( final boolean isStaticCall) { this.contractID = requireNonNull(contractID); this.senderAddress = requireNonNull(senderAddress); + this.recipientAddress = requireNonNull(recipientAddress); this.authorizingAddress = requireNonNull(authorizingAddress); this.onlyDelegatableContractKeysActive = onlyDelegatableContractKeysActive; this.enhancement = requireNonNull(enhancement); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/HasCallAttempt.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/HasCallAttempt.java index e2d13846d92c..b25e76b392af 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/HasCallAttempt.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/HasCallAttempt.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.exec.systemcontracts.has; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HasSystemContract.HAS_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.isLongZero; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.numberOfLongZero; import static java.util.Objects.requireNonNull; @@ -13,12 +14,11 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractCallAttempt; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.Call; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.CallAttemptOptions; -import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethod; -import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethod.SystemContract; import com.hedera.node.app.spi.signatures.SignatureVerifier; import com.hedera.node.config.data.HederaConfig; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Set; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Address; @@ -28,8 +28,9 @@ * everything it will need to execute. */ public class HasCallAttempt extends AbstractCallAttempt { - /** Selector for redirectForAccount(address,bytes) method. */ - public static final Function REDIRECT_FOR_ACCOUNT = new Function("redirectForAccount(address,bytes)"); + private static final Set
HAS_ADDRESSES = Set.of(Address.fromHexString(HAS_EVM_ADDRESS)); + + public static final Function LEGACY_REDIRECT_FOR_ACCOUNT = new Function("redirectForAccount(address,bytes)"); @Nullable private final Account redirectAccount; @@ -41,18 +42,12 @@ public HasCallAttempt( @NonNull final Bytes input, @NonNull final CallAttemptOptions options, @NonNull final SignatureVerifier signatureVerifier) { - super(input, options, REDIRECT_FOR_ACCOUNT); - if (isRedirect()) { - this.redirectAccount = linkedAccount(requireNonNull(redirectAddress)); - } else { - this.redirectAccount = null; - } - this.signatureVerifier = requireNonNull(signatureVerifier); - } + super(input, options, HAS_ADDRESSES, LEGACY_REDIRECT_FOR_ACCOUNT); - @Override - protected SystemContract systemContractKind() { - return SystemContractMethod.SystemContract.HAS; + this.redirectAccount = + this.maybeRedirectAddress.map(this::linkedAccount).orElse(null); + + this.signatureVerifier = requireNonNull(signatureVerifier); } @Override diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/HasCallFactory.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/HasCallFactory.java index 7d7131afc422..a97d81d7e20a 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/HasCallFactory.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/has/HasCallFactory.java @@ -69,11 +69,13 @@ public HasCallFactory( requireNonNull(input); requireNonNull(frame); final var enhancement = proxyUpdaterFor(frame).enhancement(); + return new HasCallAttempt( input, new CallAttemptOptions<>( contractID, frame.getSenderAddress(), + frame.getRecipientAddress(), frame.getSenderAddress(), addressChecks.hasParentDelegateCall(frame), enhancement, diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/HssCallAttempt.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/HssCallAttempt.java index 42d32c665216..96359127f164 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/HssCallAttempt.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/HssCallAttempt.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hss; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HssSystemContract.HSS_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.isLongZero; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.maybeMissingNumberOf; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.numberOfLongZero; @@ -15,8 +16,6 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractCallAttempt; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.Call; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.CallAttemptOptions; -import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethod; -import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethod.SystemContract; import com.hedera.node.app.spi.signatures.SignatureVerifier; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -30,8 +29,10 @@ * everything it will need to execute. */ public class HssCallAttempt extends AbstractCallAttempt { - /** Selector for redirectForScheduleTxn(address,bytes) method. */ - public static final Function REDIRECT_FOR_SCHEDULE_TXN = new Function("redirectForScheduleTxn(address,bytes)"); + private static final Set
HSS_ADDRESSES = Set.of(Address.fromHexString(HSS_EVM_ADDRESS)); + + public static final Function LEGACY_REDIRECT_FOR_SCHEDULE_TXN = + new Function("redirectForScheduleTxn(address,bytes)"); @Nullable private final Schedule redirectScheduleTxn; @@ -43,18 +44,12 @@ public HssCallAttempt( @NonNull final Bytes input, @NonNull final CallAttemptOptions options, @NonNull final SignatureVerifier signatureVerifier) { - super(input, options, REDIRECT_FOR_SCHEDULE_TXN); - if (isRedirect()) { - this.redirectScheduleTxn = linkedSchedule(requireNonNull(redirectAddress)); - } else { - this.redirectScheduleTxn = null; - } - this.signatureVerifier = signatureVerifier; - } + super(input, options, HSS_ADDRESSES, LEGACY_REDIRECT_FOR_SCHEDULE_TXN); - @Override - protected SystemContract systemContractKind() { - return SystemContractMethod.SystemContract.HSS; + this.redirectScheduleTxn = + this.maybeRedirectAddress.map(this::linkedSchedule).orElse(null); + + this.signatureVerifier = signatureVerifier; } @Override diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/HssCallFactory.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/HssCallFactory.java index e7c24922fc27..9a837fa9bf39 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/HssCallFactory.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hss/HssCallFactory.java @@ -74,6 +74,7 @@ public HssCallFactory( new CallAttemptOptions<>( contractID, frame.getSenderAddress(), + frame.getRecipientAddress(), frame.getSenderAddress(), addressChecks.hasParentDelegateCall(frame), enhancement, diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCallAttempt.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCallAttempt.java index 144bdf984a19..1d3d62e01c04 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCallAttempt.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCallAttempt.java @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_167_EVM_ADDRESS; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_16C_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.isLongZeroAddress; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.numberOfLongZero; import static java.util.Objects.requireNonNull; @@ -14,10 +16,9 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractCallAttempt; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.Call; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.CallAttemptOptions; -import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethod; -import com.hedera.node.app.service.contract.impl.exec.utils.SystemContractMethod.SystemContract; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Set; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Address; @@ -26,8 +27,10 @@ * attempt into an appropriate {@link Call} subclass, giving the {@link Call} everything it will need to execute. */ public class HtsCallAttempt extends AbstractCallAttempt { - /** Selector for redirectForToken(address,bytes) method. */ - public static final Function REDIRECT_FOR_TOKEN = new Function("redirectForToken(address,bytes)"); + private static final Set
HTS_ADDRESSES = + Set.of(Address.fromHexString(HTS_167_EVM_ADDRESS), Address.fromHexString(HTS_16C_EVM_ADDRESS)); + + public static final Function LEGACY_REDIRECT_FOR_TOKEN = new Function("redirectForToken(address,bytes)"); // The id address of the account authorizing the call, in the sense // that (1) a dispatch should omit the key of this account from the @@ -42,22 +45,15 @@ public class HtsCallAttempt extends AbstractCallAttempt { private final Token redirectToken; public HtsCallAttempt(@NonNull final Bytes input, @NonNull final CallAttemptOptions options) { - super(input, options, REDIRECT_FOR_TOKEN); - if (isRedirect()) { - this.redirectToken = linkedToken(redirectAddress); - } else { - redirectToken = null; - } + super(input, options, HTS_ADDRESSES, LEGACY_REDIRECT_FOR_TOKEN); + + this.redirectToken = this.maybeRedirectAddress.map(this::linkedToken).orElse(null); + this.authorizingId = (options.authorizingAddress() != senderAddress()) ? addressIdConverter().convertSender(options.authorizingAddress()) : senderId; } - @Override - protected SystemContract systemContractKind() { - return SystemContractMethod.SystemContract.HTS; - } - @Override protected HtsCallAttempt self() { return this; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCallFactory.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCallFactory.java index 3cf9473dece5..cae334808050 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCallFactory.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCallFactory.java @@ -71,6 +71,7 @@ public HtsCallFactory( new CallAttemptOptions<>( contractID, frame.getSenderAddress(), + frame.getRecipientAddress(), // We only need to distinguish between the EVM sender id and the // "authorizing id" for qualified delegate calls; and even then, only // for classic transfers. In that specific case, the qualified delegate diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/transfer/ClassicTransfersTranslator.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/transfer/ClassicTransfersTranslator.java index 6e1537d5a248..46442651a23a 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/transfer/ClassicTransfersTranslator.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/transfer/ClassicTransfersTranslator.java @@ -110,16 +110,19 @@ public ClassicTransfersTranslator( @Override public @NonNull Optional identifyMethod(@NonNull final HtsCallAttempt attempt) { - if (attempt.isRedirect()) return Optional.empty(); - return attempt.isMethod( - CRYPTO_TRANSFER, - CRYPTO_TRANSFER_V2, - TRANSFER_TOKENS, - TRANSFER_TOKEN, - TRANSFER_NFTS, - TRANSFER_NFT, - TRANSFER_FROM, - TRANSFER_NFT_FROM); + if (attempt.isRedirect()) { + return Optional.empty(); + } else { + return attempt.isMethod( + CRYPTO_TRANSFER, + CRYPTO_TRANSFER_V2, + TRANSFER_TOKENS, + TRANSFER_TOKEN, + TRANSFER_NFTS, + TRANSFER_NFT, + TRANSFER_FROM, + TRANSFER_NFT_FROM); + } } @Override diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/FrameBuilder.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/FrameBuilder.java index d348c622659a..6ff28ac22455 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/FrameBuilder.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/FrameBuilder.java @@ -41,9 +41,12 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.code.CodeFactory; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.worldstate.CodeDelegationHelper; /** * Infrastructure component that builds the initial {@link MessageFrame} instance for a transaction. @@ -92,7 +95,8 @@ public MessageFrame buildInitialFrameWith( @NonNull final Address from, @NonNull final Address to, final long intrinsicGas, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { final var value = transaction.weiValue(); final var ledgerConfig = config.getConfigData(LedgerConfig.class); final var nominalCoinbase = asLongZeroAddress(ledgerConfig.fundingAccount()); @@ -124,7 +128,8 @@ public MessageFrame buildInitialFrameWith( transaction, featureFlags, config.getConfigData(ContractsConfig.class), - codeFactory); + codeFactory, + gasCalculator); } } @@ -185,8 +190,8 @@ private MessageFrame finishedAsCall( @NonNull final HederaEvmTransaction transaction, @NonNull final FeatureFlags featureFlags, @NonNull final ContractsConfig config, - @NonNull final CodeFactory codeFactory) { - Code code = CodeV0.EMPTY_CODE; + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { final var contractId = transaction.contractIdOrThrow(); final var contractMustBePresent = contractMustBePresent(config, featureFlags, contractId); @@ -199,26 +204,51 @@ private MessageFrame finishedAsCall( .address(to) .contract(to) .inputData(transaction.evmPayload()) - .code(code) + .code(CodeV0.EMPTY_CODE) .build(); } final var account = worldUpdater.getHederaAccount(contractId); + final Code code; if (account != null) { // Hedera account for contract is present, get the byte code - code = account.getEvmCode(Bytes.wrap(transaction.payload().toByteArray()), codeFactory); + final var accountCode = account.getCode(); - // If after getting the code, it is empty, then check if this is allowed - if (code.equals(CodeV0.EMPTY_CODE)) { - validateTrue(emptyCodePossiblyAllowed(contractMustBePresent, transaction), INVALID_CONTRACT_ID); + // TODO(Pectra): skip delegation processing for hooks! (i.e. if account is a proxy hook) + if (CodeDelegationHelper.hasCodeDelegation(accountCode)) { + // Resolve the target account of the delegation and use its code + final var targetAddress = + Address.wrap(accountCode.slice(CodeDelegationHelper.CODE_DELEGATION_PREFIX.size())); + + final Account targetAccount = worldUpdater.getHederaAccount(targetAddress); + if (targetAccount == null || gasCalculator.isPrecompile(targetAddress)) { + code = CodeV0.EMPTY_CODE; + } else { + code = codeFactory.createCode(targetAccount.getCode()); + } + } else { + // No delegation, so try to resolve the code of the smart contract. + if (!accountCode.isEmpty()) { + // A non-delegation code is there (it must be a regular smart contract), so just use it. + code = codeFactory.createCode(accountCode); + } else { + // The code is empty. + // First validate if this is allowed, and if so, proceed. + validateTrue(emptyCodePossiblyAllowed(contractMustBePresent, transaction), INVALID_CONTRACT_ID); + code = CodeV0.EMPTY_CODE; + } } } else { + // The target account doesn't exist. Verify that it's allowed, and if so, proceed with empty code. + // Only do this check if the contract must be present if (contractMustBePresent) { validateTrue(transaction.permitsMissingContract(), INVALID_ETHEREUM_TRANSACTION); } + code = CodeV0.EMPTY_CODE; } + // TODO(Pectra): will we support access lists? If so, add EIP-7702 accessListWarmUpAddresses (see besu impl) return builder.type(MessageFrame.Type.MESSAGE_CALL) .address(to) .contract(to) @@ -252,6 +282,8 @@ private boolean contractMustBePresent( private boolean emptyCodePossiblyAllowed( final boolean contractMustBePresent, @NonNull final HederaEvmTransaction transaction) { + // TODO(Pectra): re-evaluate if this condition is still correct + // Empty code is allowed if the transaction is an Ethereum transaction or has a value or the contract does not // have to be present via config return transaction.isEthereumTransaction() || transaction.hasValue() || !contractMustBePresent; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/FrameUtils.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/FrameUtils.java index af62dbb8b0b2..361c5bd28eaf 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/FrameUtils.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/FrameUtils.java @@ -257,6 +257,7 @@ public enum CallType { } public static CallType callTypeOf(final MessageFrame frame, final EntityType expectedEntityType) { + // TODO(Pectra): double check the logic in this method if (!isDelegateCall(frame)) { return CallType.DIRECT_OR_PROXY_REDIRECT; } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v030/V030Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v030/V030Module.java index a94ca98e6ee2..23e5b5b1f9b2 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v030/V030Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v030/V030Module.java @@ -77,7 +77,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV030 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV030 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -85,7 +86,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v034/V034Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v034/V034Module.java index c8a17b8aa297..b58c1cd87283 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v034/V034Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v034/V034Module.java @@ -79,7 +79,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV034 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV034 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -87,7 +88,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v038/V038Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v038/V038Module.java index 5ad90b3a3d7d..9d1f796daa46 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v038/V038Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v038/V038Module.java @@ -79,7 +79,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV038 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV038 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -87,7 +88,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v046/V046Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v046/V046Module.java index 40d0cb811384..71c92c1b6f97 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v046/V046Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v046/V046Module.java @@ -79,7 +79,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV046 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV046 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -87,7 +88,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v050/V050Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v050/V050Module.java index 01597fb7c396..d4becaca8082 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v050/V050Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v050/V050Module.java @@ -89,7 +89,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV050 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV050 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -97,7 +98,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v051/V051Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v051/V051Module.java index 7eaa2cf117a6..9516c5d4f149 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v051/V051Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v051/V051Module.java @@ -89,7 +89,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV051 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV051 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -97,7 +98,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v065/V065Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v065/V065Module.java index 8303b14cb1df..e4f08bd012ee 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v065/V065Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v065/V065Module.java @@ -88,7 +88,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV065 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV065 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -96,7 +97,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v066/V066Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v066/V066Module.java index 0c0ef720f7d9..049779c5973b 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v066/V066Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v066/V066Module.java @@ -88,7 +88,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV066 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV066 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final GasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -96,7 +97,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v067/V067Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v067/V067Module.java index a5e9b79e7130..5d3efe79c1a0 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v067/V067Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v067/V067Module.java @@ -13,6 +13,7 @@ import com.hedera.node.app.service.contract.impl.exec.FrameRunner; import com.hedera.node.app.service.contract.impl.exec.TransactionProcessor; import com.hedera.node.app.service.contract.impl.exec.gas.CustomGasCharging; +import com.hedera.node.app.service.contract.impl.exec.gas.HederaGasCalculator; import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics; import com.hedera.node.app.service.contract.impl.exec.operations.*; import com.hedera.node.app.service.contract.impl.exec.operations.CustomSelfDestructOperation.UseEIP6780Semantics; @@ -73,7 +74,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV067 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV067 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final HederaGasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -81,7 +83,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v070/V070Module.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v070/V070Module.java index 5cb29842405f..b4352b161b63 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v070/V070Module.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/v070/V070Module.java @@ -13,6 +13,7 @@ import com.hedera.node.app.service.contract.impl.exec.FrameRunner; import com.hedera.node.app.service.contract.impl.exec.TransactionProcessor; import com.hedera.node.app.service.contract.impl.exec.gas.CustomGasCharging; +import com.hedera.node.app.service.contract.impl.exec.gas.HederaGasCalculator; import com.hedera.node.app.service.contract.impl.exec.metrics.ContractMetrics; import com.hedera.node.app.service.contract.impl.exec.operations.CustomBalanceOperation; import com.hedera.node.app.service.contract.impl.exec.operations.CustomCallCodeOperation; @@ -88,7 +89,8 @@ static TransactionProcessor provideTransactionProcessor( @ServicesV070 @NonNull final ContractCreationProcessor contractCreationProcessor, @NonNull final CustomGasCharging gasCharging, @ServicesV070 @NonNull final FeatureFlags featureFlags, - @NonNull final CodeFactory codeFactory) { + @NonNull final CodeFactory codeFactory, + @NonNull final HederaGasCalculator gasCalculator) { return new TransactionProcessor( frameBuilder, frameRunner, @@ -96,7 +98,8 @@ static TransactionProcessor provideTransactionProcessor( messageCallProcessor, contractCreationProcessor, featureFlags, - codeFactory); + codeFactory, + gasCalculator); } @Provides diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractGetBytecodeHandler.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractGetBytecodeHandler.java index 0bf8e5df28b8..9222aa06580f 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractGetBytecodeHandler.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractGetBytecodeHandler.java @@ -6,6 +6,7 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.OK; import static com.hedera.hapi.node.base.ResponseType.ANSWER_ONLY; import static com.hedera.node.app.hapi.utils.CommonPbjConverters.fromPbjResponseType; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.tuweniToPbjBytes; import static java.util.Objects.requireNonNull; import com.hedera.hapi.node.base.ContractID; @@ -21,8 +22,8 @@ import com.hedera.hapi.node.transaction.Response; import com.hedera.node.app.hapi.utils.fee.SmartContractFeeBuilder; import com.hedera.node.app.service.contract.impl.state.ContractStateStore; -import com.hedera.node.app.service.contract.impl.utils.ConversionUtils; -import com.hedera.node.app.service.contract.impl.utils.RedirectBytecodeUtils; +import com.hedera.node.app.service.contract.impl.state.ScheduleEvmAccount; +import com.hedera.node.app.service.contract.impl.state.TokenEvmAccount; import com.hedera.node.app.service.entityid.EntityIdFactory; import com.hedera.node.app.spi.fees.Fees; import com.hedera.node.app.spi.workflows.PreCheckException; @@ -130,38 +131,43 @@ public Fees computeFees(@NonNull final QueryContext context) { * @return Bytecode */ private Bytes bytecodeFrom(@NonNull final QueryContext context) { - final ContractID contractId; - final Account account; - final Token token; - final Schedule schedule; - if ((contractId = getContractId(context)) == null) { + final ContractID contractId = getContractId(context); + if (contractId == null) { return null; - } else if ((account = accountFrom(context, contractId)) != null) { + } + + // Try resolving as an account + final Account account = accountFrom(context, contractId); + if (account != null) { if (account.deleted()) { return null; - } else if (account.smartContract()) { - return bytecodeFrom(context, account); } else { - return RedirectBytecodeUtils.accountProxyBytecodePjb( - ConversionUtils.contractIDToBesuAddress(entityIdFactory, contractId)); + return bytecodeFrom(context, account); } - } else if ((token = tokenFrom(context, contractId)) != null) { + } + + // Try resolving as a token + final Token token = tokenFrom(context, contractId); + if (token != null) { if (token.deleted()) { return null; } else { - return RedirectBytecodeUtils.tokenProxyBytecodePjb( - ConversionUtils.contractIDToBesuAddress(entityIdFactory, contractId)); + return tuweniToPbjBytes(TokenEvmAccount.CODE); } - } else if ((schedule = scheduleFrom(context, contractId)) != null) { + } + + // Try resolving as a schedule + final Schedule schedule = scheduleFrom(context, contractId); + if (schedule != null) { if (schedule.deleted()) { return null; } else { - return RedirectBytecodeUtils.scheduleProxyBytecodePjb( - ConversionUtils.contractIDToBesuAddress(entityIdFactory, contractId)); + return tuweniToPbjBytes(ScheduleEvmAccount.CODE); } - } else { - return null; } + + // Fallback to null + return null; } /** diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/DispatchingEvmFrameState.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/DispatchingEvmFrameState.java index 28d8525264f3..dd1f8c184c72 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/DispatchingEvmFrameState.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/DispatchingEvmFrameState.java @@ -37,7 +37,6 @@ import com.hedera.node.app.service.contract.impl.exec.scope.ActiveContractVerificationStrategy.UseTopLevelSigs; import com.hedera.node.app.service.contract.impl.exec.scope.HandleHederaNativeOperations; import com.hedera.node.app.service.contract.impl.exec.scope.HederaNativeOperations; -import com.hedera.node.app.service.contract.impl.utils.RedirectBytecodeUtils; import com.hedera.node.app.service.entityid.EntityIdFactory; import com.swirlds.state.spi.WritableKVState; import edu.umd.cs.findbugs.annotations.NonNull; @@ -102,6 +101,8 @@ public DispatchingEvmFrameState( @Override public void setStorageValue( @NonNull final ContractID contractID, @NonNull final UInt256 key, @NonNull final UInt256 value) { + requireNonNull(contractID); + final var slotKey = new SlotKey(contractID, tuweniToPbjBytes(requireNonNull(key))); final var oldSlotValue = contractStateStore.getSlotValue(slotKey); if (oldSlotValue == null && value.isZero()) { @@ -124,6 +125,7 @@ public void setStorageValue( */ @Override public @NonNull UInt256 getStorageValue(final ContractID contractID, @NonNull final UInt256 key) { + requireNonNull(contractID); final var slotKey = new SlotKey(contractID, tuweniToPbjBytes(requireNonNull(key))); return valueOrZero(contractStateStore.getSlotValue(slotKey)); } @@ -133,6 +135,7 @@ public void setStorageValue( */ @Override public @NonNull UInt256 getOriginalStorageValue(final ContractID contractID, @NonNull final UInt256 key) { + requireNonNull(contractID); final var slotKey = new SlotKey(contractID, tuweniToPbjBytes(requireNonNull(key))); return valueOrZero(contractStateStore.getOriginalSlotValue(slotKey)); } @@ -199,6 +202,7 @@ public long getKvStateSize() { @Override public @NonNull Bytes getCode(@NonNull final ContractID contractID) { requireNonNull(contractID); + final var numberedBytecode = contractStateStore.getBytecode(contractID); if (numberedBytecode == null) { return Bytes.EMPTY; @@ -225,60 +229,6 @@ public long getKvStateSize() { } } - /** - * {@inheritDoc} - */ - @Override - public @NonNull Bytes getTokenRedirectCode(@NonNull final Address address) { - return RedirectBytecodeUtils.tokenProxyBytecodeFor(address); - } - - /** - * {@inheritDoc} - */ - @Override - public @NonNull Hash getTokenRedirectCodeHash(@NonNull final Address address) { - return codeFactory - .createCode(RedirectBytecodeUtils.tokenProxyBytecodeFor(address), false) - .getCodeHash(); - } - - /** - * {@inheritDoc} - */ - @Override - public @NonNull Bytes getAccountRedirectCode(@Nullable final Address address) { - return RedirectBytecodeUtils.accountProxyBytecodeFor(address); - } - - /** - * {@inheritDoc} - */ - @Override - public @NonNull Hash getAccountRedirectCodeHash(@Nullable final Address address) { - return codeFactory - .createCode(RedirectBytecodeUtils.accountProxyBytecodeFor(address), false) - .getCodeHash(); - } - - /** - * {@inheritDoc} - */ - @Override - public @NonNull Bytes getScheduleRedirectCode(@Nullable final Address address) { - return RedirectBytecodeUtils.scheduleProxyBytecodeFor(address); - } - - /** - * {@inheritDoc} - */ - @Override - public @NonNull Hash getScheduleRedirectCodeHash(@Nullable final Address address) { - return codeFactory - .createCode(RedirectBytecodeUtils.scheduleProxyBytecodeFor(address), false) - .getCodeHash(); - } - /** * {@inheritDoc} */ @@ -321,6 +271,7 @@ public int getNumPositiveTokenBalances(final AccountID accountID) { */ @Override public void setCode(final ContractID contractID, @NonNull final Bytes code) { + requireNonNull(contractID); contractStateStore.putBytecode(contractID, new Bytecode(tuweniToPbjBytes(requireNonNull(code)))); } @@ -568,7 +519,7 @@ public void trackSelfDestructBeneficiary( if (account.smartContract()) { return new ProxyEvmContract(account.accountId(), this, codeFactory); } else { - return new ProxyEvmAccount(account.accountId(), this); + return new ProxyEvmAccount(account.accountId(), this, codeFactory); } } final var token = nativeOperations.getToken(entityIdFactory().newTokenId(number)); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/EvmFrameState.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/EvmFrameState.java index b8c243e6bbf2..01baf2953e38 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/EvmFrameState.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/EvmFrameState.java @@ -143,7 +143,7 @@ void trackSelfDestructBeneficiary( MutableAccount getMutableAccount(Address address); /** - * Returns the storage value for the contract with the given contract id and key. + * Returns the storage value for the account with the given contract id and key. * * @param contractID the contract id * @param key the key @@ -153,7 +153,7 @@ void trackSelfDestructBeneficiary( UInt256 getStorageValue(ContractID contractID, @NonNull UInt256 key); /** - * Sets the storage value for the contract with the given contract id and key. + * Sets the storage value for the account with the given contract id and key. * @param contractID the contract id * @param key the key * @param value the value to set @@ -161,7 +161,7 @@ void trackSelfDestructBeneficiary( void setStorageValue(ContractID contractID, @NonNull UInt256 key, @NonNull UInt256 value); /** - * Returns the original storage value for the contract with the given contract id and key. + * Returns the original storage value for the account with the given contract id and key. * * @param contractID the contract id * @param key the key @@ -180,25 +180,14 @@ void trackSelfDestructBeneficiary( Bytes getCode(ContractID contractID); /** - * Sets the code for the contract with the given contract id. Only used during contract creation. + * Sets the code for the contract with the given contract id. + * Used during contract creation and when setting EIP-7702 delegation. * * @param contractID the contract id * @param code the new code */ void setCode(ContractID contractID, @NonNull Bytes code); - /** - * Returns the redirect bytecode for the token with the given address, which must be a long-zero address. - * - *

Since a {@link TokenEvmAccount} never needs its Hedera entity number, we may as well use - * the long-zero address there, and here. - * - * @param address the token long-zero address - * @return the redirect code for the token - */ - @NonNull - Bytes getTokenRedirectCode(@NonNull Address address); - /** * @param contractID the contract to extract its code hash * @return the code hash of the contract @@ -206,57 +195,6 @@ void trackSelfDestructBeneficiary( @NonNull Hash getCodeHash(ContractID contractID, @NonNull final CodeFactory codeFactory); - /** - * Returns the hash of the redirect bytecode for the token with the given address, which must be a - * long-zero address. - * - *

Since a {@link TokenEvmAccount} never needs its Hedera entity number, we may as well use - * the long-zero address there, and here. - * - * @param address the token long-zero address - * @return the redirect code for the token - */ - @NonNull - Hash getTokenRedirectCodeHash(@NonNull Address address); - - /** - * Returns the redirect bytecode for the account with the given address. This should only be called for regular accounts - * that are not contracts. - * - * @param address the account address - * @return the redirect code for the account - */ - @NonNull - Bytes getAccountRedirectCode(@Nullable Address address); - - /** - * Returns the hash of the redirect bytecode for the account with the given address. - * - * @param address the account address - * @return the redirect code for the account - */ - @NonNull - Hash getAccountRedirectCodeHash(@Nullable Address address); - - /** - * Returns the redirect bytecode for the schedule with the given address. This should only be called for schedule - * transaction entities - * - * @param address the schedule address - * @return the redirect code for the schedule - */ - @NonNull - Bytes getScheduleRedirectCode(@Nullable Address address); - - /** - * Returns the hash of the redirect bytecode for the schedule with the given address. - * - * @param address the schedule address - * @return the redirect code for the schedule - */ - @NonNull - Hash getScheduleRedirectCodeHash(@Nullable Address address); - /** * Returns the native account with the given account id. * diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/HederaEvmAccount.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/HederaEvmAccount.java index 301b722ede7a..ed9e8dc3e7d9 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/HederaEvmAccount.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/HederaEvmAccount.java @@ -4,11 +4,7 @@ import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.ContractID; import edu.umd.cs.findbugs.annotations.NonNull; -import org.apache.tuweni.bytes.Bytes; -import org.hyperledger.besu.evm.Code; -import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; -import org.hyperledger.besu.evm.code.CodeFactory; /** * A Hedera specialization of a Besu {@link MutableAccount} that provides additional Hedera-specific information. @@ -58,20 +54,4 @@ public interface HederaEvmAccount extends MutableAccount { */ @NonNull ContractID hederaContractId(); - - /** - * Returns the EVM code for this account. Added here to avoid client code needing to manage a - * cache of {@link org.hyperledger.besu.evm.Code} wrappers around raw bytecode returned by - * {@link Account#getCode()}. - * - * @param functionSelector the function selector to use when fetching the code. If more than 4 bytes for the - * function selector is passed in, only the first 4 bytes will be used. - * Only relevant for regular accounts. - * - * @param codeFactory the factory used to construct an instance of {@link org.hyperledger.besu.evm.Code} - * * from the raw bytecode associated with this account. - * @return the EVM code for this account - */ - @NonNull - Code getEvmCode(@NonNull final Bytes functionSelector, @NonNull CodeFactory codeFactory); } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmAccount.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmAccount.java index 6388e77dbc96..bb7c8f1892b9 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmAccount.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmAccount.java @@ -1,66 +1,33 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.state; -import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractNativeSystemContract.FUNCTION_SELECTOR_LENGTH; - import com.hedera.hapi.node.base.AccountID; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.Set; import org.apache.tuweni.bytes.Bytes; -import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.code.CodeFactory; /** - * A concrete subclass of {@link AbstractProxyEvmAccount} that represents a contract account. - * - * Responsible for retrieving the redirectForAccount proxy contract byte code from {@link EvmFrameState} - * if the function selector is eligible for proxy redirection. - * Otherwise, it returns the 0x bytecode. - * + * A concrete subclass of {@link AbstractProxyEvmAccount} that represents a regular account. + * Responsible for retrieving the code (either EIP-7702 delegation indicator or empty) + * from the {@link EvmFrameState}. */ public class ProxyEvmAccount extends AbstractProxyEvmAccount { + private final CodeFactory codeFactory; - /* - * Four byte function selectors for the functions that are eligible for proxy redirection - * in the Hedera Account Service system contract - */ - private static final Set ACCOUNT_PROXY_FUNCTION_SELECTOR = Set.of( - // hbarAllowance(address spender) - 0xbbee989e, - // hbarApprove(address spender, int256 amount) - 0x86aff07c, - // setUnlimitedAutomaticAssociations(bool enableAutoAssociations - 0xf5677e99); - - // Only pass in a non-null account address if the function selector is eligible for proxy redirection. - // A null address will return the 0x bytecode. - @Nullable - private Address address; - - public ProxyEvmAccount(final AccountID accountID, @NonNull final EvmFrameState state) { + public ProxyEvmAccount( + final AccountID accountID, @NonNull final EvmFrameState state, @NonNull final CodeFactory codeFactory) { super(accountID, state); - } - - @Override - public @NonNull Code getEvmCode(@NonNull final Bytes functionSelector, @NonNull final CodeFactory codeFactory) { - // Check to see if the account needs to return the proxy redirect for account bytecode - final int selector = functionSelector.size() >= FUNCTION_SELECTOR_LENGTH ? functionSelector.getInt(0) : 0; - if (ACCOUNT_PROXY_FUNCTION_SELECTOR.contains(selector)) { - address = state.getAddress(accountID); - } - return codeFactory.createCode(getCode(), false); + this.codeFactory = codeFactory; } @Override public @NonNull Bytes getCode() { - return state.getAccountRedirectCode(address); + return state.getCode(hederaContractId()); } @Override public @NonNull Hash getCodeHash() { - return state.getAccountRedirectCodeHash(address); + return state.getCodeHash(hederaContractId(), codeFactory); } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmContract.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmContract.java index 3a35f9c2d4df..9ee0177f56d8 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmContract.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ProxyEvmContract.java @@ -5,13 +5,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.code.CodeFactory; /** * A concrete subclass of {@link AbstractProxyEvmAccount} that represents a contract account. - *

- * Responsible for retrieving the contract byte code from the {@link EvmFrameState} + * Responsible for retrieving the contract byte code from the {@link EvmFrameState}. */ public class ProxyEvmContract extends AbstractProxyEvmAccount { @@ -23,11 +21,6 @@ public ProxyEvmContract( this.codeFactory = codeFactory; } - @Override - public @NonNull Code getEvmCode(@NonNull final Bytes functionSelector, @NonNull final CodeFactory codeFactory) { - return codeFactory.createCode(getCode(), false); - } - @Override public @NonNull Bytes getCode() { return state.getCode(hederaContractId()); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ScheduleEvmAccount.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ScheduleEvmAccount.java index e6534dd865d4..5f8330790e94 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ScheduleEvmAccount.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/ScheduleEvmAccount.java @@ -1,20 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.state; -import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractNativeSystemContract.FUNCTION_SELECTOR_LENGTH; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HssSystemContract.HSS_EVM_ADDRESS; +import static org.hyperledger.besu.crypto.Hash.keccak256; +import static org.hyperledger.besu.evm.worldstate.CodeDelegationHelper.CODE_DELEGATION_PREFIX; import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.Set; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; -import org.hyperledger.besu.evm.code.CodeFactory; /** - * An {@link Account} whose code proxies all calls to the {@code 0x16b} system contract, and thus can + * An {@link Account} whose code is an EIP-7702 delegation to the {@code 0x16b} system contract, and thus can * never change its storage or nonce. * *

It also cannot have a non-zero balance, as dispatching a {@code transferValue()} with a schedule @@ -27,25 +26,14 @@ */ public class ScheduleEvmAccount extends AbstractEvmEntityAccount { - /* - * Four byte function selectors for the functions that are eligible for proxy redirection - * in the Hedera Schedule Service system contract - */ - private static final Set SCHEDULE_PROXY_FUNCTION_SELECTOR = Set.of( - // deleteSchedule() - 0xc61dea85, - // signSchedule() - 0x06d15889, - // getScheduledTransactionInfo() - 0x88af14e3); + // EIP-7702 delegation to HSS system contract + public static final Bytes CODE = Bytes.concatenate(CODE_DELEGATION_PREFIX, Address.fromHexString(HSS_EVM_ADDRESS)); + public static final Hash CODE_HASH = Hash.wrap(keccak256(CODE)); public ScheduleEvmAccount(@NonNull final Address address, @NonNull final EvmFrameState state) { super(address, state); } - /** - * {@inheritDoc} - */ @Override public boolean isScheduleTxnFacade() { return true; @@ -53,22 +41,11 @@ public boolean isScheduleTxnFacade() { @Override public Bytes getCode() { - return state.getScheduleRedirectCode(address); - } - - @Override - public @NonNull Code getEvmCode(@NonNull final Bytes functionSelector, @NonNull final CodeFactory codeFactory) { - // Check to see if the account needs to return the proxy redirect for schedule bytecode - final int selector = functionSelector.size() >= FUNCTION_SELECTOR_LENGTH ? functionSelector.getInt(0) : 0; - - if (!SCHEDULE_PROXY_FUNCTION_SELECTOR.contains(selector)) { - return codeFactory.createCode(Bytes.EMPTY, false); - } - return codeFactory.createCode(getCode(), false); + return CODE; } @Override public Hash getCodeHash() { - return state.getScheduleRedirectCodeHash(address); + return CODE_HASH; } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/TokenEvmAccount.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/TokenEvmAccount.java index 631b80e824ee..e6022eb0979b 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/TokenEvmAccount.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/TokenEvmAccount.java @@ -1,17 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.state; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_167_EVM_ADDRESS; +import static org.hyperledger.besu.crypto.Hash.keccak256; +import static org.hyperledger.besu.evm.worldstate.CodeDelegationHelper.CODE_DELEGATION_PREFIX; + import edu.umd.cs.findbugs.annotations.NonNull; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; -import org.hyperledger.besu.evm.code.CodeFactory; /** - * An {@link Account} whose code proxies all calls to the {@code 0x167} system contract, and thus can + * An {@link Account} whose code is an EIP-7702 delegation to the {@code 0x167} system contract, and thus can * never change its storage or nonce. * *

It also cannot have a non-zero balance, as dispatching a {@code transferValue()} with a token @@ -24,13 +26,15 @@ */ public class TokenEvmAccount extends AbstractEvmEntityAccount { + // EIP-7702 delegation to HTS system contract + public static final Bytes CODE = + Bytes.concatenate(CODE_DELEGATION_PREFIX, Address.fromHexString(HTS_167_EVM_ADDRESS)); + public static final Hash CODE_HASH = Hash.wrap(keccak256(CODE)); + public TokenEvmAccount(@NonNull final Address address, @NonNull final EvmFrameState state) { super(address, state); } - /** - * {@inheritDoc} - */ @Override public boolean isTokenFacade() { return true; @@ -38,16 +42,11 @@ public boolean isTokenFacade() { @Override public Bytes getCode() { - return state.getTokenRedirectCode(address); - } - - @Override - public @NonNull Code getEvmCode(@NonNull final Bytes functionSelector, @NonNull final CodeFactory codeFactory) { - return codeFactory.createCode(getCode(), false); + return CODE; } @Override public Hash getCodeHash() { - return state.getTokenRedirectCodeHash(address); + return CODE_HASH; } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/hooks/ProxyEvmHook.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/hooks/ProxyEvmHook.java index 4481d8fddca3..a681b2dc5fb2 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/hooks/ProxyEvmHook.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/state/hooks/ProxyEvmHook.java @@ -14,10 +14,8 @@ import com.hedera.node.app.service.entityid.EntityIdFactory; import edu.umd.cs.findbugs.annotations.NonNull; import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.units.bigints.UInt256; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.code.CodeFactory; /** @@ -49,11 +47,6 @@ public ProxyEvmHook( this.entityIdFactory = requireNonNull(entityIdFactory); } - @Override - public @NonNull Code getEvmCode(@NonNull final Bytes functionSelector, @NonNull final CodeFactory codeFactory) { - return codeFactory.createCode(getCode(), false); - } - @Override public Address getAddress() { return HTS_HOOKS_CONTRACT_ADDRESS; @@ -75,11 +68,6 @@ public ContractID hederaContractId() { return state.getCodeHash(hookState.hookContractIdOrThrow(), codeFactory); } - @Override - public @NonNull UInt256 getStorageValue(@NonNull final UInt256 key) { - return state.getStorageValue(entityIdFactory.newContractId(HTS_HOOKS_CONTRACT_NUM), key); - } - @NonNull private static AccountID getOwnerId(final @NonNull HookId hookId) { // We always use account id for hook owner internally diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/utils/RedirectBytecodeUtils.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/utils/RedirectBytecodeUtils.java deleted file mode 100644 index 93c982abd3dd..000000000000 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/utils/RedirectBytecodeUtils.java +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package com.hedera.node.app.service.contract.impl.utils; - -import static java.util.Objects.requireNonNull; - -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import org.apache.tuweni.bytes.Bytes; -import org.hyperledger.besu.datatypes.Address; - -public class RedirectBytecodeUtils { - - private static final String ADDRESS_BYTECODE_PATTERN = "fefefefefefefefefefefefefefefefefefefefe"; - - public static final String PROXY_PRE_BYTES = "6080604052348015600f57600080fd5b50600061"; - public static final String PROXY_MID_BYTES = "905077"; - private static final String PROXY_POST_BYTES = - ADDRESS_BYTECODE_PATTERN + "600052366000602037600080366018016008845af43d806000803e8160008114" - + "605857816000f35b816000fdfea2646970667358221220d8378feed472ba49a0" - + "005514ef7087017f707b45fb9bf56bb81bb93ff19a238b64736f6c634300080b0033"; - - // The following hex string is created by compiling the contract defined in HIP-719. - // (https://hips.hedera.com/hip/hip-719). The only exception is that the function selector for `redirectForToken` - // (0x618dc65e) - // has been pre substituted before the ADDRESS_BYTECODE_PATTERN. - private static final String TOKEN_CALL_REDIRECT_CONTRACT_BINARY = PROXY_PRE_BYTES - + "0167" // System contract address for HTS - + PROXY_MID_BYTES - + "618dc65e" // function selector for `redirectForToken` - + PROXY_POST_BYTES; - - // The following byte code is created by compiling the contract defined in HIP-906 - // (https://hips.hedera.com/hip/hip-906). The only exception is that the function selector for `redirectForAccount` - // (0xe4cbd3a7) - // has been pre substituted before the ADDRESS_BYTECODE_PATTERN. - private static final String ACCOUNT_CALL_REDIRECT_CONTRACT_BINARY = PROXY_PRE_BYTES - + "016a" // System contract address for HAS - + PROXY_MID_BYTES - + "e4cbd3a7" // function selector for `redirectForAccount` - + PROXY_POST_BYTES; - - // The following byte code is copied from the `redirectForToken` and `redirectForAccount` contract defined above. - // The only exception is that the function selector for `redirectForScheduleTxn` (0x5c3889ca) - // has been pre substituted before the ADDRESS_BYTECODE_PATTERN. - private static final String SCHEDULE_CALL_REDIRECT_CONTRACT_BINARY = PROXY_PRE_BYTES - + "016b" // System contract address for HSS - + PROXY_MID_BYTES - + "5c3889ca" // function selector for `redirectForScheduleTxn` - + PROXY_POST_BYTES; - - private RedirectBytecodeUtils() { - throw new UnsupportedOperationException("Utility Class"); - } - - public static Bytes tokenProxyBytecodeFor(@NonNull final Address address) { - requireNonNull(address); - return Bytes.fromHexString( - TOKEN_CALL_REDIRECT_CONTRACT_BINARY.replace(ADDRESS_BYTECODE_PATTERN, address.toUnprefixedHexString())); - } - - public static com.hedera.pbj.runtime.io.buffer.Bytes tokenProxyBytecodePjb(@Nullable final Address address) { - return address == null - ? com.hedera.pbj.runtime.io.buffer.Bytes.EMPTY - : com.hedera.pbj.runtime.io.buffer.Bytes.fromHex(TOKEN_CALL_REDIRECT_CONTRACT_BINARY.replace( - ADDRESS_BYTECODE_PATTERN, address.toUnprefixedHexString())); - } - - public static Bytes accountProxyBytecodeFor(@Nullable final Address address) { - return address == null - ? Bytes.EMPTY - : Bytes.fromHexString(ACCOUNT_CALL_REDIRECT_CONTRACT_BINARY.replace( - ADDRESS_BYTECODE_PATTERN, address.toUnprefixedHexString())); - } - - public static com.hedera.pbj.runtime.io.buffer.Bytes accountProxyBytecodePjb(@Nullable final Address address) { - return address == null - ? com.hedera.pbj.runtime.io.buffer.Bytes.EMPTY - : com.hedera.pbj.runtime.io.buffer.Bytes.fromHex(ACCOUNT_CALL_REDIRECT_CONTRACT_BINARY.replace( - ADDRESS_BYTECODE_PATTERN, address.toUnprefixedHexString())); - } - - public static Bytes scheduleProxyBytecodeFor(@Nullable final Address address) { - return address == null - ? Bytes.EMPTY - : Bytes.fromHexString(SCHEDULE_CALL_REDIRECT_CONTRACT_BINARY.replace( - ADDRESS_BYTECODE_PATTERN, address.toUnprefixedHexString())); - } - - public static com.hedera.pbj.runtime.io.buffer.Bytes scheduleProxyBytecodePjb(@Nullable final Address address) { - return address == null - ? com.hedera.pbj.runtime.io.buffer.Bytes.EMPTY - : com.hedera.pbj.runtime.io.buffer.Bytes.fromHex(SCHEDULE_CALL_REDIRECT_CONTRACT_BINARY.replace( - ADDRESS_BYTECODE_PATTERN, address.toUnprefixedHexString())); - } -} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java index b04c9c753404..971a5f149a6c 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java @@ -6,7 +6,6 @@ requires transitive com.hedera.node.app.hapi.fees; requires transitive com.hedera.node.app.hapi.utils; requires transitive com.hedera.node.app.service.contract; - requires transitive com.hedera.node.app.service.entityid; requires transitive com.hedera.node.app.service.file; requires transitive com.hedera.node.app.service.schedule; requires transitive com.hedera.node.app.service.token; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/TestHelpers.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/TestHelpers.java index a9281f5c5263..e4903b81ee52 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/TestHelpers.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/TestHelpers.java @@ -113,6 +113,7 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.code.CodeFactory; import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.log.LogTopic; import org.hyperledger.besu.evm.operation.Operation; @@ -529,6 +530,7 @@ public class TestHelpers { List.of(new ContractNonceInfo(CALLED_CONTRACT_ID, NONCE), new ContractNonceInfo(CHILD_CONTRACT_ID, 1L)); public static final EntityNumber CALLED_CONTRACT_ENTITY_NUMBER = new EntityNumber(666); public static final CodeFactory CODE_FACTORY = new CodeFactory(0, 0); + public static final GasCalculator GAS_CALCULATOR = new HederaGasCalculatorImpl(); public static final Code CONTRACT_CODE = CODE_FACTORY.createCode(pbjToTuweniBytes(CALL_DATA), false); public static final Log BESU_LOG = new Log( NON_SYSTEM_LONG_ZERO_ADDRESS, @@ -947,7 +949,7 @@ public static org.apache.tuweni.bytes.Bytes bytesForRedirect( public static org.apache.tuweni.bytes.Bytes bytesForRedirect(final byte[] subSelector, final Address tokenAddress) { return org.apache.tuweni.bytes.Bytes.concatenate( - org.apache.tuweni.bytes.Bytes.wrap(HtsCallAttempt.REDIRECT_FOR_TOKEN.selector()), + org.apache.tuweni.bytes.Bytes.wrap(HtsCallAttempt.LEGACY_REDIRECT_FOR_TOKEN.selector()), tokenAddress, org.apache.tuweni.bytes.Bytes.of(subSelector)); } @@ -962,7 +964,7 @@ public static org.apache.tuweni.bytes.Bytes bytesForRedirectAccount( public static org.apache.tuweni.bytes.Bytes bytesForRedirectAccount( final byte[] subSelector, final Address accountAddress) { return org.apache.tuweni.bytes.Bytes.concatenate( - org.apache.tuweni.bytes.Bytes.wrap(HasCallAttempt.REDIRECT_FOR_ACCOUNT.selector()), + org.apache.tuweni.bytes.Bytes.wrap(HasCallAttempt.LEGACY_REDIRECT_FOR_ACCOUNT.selector()), accountAddress, org.apache.tuweni.bytes.Bytes.of(subSelector)); } @@ -970,7 +972,7 @@ public static org.apache.tuweni.bytes.Bytes bytesForRedirectAccount( public static org.apache.tuweni.bytes.Bytes bytesForRedirectScheduleTxn( final byte[] subSelector, final Address scheduleAddress) { return org.apache.tuweni.bytes.Bytes.concatenate( - org.apache.tuweni.bytes.Bytes.wrap(HssCallAttempt.REDIRECT_FOR_SCHEDULE_TXN.selector()), + org.apache.tuweni.bytes.Bytes.wrap(HssCallAttempt.LEGACY_REDIRECT_FOR_SCHEDULE_TXN.selector()), scheduleAddress, org.apache.tuweni.bytes.Bytes.of(subSelector)); } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/TransactionProcessorTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/TransactionProcessorTest.java index 911655689a8e..e3e39682d252 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/TransactionProcessorTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/TransactionProcessorTest.java @@ -6,7 +6,33 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_CONTRACT_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TRANSACTION_BODY; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.TRACKER_CONTEXT_VARIABLE; -import static com.hedera.node.app.service.contract.impl.test.TestHelpers.*; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CALLED_CONTRACT_ID; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CALL_DATA; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CHARGING_RESULT; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CODE_FACTORY; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.EIP_1014_ADDRESS; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.GAS_CALCULATOR; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.GAS_LIMIT; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.INVALID_CONTRACT_ADDRESS; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.MAINNET_CHAIN_ID; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.MAX_GAS_ALLOWANCE; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NETWORK_GAS_PRICE; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NONCE; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NON_SYSTEM_LONG_ZERO_ADDRESS; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NO_ALLOWANCE_CHARGING_RESULT; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.RECEIVER_ID; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.RELAYER_ID; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.SENDER_ID; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.SUCCESS_RESULT; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.USER_OFFERED_GAS_PRICE; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.VALID_CONTRACT_ADDRESS; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.VALUE; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.assertFailsWith; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.wellKnownContextWith; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.wellKnownHapiCall; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.wellKnownHapiCreate; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.wellKnownRelayedHapiCall; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.wellKnownRelayedHapiCreate; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowableOfType; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -133,7 +159,8 @@ void setUp() { messageCallProcessor, contractCreationProcessor, featureFlags, - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); opsDurationCounter = OpsDurationCounter.disabled(); } @@ -209,7 +236,8 @@ void lazyCreationAttemptWithValidAddress() { EIP_1014_ADDRESS, expectedToAddress, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY)) + CODE_FACTORY, + GAS_CALCULATOR)) .willReturn(initialFrame); given(senderAccount.getNonce()).willReturn(NONCE); given(frameRunner.runToCompletion( @@ -266,7 +294,8 @@ void lazyCreationAttemptCanCallNotExistingFeatureFlagOn() { EIP_1014_ADDRESS, expectedToAddress, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY)) + CODE_FACTORY, + GAS_CALCULATOR)) .willReturn(initialFrame); given(senderAccount.hederaId()).willReturn(SENDER_ID); given(frameRunner.runToCompletion( @@ -325,7 +354,8 @@ void callWithNoValueAndCanCallNotExistingFeatureFlagOn() { EIP_1014_ADDRESS, expectedToAddress, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY)) + CODE_FACTORY, + GAS_CALCULATOR)) .willReturn(initialFrame); given(senderAccount.hederaId()).willReturn(SENDER_ID); given(frameRunner.runToCompletion( @@ -441,7 +471,8 @@ void ethCreateHappyPathAsExpected() { EIP_1014_ADDRESS, expectedToAddress, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY)) + CODE_FACTORY, + GAS_CALCULATOR)) .willReturn(initialFrame); given(senderAccount.getNonce()).willReturn(NONCE); given(frameRunner.runToCompletion( @@ -475,7 +506,8 @@ void ethCreateHappyPathAsExpected() { EIP_1014_ADDRESS, expectedToAddress, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); inOrder.verify(frameRunner) .runToCompletion( transaction.gasLimit(), @@ -523,7 +555,8 @@ void hapiCreateHappyPathAsExpected() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, NO_ALLOWANCE_CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY)) + CODE_FACTORY, + GAS_CALCULATOR)) .willReturn(initialFrame); given(frameRunner.runToCompletion( transaction.gasLimit(), @@ -552,7 +585,8 @@ void hapiCreateHappyPathAsExpected() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, NO_ALLOWANCE_CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); inOrder.verify(frameRunner) .runToCompletion( transaction.gasLimit(), @@ -598,7 +632,8 @@ void ethCallHappyPathAsExpected() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY)) + CODE_FACTORY, + GAS_CALCULATOR)) .willReturn(initialFrame); given(frameRunner.runToCompletion( eq(transaction.gasLimit()), @@ -626,7 +661,8 @@ void ethCallHappyPathAsExpected() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); inOrder.verify(frameRunner) .runToCompletion( transaction.gasLimit(), @@ -676,7 +712,8 @@ void ethCallHappyPathAsExpectedAndCanCallNotExistingFeatureFlagOn() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY)) + CODE_FACTORY, + GAS_CALCULATOR)) .willReturn(initialFrame); given(senderAccount.hederaId()).willReturn(SENDER_ID); given(frameRunner.runToCompletion( @@ -708,7 +745,8 @@ void ethCallHappyPathAsExpectedAndCanCallNotExistingFeatureFlagOn() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); inOrder.verify(frameRunner) .runToCompletion( transaction.gasLimit(), @@ -756,7 +794,8 @@ void ethCallAsExpectedWithResourceExhaustionInCommit() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, CHARGING_RESULT.intrinsicGas(), - CODE_FACTORY)) + CODE_FACTORY, + GAS_CALCULATOR)) .willReturn(initialFrame); given(frameRunner.runToCompletion( eq(transaction.gasLimit()), diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/delegation/CodeDelegationProcessorTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/delegation/CodeDelegationProcessorTest.java index 3c516b6eae70..3a76340569bb 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/delegation/CodeDelegationProcessorTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/delegation/CodeDelegationProcessorTest.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.test.exec.delegation; +import static org.hyperledger.besu.evm.worldstate.CodeDelegationHelper.CODE_DELEGATION_PREFIX; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; @@ -199,7 +200,7 @@ void createsNewAccountAndDelegatesWhenMissingAndNonceZero() throws Exception { final var acct = mock(MutableAccount.class); final var authAddr = Address.fromHexString("0x00000000000000000000000000000000000000AA"); - final var expectedCode = Bytes.concatenate(CodeDelegationProcessor.CODE_DELEGATION_PREFIX, authAddr); + final var expectedCode = Bytes.concatenate(CODE_DELEGATION_PREFIX, authAddr); when(tx.codeDelegations()).thenReturn(List.of(del)); when(del.getChainId()).thenReturn(CHAIN_ID); @@ -264,7 +265,7 @@ void updatesExistingAccountWithEmptyCodeAndMatchingNonce() throws Exception { final var acct = mock(MutableAccount.class); final var authAddr = Address.fromHexString("0x00000000000000000000000000000000000000AC"); - final var expectedCode = Bytes.concatenate(CodeDelegationProcessor.CODE_DELEGATION_PREFIX, authAddr); + final var expectedCode = Bytes.concatenate(CODE_DELEGATION_PREFIX, authAddr); when(tx.codeDelegations()).thenReturn(List.of(del)); when(del.getChainId()).thenReturn(CHAIN_ID); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/processors/CustomMessageCallProcessorTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/processors/CustomMessageCallProcessorTest.java index e1a8f74492d8..fb9a41affd7a 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/processors/CustomMessageCallProcessorTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/processors/CustomMessageCallProcessorTest.java @@ -12,6 +12,7 @@ import static com.hedera.node.app.service.contract.impl.test.TestHelpers.REMAINING_GAS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.isSameResult; import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.INSUFFICIENT_GAS; +import static org.hyperledger.besu.evm.worldstate.CodeDelegationHelper.CODE_DELEGATION_PREFIX; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; @@ -43,6 +44,7 @@ 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.operation.Operation; @@ -129,6 +131,7 @@ void callPrngSystemContractHappyPath() { given(frame.getValue()).willReturn(Wei.ZERO); given(frame.getMessageFrameStack()).willReturn(stack); given(frame.getContextVariable(OPS_DURATION_COUNTER)).willReturn(OpsDurationCounter.disabled()); + given(frame.getWorldUpdater()).willReturn(proxyWorldUpdater); given(stack.getLast()).willReturn(frame); given(result.output()).willReturn(OUTPUT_DATA); given(result.state()).willReturn(MessageFrame.State.CODE_SUCCESS); @@ -226,6 +229,7 @@ void haltsAndTracesInsufficientGasIfPrecompileGasRequirementExceedsRemaining() { givenEvmPrecompileCall(); given(nativePrecompile.gasRequirement(INPUT_DATA)).willReturn(GAS_REQUIREMENT); given(frame.getRemainingGas()).willReturn(1L); + given(frame.getValue()).willReturn(Wei.ZERO); subject.start(frame, operationTracer); @@ -242,6 +246,7 @@ void updatesFrameBySuccessfulPrecompileResultWithGasRefund() { given(nativePrecompile.computePrecompile(INPUT_DATA, frame)).willReturn(result); given(nativePrecompile.gasRequirement(INPUT_DATA)).willReturn(GAS_REQUIREMENT); given(frame.getRemainingGas()).willReturn(3L); + given(frame.getValue()).willReturn(Wei.ZERO); given(frame.getMessageFrameStack()).willReturn(stack); given(frame.getContextVariable(OPS_DURATION_COUNTER)).willReturn(OpsDurationCounter.disabled()); given(stack.getLast()).willReturn(frame); @@ -268,6 +273,7 @@ void revertsFrameFromPrecompileResult() { given(frame.getContextVariable(OPS_DURATION_COUNTER)).willReturn(OpsDurationCounter.disabled()); given(stack.getLast()).willReturn(frame); given(frame.getContractAddress()).willReturn(Address.ALTBN128_ADD); + given(frame.getValue()).willReturn(Wei.ZERO); subject.start(frame, operationTracer); @@ -320,6 +326,49 @@ void callsToDisabledPrecompile() { verify(frame).setExceptionalHaltReason(Optional.empty()); } + @Test + void codeDelegationToPrecompileIsNoOp() { + given(registry.get(ADDRESS_6)).willReturn(nativePrecompile); + final var eoaAddress = Address.fromHexString("0x1234"); + final var eoaAccount = mock(Account.class); + given(eoaAccount.getCode()).willReturn(Bytes.concatenate(CODE_DELEGATION_PREFIX, ADDRESS_6)); + given(proxyWorldUpdater.get(eoaAddress)).willReturn(eoaAccount); + given(frame.getContractAddress()).willReturn(eoaAddress); + given(frame.getInputData()).willReturn(Bytes.EMPTY); + given(frame.getMessageFrameStack()).willReturn(stack); + given(frame.getContextVariable(CONFIG_CONTEXT_VARIABLE)).willReturn(DEFAULT_CONFIG); + given(frame.getWorldUpdater()).willReturn(proxyWorldUpdater); + given(stack.getLast()).willReturn(frame); + given(frame.getValue()).willReturn(Wei.ZERO); + + subject.start(frame, operationTracer); + + verifyNoInteractions(nativePrecompile); + verify(frame).setState(MessageFrame.State.CODE_SUCCESS); + } + + @Test + void codeDelegationToPrecompileIsNoOpWithValueTransfer() { + givenExecutingFrame(); + given(registry.get(ADDRESS_6)).willReturn(nativePrecompile); + final var eoaAddress = Address.fromHexString("0x1234"); + final var eoaAccount = mock(Account.class); + given(eoaAccount.getCode()).willReturn(Bytes.concatenate(CODE_DELEGATION_PREFIX, ADDRESS_6)); + given(proxyWorldUpdater.get(eoaAddress)).willReturn(eoaAccount); + given(frame.getContractAddress()).willReturn(eoaAddress); + given(frame.getRecipientAddress()).willReturn(eoaAddress); + given(frame.getInputData()).willReturn(Bytes.EMPTY); + given(frame.getContextVariable(CONFIG_CONTEXT_VARIABLE)).willReturn(DEFAULT_CONFIG); + given(frame.getWorldUpdater()).willReturn(proxyWorldUpdater); + given(frame.getValue()).willReturn(Wei.ONE); + + subject.start(frame, operationTracer); + + verifyNoInteractions(nativePrecompile); + verify(frame).setState(MessageFrame.State.CODE_SUCCESS); + verify(proxyWorldUpdater).tryTransfer(frame.getSenderAddress(), eoaAddress, 1, false); + } + private void givenHaltableFrame(@NonNull final AtomicBoolean isHalted) { doAnswer(invocation -> { isHalted.set(true); @@ -327,7 +376,9 @@ private void givenHaltableFrame(@NonNull final AtomicBoolean isHalted) { }) .when(frame) .setExceptionalHaltReason(any()); - doAnswer(invocation -> isHalted.get() ? MessageFrame.State.EXCEPTIONAL_HALT : MessageFrame.State.NOT_STARTED) + lenient() + .doAnswer(invocation -> + isHalted.get() ? MessageFrame.State.EXCEPTIONAL_HALT : MessageFrame.State.NOT_STARTED) .when(frame) .getState(); } @@ -343,12 +394,14 @@ private void givenCallWithIsTopLevelTransaction(@NonNull final AtomicBoolean isT private void givenCallWithCode(@NonNull final Address contract) { given(frame.getContractAddress()).willReturn(contract); + given(frame.getWorldUpdater()).willReturn(proxyWorldUpdater); } private void givenWellKnownUserSpaceCall() { given(frame.getContractAddress()).willReturn(CODE_ADDRESS); given(frame.getRecipientAddress()).willReturn(RECEIVER_ADDRESS); given(frame.getSenderAddress()).willReturn(SENDER_ADDRESS); + given(frame.getWorldUpdater()).willReturn(proxyWorldUpdater); } private void givenEvmPrecompileCall() { @@ -358,6 +411,7 @@ private void givenEvmPrecompileCall() { given(frame.getInputData()).willReturn(INPUT_DATA); given(frame.getMessageFrameStack()).willReturn(stack); given(frame.getContextVariable(CONFIG_CONTEXT_VARIABLE)).willReturn(DEFAULT_CONFIG); + given(frame.getWorldUpdater()).willReturn(proxyWorldUpdater); } private void givenDisabledEvmPrecompileCall() { @@ -370,6 +424,7 @@ private void givenDisabledEvmPrecompileCall() { given(frame.getMessageFrameStack()).willReturn(stack); given(frame.getContextVariable(CONFIG_CONTEXT_VARIABLE)).willReturn(DISABLED_PRECOMPILE_CONFIG); when(frame.getValue()).thenReturn(Wei.ZERO); + given(frame.getWorldUpdater()).willReturn(proxyWorldUpdater); given(addressChecks.isSystemAccount(ADDRESS_6)).willReturn(true); } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/common/CallAttemptTestBase.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/common/CallAttemptTestBase.java index 4200aa6f765a..c69af99a6670 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/common/CallAttemptTestBase.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/common/CallAttemptTestBase.java @@ -2,8 +2,11 @@ package com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.common; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HasSystemContract.HAS_CONTRACT_ID; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HasSystemContract.HAS_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HssSystemContract.HSS_CONTRACT_ID; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HssSystemContract.HSS_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_167_CONTRACT_ID; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_167_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.DEFAULT_CONFIG; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.EIP_1014_ADDRESS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NON_SYSTEM_LONG_ZERO_ADDRESS; @@ -161,6 +164,7 @@ protected HtsCallAttempt createHtsCallAttempt( new CallAttemptOptions<>( contractID, senderAddress, + Address.fromHexString(HTS_167_EVM_ADDRESS), authorizingAddress, onlyDelegatableContractKeysActive, mockEnhancement(), @@ -200,6 +204,7 @@ protected HasCallAttempt createHasCallAttempt( new CallAttemptOptions<>( HAS_CONTRACT_ID, OWNER_BESU_ADDRESS, + Address.fromHexString(HAS_EVM_ADDRESS), OWNER_BESU_ADDRESS, false, mockEnhancement(), @@ -244,6 +249,7 @@ protected HssCallAttempt createHssCallAttempt( new CallAttemptOptions<>( HSS_CONTRACT_ID, senderAddress, + Address.fromHexString(HSS_EVM_ADDRESS), senderAddress, onlyDelegatableContractKeysActive, mockEnhancement(), diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/HasCallFactoryTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/HasCallFactoryTest.java index 4cbabe2e6124..2a9c5dd3aac8 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/HasCallFactoryTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/has/HasCallFactoryTest.java @@ -2,6 +2,7 @@ package com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.has; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HasSystemContract.HAS_CONTRACT_ID; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HasSystemContract.HAS_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.A_NEW_ACCOUNT_ID; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.B_NEW_ACCOUNT_ID; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CALLED_EOA_ID; @@ -101,6 +102,7 @@ void instantiatesCallWithInContextEnhancementAndDelegateCallInfo() { given(frame.getWorldUpdater()).willReturn(updater); given(updater.enhancement()).willReturn(mockEnhancement()); given(frame.getSenderAddress()).willReturn(EIP_1014_ADDRESS); + given(frame.getRecipientAddress()).willReturn(Address.fromHexString(HAS_EVM_ADDRESS)); given(addressChecks.hasParentDelegateCall(frame)).willReturn(true); given(syntheticIds.converterFor(nativeOperations)).willReturn(idConverter); given(idConverter.convert(asHeadlongAddress(NON_SYSTEM_BUT_IS_LONG_ZERO_ADDRESS))) @@ -130,6 +132,7 @@ void instantiatesDirectCall() { given(frame.getWorldUpdater()).willReturn(updater); given(updater.enhancement()).willReturn(mockEnhancement()); given(frame.getSenderAddress()).willReturn(Address.ALTBN128_ADD); + given(frame.getRecipientAddress()).willReturn(Address.fromHexString(HAS_EVM_ADDRESS)); given(idConverter.convertSender(Address.ALTBN128_ADD)).willReturn(A_NEW_ACCOUNT_ID); given(addressChecks.hasParentDelegateCall(frame)).willReturn(true); given(syntheticIds.converterFor(nativeOperations)).willReturn(idConverter); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hss/HssCallFactoryTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hss/HssCallFactoryTest.java index 54d7c957368a..17c6313a3b99 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hss/HssCallFactoryTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hss/HssCallFactoryTest.java @@ -2,6 +2,7 @@ package com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hss; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HssSystemContract.HSS_CONTRACT_ID; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HssSystemContract.HSS_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.A_NEW_ACCOUNT_ID; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CALLED_SCHEDULE_ID; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.DEFAULT_CONFIG; @@ -35,6 +36,7 @@ import java.util.Deque; import java.util.List; import java.util.Objects; +import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.evm.frame.MessageFrame; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -106,6 +108,7 @@ void instantiatesCallWithInContextEnhancementAndDelegateCallInfo() { given(frame.getWorldUpdater()).willReturn(updater); given(updater.enhancement()).willReturn(mockEnhancement()); given(frame.getSenderAddress()).willReturn(EIP_1014_ADDRESS); + given(frame.getRecipientAddress()).willReturn(Address.fromHexString(HSS_EVM_ADDRESS)); given(addressChecks.hasParentDelegateCall(frame)).willReturn(true); given(syntheticIds.converterFor(nativeOperations)).willReturn(idConverter); given(nativeOperations.getSchedule(CALLED_SCHEDULE_ID)).willReturn(schedule); @@ -138,6 +141,7 @@ void instantiatesDirectCall() { given(frame.getWorldUpdater()).willReturn(updater); given(updater.enhancement()).willReturn(mockEnhancement()); given(frame.getSenderAddress()).willReturn(ALTBN128_ADD); + given(frame.getRecipientAddress()).willReturn(Address.fromHexString(HSS_EVM_ADDRESS)); given(idConverter.convertSender(ALTBN128_ADD)).willReturn(A_NEW_ACCOUNT_ID); given(addressChecks.hasParentDelegateCall(frame)).willReturn(true); given(syntheticIds.converterFor(nativeOperations)).willReturn(idConverter); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallAttemptTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallAttemptTest.java index b06ddadeb582..28ac1b263ef7 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallAttemptTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallAttemptTest.java @@ -424,7 +424,7 @@ void constructsMints(String hexedSelector, LinkedTokenType linkedTokenType) { private Bytes encodeInput(final boolean useExplicitCall, final boolean isRedirect, final byte[] selector) { if (isRedirect) { return useExplicitCall - ? Bytes.wrap(HtsCallAttempt.REDIRECT_FOR_TOKEN + ? Bytes.wrap(HtsCallAttempt.LEGACY_REDIRECT_FOR_TOKEN .encodeCallWithArgs( asHeadlongAddress(NON_SYSTEM_LONG_ZERO_ADDRESS.toArrayUnsafe()), Bytes.wrap(selector).toArrayUnsafe()) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallFactoryTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallFactoryTest.java index d818c927ae69..cdb7ab4def8e 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallFactoryTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallFactoryTest.java @@ -2,6 +2,7 @@ package com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_167_CONTRACT_ID; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HtsSystemContract.HTS_167_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.balanceof.BalanceOfTranslator.BALANCE_OF; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.A_NEW_ACCOUNT_ID; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.DEFAULT_CONFIG; @@ -96,6 +97,7 @@ void instantiatesCallWithInContextEnhancementAndDelegateCallInfo() { given(nativeOperations.getToken(FUNGIBLE_TOKEN_ID)).willReturn(FUNGIBLE_TOKEN); given(nativeOperations.entityIdFactory()).willReturn(entityIdFactory); given(frame.getSenderAddress()).willReturn(EIP_1014_ADDRESS); + given(frame.getRecipientAddress()).willReturn(Address.fromHexString(HTS_167_EVM_ADDRESS)); given(addressChecks.hasParentDelegateCall(frame)).willReturn(true); given(syntheticIds.converterFor(nativeOperations)).willReturn(idConverter); @@ -122,8 +124,8 @@ void instantiatesQualifiedDelegateCallWithRecipientAsSender() { given(nativeOperations.getToken(FUNGIBLE_TOKEN_ID)).willReturn(FUNGIBLE_TOKEN); given(nativeOperations.entityIdFactory()).willReturn(entityIdFactory); given(frame.getSenderAddress()).willReturn(Address.ALTBN128_ADD); + given(frame.getRecipientAddress()).willReturn(Address.fromHexString(HTS_167_EVM_ADDRESS)); given(idConverter.convertSender(Address.ALTBN128_ADD)).willReturn(A_NEW_ACCOUNT_ID); - given(frame.getRecipientAddress()).willReturn(EIP_1014_ADDRESS); given(addressChecks.hasParentDelegateCall(frame)).willReturn(true); given(syntheticIds.converterFor(nativeOperations)).willReturn(idConverter); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/utils/FrameBuilderTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/utils/FrameBuilderTest.java index d6db23f3e67c..a9df02fca88a 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/utils/FrameBuilderTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/utils/FrameBuilderTest.java @@ -6,11 +6,11 @@ import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.selfDestructBeneficiariesFor; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.tinybarValuesFor; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CALLED_CONTRACT_ID; -import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CALL_DATA; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CODE_FACTORY; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CONTRACT_CODE; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.DEFAULT_COINBASE; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.EIP_1014_ADDRESS; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.GAS_CALCULATOR; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.GAS_LIMIT; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.INTRINSIC_GAS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NETWORK_GAS_PRICE; @@ -107,8 +107,7 @@ void constructsExpectedFrameForCallToExtantContractIncludingOptionalContextVaria givenContractExists(); final var recordBuilder = mock(ContractOperationStreamBuilder.class); given(worldUpdater.getHederaAccount(CALLED_CONTRACT_ID)).willReturn(account); - given(account.getEvmCode(Bytes.wrap(CALL_DATA.toByteArray()), CODE_FACTORY)) - .willReturn(CONTRACT_CODE); + given(account.getCode()).willReturn(CONTRACT_CODE.getBytes()); given(worldUpdater.updater()).willReturn(stackedUpdater); given(blocks.blockValuesOf(GAS_LIMIT)).willReturn(blockValues); final var config = HederaTestConfigBuilder.create() @@ -124,7 +123,8 @@ void constructsExpectedFrameForCallToExtantContractIncludingOptionalContextVaria EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, INTRINSIC_GAS, - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); given(blocks.blockHashOf(frame, SOME_BLOCK_NO)).willReturn(Hash.EMPTY); assertEquals(1024, frame.getMaxStackSize()); @@ -146,7 +146,7 @@ void constructsExpectedFrameForCallToExtantContractIncludingOptionalContextVaria assertEquals(NON_SYSTEM_LONG_ZERO_ADDRESS, frame.getRecipientAddress()); assertEquals(NON_SYSTEM_LONG_ZERO_ADDRESS, frame.getContractAddress()); assertEquals(transaction.evmPayload(), frame.getInputData()); - assertSame(CONTRACT_CODE, frame.getCode()); + assertSame(CONTRACT_CODE.getBytes(), frame.getCode().getBytes()); assertNotNull(accessTrackerFor(frame)); assertSame(tinybarValues, tinybarValuesFor(frame)); assertSame(recordBuilder, selfDestructBeneficiariesFor(frame)); @@ -160,8 +160,7 @@ void constructsExpectedFrameForCallToExtantContractNotIncludingAccessTrackerWith given(worldUpdater.updater()).willReturn(stackedUpdater); given(blocks.blockValuesOf(GAS_LIMIT)).willReturn(blockValues); given(worldUpdater.getHederaAccount(CALLED_CONTRACT_ID)).willReturn(account); - given(account.getEvmCode(Bytes.wrap(CALL_DATA.toByteArray()), CODE_FACTORY)) - .willReturn(CONTRACT_CODE); + given(account.getCode()).willReturn(CONTRACT_CODE.getBytes()); final var config = HederaTestConfigBuilder.create() .withValue("ledger.fundingAccount", DEFAULT_COINBASE) .withValue("contracts.sidecars", "CONTRACT_BYTECODE,CONTRACT_ACTION") @@ -177,7 +176,8 @@ void constructsExpectedFrameForCallToExtantContractNotIncludingAccessTrackerWith EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, INTRINSIC_GAS, - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); given(blocks.blockHashOf(frame, SOME_BLOCK_NO)).willReturn(Hash.EMPTY); assertEquals(1024, frame.getMaxStackSize()); @@ -199,7 +199,7 @@ void constructsExpectedFrameForCallToExtantContractNotIncludingAccessTrackerWith assertEquals(NON_SYSTEM_LONG_ZERO_ADDRESS, frame.getRecipientAddress()); assertEquals(NON_SYSTEM_LONG_ZERO_ADDRESS, frame.getContractAddress()); assertEquals(transaction.evmPayload(), frame.getInputData()); - assertSame(CONTRACT_CODE, frame.getCode()); + assertSame(CONTRACT_CODE.getBytes(), frame.getCode().getBytes()); assertNull(accessTrackerFor(frame)); assertSame(tinybarValues, tinybarValuesFor(frame)); } @@ -227,7 +227,8 @@ void callFailsWhenContractNotFound() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, INTRINSIC_GAS, - CODE_FACTORY)); + CODE_FACTORY, + GAS_CALCULATOR)); } @Test @@ -237,8 +238,7 @@ void callSucceedsWhenContractNotFoundIfPermitted() { given(worldUpdater.updater()).willReturn(stackedUpdater); given(blocks.blockValuesOf(GAS_LIMIT)).willReturn(blockValues); given(worldUpdater.getHederaAccount(CALLED_CONTRACT_ID)).willReturn(account); - given(account.getEvmCode(Bytes.wrap(CALL_DATA.toByteArray()), CODE_FACTORY)) - .willReturn(CONTRACT_CODE); + given(account.getCode()).willReturn(CONTRACT_CODE.getBytes()); final var config = HederaTestConfigBuilder.create().getOrCreateConfig(); final var frame = subject.buildInitialFrameWith( @@ -251,7 +251,8 @@ void callSucceedsWhenContractNotFoundIfPermitted() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, INTRINSIC_GAS, - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); assertEquals(1024, frame.getMaxStackSize()); assertSame(stackedUpdater, frame.getWorldUpdater()); @@ -270,7 +271,7 @@ void callSucceedsWhenContractNotFoundIfPermitted() { assertEquals(NON_SYSTEM_LONG_ZERO_ADDRESS, frame.getRecipientAddress()); assertEquals(NON_SYSTEM_LONG_ZERO_ADDRESS, frame.getContractAddress()); assertEquals(transaction.evmPayload(), frame.getInputData()); - assertSame(CONTRACT_CODE, frame.getCode()); + assertSame(CONTRACT_CODE.getBytes(), frame.getCode().getBytes()); assertSame(tinybarValues, tinybarValuesFor(frame)); } @@ -293,7 +294,8 @@ void callSucceedsWhenContractFoundButDeleted() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, INTRINSIC_GAS, - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); assertEquals(1024, frame.getMaxStackSize()); assertSame(stackedUpdater, frame.getWorldUpdater()); @@ -337,7 +339,8 @@ void constructsExpectedFrameForCallToMissingContract() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, INTRINSIC_GAS, - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); given(blocks.blockHashOf(frame, SOME_BLOCK_NO)).willReturn(Hash.EMPTY); assertTrue(FrameUtils.hasActionValidationEnabled(frame)); @@ -384,7 +387,8 @@ void constructsExpectedFrameForCreate() { EIP_1014_ADDRESS, NON_SYSTEM_LONG_ZERO_ADDRESS, INTRINSIC_GAS, - CODE_FACTORY); + CODE_FACTORY, + GAS_CALCULATOR); given(blocks.blockHashOf(frame, SOME_BLOCK_NO)).willReturn(Hash.EMPTY); assertEquals(1024, frame.getMaxStackSize()); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractGetBytecodeHandlerTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractGetBytecodeHandlerTest.java index 925d260b4aa2..9841ad7f0bab 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractGetBytecodeHandlerTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractGetBytecodeHandlerTest.java @@ -5,6 +5,7 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_CONTRACT_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.OK; import static com.hedera.hapi.node.base.ResponseType.ANSWER_ONLY; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.tuweniToPbjBytes; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; @@ -28,7 +29,8 @@ import com.hedera.node.app.hapi.utils.fee.SigValueObj; import com.hedera.node.app.service.contract.impl.handlers.ContractGetBytecodeHandler; import com.hedera.node.app.service.contract.impl.state.ContractStateStore; -import com.hedera.node.app.service.contract.impl.utils.RedirectBytecodeUtils; +import com.hedera.node.app.service.contract.impl.state.ScheduleEvmAccount; +import com.hedera.node.app.service.contract.impl.state.TokenEvmAccount; import com.hedera.node.app.service.entityid.EntityIdFactory; import com.hedera.node.app.service.schedule.ReadableScheduleStore; import com.hedera.node.app.service.token.ReadableAccountStore; @@ -41,7 +43,6 @@ import com.hederahashgraph.api.proto.java.FeeComponents; import java.util.Objects; import java.util.function.Function; -import org.hyperledger.besu.datatypes.Address; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -214,6 +215,7 @@ void validateIfNoContractAccountTest() { @Test void computeFeesIfNoContractAccountTest() { givenNoContractAccount(); + given(entityIdFactory.newAccountId(contractID.contractNumOrElse(0L))).willReturn(accountId); QueryHeader defaultHeader = QueryHeader.newBuilder().responseType(ANSWER_ONLY).build(); given(contractGetBytecodeQuery.headerOrElse(QueryHeader.DEFAULT)).willReturn(defaultHeader); @@ -225,6 +227,7 @@ void computeFeesIfNoContractAccountTest() { @Test void findResponseFailsIfNoContractAccountTest() { givenNoContractAccount(); + given(entityIdFactory.newAccountId(contractID.contractNumOrElse(0L))).willReturn(accountId); given(responseHeader.nodeTransactionPrecheckCode()).willReturn(OK); given(responseHeader.responseType()).willReturn(ANSWER_ONLY); assertThat(Objects.requireNonNull( @@ -297,6 +300,7 @@ void validateFailsIfTokenWasDeletedTest() { @Test void computeFeesIfTokenWasDeletedTest() { givenTokenWasDeleted(); + given(entityIdFactory.newAccountId(contractID.contractNumOrElse(0L))).willReturn(accountId); QueryHeader defaultHeader = QueryHeader.newBuilder().responseType(ANSWER_ONLY).build(); given(contractGetBytecodeQuery.headerOrElse(QueryHeader.DEFAULT)).willReturn(defaultHeader); @@ -308,6 +312,7 @@ void computeFeesIfTokenWasDeletedTest() { @Test void findResponseIfTokenWasDeletedTest() { givenTokenWasDeleted(); + given(entityIdFactory.newAccountId(contractID.contractNumOrElse(0L))).willReturn(accountId); given(responseHeader.nodeTransactionPrecheckCode()).willReturn(OK); given(responseHeader.responseType()).willReturn(ANSWER_ONLY); assertThat(Objects.requireNonNull( @@ -365,7 +370,6 @@ void findResponsePositiveTest() { given(contractGetBytecodeQuery.contractIDOrElse(ContractID.DEFAULT)).willReturn(contractID); given(context.createStore(ReadableAccountStore.class)).willReturn(contractStore); given(contractStore.getContractById(contractID)).willReturn(account); - given(account.smartContract()).willReturn(true); given(account.accountIdOrThrow()).willReturn(accountId); given(context.createStore(ContractStateStore.class)).willReturn(stateStore); @@ -403,6 +407,13 @@ void validateAccountIdAsContractId() { @Test void computeFeesAccountIdAsContractId() { givenAccountIdAsContractId(); + given(account.accountIdOrThrow()).willReturn(accountId); + + given(context.createStore(ContractStateStore.class)).willReturn(stateStore); + final var expectedResult = Bytes.wrap(new byte[] {1, 2, 3, 4, 5}); + final var bytecode = Bytecode.newBuilder().code(expectedResult).build(); + given(stateStore.getBytecode(any())).willReturn(bytecode); + QueryHeader defaultHeader = QueryHeader.newBuilder().responseType(ANSWER_ONLY).build(); given(contractGetBytecodeQuery.headerOrElse(QueryHeader.DEFAULT)).willReturn(defaultHeader); @@ -414,12 +425,18 @@ void computeFeesAccountIdAsContractId() { @Test void findResponseAccountIdAsContractId() { givenAccountIdAsContractId(); + given(account.accountIdOrThrow()).willReturn(accountId); + given(context.createStore(ContractStateStore.class)).willReturn(stateStore); + final var expectedResult = Bytes.wrap(new byte[] {1, 2, 3, 4, 5}); + final var bytecode = Bytecode.newBuilder().code(expectedResult).build(); + given(stateStore.getBytecode(any())).willReturn(bytecode); + given(responseHeader.nodeTransactionPrecheckCode()).willReturn(OK); given(responseHeader.responseType()).willReturn(ANSWER_ONLY); - Bytes bytecode = Objects.requireNonNull( + Bytes resp = Objects.requireNonNull( subject.findResponse(context, responseHeader).contractGetBytecodeResponse()) .bytecode(); - assertThat(bytecode).isEqualTo(RedirectBytecodeUtils.accountProxyBytecodePjb(Address.ZERO)); + assertThat(resp).isEqualTo(expectedResult); } private void givenTokenIdAsContractId() { @@ -459,7 +476,7 @@ void findResponseTokenIdAsContractId() { Bytes bytecode = Objects.requireNonNull( subject.findResponse(context, responseHeader).contractGetBytecodeResponse()) .bytecode(); - assertThat(bytecode).isEqualTo(RedirectBytecodeUtils.tokenProxyBytecodePjb(Address.ZERO)); + assertThat(bytecode).isEqualTo(tuweniToPbjBytes(TokenEvmAccount.CODE)); } private void givenScheduleIdAsContractId() { @@ -501,6 +518,6 @@ void findResponseScheduleIdAsContractId() { Bytes bytecode = Objects.requireNonNull( subject.findResponse(context, responseHeader).contractGetBytecodeResponse()) .bytecode(); - assertThat(bytecode).isEqualTo(RedirectBytecodeUtils.scheduleProxyBytecodePjb(Address.ZERO)); + assertThat(bytecode).isEqualTo(tuweniToPbjBytes(ScheduleEvmAccount.CODE)); } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/DispatchingEvmFrameStateTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/DispatchingEvmFrameStateTest.java index ed050c04ebc3..31d139ee1749 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/DispatchingEvmFrameStateTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/DispatchingEvmFrameStateTest.java @@ -80,8 +80,6 @@ @ExtendWith(MockitoExtension.class) class DispatchingEvmFrameStateTest { - private static final int REDIRECT_CODE_FIXED_PREFIX_LEN = - "6080604052348015600f57600080fd5b506000610167905077618dc65e".length(); private static final int NUM_KV_SLOTS = 42; private static final long EXPIRY = 1_234_567L; private static final long ACCOUNT_NUM = 0x9abcdefabcdefbbbL; @@ -294,100 +292,10 @@ void getsZeroWordForEmptySlotValue() { @Test void getsExtantCode() { givenWellKnownBytecode(); - final var actualCode = subject.getCode(A_CONTRACT_ID); - assertEquals(pbjToTuweniBytes(SOME_PRETEND_CODE), actualCode); } - @Test - void interpolatesTokenCodeByAddress() { - final var actualCode = subject.getTokenRedirectCode(TOKEN_ADDRESS); - - assertEquals( - TOKEN_ADDRESS.toUnprefixedHexString(), - actualCode - .toUnprefixedHexString() - // EVM 20-byte address is 40 hex chars - .substring(REDIRECT_CODE_FIXED_PREFIX_LEN, REDIRECT_CODE_FIXED_PREFIX_LEN + 40)); - } - - @Test - void hashesInterpolatesTokenCode() { - final var code = subject.getTokenRedirectCode(TOKEN_ADDRESS); - final var expectedHash = Hash.hash(code); - - assertEquals(expectedHash, subject.getTokenRedirectCodeHash(TOKEN_ADDRESS)); - } - - @Test - void interpolatesAccountCodeByAddress() { - final var actualCode = subject.getAccountRedirectCode(LONG_ZERO_ADDRESS); - - assertEquals( - LONG_ZERO_ADDRESS.toUnprefixedHexString(), - actualCode - .toUnprefixedHexString() - // EVM 20-byte address is 40 hex chars - .substring(REDIRECT_CODE_FIXED_PREFIX_LEN, REDIRECT_CODE_FIXED_PREFIX_LEN + 40)); - } - - @Test - void hashesInterpolatesAccountCode() { - final var code = subject.getAccountRedirectCode(LONG_ZERO_ADDRESS); - final var expectedHash = Hash.hash(code); - - assertEquals(expectedHash, subject.getAccountRedirectCodeHash(LONG_ZERO_ADDRESS)); - } - - @Test - void interpolatesAccountCodeWhenAddressNull() { - final var actualCode = subject.getAccountRedirectCode(null); - - assertEquals(org.apache.tuweni.bytes.Bytes.EMPTY, actualCode); - } - - @Test - void hashesInterpolatesAccountCodeWhenNull() { - final var expectedHash = Hash.hash(org.apache.tuweni.bytes.Bytes.EMPTY); - - assertEquals(expectedHash, subject.getAccountRedirectCodeHash(null)); - } - - @Test - void interpolatesScheduleCodeByAddress() { - final var actualCode = subject.getScheduleRedirectCode(LONG_ZERO_ADDRESS); - - assertEquals( - LONG_ZERO_ADDRESS.toUnprefixedHexString(), - actualCode - .toUnprefixedHexString() - // EVM 20-byte address is 40 hex chars - .substring(REDIRECT_CODE_FIXED_PREFIX_LEN, REDIRECT_CODE_FIXED_PREFIX_LEN + 40)); - } - - @Test - void hashesInterpolatesScheduleCode() { - final var code = subject.getScheduleRedirectCode(LONG_ZERO_ADDRESS); - final var expectedHash = Hash.hash(code); - - assertEquals(expectedHash, subject.getScheduleRedirectCodeHash(LONG_ZERO_ADDRESS)); - } - - @Test - void interpolatesScheduleCodeWhenAddressNull() { - final var actualCode = subject.getScheduleRedirectCode(null); - - assertEquals(org.apache.tuweni.bytes.Bytes.EMPTY, actualCode); - } - - @Test - void hashesInterpolatesScheduleCodeWhenNull() { - final var expectedHash = Hash.hash(org.apache.tuweni.bytes.Bytes.EMPTY); - - assertEquals(expectedHash, subject.getScheduleRedirectCodeHash(null)); - } - @Test void getsEmptyCodeForMissing() { final var actualCode = subject.getCode(A_CONTRACT_ID); @@ -416,7 +324,6 @@ void getsExtantCodeHash() { @Test void getsEmptyCodeHashForMissing() { final var actualCodeHash = subject.getCodeHash(A_CONTRACT_ID, CODE_FACTORY); - assertSame(Hash.EMPTY, actualCodeHash); } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ProxyEvmAccountTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ProxyEvmAccountTest.java index a2737ac957fb..f47394030024 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ProxyEvmAccountTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ProxyEvmAccountTest.java @@ -1,21 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.test.state; -import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.hbarallowance.HbarAllowanceTranslator.HBAR_ALLOWANCE_PROXY; -import static com.hedera.node.app.service.contract.impl.test.TestHelpers.ACCOUNT_CALL_REDIRECT_CONTRACT_BINARY; -import static com.hedera.node.app.service.contract.impl.test.TestHelpers.ADDRESS_BYTECODE_PATTERN; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CODE_FACTORY; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.when; import com.hedera.hapi.node.base.AccountID; import com.hedera.node.app.service.contract.impl.state.DispatchingEvmFrameState; import com.hedera.node.app.service.contract.impl.state.ProxyEvmAccount; -import com.hedera.pbj.runtime.io.buffer.Bytes; -import org.hyperledger.besu.datatypes.Address; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -27,7 +18,6 @@ class ProxyEvmAccountTest { private static final long ACCOUNT_NUM = 0x9abcdefabcdefbbbL; private static final AccountID ACCOUNT_ID = AccountID.newBuilder().accountNum(ACCOUNT_NUM).build(); - private static final Bytes SOME_PRETEND_CODE = Bytes.wrap(""); @Mock private DispatchingEvmFrameState state; @@ -37,7 +27,7 @@ class ProxyEvmAccountTest { @BeforeEach void setUp() { - subject = new ProxyEvmAccount(ACCOUNT_ID, state); + subject = new ProxyEvmAccount(ACCOUNT_ID, state, CODE_FACTORY); } @Test @@ -49,61 +39,4 @@ void notTokenFacade() { void notScheduleTxnFacade() { assertFalse(subject.isScheduleTxnFacade()); } - - @Test - void returnsEvmCodeOfProxy() { - final var accountInHex = String.format("%040X", ACCOUNT_NUM); - final var expected = org.apache.tuweni.bytes.Bytes.fromHexString( - ACCOUNT_CALL_REDIRECT_CONTRACT_BINARY.replace(ADDRESS_BYTECODE_PATTERN, accountInHex)); - given(state.getAddress(ACCOUNT_ID)).willReturn(Address.fromHexString(accountInHex)); - given(state.getAccountRedirectCode(Address.fromHexString(accountInHex))).willCallRealMethod(); - - assertEquals( - CODE_FACTORY.createCode(expected, false), - subject.getEvmCode(org.apache.tuweni.bytes.Bytes.wrap(HBAR_ALLOWANCE_PROXY.selector()), CODE_FACTORY)); - } - - @Test - void returnsEvmCodeOfEmptyBytes() { - given(state.getAccountRedirectCode(null)).willCallRealMethod(); - - assertEquals( - CODE_FACTORY.createCode(org.apache.tuweni.bytes.Bytes.EMPTY, false), - subject.getEvmCode(org.apache.tuweni.bytes.Bytes.wrap(SOME_PRETEND_CODE.toByteArray()), CODE_FACTORY)); - } - - @Test - void returnsEvmCodeHashOfProxy() { - final var accountInHex = String.format("%040X", ACCOUNT_NUM); - final var expected = org.apache.tuweni.bytes.Bytes.fromHexString( - ACCOUNT_CALL_REDIRECT_CONTRACT_BINARY.replace(ADDRESS_BYTECODE_PATTERN, accountInHex)); - given(state.getAddress(ACCOUNT_ID)).willReturn(Address.fromHexString(accountInHex)); - given(state.getAccountRedirectCode(Address.fromHexString(accountInHex))).willCallRealMethod(); - - final var expectedHash = CODE_FACTORY.createCode(expected, false).getCodeHash(); - - when(state.getAccountRedirectCodeHash(Address.fromHexString(accountInHex))) - .thenReturn(expectedHash); - - subject.getEvmCode(org.apache.tuweni.bytes.Bytes.wrap(HBAR_ALLOWANCE_PROXY.selector()), CODE_FACTORY); - final var hash = subject.getCodeHash(); - - assertEquals(expectedHash, hash); - } - - @Test - void returnsEvmCodeHashOfEmptyBytes() { - given(state.getAccountRedirectCode(null)).willCallRealMethod(); - - final var expectedHash = CODE_FACTORY - .createCode(org.apache.tuweni.bytes.Bytes.EMPTY, false) - .getCodeHash(); - - when(state.getAccountRedirectCodeHash(any())).thenReturn(expectedHash); - - subject.getEvmCode(org.apache.tuweni.bytes.Bytes.wrap(SOME_PRETEND_CODE.toByteArray()), CODE_FACTORY); - final var hash = subject.getCodeHash(); - - assertEquals(expectedHash, hash); - } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ProxyEvmContractTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ProxyEvmContractTest.java index 3c212ea21bf0..9f592235a0e3 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ProxyEvmContractTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ProxyEvmContractTest.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.test.state; -import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.hbarallowance.HbarAllowanceTranslator.HBAR_ALLOWANCE_PROXY; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CODE_FACTORY; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.pbjToBesuHash; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.pbjToTuweniBytes; @@ -106,24 +105,6 @@ void returnsCode() { assertEquals(code, subject.getCode()); } - @Test - void returnsEvmCode() { - final var code = pbjToTuweniBytes(SOME_PRETEND_CODE); - given(hederaState.getCode(CONTRACT_ID)).willReturn(code); - assertEquals( - CODE_FACTORY.createCode(code, false), - subject.getEvmCode(org.apache.tuweni.bytes.Bytes.EMPTY, CODE_FACTORY)); - } - - @Test - void returnsEvmCodeButSetsState() { - final var code = pbjToTuweniBytes(SOME_PRETEND_CODE); - given(hederaState.getCode(CONTRACT_ID)).willReturn(code); - assertEquals( - CODE_FACTORY.createCode(code, false), - subject.getEvmCode(org.apache.tuweni.bytes.Bytes.wrap(HBAR_ALLOWANCE_PROXY.selector()), CODE_FACTORY)); - } - @Test void returnsCodeHash() { final var hash = pbjToBesuHash(SOME_PRETEND_CODE_HASH); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ScheduleEvmAccountTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ScheduleEvmAccountTest.java index 1bfc12d5b3ad..71d5182d4859 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ScheduleEvmAccountTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/ScheduleEvmAccountTest.java @@ -1,8 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.test.state; -import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.hbarallowance.HbarAllowanceTranslator.HBAR_ALLOWANCE_PROXY; -import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CODE_FACTORY; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.entityIdFactory; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.pbjToTuweniBytes; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -17,11 +15,10 @@ import com.hedera.node.app.service.contract.impl.state.ScheduleEvmAccount; import com.hedera.node.app.service.contract.impl.utils.ConversionUtils; import com.hedera.pbj.runtime.io.buffer.Bytes; -import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.worldstate.CodeDelegationHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -32,9 +29,6 @@ class ScheduleEvmAccountTest { private static final Address SCHEDULE_ADDRESS = Address.fromHexString("0000000000000000000000000000ffffffffffff"); private static final Bytes SOME_PRETEND_CODE = Bytes.wrap(""); - private static final Hash SOME_PRETEND_CODE_HASH = - Hash.wrap(Bytes32.wrap("".getBytes())); - private static final String SIGN_SCHEDULE_FUNCTION_SELECTOR = "06d15889"; @Mock private EvmFrameState state; @@ -79,29 +73,8 @@ void usesGivenAddress() { } @Test - void usesCodeFromState() { - final var code = pbjToTuweniBytes(SOME_PRETEND_CODE); - - given(state.getScheduleRedirectCode(SCHEDULE_ADDRESS)).willReturn(code); - - assertSame(code, subject.getCode()); - } - - @Test - void returnsEvmCode() { - final var code = pbjToTuweniBytes(SOME_PRETEND_CODE); - given(state.getScheduleRedirectCode(SCHEDULE_ADDRESS)).willReturn(code); - assertEquals( - CODE_FACTORY.createCode(code, false), - subject.getEvmCode( - org.apache.tuweni.bytes.Bytes.fromHexString(SIGN_SCHEDULE_FUNCTION_SELECTOR), CODE_FACTORY)); - } - - @Test - void usesHashFromState() { - given(state.getScheduleRedirectCodeHash(SCHEDULE_ADDRESS)).willReturn(SOME_PRETEND_CODE_HASH); - - assertSame(SOME_PRETEND_CODE_HASH, subject.getCodeHash()); + void returnsEip7702DelegationIndicatorCode() { + assertTrue(CodeDelegationHelper.hasCodeDelegation(subject.getCode())); } @Test @@ -139,21 +112,4 @@ void doesNotSupportMutators() { void neverRegularAccount() { assertFalse(subject.isRegularAccount()); } - - @Test - void returnEvmCodeWhenCalledWithExpectedFunctionSelectorBytes() { - final var code = pbjToTuweniBytes(SOME_PRETEND_CODE); - given(state.getScheduleRedirectCode(SCHEDULE_ADDRESS)).willReturn(code); - assertEquals( - CODE_FACTORY.createCode(code, false), - subject.getEvmCode( - org.apache.tuweni.bytes.Bytes.fromHexString(SIGN_SCHEDULE_FUNCTION_SELECTOR), CODE_FACTORY)); - } - - @Test - void returnEmptyEvmCodeWhenCalledWithUnexpectedFunctionSelectorBytes() { - assertEquals( - CODE_FACTORY.createCode(org.apache.tuweni.bytes.Bytes.EMPTY, false), - subject.getEvmCode(org.apache.tuweni.bytes.Bytes.wrap(HBAR_ALLOWANCE_PROXY.selector()), CODE_FACTORY)); - } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/TokenEvmAccountTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/TokenEvmAccountTest.java index d9366d3c309c..fad30640288e 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/TokenEvmAccountTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/TokenEvmAccountTest.java @@ -1,8 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 package com.hedera.node.app.service.contract.impl.test.state; -import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.has.hbarallowance.HbarAllowanceTranslator.HBAR_ALLOWANCE_PROXY; -import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CODE_FACTORY; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.entityIdFactory; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.pbjToTuweniBytes; import static org.junit.jupiter.api.Assertions.*; @@ -13,11 +11,10 @@ import com.hedera.node.app.service.contract.impl.state.TokenEvmAccount; import com.hedera.node.app.service.contract.impl.utils.ConversionUtils; import com.hedera.pbj.runtime.io.buffer.Bytes; -import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.worldstate.CodeDelegationHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -28,8 +25,6 @@ class TokenEvmAccountTest { private static final Address TOKEN_ADDRESS = Address.fromHexString("0000000000000000000000000000ffffffffffff"); private static final Bytes SOME_PRETEND_CODE = Bytes.wrap(""); - private static final Hash SOME_PRETEND_CODE_HASH = - Hash.wrap(Bytes32.wrap("".getBytes())); @Mock private EvmFrameState state; @@ -74,28 +69,8 @@ void usesGivenAddress() { } @Test - void usesCodeFromState() { - final var code = pbjToTuweniBytes(SOME_PRETEND_CODE); - - given(state.getTokenRedirectCode(TOKEN_ADDRESS)).willReturn(code); - - assertSame(code, subject.getCode()); - } - - @Test - void returnsEvmCode() { - final var code = pbjToTuweniBytes(SOME_PRETEND_CODE); - given(state.getTokenRedirectCode(TOKEN_ADDRESS)).willReturn(code); - assertEquals( - CODE_FACTORY.createCode(code, false), - subject.getEvmCode(org.apache.tuweni.bytes.Bytes.EMPTY, CODE_FACTORY)); - } - - @Test - void usesHashFromState() { - given(state.getTokenRedirectCodeHash(TOKEN_ADDRESS)).willReturn(SOME_PRETEND_CODE_HASH); - - assertSame(SOME_PRETEND_CODE_HASH, subject.getCodeHash()); + void returnsEip7702DelegationIndicatorCode() { + assertTrue(CodeDelegationHelper.hasCodeDelegation(subject.getCode())); } @Test @@ -133,13 +108,4 @@ void doesNotSupportMutators() { void neverRegularAccount() { assertFalse(subject.isRegularAccount()); } - - @Test - void returnEvmCodeWhenCalledWithExpectedFunctionSelectorBytes() { - final var code = pbjToTuweniBytes(SOME_PRETEND_CODE); - given(state.getTokenRedirectCode(TOKEN_ADDRESS)).willReturn(code); - assertEquals( - CODE_FACTORY.createCode(code, false), - subject.getEvmCode(org.apache.tuweni.bytes.Bytes.wrap(HBAR_ALLOWANCE_PROXY.selector()), CODE_FACTORY)); - } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/hooks/ProxyEvmHookTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/hooks/ProxyEvmHookTest.java index 78e8032ade3f..238b861bf406 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/hooks/ProxyEvmHookTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/state/hooks/ProxyEvmHookTest.java @@ -66,8 +66,7 @@ void coversAllPathsWithAccountOwnedHook() { final var subject = new ProxyEvmHook(state, hookState, CODE_FACTORY, entityIdFactory); - final var code = subject.getEvmCode(Bytes.wrap(new byte[] {1, 2, 3, 4}), CODE_FACTORY); - assertEquals(expectedCode, code); + assertEquals(expectedCode.getBytes(), subject.getCode()); assertEquals(hookContractCode, subject.getCode()); assertEquals(expectedHash, subject.getCodeHash()); assertEquals(HTS_HOOKS_CONTRACT_ADDRESS, subject.getAddress()); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/utils/RedirectBytecodeUtilsTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/utils/RedirectBytecodeUtilsTest.java deleted file mode 100644 index db22444d9edd..000000000000 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/utils/RedirectBytecodeUtilsTest.java +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package com.hedera.node.app.service.contract.impl.test.utils; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.hedera.node.app.hapi.utils.MiscCryptoUtils; -import com.hedera.node.app.service.contract.impl.utils.RedirectBytecodeUtils; -import java.util.function.Function; -import org.apache.tuweni.bytes.Bytes; -import org.hyperledger.besu.datatypes.Address; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -public class RedirectBytecodeUtilsTest { - - private void testProxyBytecodeFor(Function function, String signature) { - Bytes res = function.apply(Address.ZERO); - assertThat(res).isNotNull(); - assertThat(res.toFastHex(false)) - .startsWith(RedirectBytecodeUtils.PROXY_PRE_BYTES) - .contains(RedirectBytecodeUtils.PROXY_MID_BYTES) - .contains(Address.ZERO.toFastHex(false)) - .contains(Bytes.wrap(MiscCryptoUtils.keccak256DigestOf(signature.getBytes())) - .toFastHex(false) - .substring(0, 8)); - } - - @Test - void tokenProxyBytecodeFor() { - testProxyBytecodeFor(RedirectBytecodeUtils::tokenProxyBytecodeFor, "redirectForToken(address,bytes)"); - } - - @Test - void accountProxyBytecodeFor() { - testProxyBytecodeFor(RedirectBytecodeUtils::accountProxyBytecodeFor, "redirectForAccount(address,bytes)"); - } - - @Test - void scheduleProxyBytecodeFor() { - testProxyBytecodeFor(RedirectBytecodeUtils::scheduleProxyBytecodeFor, "redirectForScheduleTxn(address,bytes)"); - } - - private void testProxyBytecodePbj( - Function function, String signature) { - com.hedera.pbj.runtime.io.buffer.Bytes res = function.apply(Address.ZERO); - assertThat(res).isNotNull(); - assertThat(res.toHex()) - .startsWith(RedirectBytecodeUtils.PROXY_PRE_BYTES) - .contains(RedirectBytecodeUtils.PROXY_MID_BYTES) - .contains(Address.ZERO.toFastHex(false)) - .contains(Bytes.wrap(MiscCryptoUtils.keccak256DigestOf(signature.getBytes())) - .toFastHex(false) - .substring(0, 8)); - } - - @Test - void tokenProxyBytecodePjb() { - testProxyBytecodePbj(RedirectBytecodeUtils::tokenProxyBytecodePjb, "redirectForToken(address,bytes)"); - } - - @Test - void accountProxyBytecodePjb() { - testProxyBytecodePbj(RedirectBytecodeUtils::accountProxyBytecodePjb, "redirectForAccount(address,bytes)"); - } - - @Test - void scheduleProxyBytecodePjb() { - testProxyBytecodePbj(RedirectBytecodeUtils::scheduleProxyBytecodePjb, "redirectForScheduleTxn(address,bytes)"); - } -} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/ethereum/NonceSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/ethereum/NonceSuite.java index f244fb6eb371..c243267ea9f4 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/ethereum/NonceSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/ethereum/NonceSuite.java @@ -389,7 +389,7 @@ final Stream nonceUpdatedAfterEvmReversionDueInsufficientGas() { .signingWith(SECP_256K1_SOURCE_KEY) .payingWith(RELAYER) .nonce(0) - .gasLimit(21_064L) + .gasLimit(21_564L) .hasKnownStatus(INSUFFICIENT_GAS) .via(TX), getAliasedAccountInfo(SECP_256K1_SOURCE_KEY).has(accountWith().nonce(1L)), diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/ethereum/batch/AtomicEthereumSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/ethereum/batch/AtomicEthereumSuite.java index c33b544c20aa..464527d6a7b8 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/ethereum/batch/AtomicEthereumSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/ethereum/batch/AtomicEthereumSuite.java @@ -185,7 +185,7 @@ final Stream sendingLargerBalanceThanAvailableFailsGracefully() { .bytecode(TOKEN_CREATE_CONTRACT) .gasPrice(10L) .maxGasAllowance(ONE_HUNDRED_HBARS) - .gasLimit(5_000_000L) + .gasLimit(4_001_000L) .hasKnownStatusFrom(SUCCESS) .via("deployTokenCreateContract") .batchKey(BATCH_OPERATOR)) diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/evm/Evm38ValidationSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/evm/Evm38ValidationSuite.java index 8e3a0acd3b3b..e28a2332db32 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/evm/Evm38ValidationSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/evm/Evm38ValidationSuite.java @@ -34,10 +34,8 @@ import static com.hedera.services.bdd.suites.contract.Utils.captureOneChildCreate2MetaFor; import static com.hedera.services.bdd.suites.contract.Utils.getABIFor; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_DELETED; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_REVERT_EXECUTED; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_CONTRACT_ID; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_SOLIDITY_ADDRESS; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.LOCAL_CALL_MODIFICATION_EXCEPTION; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS; import static com.hederahashgraph.api.proto.java.TokenType.NON_FUNGIBLE_UNIQUE; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -120,13 +118,13 @@ final Stream cannotSendValueToTokenAccount() { .sending(ONE_HBAR) .payingWith(TOKEN_TREASURY) .via(internalViolation) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED)), + .hasKnownStatus(INVALID_CONTRACT_ID)), sourcing((() -> contractCall(tokenMirrorAddr.get()) .sending(1L) .payingWith(TOKEN_TREASURY) .refusingEthConversion() .via(externalViolation) - .hasKnownStatus(LOCAL_CALL_MODIFICATION_EXCEPTION))), + .hasKnownStatus(INVALID_CONTRACT_ID))), getTxnRecord(internalViolation).hasPriority(recordWith().feeGreaterThan(0L)), getTxnRecord(externalViolation).hasPriority(recordWith().feeGreaterThan(0L))); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/evm/batch/AtomicEvm38ValidationSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/evm/batch/AtomicEvm38ValidationSuite.java index 6cc17fd68ec5..22906d1cceb8 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/evm/batch/AtomicEvm38ValidationSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/evm/batch/AtomicEvm38ValidationSuite.java @@ -34,11 +34,9 @@ import static com.hedera.services.bdd.suites.contract.Utils.asHexedSolidityAddress; import static com.hedera.services.bdd.suites.contract.Utils.getABIFor; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_DELETED; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_REVERT_EXECUTED; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INNER_TRANSACTION_FAILED; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_CONTRACT_ID; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_SOLIDITY_ADDRESS; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.LOCAL_CALL_MODIFICATION_EXCEPTION; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS; import static com.hederahashgraph.api.proto.java.TokenType.NON_FUNGIBLE_UNIQUE; @@ -121,7 +119,7 @@ final Stream cannotSendValueToTokenAccount() { .sending(ONE_HBAR) .payingWith(TOKEN_TREASURY) .via(internalViolation) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED) + .hasKnownStatus(INVALID_CONTRACT_ID) .batchKey(BATCH_OPERATOR)) .payingWith(BATCH_OPERATOR) .hasKnownStatus(INNER_TRANSACTION_FAILED)), @@ -130,7 +128,7 @@ final Stream cannotSendValueToTokenAccount() { .payingWith(TOKEN_TREASURY) .refusingEthConversion() .via(externalViolation) - .hasKnownStatus(LOCAL_CALL_MODIFICATION_EXCEPTION) + .hasKnownStatus(INVALID_CONTRACT_ID) .batchKey(BATCH_OPERATOR)) .payingWith(BATCH_OPERATOR) .hasKnownStatus(INNER_TRANSACTION_FAILED))), diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCallSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCallSuite.java index a4ff53f01a7a..e43b18df0d70 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCallSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCallSuite.java @@ -2559,8 +2559,7 @@ final Stream lazyCreateInConstructor() { TRANSFER_TXN, SUCCESS, recordWith().status(SUCCESS).memo(LAZY_MEMO))); } - @HapiTest - final Stream contractIdResultsInPrecheckFail( + private Stream contractIdResultsInPrecheckFail( final String contractName, final java.util.function.Function contractId) { final var NAME = "name"; final var ERC_721_ABI = "ERC721ABI"; diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCreateSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCreateSuite.java index b4d7b41b62da..37257f3c6976 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCreateSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCreateSuite.java @@ -909,7 +909,7 @@ final Stream contractCreateGas() { .getTransactionRecord(txn) .getContractCreateResult() .getGasUsed(); - assertEquals(117661, gasUsed); + assertEquals(117683, gasUsed); })); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractDeleteSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractDeleteSuite.java index 2b424aa46b50..ef0f9969f1b7 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractDeleteSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractDeleteSuite.java @@ -43,10 +43,9 @@ import static com.hedera.services.bdd.suites.contract.Utils.asHexedSolidityAddress; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.ACCOUNT_IS_TREASURY; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_EXECUTION_EXCEPTION; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_REVERT_EXECUTED; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.FILE_DELETED; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_CONTRACT_ID; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_SIGNATURE; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.LOCAL_CALL_MODIFICATION_EXCEPTION; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.MAX_CHILD_RECORDS_EXCEEDED; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.MODIFYING_IMMUTABLE_CONTRACT; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.NOT_SUPPORTED; @@ -169,13 +168,13 @@ final Stream cannotSendValueToTokenAccount() { .sending(ONE_HBAR) .payingWith(TOKEN_TREASURY) .via(internalViolation) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED)), + .hasKnownStatus(INVALID_CONTRACT_ID)), sourcing((() -> contractCall(tokenMirrorAddr.get()) .sending(1L) .payingWith(TOKEN_TREASURY) .refusingEthConversion() .via(externalViolation) - .hasKnownStatus(LOCAL_CALL_MODIFICATION_EXCEPTION))), + .hasKnownStatus(INVALID_CONTRACT_ID))), getTxnRecord(internalViolation).hasPriority(recordWith().feeGreaterThan(0L)), getTxnRecord(externalViolation).hasPriority(recordWith().feeGreaterThan(0L))); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractGetBytecodeSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractGetBytecodeSuite.java index 4a57cdb4c2bd..1c14d8f51d8a 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractGetBytecodeSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractGetBytecodeSuite.java @@ -29,8 +29,8 @@ import static com.hedera.services.bdd.suites.contract.precompile.CreatePrecompileSuite.MEMO; import com.google.common.io.Files; -import com.hedera.node.app.service.contract.impl.utils.ConversionUtils; -import com.hedera.node.app.service.contract.impl.utils.RedirectBytecodeUtils; +import com.hedera.node.app.service.contract.impl.state.ScheduleEvmAccount; +import com.hedera.node.app.service.contract.impl.state.TokenEvmAccount; import com.hedera.services.bdd.junit.HapiTest; import com.hedera.services.bdd.spec.HapiSpecSetup; import com.hedera.services.bdd.spec.transactions.TxnUtils; @@ -40,13 +40,11 @@ import java.io.File; import java.util.Arrays; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; import java.util.stream.Stream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; import org.bouncycastle.util.encoders.Hex; -import org.hyperledger.besu.datatypes.Address; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Tag; @@ -122,17 +120,13 @@ final Stream invalidContractFromAnswerOnly() { .hasAnswerOnlyPrecheck(ResponseCodeEnum.INVALID_CONTRACT_ID)); } - private CustomSpecAssert getContractBytecodeRedirectBytecodeCheck( - String contract, String key, Function redirectBytecodeFunction) { + private CustomSpecAssert getContractBytecodeCheck(String contract, String key, Bytes expectedCode) { return withOpContext((spec, opLog) -> { final var getBytecode = getContractBytecode(contract).saveResultTo(key); allRunFor(spec, getBytecode); final var actualBytecode = spec.registry().getBytes(key); ContractID contractId = TxnUtils.asContractId(contract, spec); - final byte[] expectedBytecode = redirectBytecodeFunction - .apply(Address.wrap(Bytes.wrap(ConversionUtils.asEvmAddress(contractId.getContractNum())))) - .toArray(); - Assertions.assertArrayEquals(expectedBytecode, actualBytecode); + Assertions.assertArrayEquals(expectedCode.toArray(), actualBytecode); }); } @@ -142,7 +136,7 @@ final Stream getByteCodeWorksForNonContractAccount() { final var key = "accountByteCode"; return hapiTest( cryptoCreate(account).balance(ONE_HUNDRED_HBARS).asCallableContract(), - getContractBytecodeRedirectBytecodeCheck(account, key, RedirectBytecodeUtils::accountProxyBytecodeFor)); + getContractBytecodeCheck(account, key, Bytes.of())); } @HapiTest @@ -150,8 +144,7 @@ final Stream getByteCodeWorksForToken() { final var token = "fungibleToken"; final var key = "tokenByteCode"; return hapiTest( - tokenCreate(token).asCallableContract(), - getContractBytecodeRedirectBytecodeCheck(token, key, RedirectBytecodeUtils::tokenProxyBytecodeFor)); + tokenCreate(token).asCallableContract(), getContractBytecodeCheck(token, key, TokenEvmAccount.CODE)); } @HapiTest @@ -162,7 +155,6 @@ final Stream getByteCodeWorksForSchedule() { cryptoCreate("sender"), scheduleCreate(schedule, cryptoTransfer(tinyBarsFromTo("sender", GENESIS, 1))) .asCallableSchedule(), - getContractBytecodeRedirectBytecodeCheck( - schedule, key, RedirectBytecodeUtils::scheduleProxyBytecodeFor)); + getContractBytecodeCheck(schedule, key, ScheduleEvmAccount.CODE)); } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/batch/AtomicContractCreateSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/batch/AtomicContractCreateSuite.java index 8ce09b6a92a1..86a9c07ef5e0 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/batch/AtomicContractCreateSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/batch/AtomicContractCreateSuite.java @@ -846,7 +846,7 @@ final Stream contractCreateGas() { .getTransactionRecord(txn) .getContractCreateResult() .getGasUsed(); - assertEquals(117661, gasUsed); + assertEquals(117683, gasUsed); })); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opsduration/OpsDurationThrottleTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opsduration/OpsDurationThrottleTest.java index 588d80c1abe6..7ccd8730d080 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opsduration/OpsDurationThrottleTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opsduration/OpsDurationThrottleTest.java @@ -428,6 +428,7 @@ public Stream accountCreationConsumesOpsDuration() { final double throttlePercentUsed = getOpsDurationThrottlePercentUsed(spec); // We expect the throttle to be significantly overfilled with our test limits allRunFor(spec, valueIsInRange(throttlePercentUsed, 1000.0, 6000.0)); + restoreDefaults(spec); })); } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/AtomicCryptoTransferHTSSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/AtomicCryptoTransferHTSSuite.java index 2c9e040c8cbe..594488a3b0a3 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/AtomicCryptoTransferHTSSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/AtomicCryptoTransferHTSSuite.java @@ -1116,20 +1116,6 @@ final Stream cryptoTransferAllowanceToContractNFT() { allRunFor( spec, - // trying to transfer NFT that is not approved - contractCall( - CONTRACT, - TRANSFER_MULTIPLE_TOKENS, - transferList() - .withAccountAmounts(EMPTY_TUPLE_ARRAY) - .build(), - wrapIntoTupleArray(tokenTransferList() - .forToken(token) - .withNftTransfers(nftTransfer(owner, receiver, 1L, true)) - .build())) - .via(revertingTransferFromTxnNft) - .gas(GAS_FOR_AUTO_ASSOCIATING_CALLS) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), // transfer allowed NFT contractCall( CONTRACT, @@ -1144,25 +1130,7 @@ final Stream cryptoTransferAllowanceToContractNFT() { .via(successfulTransferFromTxn) .gas(GAS_FOR_AUTO_ASSOCIATING_CALLS) .hasKnownStatus(SUCCESS)); - }), - childRecordsCheck( - revertingTransferFromTxnNft, - CONTRACT_REVERT_EXECUTED, - recordWith() - .status(SPENDER_DOES_NOT_HAVE_ALLOWANCE) - .contractCallResult(resultWith() - .contractCallResult( - htsPrecompileResult().withStatus(SPENDER_DOES_NOT_HAVE_ALLOWANCE)))), - childRecordsCheck( - successfulTransferFromTxn, - SUCCESS, - recordWith() - .status(SUCCESS) - .contractCallResult(resultWith() - .contractCallResult( - htsPrecompileResult().withStatus(SUCCESS))) - .tokenTransfers(NonFungibleTransfers.changingNFTBalances() - .including(NFT_TOKEN, OWNER, RECEIVER, 2L)))); + })); } @HapiTest diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/PrngPrecompileSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/PrngPrecompileSuite.java index d7181def2a09..2e8f0b18b408 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/PrngPrecompileSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/PrngPrecompileSuite.java @@ -142,7 +142,7 @@ final Stream invalidLargeInputFails() { .getTransactionRecord(largeInputCall) .getContractCallResult() .getGasUsed(); - assertEquals(26_134L, gasUsed); + assertEquals(33_520L, gasUsed); })); } @@ -190,7 +190,7 @@ final Stream functionCallWithLessThanFourBytesFailsGracefully() { .getTransactionRecord(lessThan4Bytes) .getContractCallResult() .getGasUsed(); - assertEquals(21_118L, gasUsed); + assertEquals(21_120L, gasUsed); })); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/NumericValidationTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/NumericValidationTest.java index 1d35f08016ec..75a4087607c2 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/NumericValidationTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/NumericValidationTest.java @@ -486,10 +486,10 @@ public Stream failToApproveHbar() { // see also HbarAllowanceApprovalTest.hrc632ApproveFromEOA test return Stream.of( // java.lang.ArithmeticException: BigInteger out of long range - new UintTestCase(MAX_LONG_PLUS_1_BIG_INT, SUCCESS), + new UintTestCase(MAX_LONG_PLUS_1_BIG_INT, CONTRACT_REVERT_EXECUTED), // NEGATIVE_ALLOWANCE_AMOUNT - new UintTestCase(BigInteger.valueOf(-1), SUCCESS), - new UintTestCase(BigInteger.ZERO, SUCCESS)) + new UintTestCase(BigInteger.valueOf(-1), CONTRACT_REVERT_EXECUTED), + new UintTestCase(BigInteger.ZERO, CONTRACT_REVERT_EXECUTED)) .flatMap(testCase -> hapiTest(numericContract .call("hbarApproveProxy", owner, spender, testCase.amount()) .gas(1_000_000L) diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/traceability/TraceabilitySuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/traceability/TraceabilitySuite.java index a38f84a0fecd..ef0d8d7ccf76 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/traceability/TraceabilitySuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/traceability/TraceabilitySuite.java @@ -84,7 +84,6 @@ import static com.hederahashgraph.api.proto.java.TokenType.NON_FUNGIBLE_UNIQUE; import static java.util.Objects.requireNonNull; import static org.hiero.base.utility.CommonUtils.hex; -import static org.hyperledger.besu.crypto.Hash.keccak256; import com.esaulpaugh.headlong.abi.Address; import com.esaulpaugh.headlong.abi.Function; @@ -116,11 +115,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; import java.math.BigInteger; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; -import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; @@ -4553,11 +4550,11 @@ final Stream actionsShowPropagatedRevert() { somebodyElse))), serialNumberId)) .setGas(978120) - .setGasUsed(948098) + .setGasUsed(963025) .setRevertReason(ByteString.EMPTY) .build(), ContractAction.newBuilder() - .setCallType(CALL) + .setCallType(SYSTEM) .setCallOperationType(CallOperationType.OP_DELEGATECALL) .setCallingContract( spec.registry().getContractId(APPROVE_BY_DELEGATE)) @@ -4567,8 +4564,8 @@ final Stream actionsShowPropagatedRevert() { spec.registry() .getTokenID(tokenInQuestion) .getTokenNum())) - .setGas(958481) - .setGasUsed(943594) + .setGas(955921) + .setGasUsed(955921) .setInput( ByteStringUtils.wrapUnsafely( Function.parse("approve(address,uint256)") @@ -4580,56 +4577,8 @@ final Stream actionsShowPropagatedRevert() { somebodyElse))), serialNumberId) .array())) - .setRevertReason(ByteString.EMPTY) - .setCallDepth(1) - .build(), - ContractAction.newBuilder() - .setCallType(SYSTEM) - .setCallOperationType(CallOperationType.OP_DELEGATECALL) - .setCallingContract( - spec.contractIdFactory() - .apply( - spec.registry() - .getTokenID(tokenInQuestion) - .getTokenNum())) - .setRecipientContract( - spec.contractIdFactory() - .apply(359L)) - .setGas(940841) - .setGasUsed(940841) - .setInput( - ByteStringUtils.wrapUnsafely( - ArrayUtils.addAll( - ArrayUtils.addAll( - Arrays.copyOfRange( - keccak256( - Bytes.of( - "redirectForToken(address,bytes)" - .getBytes())) - .toArrayUnsafe(), - 0, - 4), - Arrays.copyOfRange( - encodeTuple( - "(address)", - hexedSolidityAddressToHeadlongAddress( - asHexedSolidityAddress( - spec.registry() - .getTokenID( - tokenInQuestion)))), - 12, - 32)), - Function.parse("approve(address,uint256)") - .encodeCallWithArgs( - hexedSolidityAddressToHeadlongAddress( - asHexedSolidityAddress( - spec.registry() - .getAccountID( - somebodyElse))), - serialNumberId) - .array()))) .setError(ByteString.copyFrom("PRECOMPILE_ERROR".getBytes())) - .setCallDepth(2) + .setCallDepth(1) .build()))); })); }