diff --git a/relayer/processes/xrpl_tx_observer.go b/relayer/processes/xrpl_tx_observer.go index 99456c91..b4087352 100644 --- a/relayer/processes/xrpl_tx_observer.go +++ b/relayer/processes/xrpl_tx_observer.go @@ -2,6 +2,7 @@ package processes import ( "context" + "strings" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/pkg/errors" @@ -82,6 +83,10 @@ func (o *XRPLTxObserver) Start(ctx context.Context) error { func (o *XRPLTxObserver) processTx(ctx context.Context, tx rippledata.TransactionWithMetaData) error { ctx = tracing.WithTracingXRPLTxHash(tracing.WithTracingID(ctx), tx.GetHash().String()) + if !txIsFinal(tx) { + o.log.Debug(ctx, "Transaction is not final", logger.StringField("txStatus", tx.MetaData.TransactionResult.String())) + return nil + } if o.cfg.BridgeXRPLAddress == tx.GetBase().Account { return o.processOutgoingTx(ctx, tx) } @@ -270,6 +275,22 @@ func (o *XRPLTxObserver) handleEvidenceSubmissionError( return err } +// txIsFinal returns value which indicates whether the transaction if final and can be used. +// Result Code Finality. +// tesSUCCESS Final when included in a validated ledger. +// Any tec code Final when included in a validated ledger. +// Any tem code Final unless the protocol changes to make the transaction valid. +// tefPAST_SEQ Final when another transaction with the same sequence number is included in a validated ledger. +// tefMAX_LEDGER Final when a validated ledger has a ledger index higher than the transaction's LastLedgerSequence field, and no validated ledger includes the transaction. +func txIsFinal(tx rippledata.TransactionWithMetaData) bool { + txResult := tx.MetaData.TransactionResult + return tx.MetaData.TransactionResult.Success() || + strings.HasPrefix(txResult.String(), xrpl.TecTxResultPrefix) || + strings.HasPrefix(txResult.String(), xrpl.TemTxResultPrefix) || + txResult.String() == xrpl.TefPastSeqTxResult || + txResult.String() == xrpl.TefMaxLedgerTxResult +} + func getTransactionResult(tx rippledata.TransactionWithMetaData) coreum.TransactionResult { if tx.MetaData.TransactionResult.Success() { return coreum.TransactionResultAccepted diff --git a/relayer/processes/xrpl_tx_observer_test.go b/relayer/processes/xrpl_tx_observer_test.go index 908693d6..1c94cf01 100644 --- a/relayer/processes/xrpl_tx_observer_test.go +++ b/relayer/processes/xrpl_tx_observer_test.go @@ -23,7 +23,10 @@ func TestXRPLTxObserver_Start(t *testing.T) { recipientXRPLAddress := xrpl.GenPrivKeyTxSigner().Account() issuerAccount := xrpl.GenPrivKeyTxSigner().Account() - failTxResult := rippledata.TransactionResult(111) + // tecPATH_PARTIAL + failTxResult := rippledata.TransactionResult(101) + // tefBAD_AUTH + notTxResult := rippledata.TransactionResult(-199) relayerAddress := coreum.GenAccount() coreumRecipientAddress := coreum.GenAccount() @@ -125,6 +128,27 @@ func TestXRPLTxObserver_Start(t *testing.T) { return xrplAccountTxScannerMock }, }, + { + name: "incoming_not_confirmed_tx", + txScannerBuilder: func(ctrl *gomock.Controller, cancel func()) processes.XRPLAccountTxScanner { + xrplAccountTxScannerMock := NewMockXRPLAccountTxScanner(ctrl) + xrplAccountTxScannerMock.EXPECT().ScanTxs(gomock.Any(), gomock.Any()).DoAndReturn( + func(ctx context.Context, ch chan<- rippledata.TransactionWithMetaData) error { + go func() { + ch <- rippledata.TransactionWithMetaData{ + Transaction: &rippledata.Payment{}, + MetaData: rippledata.MetaData{ + TransactionResult: notTxResult, + }, + } + cancel() + }() + return nil + }) + + return xrplAccountTxScannerMock + }, + }, { name: "incoming_not_payment_tx", txScannerBuilder: func(ctrl *gomock.Controller, cancel func()) processes.XRPLAccountTxScanner { diff --git a/relayer/processes/xrpl_tx_submitter.go b/relayer/processes/xrpl_tx_submitter.go index 3b21a018..030c6aac 100644 --- a/relayer/processes/xrpl_tx_submitter.go +++ b/relayer/processes/xrpl_tx_submitter.go @@ -218,7 +218,7 @@ func (s *XRPLTxSubmitter) signOrSubmitOperation(ctx context.Context, operation c } switch txRes.EngineResult.String() { - case xrpl.TefNOTicketTxResult, xrpl.TefPastSeqTxResult, xrpl.TerPreSeqTxResult: + case xrpl.TefNOTicketTxResult, xrpl.TefPastSeqTxResult: s.log.Debug(ctx, "Transaction has been already submitted", logger.StringField("txHash", tx.GetHash().String())) return nil case xrpl.TecPathDryTxResult: diff --git a/relayer/xrpl/constatns.go b/relayer/xrpl/constatns.go index fcda1c66..3c15c5f7 100644 --- a/relayer/xrpl/constatns.go +++ b/relayer/xrpl/constatns.go @@ -6,10 +6,14 @@ const ( TecPathDryTxResult = "tecPATH_DRY" // TefNOTicketTxResult defines the result which indicates the usage of the passed ticket or not created ticket. TefNOTicketTxResult = "tefNO_TICKET" - // TefPastSeqTxResult defines the result which indicates the usage of the sequence in the past. + // TefPastSeqTxResult defines that the usage of the sequence in the past. TefPastSeqTxResult = "tefPAST_SEQ" - // TerPreSeqTxResult defines the result which indicates the usage of the sequence in the future. - TerPreSeqTxResult = "terPRE_SEQ" - // TecInsufficientReserveTxResult defines the result which indicates the insufficient reserve to complete requested operation. + // TefMaxLedgerTxResult defines that ledger sequence too high. + TefMaxLedgerTxResult = "tefMAX_LEDGER" + // TecInsufficientReserveTxResult defines that reserve is insufficient to complete requested operation. TecInsufficientReserveTxResult = "tecINSUFFICIENT_RESERVE" + // TecTxResultPrefix is `tec` prefix for tx result. + TecTxResultPrefix = "tec" + // TemTxResultPrefix is `tem` prefix for tx result. + TemTxResultPrefix = "tem" )