From 5e5578c48d67a3b5d30cae2dd3c54f5c980d0df5 Mon Sep 17 00:00:00 2001 From: Stanimir Stoyanov Date: Fri, 31 Oct 2025 18:49:29 +0200 Subject: [PATCH 1/2] fix: skip finalization if contract create halted Signed-off-by: Stanimir Stoyanov --- .../impl/exec/tracers/EvmActionTracer.java | 6 +- .../contract/hapi/ContractCreateSuite.java | 17 +++ .../HushSenseManager/HushSenseManager.bin | 1 + .../HushSenseManager/HushSenseManager.json | 122 ++++++++++++++++++ .../HushSenseManager/HushSenseManager.sol | 73 +++++++++++ 5 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.bin create mode 100644 hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.json create mode 100644 hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.sol diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/tracers/EvmActionTracer.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/tracers/EvmActionTracer.java index 05b27033318a..8690dbd6c31b 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/tracers/EvmActionTracer.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/tracers/EvmActionTracer.java @@ -7,6 +7,7 @@ import static java.util.Objects.requireNonNull; import static org.hyperledger.besu.evm.frame.MessageFrame.State.CODE_EXECUTING; import static org.hyperledger.besu.evm.frame.MessageFrame.State.CODE_SUSPENDED; +import static org.hyperledger.besu.evm.frame.MessageFrame.Type.CONTRACT_CREATION; import com.hedera.hapi.streams.ContractAction; import com.hedera.hapi.streams.ContractActionType; @@ -97,7 +98,10 @@ public void traceAccountCreationResult( // reason is present, since that means creation failed before executing the frame's // code, and tracePostExecution() will never be called; so this is our only chance // to keep the action stack in sync with the message frame stack. - if (hasActionSidecarsEnabled(frame) && haltReason.isPresent()) { + // We skip finalizing for contract creation frames, as those produce empty action stack warnings. + if (hasActionSidecarsEnabled(frame) + && haltReason.isPresent() + && !frame.getType().equals(CONTRACT_CREATION)) { actionStack.finalizeLastAction(frame, stackValidationChoice(frame)); } } 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 10805fd998cb..211a481f6d03 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 @@ -44,6 +44,7 @@ import static com.hedera.services.bdd.spec.utilops.CustomSpecAssert.allRunFor; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.assertCreationMaxAssociations; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.assertCreationViaCallMaxAssociations; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.assertHgcaaLogDoesNotContain; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.contractListWithPropertiesInheritedFrom; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.getEcdsaPrivateKeyFromSpec; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.newKeyListNamed; @@ -95,6 +96,7 @@ import com.hedera.node.app.hapi.utils.ethereum.EthTxData; import com.hedera.services.bdd.junit.HapiTest; import com.hedera.services.bdd.junit.LeakyHapiTest; +import com.hedera.services.bdd.junit.hedera.NodeSelector; import com.hedera.services.bdd.spec.HapiSpec; import com.hedera.services.bdd.spec.assertions.ContractInfoAsserts; import com.hedera.services.bdd.spec.keys.KeyShape; @@ -114,6 +116,7 @@ import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -913,6 +916,20 @@ final Stream contractCreateGas() { })); } + // Reproducing issue stated in #21821 + @HapiTest + Stream contractDeployFailsDuringExecution() { + return hapiTest( + uploadInitCode("HushSenseManager"), + // fails during execution due to insufficient gas + contractCreate("HushSenseManager").gas(200_000L).hasKnownStatus(ResponseCodeEnum.INSUFFICIENT_GAS), + // Ensure we don't log for empty action stack anymore + assertHgcaaLogDoesNotContain( + NodeSelector.allNodes(), "Action stack prematurely empty", Duration.ofSeconds(10)), + // succeeds with enough gas + contractCreate("HushSenseManager").gas(1_000_000L).hasKnownStatus(ResponseCodeEnum.SUCCESS)); + } + private TransactionRecord getRecord(HapiSpec spec, String txn, ResponseCodeEnum status) { final var hapiGetRecord = getTxnRecord(txn); allRunFor(spec, hapiGetRecord); diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.bin b/hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.bin new file mode 100644 index 000000000000..f06a879ad33a --- /dev/null +++ b/hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.bin @@ -0,0 +1 @@ +608060405234801561000f575f80fd5b5061002c61002161003160201b60201c565b61003860201b60201c565b6100f9565b5f33905090565b5f805f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050815f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b610d8a806101065f395ff3fe608060405234801561000f575f80fd5b5060043610610060575f3560e01c8063715018a6146100645780637e6b8afd1461006e5780638da5cb5b1461008c578063c4d66de8146100aa578063e16151b4146100c6578063f2fde38b146100e2575b5f80fd5b61006c6100fe565b005b610076610111565b6040516100839190610755565b60405180910390f35b610094610136565b6040516100a19190610755565b60405180910390f35b6100c460048036038101906100bf919061079c565b61015d565b005b6100e060048036038101906100db91906107fd565b610237565b005b6100fc60048036038101906100f7919061079c565b61054e565b005b6101066105d0565b61010f5f61064e565b565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f805f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6101656105d0565b5f73ffffffffffffffffffffffffffffffffffffffff1660015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146101f4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101eb90610895565b60405180910390fd5b8060015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61023f6105d0565b5f73ffffffffffffffffffffffffffffffffffffffff1660015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16036102ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102c5906108fd565b60405180910390fd5b5f61016790505f8173ffffffffffffffffffffffffffffffffffffffff166349146bde8560015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518363ffffffff1660e01b815260040161033192919061091b565b6020604051808303815f875af115801561034d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103719190610975565b905060168114158015610385575060678114155b156103c5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103bc906109ea565b60405180910390fd5b5f8273ffffffffffffffffffffffffffffffffffffffff1663e0f4059a60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16865f67ffffffffffffffff81111561041f5761041e610a08565b5b60405190808252806020026020018201604052801561045257816020015b606081526020019060019003908161043d5790505b506040518463ffffffff1660e01b815260040161047193929190610b89565b6020604051808303815f875af115801561048d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104b19190610975565b9050601681146104f6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104ed90610c0f565b60405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff167fe8ea3d4dd0a2eaaf3f7532ad391255544f8a4bcf78f850bbff61d5bac9f775528560070b60405161053f9190610c45565b60405180910390a25050505050565b6105566105d0565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036105c4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105bb90610cce565b60405180910390fd5b6105cd8161064e565b50565b6105d861070f565b73ffffffffffffffffffffffffffffffffffffffff166105f6610136565b73ffffffffffffffffffffffffffffffffffffffff161461064c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161064390610d36565b60405180910390fd5b565b5f805f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050815f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b5f33905090565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61073f82610716565b9050919050565b61074f81610735565b82525050565b5f6020820190506107685f830184610746565b92915050565b5f80fd5b61077b81610735565b8114610785575f80fd5b50565b5f8135905061079681610772565b92915050565b5f602082840312156107b1576107b061076e565b5b5f6107be84828501610788565b91505092915050565b5f8160070b9050919050565b6107dc816107c7565b81146107e6575f80fd5b50565b5f813590506107f7816107d3565b92915050565b5f80604083850312156108135761081261076e565b5b5f61082085828601610788565b9250506020610831858286016107e9565b9150509250929050565b5f82825260208201905092915050565b7f436f6e747261637420616c726561647920696e697469616c697a6564000000005f82015250565b5f61087f601c8361083b565b915061088a8261084b565b602082019050919050565b5f6020820190508181035f8301526108ac81610873565b9050919050565b7f48545320746f6b656e206e6f7420696e697469616c697a6564000000000000005f82015250565b5f6108e760198361083b565b91506108f2826108b3565b602082019050919050565b5f6020820190508181035f830152610914816108db565b9050919050565b5f60408201905061092e5f830185610746565b61093b6020830184610746565b9392505050565b5f819050919050565b61095481610942565b811461095e575f80fd5b50565b5f8151905061096f8161094b565b92915050565b5f6020828403121561098a5761098961076e565b5b5f61099784828501610961565b91505092915050565b7f4854533a20546f6b656e206173736f63696174696f6e206661696c65640000005f82015250565b5f6109d4601d8361083b565b91506109df826109a0565b602082019050919050565b5f6020820190508181035f830152610a01816109c8565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610a3e816107c7565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610aa4578082015181840152602081019050610a89565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610ac982610a6d565b610ad38185610a77565b9350610ae3818560208601610a87565b610aec81610aaf565b840191505092915050565b5f610b028383610abf565b905092915050565b5f602082019050919050565b5f610b2082610a44565b610b2a8185610a4e565b935083602082028501610b3c85610a5e565b805f5b85811015610b775784840389528151610b588582610af7565b9450610b6383610b0a565b925060208a01995050600181019050610b3f565b50829750879550505050505092915050565b5f606082019050610b9c5f830186610746565b610ba96020830185610a35565b8181036040830152610bbb8184610b16565b9050949350505050565b7f4854533a204d696e74206661696c6564000000000000000000000000000000005f82015250565b5f610bf960108361083b565b9150610c0482610bc5565b602082019050919050565b5f6020820190508181035f830152610c2681610bed565b9050919050565b5f819050919050565b610c3f81610c2d565b82525050565b5f602082019050610c585f830184610c36565b92915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f20615f8201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b5f610cb860268361083b565b9150610cc382610c5e565b604082019050919050565b5f6020820190508181035f830152610ce581610cac565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65725f82015250565b5f610d2060208361083b565b9150610d2b82610cec565b602082019050919050565b5f6020820190508181035f830152610d4d81610d14565b905091905056fea2646970667358221220990e341bb8b823bd3d0ea8af0a8942d763f3c05240b5295052b5d7c26f748a2f64736f6c63430008140033 \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.json b/hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.json new file mode 100644 index 000000000000..3c23142ebaed --- /dev/null +++ b/hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.json @@ -0,0 +1,122 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "RewardMinted", + "type": "event" + }, + { + "inputs": [], + "name": "htsTokenId", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_htsTokenId", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "int64", + "name": "amount", + "type": "int64" + } + ], + "name": "mintReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.sol b/hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.sol new file mode 100644 index 000000000000..90324b945130 --- /dev/null +++ b/hedera-node/test-clients/src/main/resources/contract/contracts/HushSenseManager/HushSenseManager.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "./Ownable.sol"; + +interface IHederaTokenService { + function associateToken(address account, address token) external returns (int); + function mintToken(address token, int64 amount, bytes[] calldata metadata) external returns (int); +} + +library HederaResponseCodes { + // Minimal subset of response codes used by this contract. + int public constant SUCCESS = 22; + int public constant TOKEN_ALREADY_ASSOCIATED_TO_ACCOUNT = 103; +} + +/** + * @title HushSense Token Manager + * @notice This contract MANAGES a native Hedera HTS token. + * It is NOT an ERC-20 token itself. + * It is given the Supply Key of the HTS token so it can mint rewards. + */ +contract HushSenseManager is Ownable { + + /// @notice The address of the HTS token this contract manages + address public htsTokenId; + + /// @notice Emitted when tokens are minted as rewards + event RewardMinted(address indexed to, uint256 amount); + + constructor() { + // Contract is deployed with you as the owner + } + + /** + * @notice Links this contract to the HTS token it will manage. + * This can only be called ONCE by the owner. + * @param _htsTokenId The address (Token ID) of the HTS token. + */ + function initialize(address _htsTokenId) external onlyOwner { + require(htsTokenId == address(0), "Contract already initialized"); + htsTokenId = _htsTokenId; + } + + /** + * @notice Allows the owner (your backend) to mint new tokens as rewards. + * This function calls the native HTS precompile. + * @param to The recipient's EVM address. + * @param amount The number of tokens to mint (using 0 decimals, as defined). + */ + function mintReward(address to, int64 amount) external onlyOwner { + require(htsTokenId != address(0), "HTS token not initialized"); + + IHederaTokenService hts = IHederaTokenService(address(0x0000000000000000000000000000000000000167)); + + // 1. Associate the user with the token if they aren't already + // This is a "best-effort" call and is safe to run even if already associated. + // This requires the RECIPIENT to have 'automatic token associations' enabled + int associationResponse = hts.associateToken(to, htsTokenId); + if (associationResponse != HederaResponseCodes.SUCCESS && + associationResponse != HederaResponseCodes.TOKEN_ALREADY_ASSOCIATED_TO_ACCOUNT) { + revert("HTS: Token association failed"); + } + + // 2. Mint the tokens + int response = hts.mintToken(htsTokenId, amount, new bytes[](0)); + if (response != HederaResponseCodes.SUCCESS) { + revert("HTS: Mint failed"); + } + + emit RewardMinted(to, uint256(int256(amount))); + } +} \ No newline at end of file From 47903705b7603761663131f13b05e82918d53f9f Mon Sep 17 00:00:00 2001 From: Stanimir Stoyanov Date: Mon, 10 Nov 2025 15:36:27 +0200 Subject: [PATCH 2/2] fix: skip finalization if empty action stack Signed-off-by: Stanimir Stoyanov --- .../contract/impl/exec/tracers/EvmActionTracer.java | 7 ++----- .../contract/impl/exec/utils/ActionStack.java | 4 ++++ .../impl/test/exec/tracers/EvmActionTracerTest.java | 10 ++++++++++ .../impl/test/exec/utils/ActionStackTest.java | 13 +++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/tracers/EvmActionTracer.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/tracers/EvmActionTracer.java index 8690dbd6c31b..8299f655de78 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/tracers/EvmActionTracer.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/tracers/EvmActionTracer.java @@ -7,7 +7,6 @@ import static java.util.Objects.requireNonNull; import static org.hyperledger.besu.evm.frame.MessageFrame.State.CODE_EXECUTING; import static org.hyperledger.besu.evm.frame.MessageFrame.State.CODE_SUSPENDED; -import static org.hyperledger.besu.evm.frame.MessageFrame.Type.CONTRACT_CREATION; import com.hedera.hapi.streams.ContractAction; import com.hedera.hapi.streams.ContractActionType; @@ -98,10 +97,8 @@ public void traceAccountCreationResult( // reason is present, since that means creation failed before executing the frame's // code, and tracePostExecution() will never be called; so this is our only chance // to keep the action stack in sync with the message frame stack. - // We skip finalizing for contract creation frames, as those produce empty action stack warnings. - if (hasActionSidecarsEnabled(frame) - && haltReason.isPresent() - && !frame.getType().equals(CONTRACT_CREATION)) { + // We skip finalizing for empty action stack, as those produce warnings. + if (hasActionSidecarsEnabled(frame) && haltReason.isPresent() && !actionStack.isEmpty()) { actionStack.finalizeLastAction(frame, stackValidationChoice(frame)); } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/ActionStack.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/ActionStack.java index c10ca859d6c8..5b165f8c7f00 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/ActionStack.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/ActionStack.java @@ -266,6 +266,10 @@ public void pushActionOfIntermediate(@NonNull final MessageFrame frame) { completePush(builder, requireNonNull(frame.getMessageFrameStack().peek())); } + public boolean isEmpty() { + return actionsStack.isEmpty(); + } + private void completePush(@NonNull ContractAction.Builder builder, @NonNull final MessageFrame frame) { builder.callType(asActionType(frame.getType())) .gas(frame.getRemainingGas()) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/tracers/EvmActionTracerTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/tracers/EvmActionTracerTest.java index 21e830eb164b..0e74aab2b737 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/tracers/EvmActionTracerTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/tracers/EvmActionTracerTest.java @@ -5,6 +5,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -178,6 +179,15 @@ void accountCreationTraceFinalizesWithSidecarsAndHaltReason() { verify(actionStack).finalizeLastAction(frame, ActionStack.Validation.ON); } + @Test + void contractCreationTraceFinalizesWithSidecarsAndHaltReason() { + givenSidecarsOnly(); + given(actionStack.isEmpty()).willReturn(true); + subject.traceAccountCreationResult(frame, Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS)); + + verify(actionStack, never()).finalizeLastAction(frame, ActionStack.Validation.ON); + } + private void givenNoActionSidecars() { givenConfig(false, false); } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/utils/ActionStackTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/utils/ActionStackTest.java index 8cfe55a02951..2789e44c58a4 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/utils/ActionStackTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/utils/ActionStackTest.java @@ -36,6 +36,7 @@ import static org.hyperledger.besu.evm.frame.MessageFrame.Type.CONTRACT_CREATION; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -606,6 +607,18 @@ void tracksIntermediateCallAsExpected() { assertEquals(NON_SYSTEM_CONTRACT_ID, action.callingContract()); } + @Test + void testIsEmptyWithEmptyStack() { + assertTrue(subject.isEmpty()); + } + + @Test + void testIsEmptyWithNonEmptyStack() { + final var wrappedAction = new ActionWrapper(CALL_ACTION); + actionsStack.push(wrappedAction); + assertFalse(subject.isEmpty()); + } + private void givenResolvableEvmAddress() { given(parentFrame.getWorldUpdater()).willReturn(worldUpdater); given(worldUpdater.getHederaAccount(EIP_1014_ADDRESS)).willReturn(evmAccount);