diff --git a/server/src/main/kotlin/io/provenance/invoice/services/EventHandlerService.kt b/server/src/main/kotlin/io/provenance/invoice/services/EventHandlerService.kt index 890764b..07fa6bc 100644 --- a/server/src/main/kotlin/io/provenance/invoice/services/EventHandlerService.kt +++ b/server/src/main/kotlin/io/provenance/invoice/services/EventHandlerService.kt @@ -12,6 +12,7 @@ import io.provenance.invoice.util.enums.ExpectedPayableType import io.provenance.invoice.util.enums.InvoiceStatus import io.provenance.invoice.util.eventstream.external.StreamEvent import io.provenance.invoice.util.extension.attributeValueI +import io.provenance.invoice.util.extension.attributeValueOrNullI import io.provenance.invoice.util.extension.unpackInvoiceI import io.provenance.invoice.util.provenance.PayableContractKey import io.provenance.invoice.util.validation.InvoiceValidator @@ -54,6 +55,14 @@ class EventHandlerService( logger.info("Ignoring event [${event.txHash}] for non-invoice payable with type [$payableType]") return } + val oracleAddress = event.attributeValueOrNullI(PayableContractKey.ORACLE_ADDRESS) ?: run { + logger.info("Legacy event [${event.txHash}] included no oracle address attribute. Skipping processing") + return + } + if (oracleAddress != objectStore.oracleAccountDetail.bech32Address) { + logger.info("Ignoring event [${event.txHash}] for different oracle [$oracleAddress]") + return + } val incomingEvent = IncomingInvoiceEvent( streamEvent = event, invoiceUuid = event.attributeValueI(PayableContractKey.PAYABLE_UUID), @@ -65,6 +74,11 @@ class EventHandlerService( PayableContractKey.ORACLE_APPROVED.contractName in eventKeys -> handleOracleApprovedEvent(incomingEvent) PayableContractKey.PAYMENT_MADE.contractName in eventKeys -> handlePaymentMadeEvent(incomingEvent) } + // If this block is reached without exception and it is a retry, it is safe to mark the retryable as processed + if (isRetry) { + logger.info("Marking retried event with hash [${event.txHash}] as successfully processed") + failedEventRepository.markEventProcessed(event.txHash) + } }.getOrHandle { e -> logger.error("Failed to process event with hash [${event.txHash}] and type [${event.eventType}] at height [${event.height}]", e) handleFailedInvoiceEvent(txHash = event.txHash, isRetry = isRetry) diff --git a/server/src/main/kotlin/io/provenance/invoice/services/InvoiceService.kt b/server/src/main/kotlin/io/provenance/invoice/services/InvoiceService.kt index 4494451..f0724b9 100644 --- a/server/src/main/kotlin/io/provenance/invoice/services/InvoiceService.kt +++ b/server/src/main/kotlin/io/provenance/invoice/services/InvoiceService.kt @@ -2,6 +2,7 @@ package io.provenance.invoice.services import com.google.protobuf.Any import io.provenance.invoice.InvoiceProtos.Invoice +import io.provenance.invoice.config.app.Qualifiers import mu.KLogging import org.springframework.stereotype.Service import io.provenance.invoice.repository.InvoiceRepository @@ -11,8 +12,10 @@ import io.provenance.invoice.util.extension.scopeIdI import io.provenance.invoice.util.extension.toAssetI import io.provenance.invoice.util.extension.toProtoAnyI import io.provenance.invoice.util.extension.toUuidI +import io.provenance.invoice.util.provenance.ProvenanceAccountDetail import io.provenance.invoice.util.validation.InvoiceValidator import io.provenance.invoice.util.validation.ValidatedInvoice +import org.springframework.beans.factory.annotation.Qualifier import java.math.BigDecimal import java.util.UUID @@ -20,6 +23,7 @@ import java.util.UUID class InvoiceService( private val assetOnboardingService: AssetOnboardingService, private val invoiceRepository: InvoiceRepository, + @Qualifier(Qualifiers.ORACLE_ACCOUNT_DETAIL) private val oracleAccountDetail: ProvenanceAccountDetail ) { private companion object : KLogging() @@ -48,6 +52,7 @@ class InvoiceService( payableType = ExpectedPayableType.INVOICE.contractName, payableUuid = upsertedInvoice.uuid, scopeId = assetOnboardingResponse.writeScopeRequest.scopeIdI(), + oracleAddress = oracleAccountDetail.bech32Address, invoiceTotal = upsertedInvoice.totalOwed, invoiceDenom = upsertedInvoice.invoice.paymentDenom, ), @@ -77,6 +82,7 @@ data class PayablesContractExecutionDetail( val payableType: String, val payableUuid: UUID, val scopeId: String, + val oracleAddress: String, val invoiceDenom: String, val invoiceTotal: BigDecimal, ) diff --git a/server/src/main/kotlin/io/provenance/invoice/util/provenance/PayableContractKey.kt b/server/src/main/kotlin/io/provenance/invoice/util/provenance/PayableContractKey.kt index a0ef487..c4c3025 100644 --- a/server/src/main/kotlin/io/provenance/invoice/util/provenance/PayableContractKey.kt +++ b/server/src/main/kotlin/io/provenance/invoice/util/provenance/PayableContractKey.kt @@ -21,7 +21,8 @@ enum class PayableContractKey(val contractName: String, val isEventKey: Boolean) // All these keys are globally shared and appear on all payable contract events PAYABLE_UUID("payable_uuid", false), - PAYABLE_TYPE("payable_type", false); + PAYABLE_TYPE("payable_type", false), + ORACLE_ADDRESS("payable_oracle_address", false); companion object { val EVENT_KEYS: List by lazy { values().filter { it.isEventKey } } diff --git a/server/src/main/kotlin/io/provenance/invoice/web/controllers/FailedEventControllerV1.kt b/server/src/main/kotlin/io/provenance/invoice/web/controllers/FailedEventControllerV1.kt new file mode 100644 index 0000000..6234504 --- /dev/null +++ b/server/src/main/kotlin/io/provenance/invoice/web/controllers/FailedEventControllerV1.kt @@ -0,0 +1,21 @@ +package io.provenance.invoice.web.controllers + +import io.provenance.invoice.config.web.AppRoutes +import io.provenance.invoice.repository.FailedEventRepository +import mu.KLogging +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("${AppRoutes.V1}/failed-event", produces = ["application/json"]) +class FailedEventControllerV1(private val failedEventRepository: FailedEventRepository) { + private companion object : KLogging() + + @PostMapping("resolve/{eventHash}") + fun resolveFailedEvent(@PathVariable eventHash: String) { + logger.info("Manually resolving failed event [$eventHash]") + failedEventRepository.markEventProcessed(eventHash) + } +}