From 69380f8d933539c1345af6e886c56a7f13c2668b Mon Sep 17 00:00:00 2001 From: Edwin Greene Date: Wed, 12 Jun 2024 13:30:08 -0400 Subject: [PATCH 1/6] Suppress recoverable error Signed-off-by: Edwin Greene --- .../domain/ContractResultServiceImpl.java | 15 ++++++++--- .../importer/domain/EntityIdService.java | 9 +++++++ .../importer/domain/EntityIdServiceImpl.java | 17 ++++++++++-- .../domain/ContractResultServiceImplTest.java | 21 +++++++++++++++ .../domain/EntityIdServiceImplTest.java | 27 ++++++++++++++++++- 5 files changed, 83 insertions(+), 6 deletions(-) diff --git a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java index 2e245077fe8..9dac2ca4fba 100644 --- a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java +++ b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java @@ -44,6 +44,7 @@ import com.hederahashgraph.api.proto.java.ContractID; import com.hederahashgraph.api.proto.java.ResponseCodeEnum; import com.hederahashgraph.api.proto.java.TransactionBody; +import com.hederahashgraph.api.proto.java.TransactionRecord; import jakarta.inject.Named; import java.util.ArrayList; import java.util.List; @@ -88,13 +89,14 @@ public void process(@NonNull RecordItem recordItem, Transaction transaction) { // contractResult var transactionHandler = transactionHandlerFactory.get(TransactionType.of(transaction.getType())); - + var throwRecoverableError = shouldThrowRecoverableError(transactionRecord); // in pre-compile case transaction is not a contract type and entityId will be of a different type var contractId = (contractCallOrCreate ? Optional.ofNullable(transaction.getEntityId()) - : entityIdService.lookup(functionResult.getContractID())) + : entityIdService.lookup(functionResult.getContractID(), throwRecoverableError)) .orElse(EntityId.EMPTY); - var isRecoverableError = EntityId.isEmpty(contractId) + var isRecoverableError = throwRecoverableError + && EntityId.isEmpty(contractId) && !contractCallOrCreate && !ContractID.getDefaultInstance().equals(functionResult.getContractID()); if (isRecoverableError) { @@ -149,6 +151,13 @@ private boolean isContractCreateOrCall(TransactionBody transactionBody) { return transactionBody.hasContractCall() || transactionBody.hasContractCreateInstance(); } + private boolean shouldThrowRecoverableError(TransactionRecord transactionRecord) { + var receipt = transactionRecord.getReceipt(); + return !(receipt.getStatus().equals(ResponseCodeEnum.SUCCESS) + && receipt.getContractID().hasEvmAddress()) + && !transactionRecord.getContractCallResult().getContractID().hasEvmAddress(); + } + private void processContractAction(ContractAction action, int index, RecordItem recordItem) { long consensusTimestamp = recordItem.getConsensusTimestamp(); var contractAction = new com.hedera.mirror.common.domain.contract.ContractAction(); diff --git a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/EntityIdService.java b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/EntityIdService.java index 3dea400ad7c..9c7d697624d 100644 --- a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/EntityIdService.java +++ b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/EntityIdService.java @@ -53,6 +53,15 @@ public interface EntityIdService { */ Optional lookup(ContractID contractId); + /** + * Converts a protobuf ContractID to an EntityID, resolving any EVM addresses that may be present. + * + * @param contractId The protobuf contract ID + * @param throwRecoverableError If true, will throw a recoverable error if an EVM address cannot be found. + * @return An optional of the converted EntityId if it can be resolved, or EntityId.EMPTY if none can be resolved. + */ + Optional lookup(ContractID contractId, boolean throwRecoverableError); + /** * Specialized form of lookup(ContractID) that returns the first contract ID parameter that resolves to a non-empty * EntityId. diff --git a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/EntityIdServiceImpl.java b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/EntityIdServiceImpl.java index fb5992f505b..de693bffdc8 100644 --- a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/EntityIdServiceImpl.java +++ b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/EntityIdServiceImpl.java @@ -86,6 +86,11 @@ public Optional lookup(AccountID... accountIds) { @Override public Optional lookup(ContractID contractId) { + return lookup(contractId, true); + } + + @Override + public Optional lookup(ContractID contractId, boolean throwRecoverableError) { if (contractId == null || contractId.equals(ContractID.getDefaultInstance())) { return EMPTY; } @@ -95,7 +100,10 @@ public Optional lookup(ContractID contractId) { case EVM_ADDRESS -> cacheLookup( contractId.getEvmAddress(), () -> findByEvmAddress( - toBytes(contractId.getEvmAddress()), contractId.getShardNum(), contractId.getRealmNum())); + toBytes(contractId.getEvmAddress()), + contractId.getShardNum(), + contractId.getRealmNum(), + throwRecoverableError)); default -> { Utility.handleRecoverableError("Invalid ContractID: {}", contractId); yield Optional.empty(); @@ -158,12 +166,17 @@ public void notify(Entity entity) { } private Optional findByEvmAddress(byte[] evmAddress, long shardNum, long realmNum) { + return findByEvmAddress(evmAddress, shardNum, realmNum, true); + } + + private Optional findByEvmAddress( + byte[] evmAddress, long shardNum, long realmNum, boolean throwRecoverableError) { var id = Optional.ofNullable(DomainUtils.fromEvmAddress(evmAddress)) // Verify shard and realm match when assuming evmAddress is in the 'shard.realm.num' form .filter(e -> e.getShard() == shardNum && e.getRealm() == realmNum) .or(() -> entityRepository.findByEvmAddress(evmAddress).map(EntityId::of)); - if (id.isEmpty()) { + if (id.isEmpty() && throwRecoverableError) { Utility.handleRecoverableError("Entity not found for EVM address {}", Hex.encodeHexString(evmAddress)); } diff --git a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java index f0dc25f57d5..877463ef900 100644 --- a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java +++ b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.protobuf.ByteString; import com.hedera.mirror.common.domain.DomainBuilder; import com.hedera.mirror.common.domain.contract.ContractTransaction; import com.hedera.mirror.common.domain.entity.EntityId; @@ -91,12 +92,32 @@ private static Stream provideEntities() { .record(x -> x.setContractCallResult(builder.contractFunctionResult())) .build(); + var contractId = ContractID.newBuilder() + .setEvmAddress(ByteString.copyFromUtf8("1234")) + .build(); + Function withInactiveEvm = + (RecordItemBuilder builder) -> builder.tokenMint(TokenType.FUNGIBLE_COMMON) + .receipt(x -> x.setContractID(contractId)) + .record(x -> x.setContractCallResult(builder.contractFunctionResult())) + .build(); + + var contractIdNoEvm = ContractID.newBuilder().setContractNum(5).build(); + Function withActiveEvm = + (RecordItemBuilder builder) -> builder.tokenMint(TokenType.FUNGIBLE_COMMON) + .receipt(x -> x.setContractID(contractIdNoEvm)) + .record(x -> x.setContractCallResult(builder.contractFunctionResult(contractIdNoEvm))) + .build(); + Function contractCreate = (RecordItemBuilder builder) -> builder.contractCreate().build(); return Stream.of( Arguments.of(withoutDefaultContractId, null, true), Arguments.of(withoutDefaultContractId, EntityId.EMPTY, true), + Arguments.of(withInactiveEvm, null, false), + Arguments.of(withInactiveEvm, EntityId.EMPTY, false), + Arguments.of(withActiveEvm, null, true), + Arguments.of(withActiveEvm, EntityId.EMPTY, true), Arguments.of(withDefaultContractId, null, false), Arguments.of(withDefaultContractId, EntityId.EMPTY, false), Arguments.of(contractCreate, EntityId.EMPTY, false), diff --git a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/EntityIdServiceImplTest.java b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/EntityIdServiceImplTest.java index 5792bb5b87b..09c1f600a1b 100644 --- a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/EntityIdServiceImplTest.java +++ b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/EntityIdServiceImplTest.java @@ -32,10 +32,15 @@ import com.hederahashgraph.api.proto.java.ContractID; import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; @RequiredArgsConstructor +@ExtendWith(OutputCaptureExtension.class) class EntityIdServiceImplTest extends ImporterIntegrationTest { // in the form 'shard.realm.num' @@ -45,6 +50,8 @@ class EntityIdServiceImplTest extends ImporterIntegrationTest { 0, 0, 0, 0, 0, 0, 0, 100, // num }; + private static final String RECOVERABLE_ERROR_LOG_PREFIX = "Recoverable error. "; + private final EntityRepository entityRepository; private final EntityIdService entityIdService; @@ -213,13 +220,31 @@ void lookupContractEvmAddressSpecific() { } @Test - void lookupContractEvmAddressNoMatch() { + void lookupContractEvmAddressNoMatch(CapturedOutput output) { Entity contract = domainBuilder .entity() .customize(e -> e.alias(null).type(CONTRACT)) .get(); var contractId = getProtoContractId(contract); assertThat(entityIdService.lookup(contractId)).isEmpty(); + assertThat(output.getAll()).containsIgnoringCase(RECOVERABLE_ERROR_LOG_PREFIX); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void lookupContractEvmAddressRecoverableError(boolean throwRecoverableError, CapturedOutput output) { + Entity contract = domainBuilder + .entity() + .customize(e -> e.alias(null).type(CONTRACT)) + .get(); + var contractId = getProtoContractId(contract); + + assertThat(entityIdService.lookup(contractId, throwRecoverableError)).isEmpty(); + if (throwRecoverableError) { + assertThat(output.getAll()).containsIgnoringCase(RECOVERABLE_ERROR_LOG_PREFIX); + } else { + assertThat(output.getAll()).doesNotContainIgnoringCase(RECOVERABLE_ERROR_LOG_PREFIX); + } } @Test From a463809452b77f54f5426fe8d008516ce69fc9d5 Mon Sep 17 00:00:00 2001 From: Edwin Greene Date: Thu, 13 Jun 2024 09:55:30 -0400 Subject: [PATCH 2/6] Updates per feedback Signed-off-by: Edwin Greene --- .../importer/domain/ContractResultServiceImpl.java | 14 +++++++------- .../domain/ContractResultServiceImplTest.java | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java index 9dac2ca4fba..d1d2020294a 100644 --- a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java +++ b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java @@ -89,7 +89,7 @@ public void process(@NonNull RecordItem recordItem, Transaction transaction) { // contractResult var transactionHandler = transactionHandlerFactory.get(TransactionType.of(transaction.getType())); - var throwRecoverableError = shouldThrowRecoverableError(transactionRecord); + var throwRecoverableError = shouldThrowRecoverableError(functionResult, recordItem, transactionRecord); // in pre-compile case transaction is not a contract type and entityId will be of a different type var contractId = (contractCallOrCreate ? Optional.ofNullable(transaction.getEntityId()) @@ -98,7 +98,7 @@ public void process(@NonNull RecordItem recordItem, Transaction transaction) { var isRecoverableError = throwRecoverableError && EntityId.isEmpty(contractId) && !contractCallOrCreate - && !ContractID.getDefaultInstance().equals(functionResult.getContractID()); + && (!ContractID.getDefaultInstance().equals(functionResult.getContractID())); if (isRecoverableError) { Utility.handleRecoverableError( "Invalid contract id for contract result at {}", recordItem.getConsensusTimestamp()); @@ -151,11 +151,11 @@ private boolean isContractCreateOrCall(TransactionBody transactionBody) { return transactionBody.hasContractCall() || transactionBody.hasContractCreateInstance(); } - private boolean shouldThrowRecoverableError(TransactionRecord transactionRecord) { - var receipt = transactionRecord.getReceipt(); - return !(receipt.getStatus().equals(ResponseCodeEnum.SUCCESS) - && receipt.getContractID().hasEvmAddress()) - && !transactionRecord.getContractCallResult().getContractID().hasEvmAddress(); + private boolean shouldThrowRecoverableError( + ContractFunctionResult functionResult, RecordItem recordItem, TransactionRecord transactionRecord) { + return !(recordItem.isSuccessful() + && transactionRecord.getReceipt().getContractID().hasEvmAddress()) + && !functionResult.getContractID().hasEvmAddress(); } private void processContractAction(ContractAction action, int index, RecordItem recordItem) { diff --git a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java index 877463ef900..152893f6f81 100644 --- a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java +++ b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java @@ -114,15 +114,15 @@ private static Stream provideEntities() { return Stream.of( Arguments.of(withoutDefaultContractId, null, true), Arguments.of(withoutDefaultContractId, EntityId.EMPTY, true), - Arguments.of(withInactiveEvm, null, false), - Arguments.of(withInactiveEvm, EntityId.EMPTY, false), - Arguments.of(withActiveEvm, null, true), - Arguments.of(withActiveEvm, EntityId.EMPTY, true), Arguments.of(withDefaultContractId, null, false), Arguments.of(withDefaultContractId, EntityId.EMPTY, false), Arguments.of(contractCreate, EntityId.EMPTY, false), Arguments.of(contractCreate, null, false), - Arguments.of(contractCreate, EntityId.of(0, 0, 5), false)); + Arguments.of(contractCreate, EntityId.of(0, 0, 5), false), + Arguments.of(withInactiveEvm, null, false), + Arguments.of(withInactiveEvm, EntityId.EMPTY, false), + Arguments.of(withActiveEvm, null, true), + Arguments.of(withActiveEvm, EntityId.EMPTY, true)); } @BeforeEach From facde4e9ad104a16f6854713702357b1d51162d3 Mon Sep 17 00:00:00 2001 From: Edwin Greene Date: Thu, 13 Jun 2024 15:08:44 -0400 Subject: [PATCH 3/6] Correct supression logic Signed-off-by: Edwin Greene --- .../domain/ContractResultServiceImpl.java | 13 +++++++------ .../domain/ContractResultServiceImplTest.java | 16 +++------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java index d1d2020294a..984484cf6a4 100644 --- a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java +++ b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java @@ -95,10 +95,11 @@ public void process(@NonNull RecordItem recordItem, Transaction transaction) { ? Optional.ofNullable(transaction.getEntityId()) : entityIdService.lookup(functionResult.getContractID(), throwRecoverableError)) .orElse(EntityId.EMPTY); - var isRecoverableError = throwRecoverableError - && EntityId.isEmpty(contractId) - && !contractCallOrCreate - && (!ContractID.getDefaultInstance().equals(functionResult.getContractID())); + var isRecoverableError = + !(recordItem.isSuccessful() && functionResult.getContractID().hasEvmAddress()) + && EntityId.isEmpty(contractId) + && !contractCallOrCreate + && (!ContractID.getDefaultInstance().equals(functionResult.getContractID())); if (isRecoverableError) { Utility.handleRecoverableError( "Invalid contract id for contract result at {}", recordItem.getConsensusTimestamp()); @@ -154,8 +155,8 @@ private boolean isContractCreateOrCall(TransactionBody transactionBody) { private boolean shouldThrowRecoverableError( ContractFunctionResult functionResult, RecordItem recordItem, TransactionRecord transactionRecord) { return !(recordItem.isSuccessful() - && transactionRecord.getReceipt().getContractID().hasEvmAddress()) - && !functionResult.getContractID().hasEvmAddress(); + && (transactionRecord.getReceipt().getContractID().hasEvmAddress() + || functionResult.getContractID().hasEvmAddress())); } private void processContractAction(ContractAction action, int index, RecordItem recordItem) { diff --git a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java index 152893f6f81..c03567da819 100644 --- a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java +++ b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java @@ -92,20 +92,12 @@ private static Stream provideEntities() { .record(x -> x.setContractCallResult(builder.contractFunctionResult())) .build(); - var contractId = ContractID.newBuilder() + var contractIdWithEvm = ContractID.newBuilder() .setEvmAddress(ByteString.copyFromUtf8("1234")) .build(); Function withInactiveEvm = (RecordItemBuilder builder) -> builder.tokenMint(TokenType.FUNGIBLE_COMMON) - .receipt(x -> x.setContractID(contractId)) - .record(x -> x.setContractCallResult(builder.contractFunctionResult())) - .build(); - - var contractIdNoEvm = ContractID.newBuilder().setContractNum(5).build(); - Function withActiveEvm = - (RecordItemBuilder builder) -> builder.tokenMint(TokenType.FUNGIBLE_COMMON) - .receipt(x -> x.setContractID(contractIdNoEvm)) - .record(x -> x.setContractCallResult(builder.contractFunctionResult(contractIdNoEvm))) + .record(x -> x.setContractCallResult(builder.contractFunctionResult(contractIdWithEvm))) .build(); Function contractCreate = @@ -120,9 +112,7 @@ private static Stream provideEntities() { Arguments.of(contractCreate, null, false), Arguments.of(contractCreate, EntityId.of(0, 0, 5), false), Arguments.of(withInactiveEvm, null, false), - Arguments.of(withInactiveEvm, EntityId.EMPTY, false), - Arguments.of(withActiveEvm, null, true), - Arguments.of(withActiveEvm, EntityId.EMPTY, true)); + Arguments.of(withInactiveEvm, EntityId.EMPTY, false)); } @BeforeEach From 559cecc58887c2924a688325da5e05f5d0c6e198 Mon Sep 17 00:00:00 2001 From: Edwin Greene Date: Fri, 14 Jun 2024 10:03:43 -0400 Subject: [PATCH 4/6] Remove function result from initial logic Signed-off-by: Edwin Greene --- .../importer/domain/ContractResultServiceImpl.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java index 984484cf6a4..81bff8ce76c 100644 --- a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java +++ b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java @@ -44,7 +44,6 @@ import com.hederahashgraph.api.proto.java.ContractID; import com.hederahashgraph.api.proto.java.ResponseCodeEnum; import com.hederahashgraph.api.proto.java.TransactionBody; -import com.hederahashgraph.api.proto.java.TransactionRecord; import jakarta.inject.Named; import java.util.ArrayList; import java.util.List; @@ -89,7 +88,8 @@ public void process(@NonNull RecordItem recordItem, Transaction transaction) { // contractResult var transactionHandler = transactionHandlerFactory.get(TransactionType.of(transaction.getType())); - var throwRecoverableError = shouldThrowRecoverableError(functionResult, recordItem, transactionRecord); + var throwRecoverableError = !(recordItem.isSuccessful() + && (transactionRecord.getReceipt().getContractID().hasEvmAddress())); // in pre-compile case transaction is not a contract type and entityId will be of a different type var contractId = (contractCallOrCreate ? Optional.ofNullable(transaction.getEntityId()) @@ -152,13 +152,6 @@ private boolean isContractCreateOrCall(TransactionBody transactionBody) { return transactionBody.hasContractCall() || transactionBody.hasContractCreateInstance(); } - private boolean shouldThrowRecoverableError( - ContractFunctionResult functionResult, RecordItem recordItem, TransactionRecord transactionRecord) { - return !(recordItem.isSuccessful() - && (transactionRecord.getReceipt().getContractID().hasEvmAddress() - || functionResult.getContractID().hasEvmAddress())); - } - private void processContractAction(ContractAction action, int index, RecordItem recordItem) { long consensusTimestamp = recordItem.getConsensusTimestamp(); var contractAction = new com.hedera.mirror.common.domain.contract.ContractAction(); From 1ecb9d7e743cd1849cc922d3ef2a2d40f9158b1b Mon Sep 17 00:00:00 2001 From: Edwin Greene Date: Fri, 21 Jun 2024 10:22:22 -0400 Subject: [PATCH 5/6] Fix logic and add comment Signed-off-by: Edwin Greene --- .../domain/ContractResultServiceImpl.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java index 81bff8ce76c..73e761fe2b9 100644 --- a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java +++ b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java @@ -88,18 +88,22 @@ public void process(@NonNull RecordItem recordItem, Transaction transaction) { // contractResult var transactionHandler = transactionHandlerFactory.get(TransactionType.of(transaction.getType())); - var throwRecoverableError = !(recordItem.isSuccessful() - && (transactionRecord.getReceipt().getContractID().hasEvmAddress())); + + // Whether a recoverable error should be thrown on entity id lookup + // Inactive evm addresses which cannot be looked up should not throw recoverable errors + boolean lookupRecoverable = + !(recordItem.isSuccessful() && (functionResult.getContractID().hasEvmAddress())); + // in pre-compile case transaction is not a contract type and entityId will be of a different type var contractId = (contractCallOrCreate ? Optional.ofNullable(transaction.getEntityId()) - : entityIdService.lookup(functionResult.getContractID(), throwRecoverableError)) + : entityIdService.lookup(functionResult.getContractID(), lookupRecoverable)) .orElse(EntityId.EMPTY); - var isRecoverableError = - !(recordItem.isSuccessful() && functionResult.getContractID().hasEvmAddress()) - && EntityId.isEmpty(contractId) - && !contractCallOrCreate - && (!ContractID.getDefaultInstance().equals(functionResult.getContractID())); + var isRecoverableError = !(recordItem.isSuccessful() + && transactionRecord.getReceipt().getContractID().hasEvmAddress()) + && EntityId.isEmpty(contractId) + && !contractCallOrCreate + && (!ContractID.getDefaultInstance().equals(functionResult.getContractID())); if (isRecoverableError) { Utility.handleRecoverableError( "Invalid contract id for contract result at {}", recordItem.getConsensusTimestamp()); From c1f506de3f8b5ea910260091250dcd970ca33c10 Mon Sep 17 00:00:00 2001 From: Edwin Greene Date: Fri, 21 Jun 2024 16:12:44 -0400 Subject: [PATCH 6/6] Fix unit test Signed-off-by: Edwin Greene --- .../domain/ContractResultServiceImpl.java | 2 +- .../domain/ContractResultServiceImplTest.java | 25 ++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java index 73e761fe2b9..35db0587896 100644 --- a/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java +++ b/hedera-mirror-importer/src/main/java/com/hedera/mirror/importer/domain/ContractResultServiceImpl.java @@ -103,7 +103,7 @@ public void process(@NonNull RecordItem recordItem, Transaction transaction) { && transactionRecord.getReceipt().getContractID().hasEvmAddress()) && EntityId.isEmpty(contractId) && !contractCallOrCreate - && (!ContractID.getDefaultInstance().equals(functionResult.getContractID())); + && !ContractID.getDefaultInstance().equals(functionResult.getContractID()); if (isRecoverableError) { Utility.handleRecoverableError( "Invalid contract id for contract result at {}", recordItem.getConsensusTimestamp()); diff --git a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java index c03567da819..b20a2693916 100644 --- a/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java +++ b/hedera-mirror-importer/src/test/java/com/hedera/mirror/importer/domain/ContractResultServiceImplTest.java @@ -38,7 +38,9 @@ import com.hedera.mirror.importer.parser.record.transactionhandler.TransactionHandler; import com.hedera.mirror.importer.parser.record.transactionhandler.TransactionHandlerFactory; import com.hederahashgraph.api.proto.java.ContractID; +import com.hederahashgraph.api.proto.java.ResponseCodeEnum; import com.hederahashgraph.api.proto.java.TokenType; +import com.hederahashgraph.api.proto.java.TransactionReceipt; import java.util.ArrayList; import java.util.HashSet; import java.util.Objects; @@ -95,9 +97,22 @@ private static Stream provideEntities() { var contractIdWithEvm = ContractID.newBuilder() .setEvmAddress(ByteString.copyFromUtf8("1234")) .build(); - Function withInactiveEvm = + + // The transaction receipt does not have an evm address, so a recoverable error is expected. + Function withInactiveEvmFunctionOnly = + (RecordItemBuilder builder) -> builder.tokenMint(TokenType.FUNGIBLE_COMMON) + .record(x -> x.setReceipt( + TransactionReceipt.newBuilder().setStatus(ResponseCodeEnum.SUCCESS)) + .setContractCallResult(builder.contractFunctionResult(contractIdWithEvm))) + .build(); + + // The transaction receipt has an evm address, so no recoverable error is expected. + Function withInactiveEvmReceipt = (RecordItemBuilder builder) -> builder.tokenMint(TokenType.FUNGIBLE_COMMON) - .record(x -> x.setContractCallResult(builder.contractFunctionResult(contractIdWithEvm))) + .record(x -> x.setReceipt(TransactionReceipt.newBuilder() + .setStatus(ResponseCodeEnum.SUCCESS) + .setContractID(contractIdWithEvm)) + .setContractCallResult(builder.contractFunctionResult(contractIdWithEvm))) .build(); Function contractCreate = @@ -111,8 +126,10 @@ private static Stream provideEntities() { Arguments.of(contractCreate, EntityId.EMPTY, false), Arguments.of(contractCreate, null, false), Arguments.of(contractCreate, EntityId.of(0, 0, 5), false), - Arguments.of(withInactiveEvm, null, false), - Arguments.of(withInactiveEvm, EntityId.EMPTY, false)); + Arguments.of(withInactiveEvmFunctionOnly, null, true), + Arguments.of(withInactiveEvmFunctionOnly, EntityId.EMPTY, true), + Arguments.of(withInactiveEvmReceipt, null, false), + Arguments.of(withInactiveEvmReceipt, EntityId.EMPTY, false)); } @BeforeEach