Skip to content

arkd-wallet: source UTXOs from NBXplorer events and cache wallet key paths - #1093

Open
bitcoin-coder-bob wants to merge 4 commits into
arkade-os:masterfrom
bitcoin-coder-bob:bob/nbxplorer-efficiency
Open

arkd-wallet: source UTXOs from NBXplorer events and cache wallet key paths#1093
bitcoin-coder-bob wants to merge 4 commits into
arkade-os:masterfrom
bitcoin-coder-bob:bob/nbxplorer-efficiency

Conversation

@bitcoin-coder-bob

@bitcoin-coder-bob bitcoin-coder-bob commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two efficiency fixes to how arkd-wallet talks to NBXplorer. Both eliminate HTTP
calls for data NBXplorer already hands us in payloads we were discarding.

1. Build deposit UTXOs from the newtransaction event payload

The websocket notification handler previously called searchNewUTXOs on every
newtransaction event, which did GET /v1/cryptos/BTC/groups/{groupID}/utxos
(the entire group UTXO set) and filtered it client-side by txid. That work scales
linearly with the number of watched scripts and fires constantly.

NBXplorer 2.6.7's newtransaction event already contains the matched outputs. We
now parse them directly via a pure utxosFromTransactionEvent(message, groupID),
scoped to trackedSource == "GROUP:{groupID}" (the identifier NBXplorer reports
for addresses added through the group addresses endpoint). No extra HTTP call, and
the cost no longer depends on group size. event.Data is now json.RawMessage to
drop a redundant re-marshal; ports.Utxo gained KeyPath, carried by castUtxo.

2. Cache script -> keyPath to skip per-input lookups when signing

getPrivateKeyFromScript used up to two GetScriptPubKeyDetails HTTP calls per
input. The script -> {scheme, keyPath} mapping is immutable, and UTXO listings
already return keyPath (we were dropping it). A bounded LRU cache
(50k entries) is populated from the wallet's account GetUtxos call sites and
consulted first; the HTTP path remains as a fallback that backfills the cache. A
cache miss is always safe (it just falls back), so eviction has zero correctness
risk and memory stays flat on a long-running server. Warm-cache signing makes zero
metadata calls.

Benchmarks

BenchmarkNotificationProcessing — new event-parse is constant regardless of
group size; the old fetch-and-filter scales linearly:

group size old fetch+filter new event-parse speedup
10 118 µs 8.2 µs ~14x
100 700 µs 8.2 µs ~85x
1,000 4.87 ms 8.2 µs ~590x
10,000 34.9 ms 8.2 µs ~4,250x

(allocations at 10k: 30,141 -> 27; bytes/op: 13.9 MB -> 1.6 KB.) #2 is covered by
call-count tests: cache hit = 0 lookups, connector fallback = 2.

Testing

  • go test -race ./core/... — all subtests pass.
  • Event parsing: group vs derivation-scheme vs newblock filtering, single/multi/
    empty outputs, non-zero confirmations, malformed JSON, invalid txid, keyPath
    passthrough.
  • keyPath cache: set/get/miss/overwrite, bounded eviction (cap enforced,
    oldest-first), 100-goroutine concurrency; getPrivateKeyFromScript
    cache-hit/fallback/empty-keyPath behavior.
  • golangci-lint (v2.9.0): no new issues vs master.

Review feedback addressed

  • Unbounded cache growth → cache is now a bounded LRU (50k cap); added a test
    asserting the bound and LRU eviction. (Note: the cache is populated only from
    the wallet's account UTXO listings, not from event notifications.)
  • Positional struct initwallet is now constructed with named fields.

Notes

  • Behavior is preserved for group deposits; events for the wallet's own derivation
    schemes are ignored exactly as before. Malformed events are now logged (benign).
  • The cache key is the scriptPubKey, which maps to exactly one account/keyPath
    (the two accounts are distinct taproot xpubs), so a stale/wrong entry is not
    possible.
  • Out of scope (from the wider NBXplorer usage audit): websocket gap-recovery via
    /events?lastEventId, single-RPC block-time, the /balance endpoint, and
    reserve=true for change addresses.

Summary by CodeRabbit

  • Improvements

    • Reduced repeated lookups during wallet UTXO selection by caching key-path information.
    • Improved transaction notification processing by reading UTXO and key-path details directly from websocket events.
    • Preserved key-path information for more reliable private-key derivation and signing.
  • Tests

    • Added coverage for caching, wallet key derivation, concurrency, bounds, and notification processing.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 17b36053-259c-434a-b61e-7820d93f9923

📥 Commits

Reviewing files that changed from the base of the PR and between e3dda22 and 7138bb9.

📒 Files selected for processing (2)
  • pkg/arkd-wallet/core/application/wallet/keypath_cache_test.go
  • pkg/arkd-wallet/core/infrastructure/nbxplorer/service.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/arkd-wallet/core/application/wallet/keypath_cache_test.go
  • pkg/arkd-wallet/core/infrastructure/nbxplorer/service.go

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


Walkthrough

This PR adds a bounded LRU cache for script-to-key-path mappings. The wallet populates and reads the cache during UTXO operations and private-key derivation. NBXplorer now builds UTXOs directly from matching websocket transaction events.

Changes

Key Path Caching for UTXO and Key Derivation

Layer / File(s) Summary
Cache implementation and dependency
pkg/arkd-wallet/core/application/wallet/keypath_cache.go, pkg/arkd-wallet/core/application/wallet/keypath_cache_test.go, pkg/arkd-wallet/go.mod
Adds a concurrency-safe bounded LRU cache. Empty scripts and key paths are ignored. Tests cover lookup, eviction, overwrites, and concurrent access.
UTXO and event data contracts
pkg/arkd-wallet/core/ports/nbxplorer.go, pkg/arkd-wallet/core/infrastructure/nbxplorer/types.go
Adds KeyPath to ports.Utxo. NBXplorer event types now carry raw event data, matched outputs, key paths, and unsigned confirmation counts.
NBXplorer websocket and UTXO handling
pkg/arkd-wallet/core/infrastructure/nbxplorer/service.go, pkg/arkd-wallet/core/infrastructure/nbxplorer/service_test.go
Parses matching newtransaction events directly into UTXOs. The flow validates tracked sources, parses transaction hashes, preserves key paths, and removes the follow-up group UTXO query. Tests cover filtering, parsing errors, field mapping, and benchmark comparisons.
Wallet service key-path cache integration
pkg/arkd-wallet/core/application/wallet/service.go, pkg/arkd-wallet/core/application/wallet/service_test.go
Initializes and populates the cache during UTXO retrieval. getPrivateKeyFromScript checks the cache first, skips empty key paths, tries alternate schemes, and caches successful lookups. Tests cover cache hits, backfilling, fallback, unknown scripts, and empty paths.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 7138b

This change sources matching wallet UTXOs from NBXplorer notifications and caches key paths to reduce lookup overhead. No concrete merge-blocking risk is established in the supplied evidence.

Sequence Diagram(s)

sequenceDiagram
  participant WebsocketReader
  participant EventHandler as utxosFromTransactionEvent
  participant NotificationsChannel
  WebsocketReader->>EventHandler: newtransaction event
  EventHandler->>EventHandler: validate group and parse outputs
  EventHandler->>NotificationsChannel: UTXOs with key paths
Loading
sequenceDiagram
  participant Caller
  participant Wallet as getPrivateKeyFromScript
  participant KeyPathCache
  participant NBXplorer
  Caller->>Wallet: script
  Wallet->>KeyPathCache: get(script)
  alt Cache hit
    KeyPathCache-->>Wallet: derivation scheme and key path
    Wallet-->>Caller: derived private key
  else Cache miss
    Wallet->>NBXplorer: lookup script
    NBXplorer-->>Wallet: key path result
    Wallet->>KeyPathCache: set successful mapping
    Wallet-->>Caller: derived private key or nil
  end
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 8 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes both primary changes: sourcing UTXOs from NBXplorer events and caching wallet key paths.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ 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.

@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.

Code Review — arkd-wallet: source UTXOs from NBXplorer events and cache wallet key paths

Reviewer: Arkana (automated, aggressive)
Verdict: Request changes (1 actionable issue) + request human sign-off (signing path modified)


Summary

Clean, well-tested PR. The two optimizations are sound:

  1. Parsing UTXO data directly from the newtransaction event payload instead of re-fetching the entire group UTXO set — eliminates an O(n) HTTP call per event.
  2. Caching script → (derivationScheme, keyPath) to skip per-input NBXplorer lookups during signing.

Test coverage is thorough (event parsing, cache hit/miss/fallback/concurrency, edge cases). The json.RawMessage change for event.Data and the Confirmations int → uint32 alignment are correct improvements. The behavioral fix in getPrivateKeyFromScript (skipping schemes that return an empty KeyPath instead of trying to derive from "") is a genuine bug fix.

No cross-repo breakage: ports.Utxo is only consumed within pkg/arkd-wallet/, and the new KeyPath field is added with named-field construction everywhere.


🔴 ACTIONABLE — Unbounded cache growth

pkg/arkd-wallet/core/application/wallet/keypath_cache.go

The keyPathCache map grows without bound. Every UTXO the wallet ever sees (via ListConnectorUtxos, SelectUtxos, withdrawAll, getBalance, and event notifications) adds an entry that is never evicted.

For a long-running arkd server managing many watched addresses (e.g. thousands of boarding UTXOs per round across many rounds), this is a slow memory leak. The comment says "cached entries never need invalidation" — true for correctness, but irrelevant for memory management. Scripts from spent UTXOs will never be looked up again, yet their entries persist forever.

Options (pick one):

  • LRU with a reasonable cap (e.g. 10k–50k entries). A signing operation only needs the scripts of the inputs being signed, so eviction is always safe — the NBXplorer fallback path still works.
  • Periodic sweep: clear the cache when the wallet detects a new round/block (coarse but simple).
  • Explicit eviction after signing: remove entries for scripts that were just signed (they're spent now).

The first option is simplest and safest. An LRU miss just falls back to the existing HTTP path, so there's zero correctness risk.


🟡 STYLE — Positional struct init is fragile

pkg/arkd-wallet/core/application/wallet/service.go:65

return &wallet{opts, newOutpointLocker(time.Minute), nil, newKeyPathCache(), make(chan bool)}

This uses positional field initialization. If anyone reorders the wallet struct fields, this silently breaks at compile time only if types mismatch — otherwise it's a silent bug. Prefer named fields:

return &wallet{
    WalletOptions: opts,
    locker:        newOutpointLocker(time.Minute),
    keyPaths:      newKeyPathCache(),
    readyCh:       make(chan bool),
}

Not blocking, but worth fixing while you're here.


🟢 LOOKS GOOD

  • utxosFromTransactionEvent — pure function, well-scoped, correct TrackedSource filtering, proper error propagation. The make([]ports.Utxo, 0, len(newTxEvent.Outputs)) pre-allocation is a nice touch.
  • event.Datajson.RawMessage — correct elimination of the redundant marshal→unmarshal roundtrip.
  • matchedOutput type — clean mapping of the NBXplorer event payload. Fields match the NBXplorer 2.6.7 schema.
  • castUtxo now carries KeyPath — correct one-line addition.
  • getPrivateKeyFromScript empty-KeyPath guard — this is a bug fix: the old code would pass "" to deriveKey on the first scheme match with empty KeyPath, likely erroring out and halting the loop before trying the connector scheme. Now it correctly falls through.
  • Test coverage — event parsing (group vs derivation-scheme vs newblock, single/multi/empty outputs, confirmations, malformed JSON, invalid txid, keyPath passthrough), cache (set/get/miss/overwrite/concurrent), signing path (locked wallet, cache hit, cache miss + backfill, connector fallback, empty KeyPath fallback, unknown script). Benchmark is well-structured.

⚠️ PROTOCOL-ADJACENT — Human review required

This PR modifies getPrivateKeyFromScript, which is on the signing path. The change is a caching layer — the underlying deriveKey call and its parameters are unchanged — so the risk of fund loss is minimal. However, per protocol-critical review rules:

A human must review and approve this PR before merge. Specifically, verify:

  1. The cache can never return a stale/wrong (derivationScheme, keyPath) for a given script (it can't, because the mapping is immutable by derivation, but a human should confirm this assumption holds for all address types the wallet manages).
  2. The empty-KeyPath guard in the fallback path is correct behavior (it is — NBXplorer returns empty KeyPath for scripts not derived from the queried scheme).

@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.

Follow-up Review — new commit 923414a

Reviewer: Arkana (automated, aggressive)
Verdict: ✅ Approve (Arkana side)


Both issues from my previous review are resolved:

🔴 Unbounded cache → Fixed

sync.RWMutex + map[string]keyPathEntry replaced with hashicorp/golang-lru/v2 lru.Cache capped at 50,000 entries. The library is concurrency-safe internally, so the manual mutex removal is correct. Eviction of old entries is safe — a cache miss falls back to the NBXplorer HTTP path with zero correctness risk.

New TestKeyPathCache_Bounded test verifies the cap holds and oldest entries are evicted. Good.

🟡 Positional struct init → Fixed

wallet{} in New() now uses named fields. Clean.

No new issues in the incremental diff. The go.mod addition of hashicorp/golang-lru/v2 v2.0.7 is appropriate.


⚠️ Human review still required

This PR modifies getPrivateKeyFromScript (signing path). Per protocol-critical rules, a human must also approve before merge. The two things to verify remain unchanged from my first review:

  1. The script → (derivationScheme, keyPath) mapping is truly immutable for all address types the wallet manages.
  2. The empty-KeyPath guard correctly skips schemes that don't own the script.

@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.

Follow-up Review — new commit 923414a

Reviewer: Arkana (automated, aggressive)
Verdict: ✅ Approve (Arkana side)


Both issues from my previous review are resolved:

🔴 Unbounded cache → Fixed

sync.RWMutex + map[string]keyPathEntry replaced with hashicorp/golang-lru/v2 lru.Cache capped at 50,000 entries. The library is concurrency-safe internally, so the manual mutex removal is correct. Eviction of old entries is safe — a cache miss falls back to the NBXplorer HTTP path with zero correctness risk.

New TestKeyPathCache_Bounded test verifies the cap holds and oldest entries are evicted. Good.

🟡 Positional struct init → Fixed

wallet{} in New() now uses named fields. Clean.

No new issues in the incremental diff. The go.mod addition of hashicorp/golang-lru/v2 v2.0.7 is appropriate.


⚠️ Human review still required

This PR modifies getPrivateKeyFromScript (signing path). Per protocol-critical rules, a human must also approve before merge. The two things to verify remain unchanged from my first review:

  1. The script → (derivationScheme, keyPath) mapping is truly immutable for all address types the wallet manages.
  2. The empty-KeyPath guard correctly skips schemes that don't own the script.

@bitcoin-coder-bob
bitcoin-coder-bob marked this pull request as ready for review June 10, 2026 18:28

@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.

Catchup review — head 923414a

Re-verified the PR at the current head. My earlier concerns (unbounded cache, positional struct init) are resolved; the LRU cap + eviction test and named-field construction are in place. No new blockers on this pass — a few small residual notes only.

Confirmed correct

  • Cache scoping. cacheKeyPaths is invoked with the exact derivation scheme used for the enclosing Nbxplorer.GetUtxos call at every call site (service.go:344, 397, 842, 851, 999). Because the main and connector accounts are distinct BIP86 xpubs, script → (scheme, keyPath) is deterministic — no way to seed a wrong-key entry.
  • LRU + concurrency. hashicorp/golang-lru/v2 is safe for concurrent Add/Get. Cache is separate from keyMgr, so lock/unlock cycles do not corrupt it; on a Restore-to-a-different-seed, stale entries would fail deriveKey with "invalid xpub" rather than sign with a wrong key.
  • Small bug fix in the fallback. Previously getPrivateKeyFromScript (service.go:1013) took the first non-error response from GetScriptPubKeyDetails and passed the (possibly empty) KeyPath straight to deriveKey, which errors on "" and aborted signing before ever trying the connector scheme. The new if scriptPubKeyDetails.KeyPath == "" { continue } correctly probes the other scheme and only returns nil, nil when neither owns the script — surfacing the "not a wallet script" error at the SignTransaction level as intended.

Residual notes (non-blocking)

  1. Confirmations type widened int → uint32 in newTransactionEvent.TransactionData (types.go:141). Newtransaction events from NBXplorer are only sent for accepted mempool/confirmed txs (value ≥ 0), so this is fine in practice — but any future path that surfaces reorg-invalidated txs through this same struct (NBXplorer uses -1 elsewhere) would silently fail to unmarshal. Worth a mental note.
  2. matchedOutput.Value uint64 (types.go:132) assumes the BTC Money serializer emits a bare integer, matching utxoResponse.Value. Correct for BTC/NBXplorer 2.6.7. There is no in-tree integration test against a real NBXplorer instance that exercises the new field names (outputs, matchedOutput.*); the mocked-JSON unit tests are the sole shape check. A single e2e in arkade-regtest covering the deposit-notification path would harden this against NBXplorer version drift.
  3. Log-spam surface. The new log.Errorf("failed to parse transaction event: %s", err) in GetAddressNotifications fires on every unparseable outer frame with no rate limit. Previously errors were swallowed. If a broken upstream ever emits a stream of non-JSON frames this could flood logs. Minor.
  4. Cross-repo impact. ports.Utxo lives inside the pkg/arkd-wallet submodule and is not consumed by the external SDKs — safe.

Comment-only from me; not blocking merge.

@arkana-ai-bot

Copy link
Copy Markdown

This PR has been open for 3+ days without review. @bitcoin-coder-bob is anyone looking at this?

@arkana-ai-bot

Copy link
Copy Markdown

This PR has been open for 30+ days without review. @bitcoin-coder-bob is anyone looking at this?

@arkana-ai-bot

Copy link
Copy Markdown

This PR has been open for 5 days without review. @bitcoin-coder-bob is anyone looking at this?

@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.

This PR (NBXplorer UTXO efficiency) has been open 76 days without review. @bitcoin-coder-bob is anyone picking this up?

@arkana-ai-bot

Copy link
Copy Markdown

This PR has been open 80+ days without a formal review. @bitcoin-coder-bob — this is the bottom of the arkd-wallet stack; getting this merged would unblock the chain above it.

@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.

Arkana review — #1093 (sha 923414a)

arkd-wallet: source UTXOs from NBXplorer events and cache wallet key paths

Key path cache

  • LRU cache (50k entries) for script → (derivation scheme, key path)
  • Entries are immutable (same script always maps to the same key path), so there are no staleness concerns
  • LRU eviction is safe: a miss falls back to the NBXplorer per-input lookup
  • set() correctly ignores empty script or key path entries
  • Good test coverage: basic set/get, miss, empty rejection, bounded eviction, concurrent access

Looks correct

  • Cache is populated at ListConnectorUtxos call time, then hits during signing — right placement
  • The 50k entry bound keeps memory bounded even for a long-running operator wallet with heavy connector churn
  • Concurrent test exercises the hashicorp/golang-lru/v2 thread-safety, which is the correct dependency

One note

Only saw keypath_cache.go and part of service.go in the diff — the NBXplorer event-sourcing aspect of the PR title isn't covered here. Confirm the event-based UTXO path doesn't introduce any ordering assumptions (e.g. that a UTXO event always arrives before the signing call that needs its key path).

Verdict: cache layer looks well-designed and tested. Performance-only change, no correctness concerns here.

@arkana-ai-bot

Copy link
Copy Markdown

This PR has been open for 12+ weeks without a review. @bitcoin-coder-bob — is anyone looking at this? Is it still waiting on other PRs in the stack, or does it need a reviewer assigned?

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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.

Follow-up Review — merge commit `e3dda22`

Status: 1 commit since `923414a` — a merge of `origin/master` into the branch. Both prior issues remain Fixed. Zero remaining actionable issues in the incremental diff.


What changed since `923414a`

Commit `e3dda22` is a pure merge commit (parents: `923414a` + `f863e48`). It brings 118 master commits into the branch. The only conflict was `pkg/arkd-wallet/go.mod`, resolved correctly by preserving the `github.com/hashicorp/golang-lru/v2 v2.0.7` entry the PR introduced. No `pkg/arkd-wallet/core/` source files changed in this incremental step.

The notable master commit included here is `f863e48` ("Migrate to btcd v0.26 and the v2 sub-modules", PR #1173). The PR's test imports — `github.com/btcsuite/btcd/chaincfg/v2` and `github.com/btcsuite/btcd/wire/v2` — are consistent with that migration, and CI shows no compile failures.


Prior-issue closure

# Finding Status
1 🔴 Unbounded cache growth (keyPathCache map leaked indefinitely) Fixed in `923414a` — LRU cap of 50 000 entries via hashicorp/golang-lru/v2. TestKeyPathCache_Bounded verifies the cap.
2 🟡 Positional struct init in New() (wallet{}) Fixed in `923414a` — named fields used throughout.

New findings (incremental diff)

go.mod conflict resolution — `pkg/arkd-wallet/go.mod` correctly keeps hashicorp/golang-lru/v2 v2.0.7 from the PR side and absorbs all master-side dependency bumps. No phantom or dropped dependency. No issue.

Nothing else in the incremental diff touches wallet logic. No new findings.


⚠️ Standing requirement — human review still required

This PR modifies `getPrivateKeyFromScript` (signing path). Arkana's approval covers the caching and event-parsing mechanics. A human must also approve before merge, confirming:

  1. The `script → (derivationScheme, keyPath)` mapping is immutable for all address types this wallet manages.
  2. The empty-KeyPath guard correctly skips schemes that don't own the queried script.

…vent

Regroup only for the tests. The parse failure of a notification frame is
benign for the wallet and was previously swallowed, so it logs at warn.

@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.

Follow-up Review — head `7138bb9`

Status: 1 commit since `e3dda22`; 1 prior residual note addressed; 2 non-blocking residual notes unchanged; 0 new findings.


What changed since `e3dda22`

Commit `7138bb9` ("arkd-wallet: group the key path cache tests, warn on an unparseable event") touches exactly two files:

  1. pkg/arkd-wallet/core/application/wallet/keypath_cache_test.goTestKeyPathCache_Bounded and TestKeyPathCache_Concurrent re-homed as t.Run("bounded", …) and t.Run("concurrent", …) subtests inside TestKeyPathCache. Pure organisational refactor; test logic is byte-for-byte identical.

  2. pkg/arkd-wallet/core/infrastructure/nbxplorer/service.go:702log.Errorf("failed to parse transaction event: …")log.Warnf(…). One-line change.


Prior-issue closure

# Finding Status
🔴 Unbounded cache growth Fixed in 923414a — LRU cap 50 000 + TestKeyPathCache_Bounded. Not regressed.
🟡 Positional struct init in New() Fixed in 923414a. Not regressed.
Log-spam on malformed frames (residual note #3, catchup review) Fixed in 7138bb9 — demoted to Warnf. Directly addresses the concern.
Confirmations int → uint32 widening in newTransactionEvent.TransactionData (residual note #1) Still open (non-blocking). Reorg-invalidated frames from a future NBXplorer version that emits -1 would silently fail to unmarshal. No change in this commit; awareness recorded.
No in-tree integration test for outputs/matchedOutput.* field names against a live NBXplorer instance (residual note #2) Still open (non-blocking). Unit tests with mocked JSON remain the sole shape check; e2e coverage would harden against version drift. No change in this commit.

New findings

None. The incremental diff is two cosmetic changes with no logic impact.


⚠️ Standing requirement — human review still required

This PR modifies getPrivateKeyFromScript (signing path). Arkana's approval covers the caching and event-parsing mechanics. A human must also approve before merge, confirming:

  1. The script → (derivationScheme, keyPath) mapping is immutable for all address types this wallet manages.
  2. The empty-KeyPath guard correctly skips schemes that don't own the queried script.

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.

2 participants