Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add XRPL tx finality check. #48

Merged
merged 4 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions relayer/processes/xrpl_tx_observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package processes

import (
"context"
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
Expand Down Expand Up @@ -78,6 +79,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.BridgeAccount == tx.GetBase().Account {
return o.processOutgoingTx(ctx, tx)
}
Expand Down Expand Up @@ -255,6 +260,26 @@ func (o *XRPLTxObserver) sendXRPLTrustSetTransactionResultEvidence(ctx context.C
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
if tx.MetaData.TransactionResult.Success() ||
strings.HasPrefix(txResult.String(), xrpl.TecTxResultPrefix) ||
strings.HasPrefix(txResult.String(), xrpl.TemTxResultPrefix) ||
txResult.String() == xrpl.TefPastSeqTxResult ||
txResult.String() == xrpl.TefMaxLedgerTxResult {
return true
}

return false
}

func extractTicketSequencesFromMetaData(metaData rippledata.MetaData) []uint32 {
ticketSequences := make([]uint32, 0)
for _, node := range metaData.AffectedNodes {
Expand Down
26 changes: 25 additions & 1 deletion relayer/processes/xrpl_tx_observer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ func TestXRPLTxObserver_Start(t *testing.T) {
bridgeXRPLAddress := 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()
Expand Down Expand Up @@ -124,6 +127,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 {
Expand Down
2 changes: 1 addition & 1 deletion relayer/processes/xrpl_tx_submitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.TecInsufficientReserveTxResult:
Expand Down
14 changes: 9 additions & 5 deletions relayer/xrpl/constatns.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package xrpl

const (
// TefNOTicketTxResult defines the result which indicates the usage of the passed ticket or not created ticket.
// TefNOTicketTxResult defines that 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"
)