From 790bc4715d47e5d072eb69ca5d00f48e276b0a5d Mon Sep 17 00:00:00 2001 From: Nikita Lebedev Date: Fri, 10 Nov 2023 09:03:48 +0200 Subject: [PATCH] Fix ApproveAllowanceSuite tests (#9649) Signed-off-by: Miroslav Gatsanoga Signed-off-by: Michael Tinker Signed-off-by: Nikita Lebedev Co-authored-by: Miroslav Gatsanoga Co-authored-by: Michael Tinker --- .../src/xtest/java/common/AbstractXTest.java | 24 ++ .../java/contract/AbstractContractXTest.java | 27 +- .../xtest/java/contract/AssortedOpsXTest.java | 4 +- .../contract/AssortedOpsXTestConstants.java | 5 +- .../src/xtest/java/contract/BurnsXTest.java | 3 +- .../java/contract/ClassicViewsXTest.java | 2 +- .../java/contract/ContractLimitsXTest.java | 6 +- .../src/xtest/java/contract/Erc721XTest.java | 2 +- .../java/contract/Erc721XTestConstants.java | 1 - .../src/xtest/java/contract/FuseXTest.java | 6 +- .../xtest/java/contract/MiscViewsXTest.java | 2 +- .../java/contract/TestApproverXTest.java | 2 +- .../xtest/java/contract/XTestConstants.java | 95 +++---- .../approvals/ApproveAllowanceXTest.java | 179 ++++++++++++++ .../ApproveAllowanceXTestConstants.java | 47 ++++ .../initcode/HtsApproveAllowance.bin | 1 + .../systemcontracts/HtsSystemContract.java | 2 +- .../systemcontracts/hts/AbiConstants.java | 232 ++++++++++++++++++ .../exec/systemcontracts/hts/HtsCall.java | 6 + .../exec/systemcontracts/hts/LogBuilder.java | 123 ++++++++++ .../ClassicGrantApprovalCall.java | 45 ++++ .../IsApprovedForAllCall.java | 22 +- .../IsApprovedForAllTranslator.java | 45 +++- .../setapproval/SetApprovalForAllCall.java | 41 +++- .../HtsSystemContractTest.java | 6 +- .../hts/HtsCallAttemptTest.java | 24 +- .../systemcontracts/hts/HtsCallTestBase.java | 4 + .../ClassicGrantApprovalCallTest.java | 4 +- .../isoperator/IsApprovedForAllCallTest.java | 58 +++-- .../SetApprovalForAllCallTest.java | 19 +- .../precompile/ApproveAllowanceSuite.java | 6 + 31 files changed, 933 insertions(+), 110 deletions(-) create mode 100644 hedera-node/hedera-app/src/xtest/java/contract/approvals/ApproveAllowanceXTest.java create mode 100644 hedera-node/hedera-app/src/xtest/java/contract/approvals/ApproveAllowanceXTestConstants.java create mode 100644 hedera-node/hedera-app/src/xtest/resources/initcode/HtsApproveAllowance.bin create mode 100644 hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/AbiConstants.java create mode 100644 hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/LogBuilder.java diff --git a/hedera-node/hedera-app/src/xtest/java/common/AbstractXTest.java b/hedera-node/hedera-app/src/xtest/java/common/AbstractXTest.java index d83e98a684b0..55f417edee69 100644 --- a/hedera-node/hedera-app/src/xtest/java/common/AbstractXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/common/AbstractXTest.java @@ -281,6 +281,30 @@ protected Bytes resourceAsBytes(@NonNull final String loc) { } } + protected void addUsableRelation( + @NonNull final Map tokenRels, + @NonNull final AccountID accountID, + @NonNull final TokenID tokenID, + @NonNull final Consumer spec) { + final var rel = + TokenRelation.newBuilder().accountId(accountID).tokenId(tokenID).kycGranted(true); + spec.accept(rel); + tokenRels.put( + EntityIDPair.newBuilder().tokenId(tokenID).accountId(accountID).build(), rel.build()); + } + + protected void addNft( + @NonNull final Map nfts, + @NonNull final TokenID tokenID, + final long serialNo, + @NonNull final Consumer spec) { + final var nftId = + NftID.newBuilder().tokenId(tokenID).serialNumber(serialNo).build(); + final var nftBuilder = Nft.newBuilder().metadata(Bytes.wrap("0.0." + tokenID.tokenNum() + "." + serialNo)); + spec.accept(nftBuilder); + nfts.put(nftId, nftBuilder.build()); + } + protected void assertExpectedStorage( @NonNull ReadableKVState storage, @NonNull ReadableKVState accounts) {} diff --git a/hedera-node/hedera-app/src/xtest/java/contract/AbstractContractXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/AbstractContractXTest.java index fe888cc7f825..d02978c0d3ff 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/AbstractContractXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/AbstractContractXTest.java @@ -16,6 +16,7 @@ package contract; +import static com.hedera.node.app.service.contract.impl.ContractServiceImpl.CONTRACT_SERVICE; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.CONFIG_CONTEXT_VARIABLE; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.SYSTEM_CONTRACT_GAS_GAS_CALCULATOR_VARIABLE; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asLongZeroAddress; @@ -50,6 +51,8 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallFactory; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.SyntheticIds; +import com.hedera.node.app.service.contract.impl.handlers.ContractCallHandler; +import com.hedera.node.app.service.contract.impl.handlers.ContractCreateHandler; import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater; import com.hedera.node.app.service.contract.impl.state.ProxyWorldUpdater; import com.hedera.node.app.spi.workflows.TransactionHandler; @@ -82,8 +85,8 @@ public abstract class AbstractContractXTest extends AbstractXTest { private static final SyntheticIds LIVE_SYNTHETIC_IDS = new SyntheticIds(); private static final VerificationStrategies LIVE_VERIFICATION_STRATEGIES = new VerificationStrategies(); - static final long GAS_TO_OFFER = 2_000_000L; - static final Duration STANDARD_AUTO_RENEW_PERIOD = new Duration(7776000L); + protected static final long GAS_TO_OFFER = 2_000_000L; + protected static final Duration STANDARD_AUTO_RENEW_PERIOD = new Duration(7776000L); @Mock private MessageFrame frame; @@ -99,7 +102,7 @@ public abstract class AbstractContractXTest extends AbstractXTest { private HtsCallFactory callAttemptFactory; - private ContractScaffoldingComponent component; + protected ContractScaffoldingComponent component; @BeforeEach void setUp() { @@ -182,6 +185,14 @@ protected void runHtsCallAndExpectRevert( internalRunHtsCallAndExpectRevert(sender, input, status, null); } + protected ContractCreateHandler createHandler() { + return CONTRACT_SERVICE.handlers().contractCreateHandler(); + } + + protected ContractCallHandler callHandler() { + return CONTRACT_SERVICE.handlers().contractCallHandler(); + } + protected void runHtsCallAndExpectRevert( @NonNull final org.hyperledger.besu.datatypes.Address sender, @NonNull final org.apache.tuweni.bytes.Bytes input, @@ -255,7 +266,7 @@ private void runHtsCallAndExpect( final var call = callAttemptFactory.createCallFrom(input, frame); - final var pricedResult = call.execute(); + final var pricedResult = call.execute(frame); resultAssertions.accept(pricedResult); // Note that committing a reverted calls should have no effect on state ((SavepointStackImpl) context.savepointStack()).commitFullStack(); @@ -313,6 +324,14 @@ private Consumer resultOnlyAssertion( }; } + public static com.esaulpaugh.headlong.abi.Address asLongZeroHeadlongAddress(final AccountID accountID) { + return Address.wrap(Address.toChecksumAddress(BigInteger.valueOf(accountID.accountNumOrThrow()))); + } + + public static com.esaulpaugh.headlong.abi.Address asLongZeroHeadlongAddress(final TokenID tokenID) { + return Address.wrap(Address.toChecksumAddress(BigInteger.valueOf(tokenID.tokenNum()))); + } + public static com.esaulpaugh.headlong.abi.Address asHeadlongAddress(final byte[] address) { final var addressBytes = org.apache.tuweni.bytes.Bytes.wrap(address); final var addressAsInteger = addressBytes.toUnsignedBigInteger(); diff --git a/hedera-node/hedera-app/src/xtest/java/contract/AssortedOpsXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/AssortedOpsXTest.java index 102573357efd..04ddb97e6d40 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/AssortedOpsXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/AssortedOpsXTest.java @@ -32,17 +32,17 @@ import static contract.AssortedOpsXTestConstants.FINALIZED_AND_DESTRUCTED_CONTRACT_ID; import static contract.AssortedOpsXTestConstants.FINALIZED_AND_DESTRUCTED_ID; import static contract.AssortedOpsXTestConstants.NEXT_ENTITY_NUM; -import static contract.AssortedOpsXTestConstants.ONE_HBAR; import static contract.AssortedOpsXTestConstants.POINTLESS_INTERMEDIARY_ADDRESS; import static contract.AssortedOpsXTestConstants.POINTLESS_INTERMEDIARY_ID; import static contract.AssortedOpsXTestConstants.RELAYER_ID; import static contract.AssortedOpsXTestConstants.RUBE_GOLDBERG_CHILD_ID; import static contract.AssortedOpsXTestConstants.SALT; -import static contract.AssortedOpsXTestConstants.SENDER_ALIAS; import static contract.AssortedOpsXTestConstants.TAKE_FIVE; import static contract.AssortedOpsXTestConstants.VACATE_ADDRESS; import static contract.XTestConstants.MISC_PAYER_ID; +import static contract.XTestConstants.ONE_HBAR; import static contract.XTestConstants.SENDER_ADDRESS; +import static contract.XTestConstants.SENDER_ALIAS; import static contract.XTestConstants.SENDER_ID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/hedera-node/hedera-app/src/xtest/java/contract/AssortedOpsXTestConstants.java b/hedera-node/hedera-app/src/xtest/java/contract/AssortedOpsXTestConstants.java index 0227ff8046da..cca7c14dda82 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/AssortedOpsXTestConstants.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/AssortedOpsXTestConstants.java @@ -29,11 +29,8 @@ */ public class AssortedOpsXTestConstants { static final long NEXT_ENTITY_NUM = 1004L; - static final long ONE_HBAR = 100_000_000L; static final long EXPECTED_ASSORTED_OPS_NONCE = 4; - static final long EXPECTED_ASSORTED_OPS_BALANCE = 2 * ONE_HBAR - 5; - static final Bytes SENDER_ALIAS = - Bytes.fromHex("3a21030edcc130e13fb5102e7c883535af8c2b0a5a617231f77fd127ce5f3b9a620591"); + static final long EXPECTED_ASSORTED_OPS_BALANCE = 2 * XTestConstants.ONE_HBAR - 5; static final Bytes POINTLESS_INTERMEDIARY_ADDRESS = Bytes.fromHex("f9f3aa959ec3a248f8ff8ea1602e6714ae9cc4e3"); static final Bytes DETERMINISTIC_CHILD_ADDRESS = Bytes.fromHex("fee687d5088faff48013a6767505c027e2742536"); static final AccountID COINBASE_ID = AccountID.newBuilder().accountNum(98L).build(); diff --git a/hedera-node/hedera-app/src/xtest/java/contract/BurnsXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/BurnsXTest.java index 9a8b4f1c25eb..ba31a4a27a4f 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/BurnsXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/BurnsXTest.java @@ -43,6 +43,7 @@ import static contract.XTestConstants.SN_1234_METADATA; import static contract.XTestConstants.SN_2345; import static contract.XTestConstants.addErc20Relation; +import static contract.XTestConstants.addErc721Relation; import static contract.XTestConstants.assertSuccess; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -242,7 +243,7 @@ protected Map initialNfts() { protected Map initialTokenRelationships() { final var tokenRelationships = new HashMap(); addErc20Relation(tokenRelationships, OWNER_ID, TOKEN_BALANCE); - XTestConstants.addErc721Relation(tokenRelationships, UNAUTHORIZED_SPENDER_ID, TOKEN_BALANCE); + addErc721Relation(tokenRelationships, UNAUTHORIZED_SPENDER_ID, TOKEN_BALANCE); return tokenRelationships; } diff --git a/hedera-node/hedera-app/src/xtest/java/contract/ClassicViewsXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/ClassicViewsXTest.java index ccbfb4d11a3b..31994811ab00 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/ClassicViewsXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/ClassicViewsXTest.java @@ -17,7 +17,6 @@ package contract; import static com.hedera.node.app.service.contract.impl.ContractServiceImpl.CONTRACT_SERVICE; -import static contract.AssortedOpsXTestConstants.ONE_HBAR; import static contract.ClassicViewsXTestConstants.ADMIN_KEY; import static contract.ClassicViewsXTestConstants.AUTORENEW_SECONDS; import static contract.ClassicViewsXTestConstants.CLASSIC_QUERIES_X_TEST_ID; @@ -65,6 +64,7 @@ import static contract.XTestConstants.ERC20_TOKEN_ID; import static contract.XTestConstants.ERC721_TOKEN_ADDRESS; import static contract.XTestConstants.ERC721_TOKEN_ID; +import static contract.XTestConstants.ONE_HBAR; import com.esaulpaugh.headlong.abi.Function; import com.esaulpaugh.headlong.abi.TupleType; diff --git a/hedera-node/hedera-app/src/xtest/java/contract/ContractLimitsXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/ContractLimitsXTest.java index ce92d16a7e28..467741d39a0d 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/ContractLimitsXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/ContractLimitsXTest.java @@ -18,10 +18,10 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.MAX_ENTITIES_IN_PRICE_REGIME_HAVE_BEEN_CREATED; import static com.hedera.node.app.service.contract.impl.ContractServiceImpl.CONTRACT_SERVICE; -import static contract.AssortedOpsXTestConstants.ONE_HBAR; -import static contract.AssortedOpsXTestConstants.SENDER_ALIAS; -import static contract.Erc721XTestConstants.COINBASE_ID; +import static contract.XTestConstants.COINBASE_ID; +import static contract.XTestConstants.ONE_HBAR; import static contract.XTestConstants.SENDER_ADDRESS; +import static contract.XTestConstants.SENDER_ALIAS; import static contract.XTestConstants.SENDER_ID; import com.hedera.hapi.node.base.AccountID; diff --git a/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTest.java b/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTest.java index 14cba6e1cf25..191ded6fb6c1 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTest.java @@ -17,7 +17,6 @@ package contract; import static com.hedera.node.app.service.contract.impl.ContractServiceImpl.CONTRACT_SERVICE; -import static contract.Erc721XTestConstants.COINBASE_ID; import static contract.Erc721XTestConstants.COUNTERPARTY_ADDRESS; import static contract.Erc721XTestConstants.COUNTERPARTY_ID; import static contract.Erc721XTestConstants.ERC721_FULL_ID; @@ -31,6 +30,7 @@ import static contract.Erc721XTestConstants.PARTY_ID; import static contract.Erc721XTestConstants.TOKEN_TREASURY_ADDRESS; import static contract.Erc721XTestConstants.TOKEN_TREASURY_ID; +import static contract.XTestConstants.COINBASE_ID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTestConstants.java b/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTestConstants.java index fc49630dc5b0..e89b305a450e 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTestConstants.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTestConstants.java @@ -74,7 +74,6 @@ class Erc721XTestConstants { Map.entry( Bytes.fromHex("86B3FA87EE245373978E0D2D334DBDE866C9B8B039036B87C5EB2FD89BCB6BAB"), Bytes.fromHex("000000000000000000000000d893f18b69a06f7ffffad77202c2f627cb2c9605"))); - static final AccountID COINBASE_ID = AccountID.newBuilder().accountNum(98L).build(); static final AccountID TOKEN_TREASURY_ID = AccountID.newBuilder().accountNum(1001L).build(); static final AccountID COUNTERPARTY_ID = diff --git a/hedera-node/hedera-app/src/xtest/java/contract/FuseXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/FuseXTest.java index 1b106d579245..7631f8ad8083 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/FuseXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/FuseXTest.java @@ -17,10 +17,10 @@ package contract; import static com.hedera.node.app.service.contract.impl.ContractServiceImpl.CONTRACT_SERVICE; -import static contract.AssortedOpsXTestConstants.ONE_HBAR; -import static contract.AssortedOpsXTestConstants.SENDER_ALIAS; -import static contract.Erc721XTestConstants.COINBASE_ID; +import static contract.XTestConstants.COINBASE_ID; +import static contract.XTestConstants.ONE_HBAR; import static contract.XTestConstants.SENDER_ADDRESS; +import static contract.XTestConstants.SENDER_ALIAS; import static contract.XTestConstants.SENDER_ID; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hedera-node/hedera-app/src/xtest/java/contract/MiscViewsXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/MiscViewsXTest.java index f939109427fe..031241364308 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/MiscViewsXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/MiscViewsXTest.java @@ -17,7 +17,6 @@ package contract; import static com.hedera.node.app.service.contract.impl.ContractServiceImpl.CONTRACT_SERVICE; -import static contract.AssortedOpsXTestConstants.ONE_HBAR; import static contract.MiscViewsXTestConstants.COINBASE_ID; import static contract.MiscViewsXTestConstants.EQUIV_TINYCENTS; import static contract.MiscViewsXTestConstants.ERC20_DECIMALS; @@ -61,6 +60,7 @@ import static contract.XTestConstants.ERC20_TOKEN_ID; import static contract.XTestConstants.ERC721_TOKEN_ADDRESS; import static contract.XTestConstants.ERC721_TOKEN_ID; +import static contract.XTestConstants.ONE_HBAR; import com.esaulpaugh.headlong.abi.Function; import com.esaulpaugh.headlong.abi.TupleType; diff --git a/hedera-node/hedera-app/src/xtest/java/contract/TestApproverXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/TestApproverXTest.java index 0db7a9fa3a49..9942204a7735 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/TestApproverXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/TestApproverXTest.java @@ -18,10 +18,10 @@ import static com.hedera.node.app.service.contract.impl.ContractServiceImpl.CONTRACT_SERVICE; import static contract.AssortedOpsXTestConstants.COINBASE_ID; -import static contract.AssortedOpsXTestConstants.ONE_HBAR; import static contract.XTestConstants.AN_ED25519_KEY; import static contract.XTestConstants.ERC20_TOKEN_ADDRESS; import static contract.XTestConstants.ERC20_TOKEN_ID; +import static contract.XTestConstants.ONE_HBAR; import static contract.XTestConstants.SENDER_ADDRESS; import static contract.XTestConstants.SENDER_HEADLONG_ADDRESS; import static contract.XTestConstants.SENDER_ID; diff --git a/hedera-node/hedera-app/src/xtest/java/contract/XTestConstants.java b/hedera-node/hedera-app/src/xtest/java/contract/XTestConstants.java index 58991782cf06..de01c467eb74 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/XTestConstants.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/XTestConstants.java @@ -42,80 +42,93 @@ /** * Common constants used in "x-test" classes. */ -class XTestConstants { +public class XTestConstants { - static final long THREE_MONTHS_IN_SECONDS = 7776000L; - static final AccountID MISC_PAYER_ID = + public static final long THREE_MONTHS_IN_SECONDS = 7776000L; + public static final AccountID MISC_PAYER_ID = AccountID.newBuilder().accountNum(950L).build(); - static final TransactionBody PLACEHOLDER_CALL_BODY = TransactionBody.newBuilder() + public static final TransactionBody PLACEHOLDER_CALL_BODY = TransactionBody.newBuilder() .transactionID(TransactionID.newBuilder().accountID(MISC_PAYER_ID).build()) .contractCall(ContractCallTransactionBody.DEFAULT) .build(); - static final AccountID SENDER_ID = + public static final AccountID SENDER_ID = AccountID.newBuilder().accountNum(12345789L).build(); - static final Key SENDER_CONTRACT_ID_KEY = Key.newBuilder() + public static final Key SENDER_CONTRACT_ID_KEY = Key.newBuilder() .contractID(ContractID.newBuilder() .contractNum(SENDER_ID.accountNumOrThrow()) .build()) .build(); - static final Bytes SENDER_ADDRESS = + public static final Bytes SENDER_ADDRESS = com.hedera.pbj.runtime.io.buffer.Bytes.fromHex("f91e624b8b8ea7244e8159ba7c0deeea2b6be990"); - static final com.esaulpaugh.headlong.abi.Address SENDER_HEADLONG_ADDRESS = + public static final com.esaulpaugh.headlong.abi.Address SENDER_HEADLONG_ADDRESS = asHeadlongAddress(SENDER_ADDRESS.toByteArray()); - static final Address SENDER_BESU_ADDRESS = pbjToBesuAddress(SENDER_ADDRESS); - static final Bytes BAD_SENDER = + public static final Address SENDER_BESU_ADDRESS = pbjToBesuAddress(SENDER_ADDRESS); + public static final Bytes BAD_SENDER = com.hedera.pbj.runtime.io.buffer.Bytes.fromHex("e22e624b8b8ea7244e8159ba7c0deeea2b6be991"); - static final com.esaulpaugh.headlong.abi.Address INVALID_SENDER_HEADLONG_ADDRESS = + public static final com.esaulpaugh.headlong.abi.Address INVALID_SENDER_HEADLONG_ADDRESS = asHeadlongAddress(BAD_SENDER.toByteArray()); - static final AccountID RECEIVER_ID = + public static final AccountID RECEIVER_ID = AccountID.newBuilder().accountNum(987654321L).build(); - static final AccountID INVALID_ID = + public static final AccountID INVALID_ID = AccountID.newBuilder().accountNum(Long.MAX_VALUE).build(); - static final Key INVALID_CONTRACT_ID_KEY = Key.newBuilder() + public static final Key INVALID_CONTRACT_ID_KEY = Key.newBuilder() .contractID(ContractID.newBuilder() .contractNum(SENDER_ID.accountNumOrThrow()) .build()) .build(); - static final com.esaulpaugh.headlong.abi.Address RECEIVER_HEADLONG_ADDRESS = + public static final com.esaulpaugh.headlong.abi.Address RECEIVER_HEADLONG_ADDRESS = asHeadlongAddress(asEvmAddress(RECEIVER_ID.accountNumOrThrow())); - static final Address RECEIVER_BESU_ADDRESS = + public static final Address RECEIVER_BESU_ADDRESS = pbjToBesuAddress(Bytes.wrap(asEvmAddress(RECEIVER_ID.accountNumOrThrow()))); - static final TokenID ERC721_TOKEN_ID = TokenID.newBuilder().tokenNum(1028L).build(); - static final NftID SN_1234 = + public static final TokenID ERC721_TOKEN_ID = + TokenID.newBuilder().tokenNum(1028L).build(); + public static final NftID SN_1234 = NftID.newBuilder().tokenId(ERC721_TOKEN_ID).serialNumber(1234L).build(); - static final NftID SN_2345 = + public static final NftID SN_2345 = NftID.newBuilder().tokenId(ERC721_TOKEN_ID).serialNumber(2345L).build(); - static final NftID SN_3456 = + public static final NftID SN_3456 = NftID.newBuilder().tokenId(ERC721_TOKEN_ID).serialNumber(3456L).build(); - static final Bytes SN_1234_METADATA = Bytes.wrap("https://example.com/721/" + 1234); - static final Bytes SN_2345_METADATA = Bytes.wrap("https://example.com/721/" + 2345); - static final Bytes SN_3456_METADATA = Bytes.wrap("https://example.com/721/" + 3456); - static final com.esaulpaugh.headlong.abi.Address ERC721_TOKEN_ADDRESS = AbstractContractXTest.asHeadlongAddress( - asLongZeroAddress(ERC721_TOKEN_ID.tokenNum()).toArray()); - static final TokenID ERC20_TOKEN_ID = TokenID.newBuilder().tokenNum(1027L).build(); - static final com.esaulpaugh.headlong.abi.Address ERC20_TOKEN_ADDRESS = AbstractContractXTest.asHeadlongAddress( - asLongZeroAddress(ERC20_TOKEN_ID.tokenNum()).toArray()); - static final TokenID OTHER_TOKEN_ID = TokenID.newBuilder().tokenNum(1777L).build(); - static final com.esaulpaugh.headlong.abi.Address OTHER_TOKEN_ADDRESS = AbstractContractXTest.asHeadlongAddress( - asLongZeroAddress(OTHER_TOKEN_ID.tokenNum()).toArray()); - static final AccountID OWNER_ID = AccountID.newBuilder().accountNum(121212L).build(); - static final Bytes OWNER_ADDRESS = Bytes.fromHex("a213624b8b83a724438159ba7c0d333a2b6b3990"); - static final Address OWNER_BESU_ADDRESS = pbjToBesuAddress(OWNER_ADDRESS); - static final com.esaulpaugh.headlong.abi.Address OWNER_HEADLONG_ADDRESS = + public static final Bytes SN_1234_METADATA = Bytes.wrap("https://example.com/721/" + 1234); + public static final Bytes SN_2345_METADATA = Bytes.wrap("https://example.com/721/" + 2345); + public static final Bytes SN_3456_METADATA = Bytes.wrap("https://example.com/721/" + 3456); + public static final com.esaulpaugh.headlong.abi.Address ERC721_TOKEN_ADDRESS = + AbstractContractXTest.asHeadlongAddress( + asLongZeroAddress(ERC721_TOKEN_ID.tokenNum()).toArray()); + public static final TokenID ERC20_TOKEN_ID = + TokenID.newBuilder().tokenNum(1027L).build(); + public static final com.esaulpaugh.headlong.abi.Address ERC20_TOKEN_ADDRESS = + AbstractContractXTest.asHeadlongAddress( + asLongZeroAddress(ERC20_TOKEN_ID.tokenNum()).toArray()); + public static final TokenID OTHER_TOKEN_ID = + TokenID.newBuilder().tokenNum(1777L).build(); + public static final com.esaulpaugh.headlong.abi.Address OTHER_TOKEN_ADDRESS = + AbstractContractXTest.asHeadlongAddress( + asLongZeroAddress(OTHER_TOKEN_ID.tokenNum()).toArray()); + public static final AccountID OWNER_ID = + AccountID.newBuilder().accountNum(121212L).build(); + public static final Bytes OWNER_ADDRESS = Bytes.fromHex("a213624b8b83a724438159ba7c0d333a2b6b3990"); + public static final Address OWNER_BESU_ADDRESS = pbjToBesuAddress(OWNER_ADDRESS); + public static final com.esaulpaugh.headlong.abi.Address OWNER_HEADLONG_ADDRESS = asHeadlongAddress(OWNER_ADDRESS.toByteArray()); - static final Bytes INVALID_ACCOUNT_ADDRESS = Bytes.wrap(asEvmAddress(INVALID_ID.accountNumOrThrow())); - static final com.esaulpaugh.headlong.abi.Address INVALID_ACCOUNT_HEADLONG_ADDRESS = + public static final Bytes INVALID_ACCOUNT_ADDRESS = Bytes.wrap(asEvmAddress(INVALID_ID.accountNumOrThrow())); + public static final com.esaulpaugh.headlong.abi.Address INVALID_ACCOUNT_HEADLONG_ADDRESS = asHeadlongAddress(asEvmAddress(INVALID_ID.accountNumOrThrow())); - static final Key AN_ED25519_KEY = Key.newBuilder() + public static final Key AN_ED25519_KEY = Key.newBuilder() .ed25519(Bytes.fromHex("0101010101010101010101010101010101010101010101010101010101010101")) .build(); - static final TokenID INVALID_TOKEN_ID = + public static final TokenID INVALID_TOKEN_ID = TokenID.newBuilder().tokenNum(Long.MAX_VALUE).build(); - static final com.esaulpaugh.headlong.abi.Address INVALID_TOKEN_ADDRESS = AbstractContractXTest.asHeadlongAddress( - asLongZeroAddress(INVALID_TOKEN_ID.tokenNum()).toArray()); + public static final com.esaulpaugh.headlong.abi.Address INVALID_TOKEN_ADDRESS = + AbstractContractXTest.asHeadlongAddress( + asLongZeroAddress(INVALID_TOKEN_ID.tokenNum()).toArray()); + public static final Bytes SENDER_ALIAS = + Bytes.fromHex("3a21030edcc130e13fb5102e7c883535af8c2b0a5a617231f77fd127ce5f3b9a620591"); + public static final long ONE_HBAR = 100_000_000L; + public static final AccountID COINBASE_ID = + AccountID.newBuilder().accountNum(98L).build(); public static void addErc721Relation( final Map tokenRelationships, final AccountID accountID, final long balance) { diff --git a/hedera-node/hedera-app/src/xtest/java/contract/approvals/ApproveAllowanceXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/approvals/ApproveAllowanceXTest.java new file mode 100644 index 000000000000..af9f80007f92 --- /dev/null +++ b/hedera-node/hedera-app/src/xtest/java/contract/approvals/ApproveAllowanceXTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package contract.approvals; + +import static contract.XTestConstants.AN_ED25519_KEY; +import static contract.XTestConstants.COINBASE_ID; +import static contract.XTestConstants.ONE_HBAR; +import static contract.XTestConstants.SENDER_ADDRESS; +import static contract.XTestConstants.SENDER_ALIAS; +import static contract.XTestConstants.SENDER_ID; +import static contract.approvals.ApproveAllowanceXTestConstants.ACCOUNT_ID; +import static contract.approvals.ApproveAllowanceXTestConstants.EXPECTED_HTS_APPROVE_ALLOWANCE_CONTRACT_ID; +import static contract.approvals.ApproveAllowanceXTestConstants.GAS; +import static contract.approvals.ApproveAllowanceXTestConstants.HTS_APPROVE_ALLOWANCE_INITCODE_ID; +import static contract.approvals.ApproveAllowanceXTestConstants.IS_APPROVED_FOR_ALL_FUNCTION; +import static contract.approvals.ApproveAllowanceXTestConstants.NEXT_ENTITY_NUM; +import static contract.approvals.ApproveAllowanceXTestConstants.NFT_TOKEN_TYPE_ID; +import static contract.approvals.ApproveAllowanceXTestConstants.OWNER_ID; +import static contract.approvals.ApproveAllowanceXTestConstants.RECIPIENT_ID; +import static contract.approvals.ApproveAllowanceXTestConstants.TOKEN_TREASURY_ID; + +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.FileID; +import com.hedera.hapi.node.base.NftID; +import com.hedera.hapi.node.base.TokenID; +import com.hedera.hapi.node.base.TokenType; +import com.hedera.hapi.node.base.TransactionID; +import com.hedera.hapi.node.contract.ContractCreateTransactionBody; +import com.hedera.hapi.node.state.common.EntityIDPair; +import com.hedera.hapi.node.state.file.File; +import com.hedera.hapi.node.state.primitives.ProtoBytes; +import com.hedera.hapi.node.state.token.Account; +import com.hedera.hapi.node.state.token.AccountApprovalForAllAllowance; +import com.hedera.hapi.node.state.token.Nft; +import com.hedera.hapi.node.state.token.Token; +import com.hedera.hapi.node.state.token.TokenRelation; +import com.hedera.hapi.node.transaction.TransactionBody; +import contract.AbstractContractXTest; +import java.util.HashMap; +import java.util.Map; + +public class ApproveAllowanceXTest extends AbstractContractXTest { + @Override + protected void doScenarioOperations() { + handleAndCommitSingleTransaction(createHandler(), createHtsApproveAllowanceTxn()); + handleAndCommitSingleTransaction( + callHandler(), + createCallTransactionBody( + SENDER_ID, + 0L, + EXPECTED_HTS_APPROVE_ALLOWANCE_CONTRACT_ID, + IS_APPROVED_FOR_ALL_FUNCTION.encodeCallWithArgs( + asLongZeroHeadlongAddress(NFT_TOKEN_TYPE_ID), + asLongZeroHeadlongAddress(OWNER_ID), + asLongZeroHeadlongAddress(RECIPIENT_ID)))); + } + + @Override + protected Map initialFiles() { + final var files = new HashMap(); + files.put( + HTS_APPROVE_ALLOWANCE_INITCODE_ID, + File.newBuilder() + .contents(resourceAsBytes("initcode/HtsApproveAllowance.bin")) + .build()); + return files; + } + + @Override + protected long initialEntityNum() { + return NEXT_ENTITY_NUM - 1; + } + + @Override + protected Map initialAliases() { + final var aliases = new HashMap(); + aliases.put(ProtoBytes.newBuilder().value(SENDER_ALIAS).build(), SENDER_ID); + aliases.put(ProtoBytes.newBuilder().value(SENDER_ADDRESS).build(), SENDER_ID); + return aliases; + } + + @Override + protected Map initialTokens() { + final var tokens = new HashMap(); + tokens.put( + NFT_TOKEN_TYPE_ID, + Token.newBuilder() + .tokenId(NFT_TOKEN_TYPE_ID) + .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) + .treasuryAccountId(TOKEN_TREASURY_ID) + .adminKey(AN_ED25519_KEY) + .supplyKey(AN_ED25519_KEY) + .totalSupply(2) + .build()); + return tokens; + } + + @Override + protected Map initialNfts() { + final var nfts = super.initialNfts(); + addNft(nfts, NFT_TOKEN_TYPE_ID, 1, nft -> nft.ownerId(OWNER_ID).spenderId(RECIPIENT_ID)); + addNft(nfts, NFT_TOKEN_TYPE_ID, 2, nft -> nft.ownerId(OWNER_ID).spenderId(RECIPIENT_ID)); + return nfts; + } + + @Override + protected Map initialTokenRelationships() { + final var tokenRels = new HashMap(); + addUsableRelation(tokenRels, OWNER_ID, NFT_TOKEN_TYPE_ID, rel -> {}); + return tokenRels; + } + + @Override + protected Map initialAccounts() { + final var accounts = new HashMap(); + accounts.put( + SENDER_ID, + Account.newBuilder() + .accountId(SENDER_ID) + .alias(SENDER_ALIAS) + .tinybarBalance(100 * ONE_HBAR) + .build()); + accounts.put( + OWNER_ID, + Account.newBuilder() + .accountId(OWNER_ID) + .tinybarBalance(10_000 * ONE_HBAR) + .approveForAllNftAllowances(AccountApprovalForAllAllowance.newBuilder() + .tokenId(NFT_TOKEN_TYPE_ID) + .spenderId(RECIPIENT_ID) + .build()) + .build()); + accounts.put( + RECIPIENT_ID, + Account.newBuilder() + .accountId(RECIPIENT_ID) + .tinybarBalance(10_000 * ONE_HBAR) + .build()); + accounts.put( + ACCOUNT_ID, + Account.newBuilder() + .accountId(ACCOUNT_ID) + .tinybarBalance(10_000 * ONE_HBAR) + .build()); + accounts.put( + TOKEN_TREASURY_ID, + Account.newBuilder() + .accountId(TOKEN_TREASURY_ID) + .tinybarBalance(10_000 * ONE_HBAR) + .build()); + accounts.put(COINBASE_ID, Account.newBuilder().accountId(COINBASE_ID).build()); + return accounts; + } + + private TransactionBody createHtsApproveAllowanceTxn() { + return TransactionBody.newBuilder() + .transactionID(TransactionID.newBuilder().accountID(SENDER_ID)) + .contractCreateInstance(ContractCreateTransactionBody.newBuilder() + .autoRenewPeriod(STANDARD_AUTO_RENEW_PERIOD) + .fileID(HTS_APPROVE_ALLOWANCE_INITCODE_ID) + .gas(GAS) + .build()) + .build(); + } +} diff --git a/hedera-node/hedera-app/src/xtest/java/contract/approvals/ApproveAllowanceXTestConstants.java b/hedera-node/hedera-app/src/xtest/java/contract/approvals/ApproveAllowanceXTestConstants.java new file mode 100644 index 000000000000..3b37609856bb --- /dev/null +++ b/hedera-node/hedera-app/src/xtest/java/contract/approvals/ApproveAllowanceXTestConstants.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package contract.approvals; + +import com.esaulpaugh.headlong.abi.Function; +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.ContractID; +import com.hedera.hapi.node.base.FileID; +import com.hedera.hapi.node.base.TokenID; + +/** + * See also {@code ApproveAllowanceSuite#hapiNftIsApprovedForAll()} in the {@code test-clients} module. + */ +public class ApproveAllowanceXTestConstants { + static final FileID HTS_APPROVE_ALLOWANCE_INITCODE_ID = + FileID.newBuilder().fileNum(1002L).build(); + static final long NEXT_ENTITY_NUM = 1234L; + static final ContractID EXPECTED_HTS_APPROVE_ALLOWANCE_CONTRACT_ID = + ContractID.newBuilder().contractNum(NEXT_ENTITY_NUM).build(); + static final long GAS = 2_000_000L; + + static final AccountID OWNER_ID = AccountID.newBuilder().accountNum(1003L).build(); + static final AccountID RECIPIENT_ID = + AccountID.newBuilder().accountNum(1004L).build(); + static final AccountID ACCOUNT_ID = AccountID.newBuilder().accountNum(1005L).build(); + static final AccountID TOKEN_TREASURY_ID = + AccountID.newBuilder().accountNum(1006L).build(); + static final TokenID NFT_TOKEN_TYPE_ID = + TokenID.newBuilder().tokenNum(1007L).build(); + + static final Function IS_APPROVED_FOR_ALL_FUNCTION = + new Function("htsIsApprovedForAll(address,address,address)", "(bool)"); +} diff --git a/hedera-node/hedera-app/src/xtest/resources/initcode/HtsApproveAllowance.bin b/hedera-node/hedera-app/src/xtest/resources/initcode/HtsApproveAllowance.bin new file mode 100644 index 000000000000..232b549ac9bf --- /dev/null +++ b/hedera-node/hedera-app/src/xtest/resources/initcode/HtsApproveAllowance.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610e62806100206000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80631ae1e7be146100675780632e55e1d6146100975780635c40f557146100c75780639cba3cdc146100f7578063bfad3db014610127578063d89583ad14610157575b600080fd5b610081600480360381019061007c919061091a565b610187565b60405161008e9190610988565b60405180910390f35b6100b160048036038101906100ac91906109d9565b6101a5565b6040516100be9190610a28565b60405180910390f35b6100e160048036038101906100dc9190610a6f565b6101c1565b6040516100ee9190610988565b60405180910390f35b610111600480360381019061010c919061091a565b6101dd565b60405161011e9190610ad1565b60405180910390f35b610141600480360381019061013c9190610aec565b6101fb565b60405161014e9190610988565b60405180910390f35b610171600480360381019061016c9190610aec565b610217565b60405161017e9190610988565b60405180910390f35b60008061019585858561030c565b8093508192505050509392505050565b6000806101b28484610434565b80935081925050505092915050565b6000601660030b6101d3858585610559565b1490509392505050565b6000806101eb858585610674565b8093508192505050509392505050565b6000601660030b61020d85858561079c565b1490509392505050565b6000606061016773ffffffffffffffffffffffffffffffffffffffff16637336aaf060e01b86868660405160240161025193929190610b3f565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516102bb9190610bf0565b600060405180830381855af49150503d80600081146102f6576040519150601f19603f3d011682016040523d82523d6000602084013e6102fb565b606091505b508092508193505050509392505050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1663f49f40db60e01b88888860405160240161034893929190610c07565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516103b29190610bf0565b6000604051808303816000865af19150503d80600081146103ef576040519150601f19603f3d011682016040523d82523d6000602084013e6103f4565b606091505b509150915081610407576015600061041c565b8080602001905181019061041b9190610c8c565b5b8160030b915080945081955050505050935093915050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1663098f236660e01b878760405160240161046e929190610ccc565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104d89190610bf0565b6000604051808303816000865af19150503d8060008114610515576040519150601f19603f3d011682016040523d82523d6000602084013e61051a565b606091505b50915091508161052d5760156000610542565b808060200190518101906105419190610d33565b5b8160030b9150809450819550505050509250929050565b600080600061016773ffffffffffffffffffffffffffffffffffffffff1663367605ca60e01b87878760405160240161059493929190610d73565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516105fe9190610bf0565b6000604051808303816000865af19150503d806000811461063b576040519150601f19603f3d011682016040523d82523d6000602084013e610640565b606091505b509150915081610651576015610666565b808060200190518101906106659190610daa565b5b60030b925050509392505050565b60008060008061016773ffffffffffffffffffffffffffffffffffffffff1663927da10560e01b8888886040516024016106b093929190610c07565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161071a9190610bf0565b6000604051808303816000865af19150503d8060008114610757576040519150601f19603f3d011682016040523d82523d6000602084013e61075c565b606091505b50915091508161076f5760156000610784565b808060200190518101906107839190610dec565b5b8160030b915080945081955050505050935093915050565b600080600061016773ffffffffffffffffffffffffffffffffffffffff1663e1f21c6760e01b8787876040516024016107d793929190610b3f565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516108419190610bf0565b6000604051808303816000865af19150503d806000811461087e576040519150601f19603f3d011682016040523d82523d6000602084013e610883565b606091505b5091509150816108945760156108a9565b808060200190518101906108a89190610daa565b5b60030b925050509392505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006108e7826108bc565b9050919050565b6108f7816108dc565b811461090257600080fd5b50565b600081359050610914816108ee565b92915050565b600080600060608486031215610933576109326108b7565b5b600061094186828701610905565b935050602061095286828701610905565b925050604061096386828701610905565b9150509250925092565b60008115159050919050565b6109828161096d565b82525050565b600060208201905061099d6000830184610979565b92915050565b6000819050919050565b6109b6816109a3565b81146109c157600080fd5b50565b6000813590506109d3816109ad565b92915050565b600080604083850312156109f0576109ef6108b7565b5b60006109fe85828601610905565b9250506020610a0f858286016109c4565b9150509250929050565b610a22816108dc565b82525050565b6000602082019050610a3d6000830184610a19565b92915050565b610a4c8161096d565b8114610a5757600080fd5b50565b600081359050610a6981610a43565b92915050565b600080600060608486031215610a8857610a876108b7565b5b6000610a9686828701610905565b9350506020610aa786828701610905565b9250506040610ab886828701610a5a565b9150509250925092565b610acb816109a3565b82525050565b6000602082019050610ae66000830184610ac2565b92915050565b600080600060608486031215610b0557610b046108b7565b5b6000610b1386828701610905565b9350506020610b2486828701610905565b9250506040610b35868287016109c4565b9150509250925092565b6000606082019050610b546000830186610a19565b610b616020830185610a19565b610b6e6040830184610ac2565b949350505050565b600081519050919050565b600081905092915050565b60005b83811015610baa578082015181840152602081019050610b8f565b83811115610bb9576000848401525b50505050565b6000610bca82610b76565b610bd48185610b81565b9350610be4818560208601610b8c565b80840191505092915050565b6000610bfc8284610bbf565b915081905092915050565b6000606082019050610c1c6000830186610a19565b610c296020830185610a19565b610c366040830184610a19565b949350505050565b60008160030b9050919050565b610c5481610c3e565b8114610c5f57600080fd5b50565b600081519050610c7181610c4b565b92915050565b600081519050610c8681610a43565b92915050565b60008060408385031215610ca357610ca26108b7565b5b6000610cb185828601610c62565b9250506020610cc285828601610c77565b9150509250929050565b6000604082019050610ce16000830185610a19565b610cee6020830184610ac2565b9392505050565b6000610d00826108bc565b9050919050565b610d1081610cf5565b8114610d1b57600080fd5b50565b600081519050610d2d81610d07565b92915050565b60008060408385031215610d4a57610d496108b7565b5b6000610d5885828601610c62565b9250506020610d6985828601610d1e565b9150509250929050565b6000606082019050610d886000830186610a19565b610d956020830185610a19565b610da26040830184610979565b949350505050565b600060208284031215610dc057610dbf6108b7565b5b6000610dce84828501610c62565b91505092915050565b600081519050610de6816109ad565b92915050565b60008060408385031215610e0357610e026108b7565b5b6000610e1185828601610c62565b9250506020610e2285828601610dd7565b915050925092905056fea26469706673582212200be0cf14803dde062b4dfea27a0147cbec4f129868de6715e344ca531d24cc1964736f6c634300080e0033 \ No newline at end of file diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HtsSystemContract.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HtsSystemContract.java index 80eebf193849..2be370169ae9 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HtsSystemContract.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/HtsSystemContract.java @@ -68,7 +68,7 @@ private static FullResult resultOfExecuting( @NonNull final HtsCall call, @NonNull final Bytes input, @NonNull final MessageFrame frame) { final HtsCall.PricedResult pricedResult; try { - pricedResult = call.execute(); + pricedResult = call.execute(frame); } catch (final Exception internal) { log.error("Unhandled failure for input {} to HTS system contract", input, internal); return haltResult(ExceptionalHaltReason.PRECOMPILE_ERROR, frame.getRemainingGas()); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/AbiConstants.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/AbiConstants.java new file mode 100644 index 000000000000..acb09a7200a7 --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/AbiConstants.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2022-2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts; + +import org.apache.tuweni.bytes.Bytes; + +public final class AbiConstants { + private AbiConstants() { + throw new UnsupportedOperationException("Utility class"); + } + + // **** HIP-206 function selectors **** + // cryptoTransfer(TokenTransferList[] memory tokenTransfers) + public static final int ABI_ID_CRYPTO_TRANSFER = 0x189a554c; + // cryptoTransfer(TransferList memory transferList, TokenTransferList[] memory tokenTransfers) + public static final int ABI_ID_CRYPTO_TRANSFER_V2 = 0x0e71804f; + // transferTokens(address token, address[] memory accountId, int64[] memory amount) + public static final int ABI_ID_TRANSFER_TOKENS = 0x82bba493; + // transferToken(address token, address sender, address recipient, int64 amount) + public static final int ABI_ID_TRANSFER_TOKEN = 0xeca36917; + // transferNFTs(address token, address[] memory sender, address[] memory receiver, int64[] + // memory + // serialNumber) + public static final int ABI_ID_TRANSFER_NFTS = 0x2c4ba191; + // transferNFT(address token, address sender, address recipient, int64 serialNum) + public static final int ABI_ID_TRANSFER_NFT = 0x5cfc9011; + // mintToken(address token, uint64 amount, bytes[] memory metadata) + public static final int ABI_ID_MINT_TOKEN = 0x278e0b88; + // mintToken(address token, int64 amount, bytes[] memory metadata) + public static final int ABI_ID_MINT_TOKEN_V2 = 0xe0f4059a; + // burnToken(address token, uint64 amount, int64[] memory serialNumbers) + public static final int ABI_ID_BURN_TOKEN = 0xacb9cff9; + // burnToken(address token, int64 amount, int64[] memory serialNumbers) + public static final int ABI_ID_BURN_TOKEN_V2 = 0xd6910d06; + // deleteToken(address token) + public static final int ABI_ID_DELETE_TOKEN = 0xf069f712; + // associateTokens(address account, address[] memory tokens) + public static final int ABI_ID_ASSOCIATE_TOKENS = 0x2e63879b; + // associateToken(address account, address token) + public static final int ABI_ID_ASSOCIATE_TOKEN = 0x49146bde; + // dissociateTokens(address account, address[] memory tokens) + public static final int ABI_ID_DISSOCIATE_TOKENS = 0x78b63918; + // dissociateToken(address account, address token) + public static final int ABI_ID_DISSOCIATE_TOKEN = 0x099794e8; + // pauseToken(address token) + public static final int ABI_ID_PAUSE_TOKEN = 0x7c41ad2c; + // unpauseToken(address token) + public static final int ABI_ID_UNPAUSE_TOKEN = 0x3b3bff0f; + // allowance(address token, address owner, address spender) + public static final int ABI_ID_ALLOWANCE = 0x927da105; + // approve(address token, address spender, uint256 amount) + public static final int ABI_ID_APPROVE = 0xe1f21c67; + // approveNFT(address token, address to, uint256 tokenId) + public static final int ABI_ID_APPROVE_NFT = 0x7336aaf0; + // setApprovalForAll(address token, address operator, bool approved) + public static final int ABI_ID_SET_APPROVAL_FOR_ALL = 0x367605ca; + // getApproved(address token, uint256 tokenId) + public static final int ABI_ID_GET_APPROVED = 0x098f2366; + // isApprovedForAll(address token, address owner, address operator) + public static final int ABI_ID_IS_APPROVED_FOR_ALL = 0xf49f40db; + // transferFrom(address token, address from, address to, uint256 amount) + public static final int ABI_ID_TRANSFER_FROM = 0x15dacbea; + // transferFromNFT(address token, address from, address to, uint256 serialNumber) + public static final int ABI_ID_TRANSFER_FROM_NFT = 0x9b23d3d9; + + // **** HIP-218 + HIP-376 function selectors and event signatures **** + // redirectForToken(address token, bytes memory data) + public static final int ABI_ID_REDIRECT_FOR_TOKEN = 0x618dc65e; + // name() + public static final int ABI_ID_ERC_NAME = 0x06fdde03; + // symbol() + public static final int ABI_ID_ERC_SYMBOL = 0x95d89b41; + // decimals() + public static final int ABI_ID_ERC_DECIMALS = 0x313ce567; + // totalSupply() + public static final int ABI_ID_ERC_TOTAL_SUPPLY_TOKEN = 0x18160ddd; + // balanceOf(address account) + public static final int ABI_ID_ERC_BALANCE_OF_TOKEN = 0x70a08231; + // transfer(address recipient, uint256 amount) + public static final int ABI_ID_ERC_TRANSFER = 0xa9059cbb; + // transferFrom(address sender, address recipient, uint256 amount) + // transferFrom(address from, address to, uint256 tokenId) + public static final int ABI_ID_ERC_TRANSFER_FROM = 0x23b872dd; + // allowance(address owner, address spender) + public static final int ABI_ID_ERC_ALLOWANCE = 0xdd62ed3e; + // approve(address spender, uint256 amount) + // approve(address to, uint256 tokenId) + public static final int ABI_ID_ERC_APPROVE = 0x95ea7b3; + // setApprovalForAll(address operator, bool approved) + public static final int ABI_ID_ERC_SET_APPROVAL_FOR_ALL = 0xa22cb465; + // getApproved(uint256 tokenId) + public static final int ABI_ID_ERC_GET_APPROVED = 0x081812fc; + // isApprovedForAll(address owner, address operator) + public static final int ABI_ID_ERC_IS_APPROVED_FOR_ALL = 0xe985e9c5; + // ownerOf(uint256 tokenId) + public static final int ABI_ID_ERC_OWNER_OF_NFT = 0x6352211e; + // tokenURI(uint256 tokenId) + public static final int ABI_ID_ERC_TOKEN_URI_NFT = 0xc87b56dd; + // associate(). Should be called as IHRC(tokenAddress).associate() + public static final int ABI_ID_HRC_ASSOCIATE = 0x0a754de6; + // dissociate(). Should be called as IHRC(tokenAddress).dissociate() + public static final int ABI_ID_HRC_DISSOCIATE = 0x5c9217e0; + // wipeTokenAccount(address, address, uint32) + public static final int ABI_WIPE_TOKEN_ACCOUNT_FUNGIBLE = 0x9790686d; + // wipeTokenAccount(address, address, int64) + public static final int ABI_WIPE_TOKEN_ACCOUNT_FUNGIBLE_V2 = 0xefef57f9; + // wipeTokenAccountNFT(address, address, int64[]) + public static final int ABI_WIPE_TOKEN_ACCOUNT_NFT = 0xf7f38e26; + // isFrozen(address token, address account) + public static final int ABI_ID_IS_FROZEN = 0x46de0fb1; + // freezeToken(address token, address account) + public static final int ABI_ID_FREEZE = 0x5b8f8584; + // unfreezeToken(address token, address account) + public static final int ABI_ID_UNFREEZE = 0x52f91387; + // updateTokenInfo(address token, HederaToken tokenInfo) + public static final int ABI_ID_UPDATE_TOKEN_INFO = 0x2cccc36f; + // updateTokenInfo(address token, HederaToken tokenInfo) + public static final int ABI_ID_UPDATE_TOKEN_INFO_V2 = 0x18370d34; + // updateTokenInfo(address token, HederaToken tokenInfo) + public static final int ABI_ID_UPDATE_TOKEN_INFO_V3 = 0x7d305cfa; + // updateTokenKeys(address token, TokenKey []) + public static final int ABI_ID_UPDATE_TOKEN_KEYS = 0x6fc3cbaf; + // getTokenKey(address token, uint tokenType) + public static final int ABI_ID_GET_TOKEN_KEY = 0x3c4dd32e; + // Transfer(address indexed from, address indexed to, uint256 indexed tokenId) + // Transfer(address indexed from, address indexed to, uint256 value) + public static final Bytes TRANSFER_EVENT = + Bytes.fromHexString("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"); + // Approval(address indexed owner, address indexed spender, uint256 value) + // Approval(address indexed owner, address indexed approved, uint256 indexed tokenId) + public static final Bytes APPROVAL_EVENT = + Bytes.fromHexString("8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"); + // ApprovalForAll(address indexed owner, address indexed operator, bool approved) + public static final Bytes APPROVAL_FOR_ALL_EVENT = + Bytes.fromHexString("17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31"); + + // **** HIP-358 function selectors **** + // createFungibleToken(HederaToken memory token, uint initialTotalSupply, uint decimals) + public static final int ABI_ID_CREATE_FUNGIBLE_TOKEN = 0x7812a04b; + // createFungibleToken(HederaToken memory token, uint64 initialTotalSupply, uint32 decimals) + public static final int ABI_ID_CREATE_FUNGIBLE_TOKEN_V2 = 0xc23baeb6; + // createFungibleToken(HederaToken memory token, int64 initialTotalSupply, int32 decimals) + public static final int ABI_ID_CREATE_FUNGIBLE_TOKEN_V3 = 0x0fb65bf3; + // createFungibleTokenWithCustomFees( + // HederaToken memory token, + // uint initialTotalSupply, + // uint decimals, + // FixedFee[] memory fixedFees, + // FractionalFee[] memory fractionalFees) + public static final int ABI_ID_CREATE_FUNGIBLE_TOKEN_WITH_FEES = 0x4c381ae7; + // createFungibleTokenWithCustomFees( + // HederaToken memory token, + // uint64 initialTotalSupply, + // uint32 decimals, + // FixedFee[] memory fixedFees, + // FractionalFee[] memory fractionalFees) + public static final int ABI_ID_CREATE_FUNGIBLE_TOKEN_WITH_FEES_V2 = 0xb937581a; + // createFungibleTokenWithCustomFees( + // HederaToken memory token, + // int64 initialTotalSupply, + // int32 decimals, + // FixedFee[] memory fixedFees, + // FractionalFee[] memory fractionalFees) + public static final int ABI_ID_CREATE_FUNGIBLE_TOKEN_WITH_FEES_V3 = 0x2af0c59a; + // createNonFungibleToken(HederaToken memory token) + public static final int ABI_ID_CREATE_NON_FUNGIBLE_TOKEN = 0x9dc711e0; + // createNonFungibleToken(HederaToken memory token) + // HederaToken field maxSupply updated to int64 + public static final int ABI_ID_CREATE_NON_FUNGIBLE_TOKEN_V2 = 0x9c89bb35; + // createNonFungibleToken(HederaToken memory token) + public static final int ABI_ID_CREATE_NON_FUNGIBLE_TOKEN_V3 = 0xea83f293; + // createNonFungibleTokenWithCustomFees( + // HederaToken memory token, + // FixedFee[] memory fixedFees, + // RoyaltyFee[] memory royaltyFees) + public static final int ABI_ID_CREATE_NON_FUNGIBLE_TOKEN_WITH_FEES = 0x5bc7c0e6; + // createNonFungibleTokenWithCustomFees( + // HederaToken memory token, + // FixedFee[] memory fixedFees, + // RoyaltyFee[] memory royaltyFees) + // HederaToken field maxSupply updated to int64 + public static final int ABI_ID_CREATE_NON_FUNGIBLE_TOKEN_WITH_FEES_V2 = 0x45733969; + // createNonFungibleTokenWithCustomFees( + // HederaToken memory token, + // FixedFee[] memory fixedFees, + // RoyaltyFee[] memory royaltyFees) + public static final int ABI_ID_CREATE_NON_FUNGIBLE_TOKEN_WITH_FEES_V3 = 0xabb54eb5; + + // **** HIP-514 function selectors **** + // getFungibleTokenInfo(address token) + public static final int ABI_ID_GET_FUNGIBLE_TOKEN_INFO = 0x3f28a19b; + // getTokenInfo(address token) + public static final int ABI_ID_GET_TOKEN_INFO = 0x1f69565f; + // getNonFungibleTokenInfo(address token, int64 serialNumber) + public static final int ABI_ID_GET_NON_FUNGIBLE_TOKEN_INFO = 0x287e1da8; + // getTokenDefaultFreezeStatus(address token) + public static final int ABI_ID_GET_TOKEN_DEFAULT_FREEZE_STATUS = 0xa7daa18d; + // getTokenDefaultKycStatus(address token) + public static final int ABI_ID_GET_TOKEN_DEFAULT_KYC_STATUS = 0x335e04c1; + // isKyc(address token, address account) + public static final int ABI_ID_IS_KYC = 0xf2c31ff4; + // grantTokenKyc(address token, address account) + public static final int ABI_ID_GRANT_TOKEN_KYC = 0x8f8d7f99; + // revokeTokenKyc(address token, address account) + public static final int ABI_ID_REVOKE_TOKEN_KYC = 0xaf99c633; + // getTokenCustomFees(address token) + public static final int ABI_ID_GET_TOKEN_CUSTOM_FEES = 0xae7611a0; + // isToken(address token) + public static final int ABI_ID_IS_TOKEN = 0x19f37361; + // getTokenType(address token) + public static final int ABI_ID_GET_TOKEN_TYPE = 0x93272baf; + // getTokenExpiryInfo(address token) + public static final int ABI_ID_GET_TOKEN_EXPIRY_INFO = 0xd614cdb8; + // updateTokenExpiryInfo(address token, Expiry expiryInfoStruct) + public static final int ABI_ID_UPDATE_TOKEN_EXPIRY_INFO = 0x593d6e82; + // updateTokenExpiryInfo(address token, Expiry expiryInfoStruct) + public static final int ABI_ID_UPDATE_TOKEN_EXPIRY_INFO_V2 = 0xd27be6cd; +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCall.java index 445b42dddf58..ee8864543caa 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCall.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/HtsCall.java @@ -18,6 +18,7 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.HederaSystemContract; import edu.umd.cs.findbugs.annotations.NonNull; +import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.precompile.PrecompiledContract.PrecompileContractResult; /** @@ -49,4 +50,9 @@ public static PricedResult gasOnly(HederaSystemContract.FullResult result) { */ @NonNull PricedResult execute(); + + @NonNull + default PricedResult execute(MessageFrame frame) { + return execute(); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/LogBuilder.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/LogBuilder.java new file mode 100644 index 000000000000..a950b1774820 --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/LogBuilder.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2023 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts; + +import com.esaulpaugh.headlong.abi.Tuple; +import com.esaulpaugh.headlong.abi.TupleType; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.log.Log; +import org.hyperledger.besu.evm.log.LogTopic; + +public class LogBuilder { + private Address logger; + private final List data = new ArrayList<>(); + private final List topics = new ArrayList<>(); + final StringBuilder tupleTypes = new StringBuilder("("); + + public static LogBuilder logBuilder() { + return new LogBuilder(); + } + + public LogBuilder forLogger(final Address logger) { + this.logger = logger; + return this; + } + + public LogBuilder forEventSignature(final Bytes eventSignature) { + topics.add(generateLogTopic(eventSignature)); + return this; + } + + public LogBuilder forDataItem(final Object dataItem) { + data.add(convertDataItem(dataItem)); + addTupleType(dataItem, tupleTypes); + return this; + } + + public LogBuilder forIndexedArgument(final Object param) { + topics.add(generateLogTopic(param)); + return this; + } + + public Log build() { + if (tupleTypes.length() > 1) { + tupleTypes.deleteCharAt(tupleTypes.length() - 1); + tupleTypes.append(")"); + final var tuple = Tuple.of(data.toArray()); + final var tupleType = TupleType.parse(tupleTypes.toString()); + return new Log(logger, Bytes.wrap(tupleType.encode(tuple).array()), topics); + } else { + return new Log(logger, Bytes.EMPTY, topics); + } + } + + private Object convertDataItem(final Object param) { + if (param instanceof Address address) { + return convertBesuAddressToHeadlongAddress(address); + } else if (param instanceof Long numeric) { + return BigInteger.valueOf(numeric); + } else { + return param; + } + } + + private static LogTopic generateLogTopic(final Object param) { + byte[] array = new byte[] {}; + if (param instanceof Address address) { + array = address.toArray(); + } else if (param instanceof BigInteger numeric) { + array = numeric.toByteArray(); + } else if (param instanceof Long numeric) { + array = BigInteger.valueOf(numeric).toByteArray(); + } else if (param instanceof Boolean bool) { + array = new byte[] {(byte) (Boolean.TRUE.equals(bool) ? 1 : 0)}; + } else if (param instanceof Bytes bytes) { + array = bytes.toArray(); + } + + return LogTopic.wrap(Bytes.wrap(expandByteArrayTo32Length(array))); + } + + private static void addTupleType(final Object param, final StringBuilder stringBuilder) { + if (param instanceof Address) { + stringBuilder.append("address,"); + } else if (param instanceof BigInteger || param instanceof Long) { + stringBuilder.append("uint256,"); + } else if (param instanceof Boolean) { + stringBuilder.append("bool,"); + } + } + + private static byte[] expandByteArrayTo32Length(final byte[] bytesToExpand) { + final byte[] expandedArray = new byte[32]; + + System.arraycopy( + bytesToExpand, 0, expandedArray, expandedArray.length - bytesToExpand.length, bytesToExpand.length); + return expandedArray; + } + + private static com.esaulpaugh.headlong.abi.Address convertBesuAddressToHeadlongAddress( + @NonNull final Address address) { + return com.esaulpaugh.headlong.abi.Address.wrap( + com.esaulpaugh.headlong.abi.Address.toChecksumAddress(address.toUnsignedBigInteger())); + } +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/grantapproval/ClassicGrantApprovalCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/grantapproval/ClassicGrantApprovalCall.java index d064f2a2e180..5928787f0aac 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/grantapproval/ClassicGrantApprovalCall.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/grantapproval/ClassicGrantApprovalCall.java @@ -20,6 +20,7 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCall.PricedResult.gasOnly; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asLongZeroAddress; import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.ResponseCodeEnum; @@ -29,10 +30,15 @@ import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator; import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategy; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.HederaSystemContract.FullResult; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AbiConstants; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.LogBuilder; import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater.Enhancement; import com.hedera.node.app.spi.workflows.record.SingleTransactionRecordBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import java.math.BigInteger; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.log.Log; public class ClassicGrantApprovalCall extends AbstractGrantApprovalCall { @@ -70,7 +76,46 @@ public PricedResult execute() { ? GrantApprovalTranslator.GRANT_APPROVAL.getOutputs().encodeElements((long) status.protoOrdinal()) : GrantApprovalTranslator.GRANT_APPROVAL_NFT.getOutputs().encodeElements((long) status.protoOrdinal()); + return gasOnly(FullResult.successResult(encodedOutput, gasRequirement)); } } + + @NonNull + @Override + public PricedResult execute(final MessageFrame frame) { + final var result = execute(); + + if (result.fullResult().result().getState().equals(MessageFrame.State.COMPLETED_SUCCESS)) { + final var tokenAddress = asLongZeroAddress(token.tokenNum()); + + if (tokenType.equals(TokenType.FUNGIBLE_COMMON)) { + frame.addLog(getLogForFungibleAdjustAllowance(tokenAddress)); + } else { + frame.addLog(getLogForNftAdjustAllowance(tokenAddress)); + } + } + + return result; + } + + private Log getLogForFungibleAdjustAllowance(final Address logger) { + return LogBuilder.logBuilder() + .forLogger(logger) + .forEventSignature(AbiConstants.APPROVAL_EVENT) + .forIndexedArgument(asLongZeroAddress(senderId.accountNum())) + .forIndexedArgument(asLongZeroAddress(spender.accountNum())) + .forDataItem(amount) + .build(); + } + + private Log getLogForNftAdjustAllowance(final Address logger) { + return LogBuilder.logBuilder() + .forLogger(logger) + .forEventSignature(AbiConstants.APPROVAL_EVENT) + .forIndexedArgument(asLongZeroAddress(senderId.accountNum())) + .forIndexedArgument(asLongZeroAddress(spender.accountNum())) + .forIndexedArgument(amount) + .build(); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/isapprovedforall/IsApprovedForAllCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/isapprovedforall/IsApprovedForAllCall.java index fb74b54b9148..7ad4d68f997d 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/isapprovedforall/IsApprovedForAllCall.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/isapprovedforall/IsApprovedForAllCall.java @@ -18,14 +18,16 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ACCOUNT_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID; +import static com.hedera.hapi.node.base.ResponseCodeEnum.SUCCESS; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HederaSystemContract.FullResult.revertResult; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.HederaSystemContract.FullResult.successResult; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.isapprovedforall.IsApprovedForAllTranslator.CLASSIC_IS_APPROVED_FOR_ALL; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.isapprovedforall.IsApprovedForAllTranslator.ERC_IS_APPROVED_FOR_ALL; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.accountNumberForEvmReference; import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; import com.esaulpaugh.headlong.abi.Address; -import com.esaulpaugh.headlong.abi.Function; import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.TokenID; import com.hedera.hapi.node.base.TokenType; @@ -34,7 +36,6 @@ import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.HederaSystemContract; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AbstractRevertibleTokenViewCall; -import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes; import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -44,21 +45,22 @@ * Implements the token redirect {@code isApprovedForAll()} call of the HTS system contract. */ public class IsApprovedForAllCall extends AbstractRevertibleTokenViewCall { - public static final Function IS_APPROVED_FOR_ALL = - new Function("isApprovedForAll(address,address)", ReturnTypes.BOOL); private final Address owner; private final Address operator; + private final boolean isErcRedirect; public IsApprovedForAllCall( @NonNull final SystemContractGasCalculator gasCalculator, @NonNull final HederaWorldUpdater.Enhancement enhancement, @Nullable final Token token, @NonNull final Address owner, - @NonNull final Address operator) { + @NonNull final Address operator, + final boolean isErcRedirect) { super(gasCalculator, enhancement, token); this.owner = requireNonNull(owner); this.operator = requireNonNull(operator); + this.isErcRedirect = isErcRedirect; } /** @@ -84,8 +86,14 @@ public IsApprovedForAllCall( AccountID.newBuilder().accountNum(operatorNum).build(), token.tokenIdOrThrow()); } - return successResult( - IS_APPROVED_FOR_ALL.getOutputs().encodeElements(verdict), gasCalculator.viewGasRequirement()); + if (isErcRedirect) { + return successResult( + ERC_IS_APPROVED_FOR_ALL.getOutputs().encodeElements(verdict), gasCalculator.viewGasRequirement()); + } else { + return successResult( + CLASSIC_IS_APPROVED_FOR_ALL.getOutputs().encodeElements((long) SUCCESS.protoOrdinal(), verdict), + gasCalculator.viewGasRequirement()); + } } private boolean operatorMatches( diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/isapprovedforall/IsApprovedForAllTranslator.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/isapprovedforall/IsApprovedForAllTranslator.java index be3b9005853e..bd410e95fda7 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/isapprovedforall/IsApprovedForAllTranslator.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/isapprovedforall/IsApprovedForAllTranslator.java @@ -16,8 +16,9 @@ package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.isapprovedforall; -import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.isapprovedforall.IsApprovedForAllCall.IS_APPROVED_FOR_ALL; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.fromHeadlongAddress; +import com.esaulpaugh.headlong.abi.Function; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AbstractHtsCallTranslator; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt; import edu.umd.cs.findbugs.annotations.NonNull; @@ -30,6 +31,10 @@ */ @Singleton public class IsApprovedForAllTranslator extends AbstractHtsCallTranslator { + public static final Function CLASSIC_IS_APPROVED_FOR_ALL = + new Function("isApprovedForAll(address,address,address)", "(int64,bool)"); + public static final Function ERC_IS_APPROVED_FOR_ALL = new Function("isApprovedForAll(address,address)", "(bool)"); + @Inject public IsApprovedForAllTranslator() { // Dagger2 @@ -40,7 +45,7 @@ public IsApprovedForAllTranslator() { */ @Override public boolean matches(@NonNull final HtsCallAttempt attempt) { - return Arrays.equals(attempt.selector(), IS_APPROVED_FOR_ALL.selector()); + return matchesClassicSelector(attempt.selector()) || matchesErcSelector(attempt.selector()); } /** @@ -48,12 +53,34 @@ public boolean matches(@NonNull final HtsCallAttempt attempt) { */ @Override public IsApprovedForAllCall callFrom(@NonNull final HtsCallAttempt attempt) { - final var args = IS_APPROVED_FOR_ALL.decodeCall(attempt.input().toArrayUnsafe()); - return new IsApprovedForAllCall( - attempt.systemContractGasCalculator(), - attempt.enhancement(), - attempt.redirectToken(), - args.get(0), - args.get(1)); + if (matchesErcSelector(attempt.selector())) { + final var args = ERC_IS_APPROVED_FOR_ALL.decodeCall(attempt.input().toArrayUnsafe()); + return new IsApprovedForAllCall( + attempt.systemContractGasCalculator(), + attempt.enhancement(), + attempt.redirectToken(), + args.get(0), + args.get(1), + true); + } else { + final var args = + CLASSIC_IS_APPROVED_FOR_ALL.decodeCall(attempt.input().toArrayUnsafe()); + final var token = attempt.linkedToken(fromHeadlongAddress(args.get(0))); + return new IsApprovedForAllCall( + attempt.systemContractGasCalculator(), + attempt.enhancement(), + token, + args.get(1), + args.get(2), + false); + } + } + + private boolean matchesClassicSelector(@NonNull final byte[] selector) { + return Arrays.equals(selector, CLASSIC_IS_APPROVED_FOR_ALL.selector()); + } + + private boolean matchesErcSelector(@NonNull final byte[] selector) { + return Arrays.equals(selector, ERC_IS_APPROVED_FOR_ALL.selector()); } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/setapproval/SetApprovalForAllCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/setapproval/SetApprovalForAllCall.java index b015d6c6368b..ed1468f47d8d 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/setapproval/SetApprovalForAllCall.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/setapproval/SetApprovalForAllCall.java @@ -19,6 +19,10 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ALLOWANCE_SPENDER_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_NOT_ASSOCIATED_TO_ACCOUNT; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AbiConstants.APPROVAL_FOR_ALL_EVENT; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.setapproval.SetApprovalForAllTranslator.SET_APPROVAL_FOR_ALL; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asLongZeroAddress; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.fromHeadlongAddress; import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.ResponseCodeEnum; @@ -27,8 +31,12 @@ import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategy; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AbstractHtsCall; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.LogBuilder; import com.hedera.node.app.spi.workflows.record.SingleTransactionRecordBuilder; import edu.umd.cs.findbugs.annotations.NonNull; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.log.Log; // @Future remove to revert #9214 after modularization is completed public class SetApprovalForAllCall extends AbstractHtsCall { @@ -37,6 +45,9 @@ public class SetApprovalForAllCall extends AbstractHtsCall { private final TransactionBody transactionBody; private final AccountID sender; private final DispatchGasCalculator dispatchGasCalculator; + private final Address token; + private final Address spender; + private final boolean approved; public SetApprovalForAllCall( @NonNull final HtsCallAttempt attempt, @@ -47,10 +58,17 @@ public SetApprovalForAllCall( this.dispatchGasCalculator = gasCalculator; this.verificationStrategy = attempt.defaultVerificationStrategy(); this.sender = attempt.addressIdConverter().convertSender(attempt.senderAddress()); + + final var call = SET_APPROVAL_FOR_ALL.decodeCall(attempt.inputBytes()); + + this.token = fromHeadlongAddress(call.get(0)); + this.spender = fromHeadlongAddress(call.get(1)); + this.approved = call.get(2); } + @NonNull @Override - public @NonNull PricedResult execute() { + public PricedResult execute() { final var recordBuilder = systemContractOperations() .dispatch(transactionBody, verificationStrategy, sender, SingleTransactionRecordBuilder.class); @@ -71,4 +89,25 @@ public SetApprovalForAllCall( return completionWith(status, gasRequirement); } } + + @Override + public @NonNull PricedResult execute(final MessageFrame frame) { + final var result = execute(); + + if (result.fullResult().result().getState().equals(MessageFrame.State.COMPLETED_SUCCESS)) { + frame.addLog(getLogForSetApprovalForAll(token)); + } + + return result; + } + + private Log getLogForSetApprovalForAll(final Address logger) { + return LogBuilder.logBuilder() + .forLogger(logger) + .forEventSignature(APPROVAL_FOR_ALL_EVENT) + .forIndexedArgument(asLongZeroAddress(sender.accountNum())) + .forIndexedArgument(spender) + .forDataItem(approved) + .build(); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/HtsSystemContractTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/HtsSystemContractTest.java index c86299050bc7..7e5274bcb32e 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/HtsSystemContractTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/HtsSystemContractTest.java @@ -77,7 +77,7 @@ void returnsResultFromImpliedCall() { givenValidCallAttempt(); final var pricedResult = gasOnly(successResult(ByteBuffer.allocate(1), 123L)); - given(call.execute()).willReturn(pricedResult); + given(call.execute(frame)).willReturn(pricedResult); assertSame(pricedResult.fullResult(), subject.computeFully(Bytes.EMPTY, frame)); } @@ -94,7 +94,7 @@ void invalidCallAttemptHaltsAndConsumesRemainingGas() { @Test void internalErrorAttemptHaltsAndConsumesRemainingGas() { givenValidCallAttempt(); - given(call.execute()).willThrow(RuntimeException.class); + given(call.execute(frame)).willThrow(RuntimeException.class); final var expected = haltResult(ExceptionalHaltReason.PRECOMPILE_ERROR, frame.getRemainingGas()); final var result = subject.computeFully(Bytes.EMPTY, frame); @@ -105,7 +105,7 @@ void internalErrorAttemptHaltsAndConsumesRemainingGas() { void callWithNonGasCostNotImplemented() { givenValidCallAttempt(); final var pricedResult = new HtsCall.PricedResult(successResult(ByteBuffer.allocate(1), 123L), 456L); - given(call.execute()).willReturn(pricedResult); + given(call.execute(frame)).willReturn(pricedResult); assertThrows(AssertionError.class, () -> subject.computeFully(Bytes.EMPTY, frame)); } 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 eb26b5ae3801..56cfb58447ff 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 @@ -236,10 +236,10 @@ void constructsBalanceOf() { } @Test - void constructsIsOperator() { + void constructsIsApprovedForAllErc() { final var address = asHeadlongAddress(EIP_1014_ADDRESS); final var input = TestHelpers.bytesForRedirect( - IsApprovedForAllCall.IS_APPROVED_FOR_ALL + IsApprovedForAllTranslator.ERC_IS_APPROVED_FOR_ALL .encodeCallWithArgs(address, address) .array(), NON_SYSTEM_LONG_ZERO_ADDRESS); @@ -257,6 +257,26 @@ void constructsIsOperator() { assertInstanceOf(IsApprovedForAllCall.class, subject.asExecutableCall()); } + @Test + void constructsIsApprovedForAllClassic() { + final var address = asHeadlongAddress(EIP_1014_ADDRESS); + final var input = Bytes.wrap(IsApprovedForAllTranslator.CLASSIC_IS_APPROVED_FOR_ALL + .encodeCallWithArgs(address, address, address) + .array()); + final var subject = new HtsCallAttempt( + input, + EIP_1014_ADDRESS, + false, + mockEnhancement(), + DEFAULT_CONFIG, + addressIdConverter, + verificationStrategies, + gasCalculator, + callTranslators, + false); + assertInstanceOf(IsApprovedForAllCall.class, subject.asExecutableCall()); + } + @Test void constructsTotalSupply() { final var input = TestHelpers.bytesForRedirect( diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallTestBase.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallTestBase.java index 15d8048c98eb..1f0c53989de5 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallTestBase.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/HtsCallTestBase.java @@ -21,6 +21,7 @@ import com.hedera.node.app.service.contract.impl.exec.scope.HederaOperations; import com.hedera.node.app.service.contract.impl.exec.scope.SystemContractOperations; import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater; +import org.hyperledger.besu.evm.frame.MessageFrame; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -39,6 +40,9 @@ public class HtsCallTestBase { @Mock protected SystemContractGasCalculator gasCalculator; + @Mock + protected MessageFrame frame; + protected HederaWorldUpdater.Enhancement mockEnhancement() { return new HederaWorldUpdater.Enhancement(operations, nativeOperations, systemContractOperations); } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/grantapproval/ClassicGrantApprovalCallTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/grantapproval/ClassicGrantApprovalCallTest.java index dd48b40b3233..b715def87aed 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/grantapproval/ClassicGrantApprovalCallTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/grantapproval/ClassicGrantApprovalCallTest.java @@ -64,7 +64,7 @@ void fungibleApprove() { TokenType.FUNGIBLE_COMMON); given(systemContractOperations.dispatch(any(), any(), any(), any())).willReturn(recordBuilder); given(recordBuilder.status()).willReturn(ResponseCodeEnum.SUCCESS); - final var result = subject.execute().fullResult().result(); + final var result = subject.execute(frame).fullResult().result(); assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); assertEquals( @@ -87,7 +87,7 @@ void nftApprove() { TokenType.NON_FUNGIBLE_UNIQUE); given(systemContractOperations.dispatch(any(), any(), any(), any())).willReturn(recordBuilder); given(recordBuilder.status()).willReturn(ResponseCodeEnum.SUCCESS); - final var result = subject.execute().fullResult().result(); + final var result = subject.execute(frame).fullResult().result(); assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); assertEquals( diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/isoperator/IsApprovedForAllCallTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/isoperator/IsApprovedForAllCallTest.java index c64b04647e31..4171d0e8d807 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/isoperator/IsApprovedForAllCallTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/isoperator/IsApprovedForAllCallTest.java @@ -18,6 +18,7 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ACCOUNT_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID; +import static com.hedera.hapi.node.base.ResponseCodeEnum.SUCCESS; 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.FUNGIBLE_TOKEN; @@ -32,7 +33,10 @@ import com.esaulpaugh.headlong.abi.Address; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.isapprovedforall.IsApprovedForAllCall; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.isapprovedforall.IsApprovedForAllTranslator; import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.HtsCallTestBase; +import edu.umd.cs.findbugs.annotations.NonNull; +import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.evm.frame.MessageFrame; import org.junit.jupiter.api.Test; @@ -43,7 +47,8 @@ class IsApprovedForAllCallTest extends HtsCallTestBase { @Test void revertsWithFungibleToken() { - subject = new IsApprovedForAllCall(gasCalculator, mockEnhancement(), FUNGIBLE_TOKEN, THE_OWNER, THE_OPERATOR); + subject = new IsApprovedForAllCall( + gasCalculator, mockEnhancement(), FUNGIBLE_TOKEN, THE_OWNER, THE_OPERATOR, false); final var result = subject.execute().fullResult().result(); @@ -53,8 +58,8 @@ void revertsWithFungibleToken() { @Test void revertsWithMissingOwner() { - subject = - new IsApprovedForAllCall(gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, THE_OWNER, THE_OPERATOR); + subject = new IsApprovedForAllCall( + gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, THE_OWNER, THE_OPERATOR, false); final var result = subject.execute().fullResult().result(); @@ -64,30 +69,39 @@ void revertsWithMissingOwner() { @Test void checksForPresentOwnerAndFindsApprovedOperator() { - subject = - new IsApprovedForAllCall(gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, THE_OWNER, THE_OPERATOR); + subject = new IsApprovedForAllCall( + gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, THE_OWNER, THE_OPERATOR, false); given(nativeOperations.getAccount(A_NEW_ACCOUNT_ID.accountNumOrThrow())).willReturn(SOMEBODY); given(nativeOperations.getAccount(B_NEW_ACCOUNT_ID.accountNumOrThrow())).willReturn(OPERATOR); final var result = subject.execute().fullResult().result(); assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); - final boolean verdict = IsApprovedForAllCall.IS_APPROVED_FOR_ALL - .getOutputs() - .decode(result.getOutput().toArray()) - .get(0); - assertTrue(verdict); + assertHasSuccessVerdict(true, result.getOutput()); } @Test void checksForPresentOwnerAndDetectsNoOperator() { - subject = new IsApprovedForAllCall(gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, THE_OWNER, THE_OWNER); + subject = new IsApprovedForAllCall( + gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, THE_OWNER, THE_OWNER, false); + given(nativeOperations.getAccount(A_NEW_ACCOUNT_ID.accountNumOrThrow())).willReturn(SOMEBODY); + + final var result = subject.execute().fullResult().result(); + + assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); + assertHasSuccessVerdict(false, result.getOutput()); + } + + @Test + void ercChecksForPresentOwnerAndDetectsNoOperator() { + subject = new IsApprovedForAllCall( + gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, THE_OWNER, THE_OWNER, true); given(nativeOperations.getAccount(A_NEW_ACCOUNT_ID.accountNumOrThrow())).willReturn(SOMEBODY); final var result = subject.execute().fullResult().result(); assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); - final boolean verdict = IsApprovedForAllCall.IS_APPROVED_FOR_ALL + final boolean verdict = IsApprovedForAllTranslator.ERC_IS_APPROVED_FOR_ALL .getOutputs() .decode(result.getOutput().toArray()) .get(0); @@ -96,17 +110,25 @@ void checksForPresentOwnerAndDetectsNoOperator() { @Test void returnsFalseForPresentOwnerAndMissingOperator() { - subject = - new IsApprovedForAllCall(gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, THE_OWNER, THE_OPERATOR); + subject = new IsApprovedForAllCall( + gasCalculator, mockEnhancement(), NON_FUNGIBLE_TOKEN, THE_OWNER, THE_OPERATOR, false); given(nativeOperations.getAccount(A_NEW_ACCOUNT_ID.accountNumOrThrow())).willReturn(SOMEBODY); final var result = subject.execute().fullResult().result(); assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); - final boolean verdict = IsApprovedForAllCall.IS_APPROVED_FOR_ALL + assertHasSuccessVerdict(false, result.getOutput()); + } + + private void assertHasSuccessVerdict(final boolean verdict, @NonNull final Bytes output) { + final var outputs = IsApprovedForAllTranslator.CLASSIC_IS_APPROVED_FOR_ALL .getOutputs() - .decode(result.getOutput().toArray()) - .get(0); - assertFalse(verdict); + .decode(output.toArray()); + assertEquals(SUCCESS.protoOrdinal(), (long) outputs.get(0)); + if (verdict) { + assertTrue((boolean) outputs.get(1)); + } else { + assertFalse((boolean) outputs.get(1)); + } } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/setapproval/SetApprovalForAllCallTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/setapproval/SetApprovalForAllCallTest.java index 5db319ca8217..92a34164960f 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/setapproval/SetApprovalForAllCallTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/setapproval/SetApprovalForAllCallTest.java @@ -16,13 +16,16 @@ package com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.setapproval; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.FUNGIBLE_TOKEN_HEADLONG_ADDRESS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.OWNER_ID; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.UNAUTHORIZED_SPENDER_HEADLONG_ADDRESS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.asBytesResult; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.revertOutputFor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import com.esaulpaugh.headlong.abi.Tuple; import com.hedera.hapi.node.base.ResponseCodeEnum; import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator; @@ -33,6 +36,7 @@ import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.HtsCallTestBase; import com.hedera.node.app.service.token.records.CryptoTransferRecordBuilder; import java.math.BigInteger; +import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.precompile.PrecompiledContract.PrecompileContractResult; import org.junit.jupiter.api.BeforeEach; @@ -60,10 +64,17 @@ public class SetApprovalForAllCallTest extends HtsCallTestBase { @BeforeEach void setup() { + final Tuple tuple = + new Tuple(FUNGIBLE_TOKEN_HEADLONG_ADDRESS, UNAUTHORIZED_SPENDER_HEADLONG_ADDRESS, Boolean.TRUE); + final byte[] inputBytes = Bytes.wrapByteBuffer( + SetApprovalForAllTranslator.SET_APPROVAL_FOR_ALL.encodeCall(tuple)) + .toArray(); + given(attempt.enhancement()).willReturn(mockEnhancement()); given(attempt.addressIdConverter()).willReturn(addressIdConverter); given(attempt.addressIdConverter().convertSender(any())).willReturn(OWNER_ID); given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + given(attempt.inputBytes()).willReturn(inputBytes); subject = new SetApprovalForAllCall( attempt, TransactionBody.newBuilder().build(), SetApprovalForAllTranslator::gasRequirement); @@ -77,7 +88,7 @@ void setApprovalForAllCall_works() { given(recordBuilder.status()).willReturn(ResponseCodeEnum.SUCCESS); // When - final var result = subject.execute().fullResult().result(); + final var result = subject.execute(frame).fullResult().result(); // Then verifyResultStatus(result, ResponseCodeEnum.SUCCESS); @@ -89,7 +100,7 @@ void setApprovalForAllCallBadStatus_reverts() { given(recordBuilder.status()).willReturn(ResponseCodeEnum.ACCOUNT_DELETED); // When - final var result = subject.execute().fullResult().result(); + final var result = subject.execute(frame).fullResult().result(); // Then assertEquals(MessageFrame.State.REVERT, result.getState()); @@ -102,7 +113,7 @@ void setApprovalForAllCallInvalidToken_success() { given(recordBuilder.status()).willReturn(ResponseCodeEnum.INVALID_TOKEN_ID); // When - final var result = subject.execute().fullResult().result(); + final var result = subject.execute(frame).fullResult().result(); // Then verifyResultStatus(result, ResponseCodeEnum.INVALID_TOKEN_ID); @@ -114,7 +125,7 @@ void setApprovalForAllCallInvalidAccount_success() { given(recordBuilder.status()).willReturn(ResponseCodeEnum.INVALID_ALLOWANCE_SPENDER_ID); // When - final var result = subject.execute().fullResult().result(); + final var result = subject.execute(frame).fullResult().result(); // Then assertEquals(MessageFrame.State.REVERT, result.getState()); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/ApproveAllowanceSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/ApproveAllowanceSuite.java index cafa8d6303bf..aab31e2e74cd 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/ApproveAllowanceSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/ApproveAllowanceSuite.java @@ -126,6 +126,7 @@ public boolean canRunConcurrent() { return true; } + @HapiTest private HapiSpec htsTokenApproveToInnerContract() { final var approveTxn = "NestedChildren"; final var nestedContract = DIRECT_ERC_CALLEE; @@ -192,6 +193,7 @@ private HapiSpec htsTokenApproveToInnerContract() { })); } + @HapiTest private HapiSpec htsTokenAllowance() { final var theSpender = SPENDER; final var allowanceTxn = ALLOWANCE_TX; @@ -248,6 +250,7 @@ private HapiSpec htsTokenAllowance() { .withAllowance(2))))); } + @HapiTest private HapiSpec htsTokenApprove() { final var approveTxn = "approveTxn"; final var theSpender = SPENDER; @@ -308,6 +311,7 @@ private HapiSpec htsTokenApprove() { })); } + @HapiTest private HapiSpec hapiNftIsApprovedForAll() { final var notApprovedTxn = "notApprovedTxn"; final var approvedForAllTxn = "approvedForAllTxn"; @@ -395,6 +399,7 @@ private HapiSpec hapiNftIsApprovedForAll() { .withIsApprovedForAll(SUCCESS, false))))); } + @HapiTest private HapiSpec hapiNftGetApproved() { final var theSpender = SPENDER; final var theSpender2 = "spender2"; @@ -454,6 +459,7 @@ private HapiSpec hapiNftGetApproved() { .getAccountID(theSpender))))))))); } + @HapiTest private HapiSpec hapiNftSetApprovalForAll() { final var theSpender = SPENDER; final var theSpender2 = "spender2";