Skip to content

Commit de51911

Browse files
committed
add: closing artifact on failure
1 parent 95076a9 commit de51911

4 files changed

Lines changed: 65 additions & 1 deletion

File tree

universalClient/tss/coordinator/coordinator_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ func (m *coordMockTxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *
7575
return args.String(0), args.Error(1)
7676
}
7777

78+
func (m *coordMockTxBuilder) CleanupOutboundArtifacts(ctx context.Context, data *uexecutortypes.OutboundCreatedEvent) error {
79+
args := m.Called(ctx, data)
80+
return args.Error(0)
81+
}
82+
7883
type coordMockChainClient struct {
7984
builder *coordMockTxBuilder
8085
builderErr error

universalClient/tss/txbroadcaster/broadcaster_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ func (m *mockTxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *commo
7575
return args.String(0), args.Error(1)
7676
}
7777

78+
func (m *mockTxBuilder) CleanupOutboundArtifacts(ctx context.Context, data *uexecutortypes.OutboundCreatedEvent) error {
79+
args := m.Called(ctx, data)
80+
return args.Error(0)
81+
}
82+
7883
type mockChainClient struct{ builder *mockTxBuilder }
7984

8085
func (m *mockChainClient) Start(context.Context) error { return nil }

universalClient/tss/txresolver/resolver_test.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ func (m *mockTxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *commo
7474
return args.String(0), args.Error(1)
7575
}
7676

77+
func (m *mockTxBuilder) CleanupOutboundArtifacts(ctx context.Context, data *uexecutortypes.OutboundCreatedEvent) error {
78+
args := m.Called(ctx, data)
79+
return args.Error(0)
80+
}
81+
7782
type mockChainClient struct{ builder *mockTxBuilder }
7883

7984
func (m *mockChainClient) Start(context.Context) error { return nil }
@@ -270,7 +275,8 @@ func TestSVM_PDAExists_MarksCompleted(t *testing.T) {
270275
}
271276

272277
func TestSVM_PDANotFound_VotesFailureAndReverts(t *testing.T) {
273-
// PDA not found → vote failure → REVERTED.
278+
// PDA not found → vote failure → REVERTED, plus cleanup of any orphaned
279+
// StoredIxData PDA from the ref-finalize route.
274280
evtStore, db := setupTestDB(t)
275281
builder := &mockTxBuilder{}
276282
client := &mockChainClient{builder: builder}
@@ -280,6 +286,7 @@ func TestSVM_PDANotFound_VotesFailureAndReverts(t *testing.T) {
280286
insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:", eventData)
281287

282288
builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, nil)
289+
builder.On("CleanupOutboundArtifacts", mock.Anything, mock.Anything).Return(nil).Once()
283290

284291
// No PushSigner — voteFailure will log warning and return nil, but won't mark REVERTED
285292
// (because pushSigner is nil, it returns early). This validates the code path.
@@ -291,6 +298,38 @@ func TestSVM_PDANotFound_VotesFailureAndReverts(t *testing.T) {
291298
// The event stays BROADCASTED because the vote+revert is skipped.
292299
updated := getEvent(t, db, "ev-1")
293300
require.Equal(t, store.StatusBroadcasted, updated.Status)
301+
// Explicit assertion: cleanup must have been invoked once for the failure path.
302+
builder.AssertExpectations(t)
303+
}
304+
305+
func TestSVM_PDANotFound_CleanupErrorIsBestEffort(t *testing.T) {
306+
// A failure from CleanupOutboundArtifacts must NOT crash the resolver or
307+
// roll back the failure-vote transition. Cleanup is opex-only.
308+
evtStore, db := setupTestDB(t)
309+
builder := &mockTxBuilder{}
310+
client := &mockChainClient{builder: builder}
311+
ch := newTestChains(t, "solana:mainnet", uregistrytypes.VmType_SVM, client)
312+
313+
eventData := makeOutboundEventData("tx-123", "utx-456", "solana:mainnet")
314+
insertBroadcastedEvent(t, db, "ev-1", "solana:mainnet", "solana:mainnet:", eventData)
315+
316+
builder.On("IsAlreadyExecuted", mock.Anything, "tx-123").Return(false, nil)
317+
builder.On("CleanupOutboundArtifacts", mock.Anything, mock.Anything).
318+
Return(assert.AnError).Once()
319+
320+
resolver := newResolver(evtStore, ch)
321+
ev := getEvent(t, db, "ev-1")
322+
323+
// Should not panic even though cleanup returned an error.
324+
require.NotPanics(t, func() {
325+
resolver.resolveSVM(context.Background(), &ev, "solana:mainnet")
326+
})
327+
328+
// State transition behaves identically to the success-cleanup case
329+
// (BROADCASTED here because no push signer is configured).
330+
updated := getEvent(t, db, "ev-1")
331+
require.Equal(t, store.StatusBroadcasted, updated.Status)
332+
builder.AssertExpectations(t)
294333
}
295334

296335
func TestSVM_PDACheckFails_StaysBroadcasted(t *testing.T) {

universalClient/tss/txresolver/svm.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package txresolver
22

33
import (
44
"context"
5+
"encoding/json"
56

67
"github.com/pushchain/push-chain-node/universalClient/store"
8+
uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types"
79
)
810

911
// resolveSVM checks the on-chain ExecutedTx PDA and moves the event to COMPLETED or REVERTED.
@@ -55,4 +57,17 @@ func (r *Resolver) resolveSVM(ctx context.Context, event *store.Event, chainID s
5557

5658
// PDA not found — tx was not executed on destination chain, no gas consumed
5759
_ = r.voteOutboundFailureAndMarkReverted(ctx, event, txID, utxID, "", 0, "0", "tx not executed on destination chain")
60+
61+
// Best-effort: close any orphaned StoredIxData PDA from the ref-finalize
62+
// route so the relayer recovers the ~0.002 SOL of rent. No-op for direct-
63+
// route outbounds and for ref-route outbounds whose PDA was already auto-
64+
// closed by a successful finalize.
65+
var data uexecutortypes.OutboundCreatedEvent
66+
if err := json.Unmarshal(event.EventData, &data); err != nil {
67+
return
68+
}
69+
if err := builder.CleanupOutboundArtifacts(ctx, &data); err != nil {
70+
r.logger.Warn().Err(err).Str("event_id", event.EventID).Str("tx_id", txID).
71+
Msg("SVM cleanup of stored_ix_data PDA failed; rent recovery deferred")
72+
}
5873
}

0 commit comments

Comments
 (0)