From 4f9b8f013003bccd0ee68ffa0d1cf4d4410e2259 Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Tue, 5 Dec 2023 20:46:11 -0600 Subject: [PATCH 1/9] Fix owner/spender addresses returned by GetNftTokenInfo Signed-off-by: Michael Tinker --- .../exec/systemcontracts/hts/ReturnTypes.java | 4 +++ .../systemcontracts/hts/TokenTupleUtils.java | 25 ++++++++++++++++--- .../hts/nfttokeninfo/NftTokenInfoCall.java | 14 ++++++++--- .../CryptoDeleteAllowanceHandler.java | 2 +- .../impl/handlers/TokenGetNftInfoHandler.java | 4 +-- .../transfer/NFTOwnersChangeStep.java | 12 +++------ .../CryptoDeleteAllowanceHandlerTest.java | 20 +++++++-------- .../contract/hapi/ContractCallLocalSuite.java | 1 + 8 files changed, 52 insertions(+), 30 deletions(-) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ReturnTypes.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ReturnTypes.java index 3822a478cf78..644d31fe0357 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ReturnTypes.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ReturnTypes.java @@ -18,8 +18,11 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_FULL_PREFIX_SIGNATURE_FOR_PRECOMPILE; import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_SIGNATURE; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asEvmAddress; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asHeadlongAddress; import static java.util.Objects.requireNonNull; +import com.esaulpaugh.headlong.abi.Address; import com.esaulpaugh.headlong.abi.TupleType; import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.ContractID; @@ -42,6 +45,7 @@ private ReturnTypes() { // When no value is set for AccountID, ContractID or TokenId the return value is set to 0. public static final AccountID ZERO_ACCOUNT_ID = AccountID.newBuilder().accountNum(0).build(); + public static final Address ZERO_ADDRESS = asHeadlongAddress(asEvmAddress(0L)); public static final ContractID ZERO_CONTRACT_ID = ContractID.newBuilder().contractNum(0).build(); public static final TokenID ZERO_TOKEN_ID = TokenID.newBuilder().tokenNum(0).build(); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java index 2d68c4575261..1f6bd30fb115 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java @@ -17,13 +17,17 @@ package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.ZERO_ACCOUNT_ID; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.ZERO_ADDRESS; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.ZERO_CONTRACT_ID; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.ZERO_FIXED_FEE; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.ZERO_FRACTION; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.ZERO_TOKEN_ID; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.headlongAddressOf; +import static java.util.Objects.requireNonNull; +import com.esaulpaugh.headlong.abi.Address; import com.esaulpaugh.headlong.abi.Tuple; +import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.Key; import com.hedera.hapi.node.base.Timestamp; import com.hedera.hapi.node.state.token.Nft; @@ -32,6 +36,7 @@ import com.hedera.hapi.node.transaction.FixedFee; import com.hedera.hapi.node.transaction.FractionalFee; import com.hedera.hapi.node.transaction.RoyaltyFee; +import com.hedera.node.app.service.contract.impl.exec.scope.HederaNativeOperations; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.hederahashgraph.api.proto.java.TokenSupplyType; import edu.umd.cs.findbugs.annotations.NonNull; @@ -230,17 +235,29 @@ public static Tuple nftTokenInfoTupleFor( @NonNull final Token token, @NonNull final Nft nft, final long serialNumber, - @NonNull final String ledgerId) { + @NonNull final String ledgerId, + @NonNull final HederaNativeOperations nativeOperations) { + requireNonNull(nft); + requireNonNull(token); + requireNonNull(ledgerId); + requireNonNull(nativeOperations); final var nftMetaData = nft.metadata() != null ? nft.metadata().toByteArray() : Bytes.EMPTY.toByteArray(); - return Tuple.of( tokenInfoTupleFor(token, ledgerId), serialNumber, - headlongAddressOf(nft.ownerIdOrElse(ZERO_ACCOUNT_ID)), + priorityAddressOf(nft.ownerIdOrElse(token.treasuryAccountIdOrThrow()), nativeOperations), nft.mintTimeOrElse(new Timestamp(0, 0)).seconds(), nftMetaData, - headlongAddressOf(nft.spenderIdOrElse(ZERO_ACCOUNT_ID))); + priorityAddressOf(nft.spenderIdOrElse(ZERO_ACCOUNT_ID), nativeOperations)); + } + + private static Address priorityAddressOf( + @NonNull final AccountID accountId, @NonNull final HederaNativeOperations nativeOperations) { + requireNonNull(accountId); + return (ZERO_ACCOUNT_ID == accountId) + ? ZERO_ADDRESS + : headlongAddressOf(requireNonNull(nativeOperations.getAccount(accountId.accountNumOrThrow()))); } /** diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCall.java index 08cfd1811fee..310c69ccd773 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCall.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCall.java @@ -35,9 +35,12 @@ import com.swirlds.config.api.Configuration; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; public class NftTokenInfoCall extends AbstractNonRevertibleTokenViewCall { + private static final Logger logger = LogManager.getLogger(NftTokenInfoCall.class); private final Configuration configuration; private final boolean isStaticCall; private final long serialNumber; @@ -88,13 +91,16 @@ public NftTokenInfoCall( } final var nonNullNft = nft != null ? nft : Nft.DEFAULT; + logger.info( + "NFT token info call for token {} and serial number {} " + "returned nft {}", + token.tokenIdOrElse(ZERO_TOKEN_ID).tokenNum(), + serialNumber, + nft); final var ledgerConfig = configuration.getConfigData(LedgerConfig.class); final var ledgerId = Bytes.wrap(ledgerConfig.id().toByteArray()).toString(); + final var nftTokenInfo = nftTokenInfoTupleFor(token, nonNullNft, serialNumber, ledgerId, nativeOperations()); return successResult( - NON_FUNGIBLE_TOKEN_INFO - .getOutputs() - .encodeElements( - status.protoOrdinal(), nftTokenInfoTupleFor(token, nonNullNft, serialNumber, ledgerId)), + NON_FUNGIBLE_TOKEN_INFO.getOutputs().encodeElements(status.protoOrdinal(), nftTokenInfo), gasRequirement); } } diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java index 82816bd488ce..5fa6c98ce35d 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java @@ -174,7 +174,7 @@ private void deleteNftSerials( // Clear spender on the nft // sets account number to AccountIDProtoCodec.ACCOUNT_UNSET - final var copy = nft.copyBuilder().spenderId(AccountID.DEFAULT).build(); + final var copy = nft.copyBuilder().spenderId((AccountID) null).build(); nftStore.put(copy); } } diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenGetNftInfoHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenGetNftInfoHandler.java index 9e74a1211145..d4765559d6da 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenGetNftInfoHandler.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenGetNftInfoHandler.java @@ -135,14 +135,14 @@ private Optional infoForNft( requireNonNull(config); final var nft = readableNftStore.get(nftId.tokenId(), nftId.serialNumber()); - final var token = readableTokenStore.get(nftId.tokenId()); + final var token = requireNonNull(readableTokenStore).get(nftId.tokenId()); if (nft == null) { return Optional.empty(); } else { final var info = TokenNftInfo.newBuilder() .ledgerId(config.id()) .nftID(nftId) - .accountID(nft.hasOwnerId() ? nft.ownerId() : token.treasuryAccountId()) + .accountID(nft.ownerIdOrElse(token.treasuryAccountIdOrThrow())) .creationTime(nft.mintTime()) .metadata(nft.metadata()) .spenderId(nft.spenderId()) diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/transfer/NFTOwnersChangeStep.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/transfer/NFTOwnersChangeStep.java index 8e9b9011d46d..c46e058d36d9 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/transfer/NFTOwnersChangeStep.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/transfer/NFTOwnersChangeStep.java @@ -87,18 +87,12 @@ public void doIn(final TransferContext transferContext) { validateSpenderHasAllowance(senderAccount, topLevelPayer, tokenId, nft); } - final var copyNft = nft.copyBuilder(); - // If the nft owner is not set then set it to the treasury account - if (!nft.hasOwnerId() || nft.ownerId().equals(AccountID.DEFAULT)) { - copyNft.ownerId(treasury); - } // owner of nft should match the sender in transfer list if (nft.hasOwnerId()) { validateTrue(nft.ownerId().equals(senderId), SENDER_DOES_NOT_OWN_NFT_SERIAL_NO); } else { validateTrue(treasury.equals(senderId), SENDER_DOES_NOT_OWN_NFT_SERIAL_NO); } - nftStore.put(copyNft.build()); // Update the ownership of the nft updateOwnership( @@ -174,14 +168,14 @@ private void updateOwnership( final var toNumPositiveBalances = receiverAccount.numberPositiveBalances(); final var isTreasuryReturn = treasuryId.equals(receiverAccount.accountId()); - // If the token is being returned back to treasury set the owner to treasury + // If the token is being returned back to treasury null out the owner if (isTreasuryReturn) { - nftCopy.ownerId(AccountID.DEFAULT); + nftCopy.ownerId((AccountID) null); } else { nftCopy.ownerId(receiverAccount.accountId()); } // wipe the spender on this NFT - nftCopy.spenderId(AccountID.DEFAULT); + nftCopy.spenderId((AccountID) null); // adjust number of positive balances final var updatedFromPositiveBalances = diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java index d95ecb99ee38..d2001cf4a35f 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteAllowanceHandlerTest.java @@ -113,8 +113,8 @@ void happyPathDeletesAllowances() { assertThat(ownerAccount.approveForAllNftAllowances()).hasSize(1); assertThat(writableNftStore.get(nftIdSl1).ownerId()).isEqualTo(ownerId); assertThat(writableNftStore.get(nftIdSl2).ownerId()).isEqualTo(ownerId); - assertThat(writableNftStore.get(nftIdSl1).spenderId().accountNum()).isNull(); - assertThat(writableNftStore.get(nftIdSl2).spenderId().accountNum()).isNull(); + assertThat(writableNftStore.get(nftIdSl1).spenderId()).isNull(); + assertThat(writableNftStore.get(nftIdSl2).spenderId()).isNull(); } @Test @@ -137,8 +137,8 @@ void canDeleteAllowancesOnTreasury() { assertThat(ownerAccount.approveForAllNftAllowances()).hasSize(1); assertThat(writableNftStore.get(nftIdSl1).ownerId()).isEqualTo(ownerId); assertThat(writableNftStore.get(nftIdSl2).ownerId()).isEqualTo(ownerId); - assertThat(writableNftStore.get(nftIdSl1).spenderId().accountNum()).isNull(); - assertThat(writableNftStore.get(nftIdSl2).spenderId().accountNum()).isNull(); + assertThat(writableNftStore.get(nftIdSl1).spenderId()).isNull(); + assertThat(writableNftStore.get(nftIdSl2).spenderId()).isNull(); } @Test @@ -189,8 +189,8 @@ void failsDeleteAllowancesOnInvalidTreasury() { assertThat(ownerAccount.approveForAllNftAllowances()).hasSize(1); assertThat(writableNftStore.get(nftIdSl1).ownerId()).isEqualTo(ownerId); assertThat(writableNftStore.get(nftIdSl2).ownerId()).isEqualTo(ownerId); - assertThat(writableNftStore.get(nftIdSl1).spenderId().accountNum()).isNull(); - assertThat(writableNftStore.get(nftIdSl2).spenderId().accountNum()).isNull(); + assertThat(writableNftStore.get(nftIdSl1).spenderId()).isNull(); + assertThat(writableNftStore.get(nftIdSl2).spenderId()).isNull(); } @Test @@ -219,8 +219,8 @@ void doesntThrowIfAllowanceToBeDeletedDoesNotExist() { assertThat(ownerAccount.approveForAllNftAllowances()).hasSize(1); assertThat(writableNftStore.get(nftIdSl1).ownerId()).isEqualTo(ownerId); assertThat(writableNftStore.get(nftIdSl2).ownerId()).isEqualTo(ownerId); - assertThat(writableNftStore.get(nftIdSl1).spenderId().accountNum()).isNull(); - assertThat(writableNftStore.get(nftIdSl2).spenderId().accountNum()).isNull(); + assertThat(writableNftStore.get(nftIdSl1).spenderId()).isNull(); + assertThat(writableNftStore.get(nftIdSl2).spenderId()).isNull(); } @Test @@ -273,8 +273,8 @@ void considersPayerIfOwnerNotSpecified() { assertThat(ownerAccount.approveForAllNftAllowances()).hasSize(1); assertThat(writableNftStore.get(nftIdSl1).ownerId()).isEqualTo(payerId); assertThat(writableNftStore.get(nftIdSl2).ownerId()).isEqualTo(payerId); - assertThat(writableNftStore.get(nftIdSl1).spenderId().accountNum()).isNull(); - assertThat(writableNftStore.get(nftIdSl2).spenderId().accountNum()).isNull(); + assertThat(writableNftStore.get(nftIdSl1).spenderId()).isNull(); + assertThat(writableNftStore.get(nftIdSl2).spenderId()).isNull(); } private TransactionBody cryptoDeleteAllowanceTransaction(final AccountID txnPayer) { diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCallLocalSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCallLocalSuite.java index 249f67e3591f..890d553d6bb8 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCallLocalSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractCallLocalSuite.java @@ -110,6 +110,7 @@ public List getSpecsInSuite() { htsOwnershipCheckWorksWithAliasAddress()); } + @HapiTest private HapiSpec htsOwnershipCheckWorksWithAliasAddress() { final AtomicReference ecdsaAccountId = new AtomicReference<>(); final AtomicReference ecdsaAccountIdLongZeroAddress = new AtomicReference<>(); From 403c381777b626dba637a4f23edee0f7f1a91d4c Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Tue, 5 Dec 2023 20:48:53 -0600 Subject: [PATCH 2/9] spotless Signed-off-by: Michael Tinker --- .../hts/nfttokeninfo/NftTokenInfoCall.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCall.java index 310c69ccd773..d203e7731a91 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCall.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCall.java @@ -35,12 +35,9 @@ import com.swirlds.config.api.Configuration; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; public class NftTokenInfoCall extends AbstractNonRevertibleTokenViewCall { - private static final Logger logger = LogManager.getLogger(NftTokenInfoCall.class); private final Configuration configuration; private final boolean isStaticCall; private final long serialNumber; @@ -91,11 +88,6 @@ public NftTokenInfoCall( } final var nonNullNft = nft != null ? nft : Nft.DEFAULT; - logger.info( - "NFT token info call for token {} and serial number {} " + "returned nft {}", - token.tokenIdOrElse(ZERO_TOKEN_ID).tokenNum(), - serialNumber, - nft); final var ledgerConfig = configuration.getConfigData(LedgerConfig.class); final var ledgerId = Bytes.wrap(ledgerConfig.id().toByteArray()).toString(); final var nftTokenInfo = nftTokenInfoTupleFor(token, nonNullNft, serialNumber, ledgerId, nativeOperations()); From 1525d03564bb4c64a8104949ef69615eae10b9b2 Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Tue, 5 Dec 2023 22:37:00 -0600 Subject: [PATCH 3/9] Avoid NPE for missing token Signed-off-by: Michael Tinker --- .../impl/exec/systemcontracts/hts/TokenTupleUtils.java | 2 +- .../hts/nfttokeninfo/NftTokenInfoCallTest.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java index 1f6bd30fb115..851f55edd9f0 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java @@ -246,7 +246,7 @@ public static Tuple nftTokenInfoTupleFor( return Tuple.of( tokenInfoTupleFor(token, ledgerId), serialNumber, - priorityAddressOf(nft.ownerIdOrElse(token.treasuryAccountIdOrThrow()), nativeOperations), + priorityAddressOf(nft.ownerIdOrElse(token.treasuryAccountIdOrElse(ZERO_ACCOUNT_ID)), nativeOperations), nft.mintTimeOrElse(new Timestamp(0, 0)).seconds(), nftMetaData, priorityAddressOf(nft.spenderIdOrElse(ZERO_ACCOUNT_ID), nativeOperations)); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCallTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCallTest.java index f6ce2c30e3bf..fc3142ef6cd6 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCallTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/nfttokeninfo/NftTokenInfoCallTest.java @@ -27,7 +27,9 @@ import static com.hedera.node.app.service.contract.impl.test.TestHelpers.EXPECTE_KEYLIST; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.FUNGIBLE_EVERYTHING_TOKEN; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.LEDGER_ID; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.OPERATOR; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.SENDER_ID; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.SOMEBODY; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.revertOutputFor; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.headlongAddressOf; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -64,6 +66,11 @@ void returnsNftTokenInfoStatusForPresentToken() { when(nativeOperations.getNft(FUNGIBLE_EVERYTHING_TOKEN.tokenId().tokenNum(), 2L)) .thenReturn(CIVILIAN_OWNED_NFT); + when(nativeOperations.getAccount(CIVILIAN_OWNED_NFT.ownerIdOrThrow().accountNumOrThrow())) + .thenReturn(SOMEBODY); + when(nativeOperations.getAccount(CIVILIAN_OWNED_NFT.spenderIdOrThrow().accountNumOrThrow())) + .thenReturn(OPERATOR); + final var subject = new NftTokenInfoCall(gasCalculator, mockEnhancement(), false, FUNGIBLE_EVERYTHING_TOKEN, 2L, config); From d003694515af0dd077c94c11fc367059256d5c84 Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Wed, 6 Dec 2023 10:00:55 -0600 Subject: [PATCH 4/9] Enable fridayThe13thSpec() Signed-off-by: Michael Tinker --- .../contract/impl/handlers/ContractUpdateHandler.java | 8 +++++++- .../token/impl/handlers/CryptoDeleteAllowanceHandler.java | 6 ++---- .../token/impl/handlers/TokenGetNftInfoHandler.java | 6 +++--- .../suites/contract/hapi/ContractMusicalChairsSuite.java | 4 ++++ .../bdd/suites/contract/hapi/ContractUpdateSuite.java | 1 + 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractUpdateHandler.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractUpdateHandler.java index dce7d235cadf..e262b76ffe8b 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractUpdateHandler.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractUpdateHandler.java @@ -75,7 +75,13 @@ public void preHandle(@NonNull final PreHandleContext context) throws PreCheckEx final var op = context.body().contractUpdateInstanceOrThrow(); if (isAdminSigRequired(op)) { - context.requireKeyOrThrow(op.contractIDOrElse(ContractID.DEFAULT), INVALID_CONTRACT_ID); + final var accountStore = context.createStore(ReadableAccountStore.class); + final var targetId = op.contractIDOrElse(ContractID.DEFAULT); + final var maybeContract = accountStore.getContractById(targetId); + if (maybeContract != null && maybeContract.keyOrThrow().key().kind() == Key.KeyOneOfType.CONTRACT_ID) { + throw new PreCheckException(MODIFYING_IMMUTABLE_CONTRACT); + } + context.requireKeyOrThrow(targetId, INVALID_CONTRACT_ID); } if (hasCryptoAdminKey(op)) { context.requireKey(op.adminKeyOrThrow()); diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java index 5fa6c98ce35d..6ab784ecfe85 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoDeleteAllowanceHandler.java @@ -172,10 +172,8 @@ private void deleteNftSerials( final AccountID accountOwner = owner.accountId(); validateTrue(isValidOwner(nft, accountOwner, token), SENDER_DOES_NOT_OWN_NFT_SERIAL_NO); - // Clear spender on the nft - // sets account number to AccountIDProtoCodec.ACCOUNT_UNSET - final var copy = nft.copyBuilder().spenderId((AccountID) null).build(); - nftStore.put(copy); + // Null out spender on the nft + nftStore.put(nft.copyBuilder().spenderId((AccountID) null).build()); } } } diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenGetNftInfoHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenGetNftInfoHandler.java index d4765559d6da..71ebcf94fce6 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenGetNftInfoHandler.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/TokenGetNftInfoHandler.java @@ -80,7 +80,7 @@ public void validate(@NonNull final QueryContext context) throws PreCheckExcepti validateTruePreCheck(nftId.hasTokenId(), INVALID_TOKEN_ID); validateTruePreCheck(nftId.serialNumber() > 0, INVALID_TOKEN_NFT_SERIAL_NUMBER); - final var nft = nftStore.get(nftId.tokenId(), nftId.serialNumber()); + final var nft = nftStore.get(nftId.tokenIdOrThrow(), nftId.serialNumber()); validateFalsePreCheck(nft == null, INVALID_NFT_ID); } @@ -134,8 +134,8 @@ private Optional infoForNft( requireNonNull(readableTokenStore); requireNonNull(config); - final var nft = readableNftStore.get(nftId.tokenId(), nftId.serialNumber()); - final var token = requireNonNull(readableTokenStore).get(nftId.tokenId()); + final var nft = readableNftStore.get(nftId.tokenIdOrThrow(), nftId.serialNumber()); + final var token = requireNonNull(readableTokenStore.get(nftId.tokenIdOrThrow())); if (nft == null) { return Optional.empty(); } else { diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractMusicalChairsSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractMusicalChairsSuite.java index 78bc89978737..bcd7c1127f8e 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractMusicalChairsSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractMusicalChairsSuite.java @@ -32,6 +32,8 @@ import static com.hedera.services.bdd.suites.contract.Utils.asAddress; import static com.hedera.services.bdd.suites.contract.Utils.getABIFor; +import com.hedera.services.bdd.junit.HapiTest; +import com.hedera.services.bdd.junit.HapiTestSuite; import com.hedera.services.bdd.spec.HapiSpec; import com.hedera.services.bdd.spec.HapiSpecOperation; import com.hedera.services.bdd.spec.transactions.TxnVerbs; @@ -44,6 +46,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +@HapiTestSuite public class ContractMusicalChairsSuite extends HapiSuite { private static final Logger log = LogManager.getLogger(ContractMusicalChairsSuite.class); @@ -62,6 +65,7 @@ public List getSpecsInSuite() { return List.of(playGame()); } + @HapiTest private HapiSpec playGame() { final var dj = "dj"; final var players = IntStream.range(1, 30).mapToObj(i -> "Player" + i).toList(); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractUpdateSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractUpdateSuite.java index 5cb6b8d9ada1..e611225dbbdb 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractUpdateSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractUpdateSuite.java @@ -339,6 +339,7 @@ private HapiSpec givenAdminKeyMustBeValid() { .hasKnownStatus(INVALID_ADMIN_KEY)); } + @HapiTest HapiSpec fridayThe13thSpec() { final var contract = "SimpleStorage"; final var suffix = "Clone"; From f2310985733114251d7ab0146a6754cd28271bc7 Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Wed, 6 Dec 2023 20:07:56 -0600 Subject: [PATCH 5/9] Enable a few more HapiTest Signed-off-by: Michael Tinker --- .../com/hedera/node/app/spi/key/KeyUtils.java | 29 +++++++++++------ .../exec/scope/HandleHederaOperations.java | 1 - .../handlers/ContractCallLocalHandler.java | 2 +- .../impl/handlers/ContractDeleteHandler.java | 5 +++ .../infra/HevmStaticTransactionFactory.java | 32 ++++++++++--------- .../handlers/ContractUpdateHandlerTest.java | 2 ++ .../HevmStaticTransactionFactoryTest.java | 13 ++++++-- .../token/impl/api/TokenServiceApiImpl.java | 7 +++- .../contract/hapi/ContractUpdateSuite.java | 1 + .../opcodes/Create2OperationSuite.java | 3 ++ .../src/main/resource/spec-default.properties | 2 +- 11 files changed, 66 insertions(+), 31 deletions(-) diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/key/KeyUtils.java b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/key/KeyUtils.java index 0f9648a5a2ba..30d86f35e8cc 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/key/KeyUtils.java +++ b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/key/KeyUtils.java @@ -16,12 +16,15 @@ package com.hedera.node.app.spi.key; +import static java.util.Objects.requireNonNull; + import com.hedera.hapi.node.base.ContractID; import com.hedera.hapi.node.base.Key; import com.hedera.hapi.node.base.Key.KeyOneOfType; import com.hedera.hapi.node.base.KeyList; import com.hedera.hapi.node.base.ThresholdKey; import com.hedera.pbj.runtime.io.buffer.Bytes; +import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.Collections; @@ -106,12 +109,8 @@ private static boolean isEmptyInternal(@Nullable final Key pbjKey, boolean honor return ((Bytes) key.value()).length() == 0; } else if (pbjKey.hasEcdsaSecp256k1()) { return ((Bytes) key.value()).length() == 0; - } else if (pbjKey.hasDelegatableContractId()) { - return !((ContractID) key.value()).hasContractNum() - || (((ContractID) key.value()).hasContractNum() && ((ContractID) key.value()).contractNum() == 0); - } else if (pbjKey.hasContractID()) { - return !((ContractID) key.value()).hasContractNum() - || (((ContractID) key.value()).hasContractNum() && ((ContractID) key.value()).contractNum() == 0); + } else if (pbjKey.hasDelegatableContractId() || pbjKey.hasContractID()) { + return isEmptyContractId((ContractID) key.value()); } // ECDSA_384 and RSA_3072 are not supported yet return true; @@ -152,12 +151,22 @@ public static boolean isValid(@Nullable final Key pbjKey) { final var ecKey = ((Bytes) key.value()); return ecKey.length() == ECDSA_SECP256K1_COMPRESSED_KEY_LENGTH && (ecKey.getByte(0) == EVEN_PARITY || ecKey.getByte(0) == ODD_PARITY); - } else if (pbjKey.hasDelegatableContractId()) { - return ((ContractID) key.value()).contractNum().intValue() > 0; - } else if (pbjKey.hasContractID()) { - return ((ContractID) key.value()).contractNum().intValue() > 0; + } else if (pbjKey.hasDelegatableContractId() || pbjKey.hasContractID()) { + return isPlausibleContractId((ContractID) key.value()); } // ECDSA_384 and RSA_3072 are not supported yet return true; } + + private static boolean isPlausibleContractId(@NonNull final ContractID contractId) { + requireNonNull(contractId); + return contractId.contractNumOrElse(0L) > 0 + || contractId.evmAddressOrElse(Bytes.EMPTY).length() == 20L; + } + + private static boolean isEmptyContractId(@NonNull final ContractID contractId) { + requireNonNull(contractId); + return contractId.contractNumOrElse(0L) == 0 + && contractId.evmAddressOrElse(Bytes.EMPTY).length() == 0L; + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleHederaOperations.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleHederaOperations.java index 75d528323f80..fd525421fe77 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleHederaOperations.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleHederaOperations.java @@ -335,7 +335,6 @@ private void dispatchAndMarkCreation( (bodyToExternalize == null) ? SUPPRESSING_EXTERNALIZED_RECORD_CUSTOMIZER : contractBodyCustomizerFor(bodyToExternalize)); - final var contractId = ContractID.newBuilder().contractNum(number).build(); // add additional create record fields recordBuilder diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCallLocalHandler.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCallLocalHandler.java index 65d0347bff64..31bc2b9202b3 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCallLocalHandler.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractCallLocalHandler.java @@ -106,7 +106,7 @@ public Response findResponse(@NonNull final QueryContext context, @NonNull final requireNonNull(context); requireNonNull(header); - var component = provider.get().create(context, Instant.now(), CONTRACT_CALL_LOCAL); + final var component = provider.get().create(context, Instant.now(), CONTRACT_CALL_LOCAL); final var outcome = component.contextQueryProcessor().call(); final var responseHeader = outcome.isSuccess() diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractDeleteHandler.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractDeleteHandler.java index 0e03a402ce16..ce5b21c9ea7a 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractDeleteHandler.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/handlers/ContractDeleteHandler.java @@ -16,14 +16,17 @@ package com.hedera.node.app.service.contract.impl.handlers; +import static com.hedera.hapi.node.base.ResponseCodeEnum.CONTRACT_DELETED; import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_CONTRACT_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TRANSFER_ACCOUNT_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.MODIFYING_IMMUTABLE_CONTRACT; import static com.hedera.hapi.node.base.ResponseCodeEnum.OBTAINER_DOES_NOT_EXIST; import static com.hedera.hapi.node.base.ResponseCodeEnum.OBTAINER_REQUIRED; +import static com.hedera.hapi.node.base.ResponseCodeEnum.OBTAINER_SAME_CONTRACT_ID; import static com.hedera.hapi.node.base.ResponseCodeEnum.PERMANENT_REMOVAL_REQUIRES_SYSTEM_INITIATION; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asNumericContractId; import static com.hedera.node.app.spi.validation.Validations.mustExist; +import static com.hedera.node.app.spi.workflows.HandleException.validateFalse; import static com.hedera.node.app.spi.workflows.HandleException.validateTrue; import static com.hedera.node.app.spi.workflows.PreCheckException.validateFalsePreCheck; import static java.util.Objects.requireNonNull; @@ -86,11 +89,13 @@ public void handle(@NonNull final HandleContext context) throws HandleException validateTrue(op.hasTransferAccountID() || op.hasTransferContractID(), OBTAINER_REQUIRED); final var accountStore = context.readableStore(ReadableAccountStore.class); final var toBeDeleted = requireNonNull(accountStore.getContractById(op.contractIDOrThrow())); + validateFalse(toBeDeleted.deleted(), CONTRACT_DELETED); final var obtainer = getObtainer(accountStore, op); validateTrue(obtainer != null, OBTAINER_DOES_NOT_EXIST); if (obtainer.deleted()) { throw new HandleException(obtainer.smartContract() ? INVALID_CONTRACT_ID : OBTAINER_DOES_NOT_EXIST); } + validateFalse(toBeDeleted.accountIdOrThrow().equals(obtainer.accountIdOrThrow()), OBTAINER_SAME_CONTRACT_ID); final var recordBuilder = context.recordBuilder(ContractDeleteRecordBuilder.class); final var deletedId = toBeDeleted.accountIdOrThrow(); context.serviceApi(TokenServiceApi.class) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmStaticTransactionFactory.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmStaticTransactionFactory.java index 0a69e9105ef0..b80c07b56f16 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmStaticTransactionFactory.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmStaticTransactionFactory.java @@ -19,18 +19,21 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.INSUFFICIENT_GAS; import static com.hedera.hapi.node.base.ResponseCodeEnum.MAX_GAS_LIMIT_EXCEEDED; import static com.hedera.node.app.service.contract.impl.hevm.HederaEvmTransaction.NOT_APPLICABLE; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.EVM_ADDRESS_LENGTH_AS_LONG; import static com.hedera.node.app.spi.workflows.HandleException.validateTrue; +import static java.util.Objects.requireNonNull; import static org.apache.tuweni.bytes.Bytes.EMPTY; import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.ContractID; import com.hedera.hapi.node.contract.ContractCallLocalQuery; import com.hedera.hapi.node.transaction.Query; import com.hedera.node.app.service.contract.impl.annotations.QueryScope; import com.hedera.node.app.service.contract.impl.hevm.HederaEvmTransaction; +import com.hedera.node.app.service.token.ReadableAccountStore; import com.hedera.node.app.spi.workflows.QueryContext; import com.hedera.node.config.data.ContractsConfig; import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.Objects; import javax.inject.Inject; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -44,14 +47,16 @@ public class HevmStaticTransactionFactory { private static final long INTRINSIC_GAS_LOWER_BOUND = 21_000L; private final ContractsConfig contractsConfig; private final GasCalculator gasCalculator; + private final QueryContext context; private final AccountID payerId; @Inject public HevmStaticTransactionFactory( @NonNull final QueryContext context, @NonNull final GasCalculator gasCalculator) { - this.contractsConfig = Objects.requireNonNull(context).configuration().getConfigData(ContractsConfig.class); - this.gasCalculator = gasCalculator; - this.payerId = Objects.requireNonNull(context.payer()); + this.context = requireNonNull(context); + this.contractsConfig = context.configuration().getConfigData(ContractsConfig.class); + this.gasCalculator = requireNonNull(gasCalculator); + this.payerId = requireNonNull(context.payer()); } /** @@ -65,18 +70,15 @@ public HederaEvmTransaction fromHapiQuery(@NonNull final Query query) { final var op = query.contractCallLocalOrThrow(); assertValidCall(op); final var senderId = op.hasSenderId() ? op.senderIdOrThrow() : payerId; + var targetId = op.contractIDOrThrow(); + // For mono-service fidelity, allow calls using 0.0.X id even to contracts with a priority EVM address + final var maybeContract = + context.createStore(ReadableAccountStore.class).getContractById(targetId); + if (maybeContract != null && maybeContract.alias().length() == EVM_ADDRESS_LENGTH_AS_LONG) { + targetId = ContractID.newBuilder().evmAddress(maybeContract.alias()).build(); + } return new HederaEvmTransaction( - senderId, - null, - op.contractIDOrThrow(), - NOT_APPLICABLE, - op.functionParameters(), - null, - 0L, - op.gas(), - 1L, - 0L, - null); + senderId, null, targetId, NOT_APPLICABLE, op.functionParameters(), null, 0L, op.gas(), 1L, 0L, null); } private void assertValidCall(@NonNull final ContractCallLocalQuery body) { diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractUpdateHandlerTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractUpdateHandlerTest.java index 589f1f9866f5..2aa034eb61ea 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractUpdateHandlerTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/handlers/ContractUpdateHandlerTest.java @@ -25,6 +25,7 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.MODIFYING_IMMUTABLE_CONTRACT; import static com.hedera.hapi.node.base.ResponseCodeEnum.NOT_SUPPORTED; import static com.hedera.hapi.node.base.ResponseCodeEnum.REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.AN_ED25519_KEY; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.assertFailsWith; import static com.hedera.node.app.service.token.api.AccountSummariesApi.SENTINEL_ACCOUNT_ID; import static com.hedera.node.app.spi.HapiUtils.EMPTY_KEY_LIST; @@ -143,6 +144,7 @@ void sigRequiredWithoutKeyFails() throws PreCheckException { @Test void invalidAutoRenewAccountIdFails() throws PreCheckException { + when(payerAccount.keyOrThrow()).thenReturn(AN_ED25519_KEY); when(accountStore.getContractById(targetContract)).thenReturn(payerAccount); final var txn = TransactionBody.newBuilder() diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmStaticTransactionFactoryTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmStaticTransactionFactoryTest.java index 32a2acdf22ef..7997fe80f6ae 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmStaticTransactionFactoryTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmStaticTransactionFactoryTest.java @@ -17,6 +17,8 @@ package com.hedera.node.app.service.contract.impl.test.infra; import static com.hedera.hapi.node.base.ResponseCodeEnum.MAX_GAS_LIMIT_EXCEEDED; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.ALIASED_SOMEBODY; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CALLED_CONTRACT_EVM_ADDRESS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CALLED_CONTRACT_ID; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.CALL_DATA; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.DEFAULT_CONFIG; @@ -35,6 +37,7 @@ import com.hedera.hapi.node.transaction.Query; import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.service.contract.impl.infra.HevmStaticTransactionFactory; +import com.hedera.node.app.service.token.ReadableAccountStore; import com.hedera.node.app.spi.workflows.QueryContext; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.function.Consumer; @@ -53,6 +56,9 @@ class HevmStaticTransactionFactoryTest { @Mock private QueryContext context; + @Mock + private ReadableAccountStore accountStore; + private HevmStaticTransactionFactory subject; @BeforeEach @@ -63,7 +69,7 @@ void setUp() { } @Test - void fromQueryWorkWithSender() { + void fromQueryWorkWithSenderAndUsesPriorityAddress() { final var query = Query.newBuilder() .contractCallLocal(callLocalWith(b -> { b.gas(21_000L); @@ -72,10 +78,12 @@ void fromQueryWorkWithSender() { b.functionParameters(CALL_DATA); })) .build(); + given(context.createStore(ReadableAccountStore.class)).willReturn(accountStore); + given(accountStore.getContractById(CALLED_CONTRACT_ID)).willReturn(ALIASED_SOMEBODY); var transaction = subject.fromHapiQuery(query); assertThat(transaction.senderId()).isEqualTo(SENDER_ID); assertThat(transaction.relayerId()).isNull(); - assertThat(transaction.contractId()).isEqualTo(CALLED_CONTRACT_ID); + assertThat(transaction.contractId()).isEqualTo(CALLED_CONTRACT_EVM_ADDRESS); assertThat(transaction.nonce()).isEqualTo(-1); assertThat(transaction.payload()).isEqualTo(CALL_DATA); assertThat(transaction.chainId()).isNull(); @@ -94,6 +102,7 @@ void fromQueryWorkWithNoSender() { TransactionBody.newBuilder().transactionID(transactionID).build(); final var payment = Transaction.newBuilder().body(txBody).build(); final var queryHeader = QueryHeader.newBuilder().payment(payment).build(); + given(context.createStore(ReadableAccountStore.class)).willReturn(accountStore); final var query = Query.newBuilder() .contractCallLocal(callLocalWith(b -> { diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/api/TokenServiceApiImpl.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/api/TokenServiceApiImpl.java index 9a6685fef1b0..2a3beec030de 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/api/TokenServiceApiImpl.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/api/TokenServiceApiImpl.java @@ -489,7 +489,12 @@ public void deleteAndTransfer( // get the account from account store that has all balance changes // commit the account with deleted flag set to true final var updatedDeleteAccount = requireNonNull(accountStore.getForModify(deletedId)); - accountStore.put(updatedDeleteAccount.copyBuilder().deleted(true).build()); + accountStore.removeAlias(updatedDeleteAccount.alias()); + accountStore.put(updatedDeleteAccount + .copyBuilder() + .alias(Bytes.EMPTY) + .deleted(true) + .build()); // add the transfer account for this deleted account to record builder. // This is needed while computing staking rewards. In the future it will also be added diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractUpdateSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractUpdateSuite.java index e611225dbbdb..d0f27e30e5a3 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractUpdateSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/hapi/ContractUpdateSuite.java @@ -314,6 +314,7 @@ private HapiSpec immutableContractKeyFormIsStandard() { .then(getContractInfo(CONTRACT).has(contractWith().immutableContractKey(CONTRACT))); } + @HapiTest private HapiSpec canMakeContractImmutableWithEmptyKeyList() { return defaultHapiSpec("CanMakeContractImmutableWithEmptyKeyList") .given( diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opcodes/Create2OperationSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opcodes/Create2OperationSuite.java index 26aff32aba23..988cf88b25ed 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opcodes/Create2OperationSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opcodes/Create2OperationSuite.java @@ -837,6 +837,7 @@ private HapiSpec eip1014AliasIsPriorityInErcOwnerPrecompile() { } @SuppressWarnings("java:S5669") + // Missing creation record private HapiSpec canUseAliasesInPrecompilesAndContractKeys() { final var creation2 = CREATE_2_TXN; final var contract = "Create2PrecompileUser"; @@ -1069,6 +1070,7 @@ private HapiSpec cannotSelfDestructToMirrorAddress() { // https://github.com/hashgraph/hedera-services/issues/2874 @SuppressWarnings("java:S5669") + @HapiTest private HapiSpec canDeleteViaAlias() { final var adminKey = ADMIN_KEY; final var creation2 = CREATE_2_TXN; @@ -1216,6 +1218,7 @@ private HapiSpec create2InputAddressIsStableWithTopLevelCallWhetherMirrorOrAlias } @SuppressWarnings("java:S5669") + @HapiTest private HapiSpec priorityAddressIsCreate2ForStaticHapiCalls() { final var contract = "AddressValueRet"; diff --git a/hedera-node/test-clients/src/main/resource/spec-default.properties b/hedera-node/test-clients/src/main/resource/spec-default.properties index 74aa7244b040..a9960be5c818 100644 --- a/hedera-node/test-clients/src/main/resource/spec-default.properties +++ b/hedera-node/test-clients/src/main/resource/spec-default.properties @@ -110,7 +110,7 @@ num.opFinisher.threads=8 persistentEntities.dir.path= persistentEntities.updateCreatedManifests=true spec.autoScheduledTxns= -spec.streamlinedIngestChecks=INVALID_FILE_ID,ENTITY_NOT_ALLOWED_TO_DELETE,AUTHORIZATION_FAILED,INVALID_PRNG_RANGE,INVALID_STAKING_ID,NOT_SUPPORTED,TOKEN_ID_REPEATED_IN_TOKEN_LIST,ALIAS_ALREADY_ASSIGNED,INVALID_ALIAS_KEY,KEY_REQUIRED,BAD_ENCODING,AUTORENEW_DURATION_NOT_IN_RANGE,INVALID_ZERO_BYTE_IN_STRING,INVALID_ADMIN_KEY,ACCOUNT_DELETED,BUSY,INSUFFICIENT_PAYER_BALANCE,INSUFFICIENT_TX_FEE,INVALID_ACCOUNT_ID,INVALID_NODE_ACCOUNT,INVALID_SIGNATURE,INVALID_TRANSACTION,INVALID_TRANSACTION_BODY,INVALID_TRANSACTION_DURATION,INVALID_TRANSACTION_ID,INVALID_TRANSACTION_START,KEY_PREFIX_MISMATCH,MEMO_TOO_LONG,PAYER_ACCOUNT_NOT_FOUND,PLATFORM_NOT_ACTIVE,TRANSACTION_EXPIRED,TRANSACTION_HAS_UNKNOWN_FIELDS,TRANSACTION_ID_FIELD_NOT_ALLOWED,TRANSACTION_OVERSIZE,TRANSFER_ACCOUNT_SAME_AS_DELETE_ACCOUNT,EMPTY_ALLOWANCES,REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT,TOKEN_HAS_NO_FREEZE_KEY,TOKEN_HAS_NO_SUPPLY_KEY,INVALID_TOKEN_INITIAL_SUPPLY,INVALID_TOKEN_DECIMALS,INVALID_TOKEN_MAX_SUPPLY,ACCOUNT_REPEATED_IN_ACCOUNT_AMOUNTS,TRANSFERS_NOT_ZERO_SUM_FOR_TOKEN,INVALID_ACCOUNT_AMOUNTS,TOKEN_NAME_TOO_LONG,TOKEN_SYMBOL_TOO_LONG,INVALID_TOKEN_NFT_SERIAL_NUMBER,PERMANENT_REMOVAL_REQUIRES_SYSTEM_INITIATION,MISSING_TOKEN_SYMBOL,MISSING_TOKEN_NAME,INVALID_EXPIRATION_TIME,EMPTY_TOKEN_TRANSFER_ACCOUNT_AMOUNTS,INVALID_ALLOWANCE_OWNER_ID,FUNGIBLE_TOKEN_IN_NFT_ALLOWANCES,TOKEN_NOT_ASSOCIATED_TO_ACCOUNT,MAX_ALLOWANCES_EXCEEDED,INVALID_ALLOWANCE_SPENDER_ID,AMOUNT_EXCEEDS_TOKEN_MAX_SUPPLY,NFT_IN_FUNGIBLE_TOKEN_ALLOWANCES,NEGATIVE_ALLOWANCE_AMOUNT,DELEGATING_SPENDER_DOES_NOT_HAVE_APPROVE_FOR_ALL,DELEGATING_SPENDER_CANNOT_GRANT_APPROVE_FOR_ALL,INVALID_TOKEN_MINT_AMOUNT,INVALID_TOKEN_BURN_AMOUNT,INVALID_WIPING_AMOUNT,INVALID_NFT_ID,BATCH_SIZE_LIMIT_EXCEEDED,METADATA_TOO_LONG,INVALID_RENEWAL_PERIOD,INVALID_CUSTOM_FEE_SCHEDULE_KEY,MAX_GAS_LIMIT_EXCEEDED +spec.streamlinedIngestChecks=INVALID_FILE_ID,ENTITY_NOT_ALLOWED_TO_DELETE,AUTHORIZATION_FAILED,INVALID_PRNG_RANGE,INVALID_STAKING_ID,NOT_SUPPORTED,TOKEN_ID_REPEATED_IN_TOKEN_LIST,ALIAS_ALREADY_ASSIGNED,INVALID_ALIAS_KEY,KEY_REQUIRED,BAD_ENCODING,AUTORENEW_DURATION_NOT_IN_RANGE,INVALID_ZERO_BYTE_IN_STRING,INVALID_ADMIN_KEY,ACCOUNT_DELETED,BUSY,INSUFFICIENT_PAYER_BALANCE,INSUFFICIENT_TX_FEE,INVALID_ACCOUNT_ID,INVALID_NODE_ACCOUNT,INVALID_SIGNATURE,INVALID_TRANSACTION,INVALID_TRANSACTION_BODY,INVALID_TRANSACTION_DURATION,INVALID_TRANSACTION_ID,INVALID_TRANSACTION_START,KEY_PREFIX_MISMATCH,MEMO_TOO_LONG,PAYER_ACCOUNT_NOT_FOUND,PLATFORM_NOT_ACTIVE,TRANSACTION_EXPIRED,TRANSACTION_HAS_UNKNOWN_FIELDS,TRANSACTION_ID_FIELD_NOT_ALLOWED,TRANSACTION_OVERSIZE,TRANSFER_ACCOUNT_SAME_AS_DELETE_ACCOUNT,EMPTY_ALLOWANCES,REQUESTED_NUM_AUTOMATIC_ASSOCIATIONS_EXCEEDS_ASSOCIATION_LIMIT,TOKEN_HAS_NO_FREEZE_KEY,TOKEN_HAS_NO_SUPPLY_KEY,INVALID_TOKEN_INITIAL_SUPPLY,INVALID_TOKEN_DECIMALS,INVALID_TOKEN_MAX_SUPPLY,ACCOUNT_REPEATED_IN_ACCOUNT_AMOUNTS,TRANSFERS_NOT_ZERO_SUM_FOR_TOKEN,INVALID_ACCOUNT_AMOUNTS,TOKEN_NAME_TOO_LONG,TOKEN_SYMBOL_TOO_LONG,INVALID_TOKEN_NFT_SERIAL_NUMBER,PERMANENT_REMOVAL_REQUIRES_SYSTEM_INITIATION,MISSING_TOKEN_SYMBOL,MISSING_TOKEN_NAME,INVALID_EXPIRATION_TIME,EMPTY_TOKEN_TRANSFER_ACCOUNT_AMOUNTS,INVALID_ALLOWANCE_OWNER_ID,FUNGIBLE_TOKEN_IN_NFT_ALLOWANCES,TOKEN_NOT_ASSOCIATED_TO_ACCOUNT,MAX_ALLOWANCES_EXCEEDED,INVALID_ALLOWANCE_SPENDER_ID,AMOUNT_EXCEEDS_TOKEN_MAX_SUPPLY,NFT_IN_FUNGIBLE_TOKEN_ALLOWANCES,NEGATIVE_ALLOWANCE_AMOUNT,DELEGATING_SPENDER_DOES_NOT_HAVE_APPROVE_FOR_ALL,DELEGATING_SPENDER_CANNOT_GRANT_APPROVE_FOR_ALL,INVALID_TOKEN_MINT_AMOUNT,INVALID_TOKEN_BURN_AMOUNT,INVALID_WIPING_AMOUNT,INVALID_NFT_ID,BATCH_SIZE_LIMIT_EXCEEDED,METADATA_TOO_LONG,INVALID_RENEWAL_PERIOD,INVALID_CUSTOM_FEE_SCHEDULE_KEY,MAX_GAS_LIMIT_EXCEEDED,CONTRACT_DELETED status.deferredResolves.doAsync=true status.preResolve.pause.ms=0 status.wait.sleep.ms=500 From 34fe92a63b93ca54f3f31e320646879af12a12af Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Wed, 6 Dec 2023 23:14:43 -0600 Subject: [PATCH 6/9] fix unit test Signed-off-by: Michael Tinker --- .../token/impl/test/handlers/CryptoDeleteHandlerTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteHandlerTest.java index 3a68eaadc2b7..b704631a47f8 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoDeleteHandlerTest.java @@ -41,6 +41,7 @@ import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.TransactionID; +import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.hedera.hapi.node.state.token.Account; import com.hedera.hapi.node.token.CryptoDeleteTransactionBody; import com.hedera.hapi.node.transaction.TransactionBody; @@ -270,6 +271,7 @@ void failsIfAccountIsAlreadyDeleted() { @Test void happyPathWorks() { + given(writableStates.get(ALIASES)).willReturn(writableAliases); updateWritableStore( Map.of(accountNum, account, deleteAccountNum, deleteAccount, transferAccountNum, transferAccount)); From 39ad2d0d60874d48455066f2ed9c32923d8819ae Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Thu, 7 Dec 2023 15:10:50 -0600 Subject: [PATCH 7/9] Reduce method visibility Signed-off-by: Michael Tinker --- .../impl/exec/systemcontracts/hts/ownerof/OwnerOfCall.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ownerof/OwnerOfCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ownerof/OwnerOfCall.java index 647700f6b67d..8bfe41e91d47 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ownerof/OwnerOfCall.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ownerof/OwnerOfCall.java @@ -63,7 +63,7 @@ public OwnerOfCall( } } - public static Long getOwnerAccountNum(Nft nft, Token token) { + private long getOwnerAccountNum(@NonNull final Nft nft, @NonNull final Token token) { final var explicitId = nft.ownerIdOrElse(AccountID.DEFAULT); if (explicitId.accountNumOrElse(TREASURY_OWNER_NUM) == TREASURY_OWNER_NUM) { return token.treasuryAccountIdOrThrow().accountNumOrThrow(); From 6f1c9697641097b4c2e1288be229dc59956f5c4f Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Thu, 7 Dec 2023 15:42:06 -0600 Subject: [PATCH 8/9] Remove AccountID.DEFAULT checks from RecordFinalizerBase Signed-off-by: Michael Tinker --- .../exec/systemcontracts/hts/TokenTupleUtils.java | 2 +- .../app/service/token/impl/RecordFinalizerBase.java | 12 +++++------- .../handlers/FinalizeChildRecordHandlerTest.java | 7 ++++++- .../handlers/FinalizeParentRecordHandlerTest.java | 10 ++++++++-- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java index 851f55edd9f0..1f6bd30fb115 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java @@ -246,7 +246,7 @@ public static Tuple nftTokenInfoTupleFor( return Tuple.of( tokenInfoTupleFor(token, ledgerId), serialNumber, - priorityAddressOf(nft.ownerIdOrElse(token.treasuryAccountIdOrElse(ZERO_ACCOUNT_ID)), nativeOperations), + priorityAddressOf(nft.ownerIdOrElse(token.treasuryAccountIdOrThrow()), nativeOperations), nft.mintTimeOrElse(new Timestamp(0, 0)).seconds(), nftMetaData, priorityAddressOf(nft.spenderIdOrElse(ZERO_ACCOUNT_ID), nativeOperations)); diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/RecordFinalizerBase.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/RecordFinalizerBase.java index b80bbe6a522f..d9f01c873a02 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/RecordFinalizerBase.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/RecordFinalizerBase.java @@ -176,25 +176,23 @@ protected Map> nftChangesFrom( // The NFT may not have existed before, in which case we'll use a null sender account ID AccountID senderAccountId = null; - final var token = readableTokenStore.get(nftId.tokenId()); + final var token = requireNonNull(readableTokenStore.get(nftId.tokenIdOrThrow())); if (persistedNft != null) { - final boolean hasOwnerId = - persistedNft.hasOwnerId() && !persistedNft.ownerId().equals(AccountID.DEFAULT); // If the NFT did not have an owner before set it to the treasury account - senderAccountId = hasOwnerId ? persistedNft.ownerId() : token.treasuryAccountId(); + senderAccountId = persistedNft.hasOwnerId() ? persistedNft.ownerId() : token.treasuryAccountIdOrThrow(); } else { senderAccountId = ZERO_ACCOUNT_ID; } // If the NFT has been burned or wiped, modifiedNft will be null. In that case the receiverId // will be explicitly set as 0.0.0 - AccountID receiverAccountId = null; + AccountID receiverAccountId; final var builder = NftTransfer.newBuilder(); if (modifiedNft != null) { - if (modifiedNft.hasOwnerId() && !AccountID.DEFAULT.equals(modifiedNft.ownerId())) { + if (modifiedNft.hasOwnerId()) { receiverAccountId = modifiedNft.ownerId(); } else { - receiverAccountId = token.treasuryAccountId(); + receiverAccountId = token.treasuryAccountIdOrThrow(); } } else { receiverAccountId = ZERO_ACCOUNT_ID; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/FinalizeChildRecordHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/FinalizeChildRecordHandlerTest.java index 189b2978dcda..e72205088d7b 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/FinalizeChildRecordHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/FinalizeChildRecordHandlerTest.java @@ -463,6 +463,7 @@ void handleNftTransfersToNewAccountSuccess() { .alias(Bytes.wrap("00000000000000000003")) .build()); writableNftStore.put(nft.copyBuilder().ownerId(ACCOUNT_3434_ID).build()); + readableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE); context = mockContext(); given(context.configuration()).willReturn(configuration); @@ -499,6 +500,7 @@ void handleNewNftTransferToAccountSuccess() { .ownerId(ACCOUNT_1212_ID) .build(); writableNftStore.put(newNft.copyBuilder().ownerId(ACCOUNT_3434_ID).build()); + readableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE); context = mockContext(); given(context.configuration()).willReturn(configuration); @@ -538,6 +540,7 @@ void handleNftTransfersToExistingAccountSuccess() { // Set up NFTs for token ID 246 (serials 222, 223) final var token246Id = asToken(246); + final var token246 = Token.newBuilder().tokenId(token246Id).build(); final var nftId222 = NftID.newBuilder().tokenId(token246Id).serialNumber(222).build(); final var nft222 = @@ -567,6 +570,7 @@ void handleNftTransfersToExistingAccountSuccess() { writableNftStore.put(nft112.copyBuilder().ownerId(ACCOUNT_3434_ID).build()); writableNftStore.put(nft222.copyBuilder().ownerId(ACCOUNT_1212_ID).build()); writableNftStore.put(nft223.copyBuilder().ownerId(ACCOUNT_1212_ID).build()); + readableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE, token246); context = mockContext(); final var config = HederaTestConfigBuilder.create() .withValue("staking.isEnabled", String.valueOf(false)) @@ -623,6 +627,7 @@ void handleCombinedHbarAndTokenTransfersSuccess() { .balance(50) .build(); final var token654Id = asToken(654); + final var token654 = Token.newBuilder().tokenId(token654Id).build(); final var token654Rel = givenNonFungibleTokenRelation() .copyBuilder() .tokenId(token654Id) @@ -658,7 +663,7 @@ void handleCombinedHbarAndTokenTransfersSuccess() { writableNftStore.put(nft.copyBuilder().ownerId(ACCOUNT_1212_ID).build()); writableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE); - readableTokenStore = TestStoreFactory.newReadableStoreWithTokens(TOKEN_321_FUNGIBLE); + readableTokenStore = TestStoreFactory.newReadableStoreWithTokens(TOKEN_321_FUNGIBLE, token654); context = mockContext(); given(context.configuration()).willReturn(configuration); diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/FinalizeParentRecordHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/FinalizeParentRecordHandlerTest.java index 9c1b7f5498da..615899777dc0 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/FinalizeParentRecordHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/FinalizeParentRecordHandlerTest.java @@ -419,6 +419,7 @@ void nftBurnsOrWipesAreAccounted() { writableNftStore.remove( NftID.newBuilder().tokenId(TOKEN_321).serialNumber(1).build()); + readableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE); context = mockContext(); given(context.configuration()).willReturn(configuration); @@ -699,6 +700,7 @@ void handleNftTransfersToNewAccountSuccess() { .alias(Bytes.wrap("00000000000000000003")) .build()); writableNftStore.put(nft.copyBuilder().ownerId(ACCOUNT_3434_ID).build()); + readableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE); context = mockContext(); given(context.configuration()).willReturn(configuration); @@ -735,6 +737,7 @@ void handleNewNftTransferToAccountSuccess() { .ownerId(ACCOUNT_1212_ID) .build(); writableNftStore.put(newNft.copyBuilder().ownerId(ACCOUNT_3434_ID).build()); + readableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE); context = mockContext(); given(context.configuration()).willReturn(configuration); @@ -774,6 +777,7 @@ void handleNftTransfersToExistingAccountSuccess() { // Set up NFTs for token ID 246 (serials 222, 223) final var token246Id = asToken(246); + final var token264 = Token.newBuilder().tokenId(token246Id).build(); final var nftId222 = NftID.newBuilder().tokenId(token246Id).serialNumber(222).build(); final var nft222 = @@ -803,6 +807,7 @@ void handleNftTransfersToExistingAccountSuccess() { writableNftStore.put(nft112.copyBuilder().ownerId(ACCOUNT_3434_ID).build()); writableNftStore.put(nft222.copyBuilder().ownerId(ACCOUNT_1212_ID).build()); writableNftStore.put(nft223.copyBuilder().ownerId(ACCOUNT_1212_ID).build()); + readableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE, token264); context = mockContext(); final var config = HederaTestConfigBuilder.create() .withValue("staking.isEnabled", String.valueOf(false)) @@ -860,6 +865,7 @@ void handleCombinedHbarAndTokenTransfersSuccess() { .balance(50) .build(); final var token654Id = asToken(654); + final var token654 = Token.newBuilder().tokenId(token654Id).build(); final var token654Rel = givenNonFungibleTokenRelation() .copyBuilder() .tokenId(token654Id) @@ -893,8 +899,8 @@ void handleCombinedHbarAndTokenTransfersSuccess() { .build()); // Make NFT changes writableNftStore.put(nft.copyBuilder().ownerId(ACCOUNT_1212_ID).build()); - writableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE); - readableTokenStore = TestStoreFactory.newReadableStoreWithTokens(TOKEN_321_FUNGIBLE); + writableTokenStore = TestStoreFactory.newWritableStoreWithTokens(TOKEN_321_FUNGIBLE, token654); + readableTokenStore = TestStoreFactory.newReadableStoreWithTokens(TOKEN_321_FUNGIBLE, token654); context = mockContext(); given(context.configuration()).willReturn(configuration); From c0eca918674e3461e99072320af5e0e931593b03 Mon Sep 17 00:00:00 2001 From: Michael Tinker Date: Thu, 7 Dec 2023 16:49:41 -0600 Subject: [PATCH 9/9] fix unit test Signed-off-by: Michael Tinker --- .../impl/exec/systemcontracts/hts/TokenTupleUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java index 1f6bd30fb115..873b8db6f177 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java @@ -246,7 +246,9 @@ public static Tuple nftTokenInfoTupleFor( return Tuple.of( tokenInfoTupleFor(token, ledgerId), serialNumber, - priorityAddressOf(nft.ownerIdOrElse(token.treasuryAccountIdOrThrow()), nativeOperations), + // The odd construct allowing a token to not have a treasury account set is to accommodate + // Token.DEFAULT being passed into this method, which a few HtsCall implementations do + priorityAddressOf(nft.ownerIdOrElse(token.treasuryAccountIdOrElse(ZERO_ACCOUNT_ID)), nativeOperations), nft.mintTimeOrElse(new Timestamp(0, 0)).seconds(), nftMetaData, priorityAddressOf(nft.spenderIdOrElse(ZERO_ACCOUNT_ID), nativeOperations));