Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
36 changes: 26 additions & 10 deletions chain_capabilities/stellar/actions/tx_hash_retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@ func (r *TxHashRetriever) GetSuccessfulTransmissionHash(ctx context.Context) (st
if err != nil {
return "", err
}
for _, d := range details {
if d.isSuccess {
return d.txHash, nil
}
selected, ok := earliestEvent(details, func(d eventDetails) bool {
return d.isSuccess
})
if ok {
return selected.txHash, nil
}
r.lggr.Errorw("No successful transmission found", "txCount", len(details), "transactions", details.String())
return "", fmt.Errorf("no successful transmission found. Found %d transactions (all failed): %s",
Expand All @@ -97,14 +98,14 @@ func (r *TxHashRetriever) GetFailedTransmissionHashWithCount(ctx context.Context
return "", 0, fmt.Errorf("no failed transmission found")
}

earliestIdx := 0
for i, d := range details {
if d.ledger < details[earliestIdx].ledger {
earliestIdx = i
}
selected, ok := earliestEvent(details, func(d eventDetails) bool {
return !d.isSuccess
})
if !ok {
return "", len(details), fmt.Errorf("no failed transmission found")
}

selectedHash := details[earliestIdx].txHash
selectedHash := selected.txHash
r.lggr.Debugw("Returning earliest failed transmission",
append([]any{
"txCount", len(details),
Expand All @@ -115,6 +116,21 @@ func (r *TxHashRetriever) GetFailedTransmissionHashWithCount(ctx context.Context
return selectedHash, len(details), nil
}

func earliestEvent(details eventDetailsList, match func(eventDetails) bool) (eventDetails, bool) {
var selected eventDetails
found := false
for _, d := range details {
if !match(d) {
continue
}
if !found || d.ledger < selected.ledger {
selected = d
found = true
}
}
return selected, found
}

func (r *TxHashRetriever) fetchAndParseEvents(ctx context.Context) (eventDetailsList, error) {
searchRange, err := r.forwarderClient.GetReportProcessedEventSearchRange(ctx)
if err != nil {
Expand Down
14 changes: 14 additions & 0 deletions chain_capabilities/stellar/actions/tx_hash_retriever_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,20 @@ func TestTxHashRetriever_GetSuccessfulTransmissionHash(t *testing.T) {
require.Equal(t, testTxHash, hash)
})

t.Run("returns earliest successful hash by ledger", func(t *testing.T) {
t.Parallel()
client := &stubForwarderClient{events: []ReportProcessedEvent{
{TxHash: "later", Ledger: 200, Success: true},
{TxHash: "failed", Ledger: 50, Success: false},
{TxHash: testTxHash, Ledger: 100, Success: true},
}}
retriever := NewTxHashRetriever(client, lggr, transmissionID)

hash, err := retriever.GetSuccessfulTransmissionHash(t.Context())
require.NoError(t, err)
require.Equal(t, testTxHash, hash)
})

t.Run("returns error when all events failed", func(t *testing.T) {
t.Parallel()
client := &stubForwarderClient{events: []ReportProcessedEvent{
Expand Down
31 changes: 17 additions & 14 deletions chain_capabilities/stellar/actions/write_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
return wr.execute(ctx, request, metadata, telemetryContext)
}

func (wr *writeReport) execute(

Check warning on line 112 in chain_capabilities/stellar/actions/write_report.go

View check run for this annotation

CL-sonarqube-production / SonarQube Code Analysis

Refactor this method to reduce its Cognitive Complexity from 69 to the 30 allowed.

[S3776] Cognitive Complexity of functions should not be too high See more on https://sonarqube.main.prod.cldev.sh/project/issues?id=smartcontractkit_capabilities&pullRequest=738&issues=bb453806-daa4-4294-b1aa-1c722523615b&open=bb453806-daa4-4294-b1aa-1c722523615b
ctx context.Context,
request *stellarcap.WriteReportRequest,
metadata capabilities.RequestMetadata,
Expand Down Expand Up @@ -245,19 +245,9 @@
return readInfo, nil
})
if pollErr != nil {
// Transmission info may lag even when ReportProcessed events are already indexed (e.g. duplicate
// submit where another node's tx succeeded). Prefer the canonical event hash over local TXM data.
wr.lggr.Warnw("Failed to poll transmission info after submit, attempting event-based tx hash lookup", "error", pollErr)
txHash, lookupErr := txHashRetriever.GetSuccessfulTransmissionHash(ctx)
if lookupErr == nil {
reply, buildErr := wr.buildSuccessReply(ctx, request, telemetryContext, txHash)
return reply, ownMeteringMetadata, buildErr
}

wr.lggr.Errorw(
"Failed to determine canonical transmission outcome after submit",
"Failed to confirm transmission outcome after submit",
"pollError", pollErr,
"eventLookupError", lookupErr,
"localTxHash", submitResp.TxHash,
"localTxStatus", submitResp.TxStatus,
)
Expand All @@ -267,6 +257,11 @@

switch postInfo.State {
case TransmissionStateSucceeded:
if submitResp.TxStatus == stellartypes.TxSuccess && submitResp.TxHash != "" {
reply, err := wr.buildSuccessReply(ctx, request, telemetryContext, submitResp.TxHash)
return reply, ownMeteringMetadata, err
}

txHash, err := txHashRetriever.GetSuccessfulTransmissionHash(ctx)
if err != nil {
// A submit occurred and was paid for; bill the local submit hash even though
Expand All @@ -281,6 +276,15 @@
reply, err := wr.buildSuccessReply(ctx, request, telemetryContext, txHash)
return reply, ownMeteringMetadata, err
case TransmissionStateFailed, TransmissionStateInvalidReceiver:
if submitResp.TxStatus == stellartypes.TxSuccess && submitResp.TxHash != "" {
wr.lggr.Errorw("Made a new transmission attempt - transmission failed", "txHash", submitResp.TxHash, "transmissionState", postInfo.State)
reply, err := wr.buildRevertReplyFromTx(ctx, request, telemetryContext, submitResp.TxHash, postInfo, transmissionID)
if err != nil {
return nil, ownMeteringMetadata, revertReplyBuildError(postInfo, transmissionID, err)
}
return reply, ownMeteringMetadata, nil
}

txHash, err := txHashRetriever.GetFailedTransmissionHash(ctx)
if err != nil {
if errors.Is(err, ErrUnexpectedSuccessfulTransmission) {
Expand Down Expand Up @@ -557,10 +561,9 @@
message = new(unknownIssueExecutingReceiverContractMessage)
}

// A ReportProcessed event is only available for a transaction that reached the
// forwarder and committed its outcome. Receiver failure is reported separately.
txStatus := stellarcap.TxStatus_TX_STATUS_SUCCESS
if receiverStatus == stellarcap.ReceiverContractExecutionStatus_RECEIVER_CONTRACT_EXECUTION_STATUS_REVERTED {
txStatus = stellarcap.TxStatus_TX_STATUS_REVERTED
}

reply := &stellarcap.WriteReportReply{
TxHash: new(txHash),
Expand Down
Loading
Loading