Dial primary plus fallback arkd-wallets - #1099
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
f68b468 to
5ee109f
Compare
3332bdd to
b1742ec
Compare
b1742ec to
841da39
Compare
03bc417 to
517fe5c
Compare
141b8d3 to
8af3323
Compare
ghost
left a comment
There was a problem hiding this comment.
🔍 Arkana Code Review — bob/arkd-multi-wallet-dial
Plumbing PR to dial and validate fallback LP wallets at startup. The actual sweep fallback wiring is deferred. Overall structure is clean — good network validation, good cleanup on dial failure, solid test coverage. A few issues to address:
🔴 Must Fix
1. Fallback wallets not locked on shutdown — security gap
internal/interface/grpc/service.go (new code in stop()): Fallback wallets get Close() only. The primary wallet gets Lock(ctx) + Close() in application/service.go:394-397. An unlocked arkd-wallet that's still running after arkd shuts down is network-accessible with full signing authority. Lock fallbacks before closing them.
// Current (stop):
for _, fb := range s.appConfig.FallbackWalletServices() {
fb.Close()
}
// Suggested:
for _, fb := range s.appConfig.FallbackWalletServices() {
_ = fb.Lock(context.Background()) // best-effort, same as primary
fb.Close()
}2. No dedup or self-reference check on fallback addresses
internal/config/config.go (dialFallbackWallets): If a user accidentally sets ARKD_WALLET_FALLBACK_ADDRS=localhost:6060 (same as primary) or a:6060,a:6060, arkd opens duplicate connections to the same wallet. When sweep fallback logic lands, this could cause double-spend attempts or lock contention. Add validation:
- Reject any fallback addr that matches
c.WalletAddr - Reject duplicate entries in the list
🟡 Suggestions
3. FallbackWalletServices() returns the backing slice directly
internal/config/config.go:664: Callers can mutate the returned slice. In a codebase where bugs lose money, return a copy:
func (c *Config) FallbackWalletServices() []ports.WalletService {
out := make([]ports.WalletService, len(c.walletFallbacks))
copy(out, c.walletFallbacks)
return out
}4. ensureWalletReady error messages use index, not address
internal/interface/grpc/service.go:671-686: Error messages say "fallback wallet 0 is not initialized" — an operator has to count their comma-separated list to figure out which one. Include the address in the error for operability. You have access to s.appConfig.WalletFallbackAddrs for this.
5. Docker compose: wallet-2 has no restart: unless-stopped health check
docker-compose.regtest.yml: The primary wallet has a dependency chain, but wallet-2 only depends on nbxplorer. Not blocking, but consider adding a healthcheck or at least a comment noting that arkd will crash-loop until wallet-2 is ready (which is the intended behavior per ensureWalletReady).
✅ What looks good
- Network mismatch is a hard fatal — correct, prevents silent misconfiguration
closeWallets()cleanup on partial dial failure — proper resource management- Test coverage:
TestParseWalletFallbackAddrscovers edge cases;TestDialFallbackWalletscovers same-network, mismatch, and dial-error paths with Close() assertion counting - E2E setup correctly initializes/unlocks wallet-2 before arkd starts
newWalletClientindirection for testability is a good pattern- README accurately describes the current scope and explicitly says sweep wiring is deferred
513eca3 to
9e5e7cb
Compare
a60692f to
835c866
Compare
ghost
left a comment
There was a problem hiding this comment.
🔍 Arkana Follow-up Review — bob/arkd-multi-wallet-dial
Reviewed the force-push (rebase onto updated #1098). No substantive code changes to this PR's layer — all 5 findings from my previous review remain unaddressed.
🔴 Still open — must fix
1. Fallback wallets not locked on shutdown
internal/interface/grpc/service.go:235-237 — still just fb.Close(), no fb.Lock(). The primary wallet gets Lock() + Close() in application/service.go. An unlocked fallback arkd-wallet remains network-accessible with signing authority after arkd shuts down.
2. No dedup or self-reference check on fallback addresses
internal/config/config.go:dialFallbackWallets — still no validation that a fallback addr ≠ c.WalletAddr or that there are no duplicate entries. When the sweep fallback in #1101 lands, duplicates could cause double-spend attempts or lock contention.
🟡 Still open — suggestions
3. FallbackWalletServices() returns backing slice (config.go:668-671)
4. Error messages use index not address (service.go:684-706)
These are the same as before. See my original review for details and suggested fixes.
No new issues introduced by the rebase. Blocking on findings 1 and 2.
835c866 to
d6d6423
Compare
ghost
left a comment
There was a problem hiding this comment.
🔍 Arkana Review #3 — bob/arkd-multi-wallet-dial
Third pass after another force-push (rebase). Diff unchanged — no new code to review. All previous findings remain open.
🔴 Still blocking
1. Fallback wallets not locked on shutdown — internal/interface/grpc/service.go:235-237
Primary gets Lock() + Close(). Fallbacks only get Close(). An unlocked arkd-wallet with signing authority stays network-accessible after arkd shuts down.
2. No dedup / self-reference check — internal/config/config.go:dialFallbackWallets
No validation that a fallback addr ≠ primary WalletAddr, or that fallback list has no duplicates. With #1101's sweep fallback, duplicates → double-spend risk or lock contention.
🟡 Still open
3. FallbackWalletServices() returns backing slice — config.go:668-671
4. Error messages use index not address — service.go:684-706
See review #1 for details and suggested fixes on all four items.
d6d6423 to
57edf9d
Compare
ghost
left a comment
There was a problem hiding this comment.
🔍 Arkana Review #4 — bob/arkd-multi-wallet-dial
Fourth pass after rebase. No code changes — diff identical to reviews #1–#3. All findings remain open.
🔴 Blocking
1. Fallback wallets not locked on shutdown — internal/interface/grpc/service.go:235-237
Primary gets Lock() + Close(). Fallbacks only get Close(). Unlocked arkd-wallet with signing authority stays network-accessible after arkd shuts down.
2. No dedup / self-reference check — internal/config/config.go:dialFallbackWallets
No validation that fallback addr ≠ primary WalletAddr or that the list has no duplicates. With #1101's sweep fallback, duplicates → double-spend risk or lock contention.
🟡 Open
3. FallbackWalletServices() returns backing slice — config.go:668-671
4. Error messages use index not address — service.go:684-706
See review #1 for details and suggested fixes on all four items.
Add ARKD_WALLET_FALLBACK_ADDRS so arkd can connect to a primary arkd-wallet plus additional LP wallets. Fallbacks are dialed and validated (reachable, same network, initialized and unlocked) at startup; the primary is unchanged and remains the sole source of the forfeit pubkey, addresses and signing. Fallbacks are not used by sweep yet. The regtest compose stack now runs a second arkd-wallet so the existing e2e suite exercises arkd with a fallback plugged in.
edf1888 to
5d9ad68
Compare
57edf9d to
03d4fe9
Compare
ghost
left a comment
There was a problem hiding this comment.
🔍 Arkana Review #5 — bob/arkd-multi-wallet-dial
Fifth pass after rebase. Diff identical to reviews #1–#4. No code changes — all findings remain open.
🔴 Blocking
1. Fallback wallets not locked on shutdown — internal/interface/grpc/service.go:235-237
Primary gets Lock() + Close(). Fallbacks only get Close(). Unlocked arkd-wallet with signing authority stays network-accessible after arkd shuts down.
2. No dedup / self-reference check — internal/config/config.go:dialFallbackWallets
No validation that fallback addr ≠ primary WalletAddr or that the list has no duplicates. With #1101's sweep fallback, duplicates → double-spend risk or lock contention.
🟡 Open
3. FallbackWalletServices() returns backing slice — config.go:668-671
4. Error messages use index not address — service.go:684-706
See review #1 for details and suggested fixes on all four items.
Pair each dialed fallback wallet with its address (FallbackWallet) so ensureWalletReady reports the specific wallet that failed (host:port) instead of a positional index, which isn't actionable when the error surfaces. Also clarify the comment on why fallback balance isn't checked.
ghost
left a comment
There was a problem hiding this comment.
🔍 Arkana Review #6 — bob/arkd-multi-wallet-dial
Reviewed new commit 98f24639 — introduces FallbackWallet struct pairing Addr + Service, renames FallbackWalletServices() → FallbackWallets(), and updates ensureWalletReady errors to use %q with fb.Addr instead of positional index.
New commit assessment
Good: The FallbackWallet struct is a clean solution to finding #4. Error messages now name the wallet by host:port — much better for operators. The type change is well-propagated through dialFallbackWallets, closeWallets, stop(), and ensureWalletReady.
Tests: TestDialFallbackWallets still works correctly — the fakeFallbackWallet satisfies ports.WalletService which is wrapped in FallbackWallet.Service. No test gaps introduced.
🟠 New issue from this commit
5. Stacked PR #1101 will break — #1101 calls c.FallbackWalletServices() (the old name) and expects []ports.WalletService (the old type) in at least 3 call sites. After this rename, #1101 won't compile when rebased. Not blocking for this PR, but Bob should rebase #1101 onto this branch before merging either.
Findings status
| # | Finding | Status |
|---|---|---|
| 1 | 🔴 Fallback wallets not locked on shutdown (service.go:238-239) |
Still open — stop() still only calls fb.Service.Close(), no Lock() |
| 2 | 🔴 No dedup / self-reference check (config.go:851-875) |
Still open — no validation that fallback addr ≠ primary or no duplicates |
| 3 | 🟡 FallbackWallets() returns backing slice (config.go:679-681) |
Still open |
| 4 | ✅ Error messages use index not address | Fixed by this commit |
| 5 | 🟠 Stacked PR #1101 uses old API name | New — compile break in downstream |
Blocking on findings 1 and 2. Finding 5 is a heads-up for the PR stack.
On this branch arkd only dials and readiness-checks fallback wallets at startup; sweep signing isn't wired up yet. Reword the docker-compose and dialFallbackWallets comments so they don't imply active sweep-fallback use.
ghost
left a comment
There was a problem hiding this comment.
🔍 Arkana Review #7 — bob/arkd-multi-wallet-dial
New commit 2738e658 is comment-only — rewords the docker-compose comment and dialFallbackWallets godoc to clarify sweep signing isn't wired up yet. No logic changes. No new issues.
Findings status (unchanged from review #6)
| # | Finding | Status |
|---|---|---|
| 1 | 🔴 Fallback wallets not locked on shutdown (service.go:238-239) — stop() calls fb.Service.Close() but not Lock(). Primary gets both. An unlocked arkd-wallet with signing authority stays network-accessible after arkd shuts down. |
Still open |
| 2 | 🔴 No dedup / self-reference check (config.go:853-875) — no validation that a fallback addr ≠ primary WalletAddr or that the fallback list has no duplicates. With #1101's sweep fallback, duplicates → double-spend risk or lock contention. |
Still open |
| 3 | 🟡 FallbackWallets() returns backing slice (config.go:681) — caller can mutate c.walletFallbacks. Return a copy or document the contract. |
Still open |
| 4 | ✅ Error messages use index not address | Fixed in 98f2463 |
| 5 | 🟠 Stacked PR #1101 uses old API name — #1101 calls FallbackWalletServices() (old name) and expects []ports.WalletService. Needs rebase after this lands. |
Still open |
Blocking on findings 1 and 2.
…er fallback Fail fast at dial time if a fallback address equals the primary WalletAddr or repeats another fallback, instead of silently dialing the same wallet twice (pointless now, double-spend/lock-contention risk once fallbacks sign sweeps). Matching is by literal address.
ghost
left a comment
There was a problem hiding this comment.
🔍 Arkana Review #8 — bob/arkd-multi-wallet-dial
New commit ba566dab adds dedup and self-reference validation in dialFallbackWallets. Assessment:
✅ Finding #2 is now fixed
The implementation is correct:
- Primary check (
config.go:861):addr == c.WalletAddrrejects before dialing, avoiding a wasted connection. Cleans up already-dialed fallbacks viacloseWallets(). - Duplicate check (
config.go:865-868):seenmap with literal string matching. Also rejects before dialing and cleans up. - Test coverage: Two new test cases verify error messages, cleanup (Close counts), and that no unnecessary dials are made (
require.Equal(t, 1, calls)). Good.
One note: the matching is literal (addr == c.WalletAddr). Addresses like localhost:6060 vs 127.0.0.1:6060 vs [::1]:6060 would bypass this check. This is acceptable for now since the operator controls both values and the PR description documents this, but worth a follow-up comment in the code.
Updated findings status
| # | Finding | Status |
|---|---|---|
| 1 | 🔴 Fallback wallets not locked on shutdown (service.go:238-239) — stop() calls fb.Service.Close() but not Lock(). Primary gets both. An unlocked arkd-wallet with signing authority stays network-accessible after arkd shuts down. |
Still open |
| 2 | ✅ No dedup / self-reference check | Fixed in ba566dab |
| 3 | 🟡 FallbackWallets() returns backing slice (config.go:681) — caller can mutate c.walletFallbacks. Return a copy or document the contract. |
Still open |
| 4 | ✅ Error messages use index not address | Fixed in 98f2463 |
| 5 | 🟠 Stacked PR #1101 uses old API name — #1101 calls FallbackWalletServices() (old name) and expects []ports.WalletService. Needs rebase after this lands. |
Still open |
Still blocking on finding #1. The lock-on-shutdown gap is a real security concern for when fallback wallets start signing sweeps in #1101.
1: arkd never unlocks wallets anymore so it shouldnt lock them either |
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Reviewed at ba566da (head). Not protocol-critical: additive infra only — fallbacks are dialed and readiness-checked at startup, but the primary path (forfeit key/address, connector address, scanning, signing, sweep, main-account balance warning) is untouched, and nothing in this diff uses the fallbacks for signing.
Design is clean:
parseWalletFallbackAddrscorrectly comma-splits (viper.GetStringSlice would whitespace-split, per the PR description); trims and drops empties.dialFallbackWallets(internal/config/config.go:830-863) dedupes vs primary and vs prior fallbacks, dials via the test-swappablenewWalletClient, validates network name match, and closes previously-dialed connections on any failure viacloseWallets.service.stop()(internal/interface/grpc/service.go:235-240) closes fallback connections; comment correctly notes the primary is closed byapplication.service.Stopatinternal/core/application/service.go:436.ensureWalletReady(internal/interface/grpc/service.go:692-712) reuses the existingctxfrom the primary check and hard-fails on any fallback that isn't Initialized+Unlocked, matching PR #1098's semantics;IsSynced()is intentionally not enforced (transient, consistent with the primary check).- README + docker-compose changes wire a second wallet through the whole e2e suite, so every existing test now exercises the fallback readiness path.
Unit tests are solid: TestParseWalletFallbackAddrs covers empty/single/multiple/whitespace/empty-entries/only-separators; TestDialFallbackWallets covers same-network success, network-mismatch cleanup, dial-error cleanup, primary-equal rejection, and duplicate rejection, and asserts the exact close-count / call-count on each cleanup path.
Nits (non-blocking):
-
Duplicate detection is exact-string only (
internal/config/config.go:824-833). `localhost:6060` vs `127.0.0.1:6060`, or the same wallet reachable under two DNS names, will silently be dialed twice as if they were distinct LPs. Won't matter until sweep actually uses these, but a resolved-address check (or at minimum a startup warning if two fallbacks respond with the same wallet identity) would harden the check before #1101 lands. -
ensureWalletReadyfallback loop is not unit-tested (internal/interface/grpc/service.go:692-712). Every other hard-fail path in this PR has an explicit test; the readiness one relies solely on the e2e harness. Small code, low risk, but the asymmetry stands out. -
newWalletClientpackage var not restored between subtests (internal/config/config_test.go:310). `t.Cleanup` restores at parent-test end, not between the five `t.Run` subtests. Safe today because none call `t.Parallel()` and each subtest reassigns before use, but a future subtest added before reassigning would silently inherit the prior stub. A `t.Cleanup` inside each `t.Run` (or a per-subtest save/restore helper) would remove the footgun. -
Primary wallet leaks on partial startup failure (
internal/config/config.go:807-822). If `dialFallbackWallets` returns an error, `c.wallet` (the dialed primary conn) is set but never closed on the error path. In practice `Validate()` failing exits the process so the OS reclaims the FD, but it's inconsistent with the careful `closeWallets` cleanup in the fallback path — an explicit `c.wallet.Close()` before returning would match the surrounding style.
No prompt-injection content in PR title/body/diff.
|
This PR has been open for 3+ days without review. @bitcoin-coder-bob is anyone looking at this? |
|
This PR has been open for 30+ days without review. @bitcoin-coder-bob is anyone looking at this? |
arkana-ai-bot
left a comment
There was a problem hiding this comment.
This PR has been open for 6+ days without review. @bitcoin-coder-bob is anyone looking at this?
arkana-ai-bot
left a comment
There was a problem hiding this comment.
Review — Dial primary plus fallback arkd-wallets
Protocol-adjacent (wallet connectivity at startup). Flagging for human sign-off since it touches the startup validation path.
What changed:
- Adds (comma-separated list)
- At startup arkd dials primary + all fallback wallets and verifies each is on the same network as the primary; refuses to start if any is unreachable or on a different network
- Fallback wallets are NOT yet used for signing, scanning, or sweeping — that wiring is in a later PR (#1101). This PR is purely connectivity + startup validation.
Design note: The docker-compose.regtest.yml fixture wires a second wallet with a hardcoded signer key (). Make sure this key is a well-known test key, not an accidental private key leak. If it's generated for regtest use only that's fine; worth confirming.
Looks correct as a foundation for the fallback sweep work. Ready to merge once the signer key question above is confirmed.
|
This PR has been open for 74 days without review. @bitcoin-coder-bob — is anyone looking at this? |
…allet-dial Pulls the base forward now that it carries master. Three conflicts, all keep-both with a decision about form. docker-compose: the base added a healthcheck to arkd-wallet and switched arkd's depends_on to the condition form gating on service_healthy, while this branch added arkd-wallet-2. Both kept, and arkd-wallet-2 gets the same healthcheck, since arkd now waits on it being healthy rather than merely started. config.go: the base adds BatchTrigger and drops roundReportSvc, master having removed the report service entirely, so that removal is taken; this branch's walletFallbacks is kept alongside. utils_test.go: this branch generalises setupArkdWallet into setupArkdWalletAt with a base URL so the same helper drives the primary and the fallback wallet. The single-URL form the base carries is dropped, its only caller already having moved to the new one. Not verified here: the e2e suite needs the regtest stack and this changes both the compose topology and the harness setup path, so CI is the real check.
arkana-ai-bot
left a comment
There was a problem hiding this comment.
🔍 Arkana Review #9 — `bob/arkd-multi-wallet-dial`
Status: Merge of `bob/arkd-require-unlocked-wallet` into this branch. One prior finding is now no longer applicable (finding #1), one is still open (finding #3). Two are confirmed fixed. No new blocking issues in the incremental diff.
Prior findings — closure
| # | Finding | Status |
|---|---|---|
| 1 | 🔴 Fallback wallets not locked on shutdown | No longer applicable — @bitcoin-coder-bob is correct: the new code does not call Lock() on the primary wallet at shutdown either. application/service.go:422-451 calls only s.wallet.Close(), no Lock(). The shutdown contract is now Close-only for all wallets; the fallback behaviour matches the primary. Dropping as a blocker. |
| 2 | ✅ No dedup / self-reference check | Fixed in ba566dab — confirmed in review #8; unchanged here. |
| 3 | 🟡 FallbackWallets() returns backing slice |
Still open — internal/config/config.go:687 still returns c.walletFallbacks directly. Callers in stop() and ensureWalletReady only read the slice today, but the contract is undocumented and a future caller could mutate it. Non-blocking for this PR given the current read-only callers. |
| 4 | ✅ Error messages use index not address | Fixed in 98f2463 — unchanged. |
| 5 | 🟠 Stacked PR #1101 uses old API name | No longer applicable — #1101 will need a rebase after this lands regardless; the rename was already a known dependency. |
Incremental additions (new since ba566dab)
docker-compose.regtest.yml — arkd-wallet-2 service looks correct:
restart: unless-stoppedandhealthcheck(nc -z localhost 6060) present — good, matches the intent to have arkd wait until the wallet is reachable.arkdnow hasarkd-wallet-2: condition: service_healthyin itsdepends_on. This is the right binding.ARKD_WALLET_DATADIR=./data/regtest-2separates the data directories from wallet-1. Good.ARKD_WALLET_FALLBACK_ADDRS=arkd-wallet-2:6060uses the Docker-internal hostname, which is correct for intra-container gRPC communication. The e2e test correctly uses the mapped external port6061viawalletUrl2.- The hardcoded
ARKD_WALLET_SIGNER_KEYfor wallet-2 is a regtest dev key. Confirm it differs from the primary wallet's signer key; dialing two wallets with the same signing key would give the fallback redundant-but-useless signing authority (not dangerous today, but confusing once sweep wiring lands in #1101).
internal/test/e2e/utils_test.go — Refactor of setupArkdWallet → setupArkdWalletAt(httpClient, baseURL) is clean. Both wallets are initialised and unlocked before arkd starts; error messages now include the wallet URL. No gaps.
README.md — Documentation accurately reflects the current scope: fallbacks are dialed and readiness-checked; sweep signing is explicitly deferred. No issues.
Nits (non-blocking, noted for completeness)
- Primary wallet leaks on partial startup failure (
internal/config/config.go:807-822): ifdialFallbackWalletsreturns an error,c.walletis set but never explicitly closed on the error return path. In practice the process exits immediately, so the OS reclaims the FD. But it's inconsistent with thecloseWalletscleanup discipline applied to the fallback path — an explicitc.wallet.Close()before returning on error would match the surrounding style. ensureWalletReadyfallback loop is not unit-tested (internal/interface/grpc/service.go:692-712): the initialized/unlocked hard-fail paths for fallbacks are exercised only by the e2e harness. Low risk given the code is simple, but the asymmetry with the thoroughly unit-testeddialFallbackWalletsis worth noting.- Exact-string dedup (
internal/config/config.go:824-833):localhost:6060vs127.0.0.1:6060pointing at the same wallet will bypass the duplicate check. Acceptable today; worth a note in the code or a TODO for before #1101 lands.
No new blocking issues. Dropping the changes requested status — finding #3 is a low-severity suggestion with no current caller at risk.
|
This PR has been open for 4-5+ days without a review. @bitcoin-coder-bob is anyone looking at this? (Dial primary plus fallback arkd-wallets; 4 days without review.) |
|
This PR has been open for 11+ weeks without a review. @bitcoin-coder-bob — is anyone looking at this? |
|
This PR has been open for 83+ days without a review. @bitcoin-coder-bob — dial primary plus fallback arkd-wallets: is this still relevant / waiting on other PRs? |
|
This PR has been open for 3+ months without a review. @bitcoin-coder-bob is anyone looking at this? Let us know if it needs a rebase or if there's a blocker. |
Why
Next step towards letting a single
arkdfront more than one LP. This teachesarkdto connect to a primaryarkd-walletplus N fallback wallets belonging to additional liquidity providers. It is purely additive: the primary path is unchanged and fallbacks are dialed and validated but not yet used by sweep (that wiring lands in a follow-up). Very simple: we just support adding other wallets and try connecting to them, we dont actually use the wallets yet for sweeps/fallbacks. The PR is just a small step to introduce new wallet support before actually using the wallets for functionality.Stacked PRs (merge top-down):
add Settings domain with DB persistence and admin CRUD API #939— ✅ merged intomastermaster, merges firstbob/arkd-require-unlocked-walletbob/arkd-multi-wallet-dial)This PR depends on #1098 landing first.
What changes
Config
ARKD_WALLET_FALLBACK_ADDRS(comma-separatedhost:port). Parsed with an explicit comma split (parseWalletFallbackAddrs), notviper.GetStringSlice, because the latter splits on whitespace, not commas (verified empirically), and would silently mis-parse the list.walletService()dials the primary (unchanged), thendialFallbackWallets()dials each fallback via an indirectednewWalletClient(so it is unit-testable) and hard-fails if a fallback is unreachable or on a different network than the primary, closing any partially-dialed connections to avoid leaks. It also rejects a fallback whose address duplicates the primary or another fallback.Config.walletFallbacksand exposed viaFallbackWallets()(each pairing the dial address with the wallet client). The primary staysc.wallet, so GetInfo, the forfeit pubkey/address, connector address, scanning, signing, and sweep are all untouched.Startup / shutdown
ensureWalletReady()now also requires each fallback wallet to be initialized and unlocked (hard-fail); the zero-balance warning stays primary-only.stop()closes the fallback connections (the primary is closed by the app service).Exercised end to end
docker-compose.regtest.ymlnow runs a secondarkd-wallet(arkd-wallet-2, host port 6061, its own datadir/volume/signer key) wired intoarkdviaARKD_WALLET_FALLBACK_ADDRS=arkd-wallet-2:6060.arkdstarts, so the entire existing e2e suite now runs with a fallback wallet plugged in.Docs / tests
ARKD_WALLET_FALLBACK_ADDRSrow and a "Configuring multiple LP wallets" subsection.TestParseWalletFallbackAddrs(parsing edge cases) andTestDialFallbackWallets(same-network success; network-mismatch hard-fail with cleanup; dial-error hard-fail with cleanup; fallback-equals-primary hard-fail; duplicate-fallback hard-fail).Operator impact
Every wallet, primary and fallback, must be initialized and unlocked out of band and must be on the same network as the primary;
arkdvalidates this at startup and refuses to start otherwise.Testing
go build ./...,go vet,make lint(0 issues), the new config unit tests, and the e2e package compile all pass.Status
Draft: depends on #1098 landing first; the sweep fallback that actually uses these wallets is #1101.