Skip to content

fix: tolerate wallet connection loss - #1080

Open
Dunsin-cyber wants to merge 2 commits into
arkade-os:masterfrom
Dunsin-cyber:fix/tolerate-wallet-connection-loss
Open

fix: tolerate wallet connection loss#1080
Dunsin-cyber wants to merge 2 commits into
arkade-os:masterfrom
Dunsin-cyber:fix/tolerate-wallet-connection-loss

Conversation

@Dunsin-cyber

@Dunsin-cyber Dunsin-cyber commented May 23, 2026

Copy link
Copy Markdown
Contributor

What's wrong

The code assumed calls to the wallet (and nbxplorer behind it) would never fail. So a brief connection drop could break a running flow or, worse, save wrong data to disk.

The worst case is in scheduleSweepBatchOutput. When it couldn't reach the wallet to ask "what block are we on?", it just made up the answer (block height 0). That scheduled the sweep at around block 40 instead of the real currentHeight + 40 — so the sweep fires way too early and the safety window users rely on to pull their money out basically disappears.

Restarting didn't help, because the wrong schedule was already saved. The same path could also hit a nil value and crash the process (#1031).

What this changes

Instead of pretending a failed call worked, we retry — and if the wallet still can't be reached, we stop and try again later instead of saving bad data.

  • Retry wallet calls automatically when the connection blips
  • waitForConfirmation keeps trying instead of giving up on the first error
  • scheduleSweepBatchOutput stops instead of making up a block height (fixes Panic in scheduleSweepBatchOutput when nbxplorer loses DB connection #1031); the round is picked back up on the next startup
  • Withdraw does NOT auto-retry, so we never send money twice

Summary by CodeRabbit

  • Bug Fixes

    • Confirmation polling now respects shutdown and treats transient confirmation errors as retryable, avoiding inaccurate scheduling.
    • Sweep scheduling stops when the wallet is unavailable instead of guessing times.
    • Withdrawal calls are protected from automatic retries to prevent potential double-spends.
  • Tests

    • Added a unit test ensuring withdrawal requests do not get retried by the client retry logic.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR adds resilience to wallet interactions across three layers: the gRPC client now automatically retries transient failures with exponential backoff; confirmation polling treats wallet errors as transient and retries on each tick; and sweep scheduling fails safely instead of dereferencing nil responses, preventing crashes during temporary wallet unavailability.

Changes

Sweep scheduling resilience and wallet retry handling

Layer / File(s) Summary
Wallet gRPC retry configuration
internal/infrastructure/wallet/wallet_client.go, internal/infrastructure/wallet/wallet_client_test.go
Adds time and grpc_retry imports; configures a unary client interceptor with max retry attempts, exponential backoff, and retryable gRPC status codes; explicitly disables retry for Withdraw calls using grpc_retry.WithMax(0) and adds a test ensuring Withdraw does not retry while other calls are retried.
Transient error handling in confirmation polling
internal/core/application/utils.go
Reworks waitForConfirmation so that errors from wallet.IsTransactionConfirmed are treated as transient: the function logs a warning and continues the polling loop until confirmed or context cancellation; successful confirmation becomes the exit path.
Safe failure handling in sweep scheduling
internal/core/application/service.go, internal/core/application/sweeper.go
Switches confirmation waits to use the service/sweeper shutdown-aware context; on waitForConfirmation failure, the code logs an error indicating wallet unavailability and returns early instead of using time.Now() as a fallback and proceeding with scheduling.

Sequence Diagram

sequenceDiagram
  participant Scheduler as scheduleSweepBatchOutput / createBatchSweepTask
  participant ConfirmUtil as waitForConfirmation
  participant WalletClient as Wallet gRPC Client
  participant WalletServer as Wallet Server
  Scheduler->>ConfirmUtil: Poll for confirmation (retry loop, uses s.ctx)
  ConfirmUtil->>WalletClient: IsTransactionConfirmed RPC
  WalletClient->>WalletServer: RPC attempt (unary retry interceptor)
  alt Transient failure
    WalletServer-->>WalletClient: Retryable error (e.g., Unavailable)
    WalletClient->>WalletServer: Retries with backoff
    WalletServer-->>WalletClient: Retryable error
    WalletClient-->>ConfirmUtil: error (per-call may disable retry)
    ConfirmUtil->>ConfirmUtil: Log transient warning, wait next tick
    ConfirmUtil->>WalletClient: Retry on next tick
  else Confirmation succeeds
    WalletServer-->>WalletClient: Confirmed + blockTimestamp
    WalletClient-->>ConfirmUtil: Confirmed + blockTimestamp
    ConfirmUtil-->>Scheduler: blockTimestamp
    Scheduler->>Scheduler: Schedule batch sweep
  else Confirmation loop canceled or unrecoverable
    ConfirmUtil-->>Scheduler: Error (wallet unavailable)
    Scheduler->>Scheduler: Log and return early (skip scheduling)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • altafan
  • louisinger
  • sekulicd
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: making the codebase tolerant of wallet connection loss through retry logic and graceful error handling.
Linked Issues check ✅ Passed The PR addresses all coding objectives from issue #1031: adds nil/error guards, implements retry logic for transient wallet errors, and prevents fabricating block heights on failures.
Out of Scope Changes check ✅ Passed All changes are directly scoped to addressing wallet connection resilience and error handling; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Code Review — #1080

Overall direction is correct and important — the old time.Now() fallback on block-height schedulers was a real bug that could shorten the safety window. Retrying transient wallet errors instead of bailing is the right call. But there are issues that need fixing before merge.


🔴 MUST FIX

1. waitForConfirmation now retries forever with context.Background() — no escape hatch
internal/core/application/service.go:3550scheduleSweepBatchOutput passes context.Background(). With the old code, a wallet error would break the loop and return. With this PR, a wallet error logs a warning and continues, meaning the goroutine polls indefinitely if the wallet stays down or the txid is somehow invalid.

This is a goroutine leak. If the wallet goes down for hours, you accumulate one stuck goroutine per round that completes during the outage. Since scheduleSweepBatchOutput is called via go s.scheduleSweepBatchOutput(round) at service.go:349, these are fire-and-forget goroutines with no cancellation.

Fix: Either:

  • Add a max retry count or timeout context in scheduleSweepBatchOutput (e.g., context.WithTimeout(ctx, 1*time.Hour)), then let the return path handle it as before — bail and rely on next-startup recovery.
  • Or add a retry ceiling inside waitForConfirmation itself (e.g., N consecutive errors → return error).

2. Same bug exists in sweeper.go:475-482 — not fixed by this PR
internal/core/application/sweeper.go:475-482 has the exact same pattern this PR fixes in service.go:3552-3556:

blockTimestamp, err := waitForConfirmation(context.Background(), rootInput, s.wallet)
if err != nil {
    blockTimestamp = &ports.BlockTimestamp{Time: time.Now().Unix()}
}

This is the same dangerous fallback on block-height schedulers. If you're fixing the pattern in scheduleSweepBatchOutput, you should fix it here too, otherwise the same bug class remains.

Similarly, fraud.go:46-61 has a variant of this pattern that falls back to GetCurrentBlockTime — which is better but still problematic if the wallet is truly unreachable.

3. grpc_retry dependency — does it build?
wallet_client.go imports github.com/grpc-ecosystem/go-grpc-middleware/retry, but the PR doesn't modify go.mod or go.sum. While go-grpc-middleware v1.4.0 is in go.mod, the /retry sub-package may or may not be included in the module's dependency graph already. If CI passes, this is fine — but please confirm. If it doesn't build, go.sum needs updating.


🟡 SHOULD FIX

4. Withdraw retry opt-out: grpc_retry.WithMax(0) — verify semantics
wallet_client.go:458 — The comment says "opt out of the interceptor" by passing grpc_retry.WithMax(0). Double-check that WithMax(0) means "zero retries" and not "unlimited retries" in go-grpc-middleware/retry. The library source uses maxRetry uint0 should mean no retries, but this is safety-critical (double-spend vector). Please add a unit test or at minimum a code comment citing the library behavior.

5. waitForConfirmation silently swallows non-transient errors
utils.go:544-548 — After this PR, ALL errors from IsTransactionConfirmed are treated as transient and retried. But some errors are permanent (e.g., codes.InvalidArgument if the txid is malformed, codes.NotFound if the tx doesn't exist). These will now retry forever instead of surfacing immediately.

Consider distinguishing transient vs. permanent errors, or at minimum log at Error level after N consecutive failures.

6. No tests for the new retry/bail-out behavior
The PR description checkbox "Add a safety net so a crash in a background task can't take down the server" is unchecked. There are no new tests for:

  • waitForConfirmation retrying on transient error then succeeding
  • scheduleSweepBatchOutput returning early when waitForConfirmation fails
  • Withdraw not retrying

These are protocol-critical code paths — they deserve test coverage.


✅ LOOKS GOOD

  • The return instead of fallback in scheduleSweepBatchOutput is the correct fix — relying on next-startup recovery is far safer than scheduling at the wrong height.
  • Disabling retry on Withdraw is the right call — idempotency isn't guaranteed.
  • Using grpc_retry at the transport level rather than hand-rolling retry loops is clean.
  • Log level escalation from Warn to Error on the bail-out path is appropriate.

⚠️ PROTOCOL-CRITICAL FLAG

This PR touches sweep scheduling and round lifecycle — both protocol-critical paths. The sweep schedule determines when users lose the ability to unilaterally exit. A bug here directly impacts fund safety. Requesting human review even though the direction is correct.

cc @louisinger — you authored the original fallback in 5d4ecade. The same pattern in sweeper.go:475-482 should be addressed.

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Code Review — #1080

Overall direction is correct and important — the old time.Now() fallback on block-height schedulers was a real bug that could shorten the safety window. Retrying transient wallet errors instead of bailing is the right call. But there are issues that need fixing before merge.


🔴 MUST FIX

1. waitForConfirmation now retries forever with context.Background() — no escape hatch
internal/core/application/service.go:3550scheduleSweepBatchOutput passes context.Background(). With the old code, a wallet error would break the loop and return. With this PR, a wallet error logs a warning and continues, meaning the goroutine polls indefinitely if the wallet stays down or the txid is somehow invalid.

This is a goroutine leak. If the wallet goes down for hours, you accumulate one stuck goroutine per round that completes during the outage. Since scheduleSweepBatchOutput is called via go s.scheduleSweepBatchOutput(round) at service.go:349, these are fire-and-forget goroutines with no cancellation.

Fix: Either:

  • Add a max retry count or timeout context in scheduleSweepBatchOutput (e.g., context.WithTimeout(ctx, 1*time.Hour)), then let the return path handle it as before — bail and rely on next-startup recovery.
  • Or add a retry ceiling inside waitForConfirmation itself (e.g., N consecutive errors → return error).

2. Same bug exists in sweeper.go:475-482 — not fixed by this PR
internal/core/application/sweeper.go:475-482 has the exact same pattern this PR fixes in service.go:3552-3556:

blockTimestamp, err := waitForConfirmation(context.Background(), rootInput, s.wallet)
if err != nil {
    blockTimestamp = &ports.BlockTimestamp{Time: time.Now().Unix()}
}

This is the same dangerous fallback on block-height schedulers. If you're fixing the pattern in scheduleSweepBatchOutput, you should fix it here too, otherwise the same bug class remains.

Similarly, fraud.go:46-61 has a variant of this pattern that falls back to GetCurrentBlockTime — which is better but still problematic if the wallet is truly unreachable.

3. grpc_retry dependency — does it build?
wallet_client.go imports github.com/grpc-ecosystem/go-grpc-middleware/retry, but the PR doesn't modify go.mod or go.sum. While go-grpc-middleware v1.4.0 is in go.mod, the /retry sub-package may or may not be included in the module's dependency graph already. If CI passes, this is fine — but please confirm. If it doesn't build, go.sum needs updating.


🟡 SHOULD FIX

4. Withdraw retry opt-out: grpc_retry.WithMax(0) — verify semantics
wallet_client.go:458 — The comment says "opt out of the interceptor" by passing grpc_retry.WithMax(0). Double-check that WithMax(0) means "zero retries" and not "unlimited retries" in go-grpc-middleware/retry. The library source uses maxRetry uint0 should mean no retries, but this is safety-critical (double-spend vector). Please add a unit test or at minimum a code comment citing the library behavior.

5. waitForConfirmation silently swallows non-transient errors
utils.go:544-548 — After this PR, ALL errors from IsTransactionConfirmed are treated as transient and retried. But some errors are permanent (e.g., codes.InvalidArgument if the txid is malformed, codes.NotFound if the tx doesn't exist). These will now retry forever instead of surfacing immediately.

Consider distinguishing transient vs. permanent errors, or at minimum log at Error level after N consecutive failures.

6. No tests for the new retry/bail-out behavior
The PR description checkbox "Add a safety net so a crash in a background task can't take down the server" is unchecked. There are no new tests for:

  • waitForConfirmation retrying on transient error then succeeding
  • scheduleSweepBatchOutput returning early when waitForConfirmation fails
  • Withdraw not retrying

These are protocol-critical code paths — they deserve test coverage.


✅ LOOKS GOOD

  • The return instead of fallback in scheduleSweepBatchOutput is the correct fix — relying on next-startup recovery is far safer than scheduling at the wrong height.
  • Disabling retry on Withdraw is the right call — idempotency isn't guaranteed.
  • Using grpc_retry at the transport level rather than hand-rolling retry loops is clean.
  • Log level escalation from Warn to Error on the bail-out path is appropriate.

⚠️ PROTOCOL-CRITICAL FLAG

This PR touches sweep scheduling and round lifecycle — both protocol-critical paths. The sweep schedule determines when users lose the ability to unilaterally exit. A bug here directly impacts fund safety. Requesting human review even though the direction is correct.

cc @louisinger — you authored the original fallback in 5d4ecade. The same pattern in sweeper.go:475-482 should be addressed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/core/application/utils.go`:
- Around line 544-549: The loop in waitForConfirmation currently swallows errors
from IsTransactionConfirmed and retries forever, which can strand background
work; modify waitForConfirmation to respect a caller context
deadline/cancellation or enforce a capped transient-error budget: accept a
context.Context (or use the existing one) and return when ctx.Done() triggers,
and/or track a retry counter with backoff and a maxAttempts after which return
the last error; update references in waitForConfirmation and the
IsTransactionConfirmed error handling (txid, retry logic) so the function
returns a bounded error instead of looping indefinitely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 05bd81db-b527-4f31-a0b9-b8e9b398c19b

📥 Commits

Reviewing files that changed from the base of the PR and between 299b7ad and 4857c19.

📒 Files selected for processing (3)
  • internal/core/application/service.go
  • internal/core/application/utils.go
  • internal/infrastructure/wallet/wallet_client.go

Comment thread internal/core/application/utils.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/core/application/sweeper.go (1)

465-477: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the subtree argument for expiry/confirmation anchor.

scheduleForSubTree computes vtxoTreeExpiry and rootInput from outer vtxoTree instead of the tree parameter. That can schedule subtree sweeps from the wrong confirmation point and wrong expiry basis.

💡 Proposed fix
 		scheduleForSubTree := func(txid string, tree *tree.TxTree) {
-			vtxoTreeExpiry, err := s.getVtxoTreeExpiry(vtxoTree)
+			vtxoTreeExpiry, err := s.getVtxoTreeExpiry(tree)
 			if err != nil {
 				log.WithError(err).
 					Errorf("failed to get vtxo tree expiry for batch %s", commitmentTxid)
 				return
 			}
@@
-			rootInput := vtxoTree.Root.UnsignedTx.TxIn[0].PreviousOutPoint.Hash.String()
+			rootInput := tree.Root.UnsignedTx.TxIn[0].PreviousOutPoint.Hash.String()
 			blockTimestamp, err := waitForConfirmation(s.ctx, rootInput, s.wallet)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/application/sweeper.go` around lines 465 - 477, The
scheduleForSubTree closure incorrectly uses the outer vtxoTree variable instead
of its tree parameter, causing wrong expiry and confirmation anchors; change the
two usages so s.getVtxoTreeExpiry(...) is called with the local tree (i.e.,
s.getVtxoTreeExpiry(tree)) and derive rootInput from
tree.Root.UnsignedTx.TxIn[0].PreviousOutPoint.Hash.String() (leave the existing
error handling and commitmentTxid context intact).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/core/application/sweeper.go`:
- Around line 465-477: The scheduleForSubTree closure incorrectly uses the outer
vtxoTree variable instead of its tree parameter, causing wrong expiry and
confirmation anchors; change the two usages so s.getVtxoTreeExpiry(...) is
called with the local tree (i.e., s.getVtxoTreeExpiry(tree)) and derive
rootInput from tree.Root.UnsignedTx.TxIn[0].PreviousOutPoint.Hash.String()
(leave the existing error handling and commitmentTxid context intact).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 854e4aa0-8e38-4887-a52f-12e5da991c82

📥 Commits

Reviewing files that changed from the base of the PR and between 4857c19 and 518b726.

📒 Files selected for processing (5)
  • internal/core/application/service.go
  • internal/core/application/sweeper.go
  • internal/core/application/utils.go
  • internal/infrastructure/wallet/wallet_client.go
  • internal/infrastructure/wallet/wallet_client_test.go

@Dunsin-cyber

Copy link
Copy Markdown
Contributor Author

hi @altafan , please review

@arkana-ai-bot arkana-ai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PROTOCOL-CRITICAL: human review required.

Flagging for human protocol review before merge — sweep scheduling and forfeit-adjacent paths are money-at-risk code. The fix itself looks correct and addresses a real bug: the previous blockTimestamp = &ports.BlockTimestamp{Time: time.Now().Unix()} fallback left Height == 0, so in a BlockHeight scheduler regime the sweep was scheduled at vtxoTreeExpiry.Value (≈40) instead of currentHeight + vtxoTreeExpiry.Value — potentially firing an on-chain sweep well before users' unilateral-exit window, exactly as the PR body describes. Reverting the #1034 fallback and bailing to next-boot is the right call.

Findings / questions:

  1. grpc_retry with DeadlineExceeded on non-Withdraw side-effect RPCsinternal/infrastructure/wallet/wallet_client.go:34-39. Withdraw is correctly opted out with WithMax(0), but the interceptor still auto-retries every other call on DeadlineExceeded, including ones with server-side side effects: DeriveAddresses / DeriveConnectorAddress (may burn nbxplorer address indices), LockConnectorUtxos, RescanUtxos, LoadSignerKey, and BroadcastTransaction. Broadcast and (probably) LockConnectorUtxos are naturally idempotent; the derivation calls resolve to Nbxplorer.GetNewUnusedAddress(..., i) (pkg/arkd-wallet/core/application/wallet/service.go:947-957) with an explicit index so they should be safe, but this is worth confirming against nbxplorer semantics. If any of these are non-idempotent, please either opt them out with WithMax(0) or drop DeadlineExceeded from the retryable set (keep Unavailable/ResourceExhausted, which imply the RPC never reached the server).

  2. waitForConfirmation now retries every non-ctx error foreverinternal/core/application/utils.go:544-553. That's the intended behavior for wallet flakes, but it also masks permanent errors (invalid txid, wrong network, wallet returning a structural error). At regtest tick=1s the loop is tight; at mainnet tick=1m operators will get one warn/min forever. Consider: (a) surfacing a distinct error class after N consecutive failures to alert operators, or (b) at least tightening the log message so it's clearly "will retry forever until wallet recovers or shutdown", not just "transient".

  3. fraud.go:46-61 still has the old fallback pattern — falls back to s.wallet.GetCurrentBlockTime(ctx) when waitForConfirmation errors. With the new semantics waitForConfirmation only returns ctx.Err(), so that fallback is now effectively dead code (if ctx is canceled, GetCurrentBlockTime(ctx) will also fail). Not a regression, but the inconsistency invites future reintroduction of the same bug you just fixed — worth cleaning up in this PR while it's in view.

  4. "picked up on next startup" claim — verified: sweeper.start()GetSweepableRounds()createBatchSweepTask() at sweeper.go:64,100 will re-run for any completed-but-unswept round. So the bail-out in service.go:3552 and sweeper.go:476 is genuinely recoverable. No test proves this though — a unit test covering "wallet down during round completion → wallet up on restart → sweep scheduled correctly" would harden the guarantee.

  5. Log wording"wallet unavailable; cannot schedule sweep..." at service.go:3554 / sweeper.go:478 will also fire on service shutdown (ctx canceled). Consider distinguishing ctx.Err() != nil (shutdown, expected) from an actual wallet failure to keep alerting signal clean.

  6. TestTestWithdrawDoesNotRetry at wallet_client_test.go:33-52 is solid; the control case with a raw client proving 5 retries do occur under the same interceptor is the right way to guard against silent removal of the WithMax(0) opt-out. Good.

  7. Sweeper s.ctx field — set once in start() at sweeper.go:62, read from goroutines. Pre-existing pattern, but note that createBatchSweepTask closures scheduled by scheduleBatchSweep capture s and read s.ctx at fire time; safe today because start() runs before the scheduler dispatches, but no synchronization guards this ordering.

Nothing in this PR requires a change for correctness of the fix — items 1, 2, 3, 5 are the highest-value follow-ups. Requesting changes only to enforce the protocol-review gate on sweeper.go / service.go.

@arkana-ai-bot

Copy link
Copy Markdown

Changes were requested 2+ days ago. @Dunsin-cyber need any help addressing the feedback?

@arkana-ai-bot

Copy link
Copy Markdown

Changes were requested 8+ days ago. @Dunsin-cyber need any help addressing the feedback?

@arkana-ai-bot

Copy link
Copy Markdown

Changes were requested 5 days ago. @Dunsin-cyber need any help addressing the feedback?

@arkana-ai-bot arkana-ai-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes were requested 94 days ago on this wallet connection-loss fix. @Dunsin-cyber need any help addressing the feedback?

@arkana-ai-bot

Copy link
Copy Markdown

Changes were requested 4-5+ days ago. @Dunsin-cyber need any help addressing the feedback?

@arkana-ai-bot

Copy link
Copy Markdown

Changes were requested on this PR (tolerate wallet connection loss). @Dunsin-cyber need any help addressing the feedback?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Panic in scheduleSweepBatchOutput when nbxplorer loses DB connection

2 participants