Repository navigation
[Bug]: Continue on Let your contacts pay you does not finish #1406
Description
What happened?
After importing a Pubky Ring profile with 61 contacts, Continue on "Let your contacts pay you" stays busy for a long time. It does finish. The session log shows why it takes that long.
Saving the 61 contacts was the shorter part. On 1 Oct 2026 at 15:38:02 the app had already discovered 61 follows. Import finished at 15:39:28. The following work is a walk of Paykit receiver paths. From 15:35:49 through 15:57:28 the log records 68 failures, all transport_error / list_paykit_receiver_paths, and all for the same 7 pubkys (about 9–10 failures each). Those are the same 7 keys whose profile fetch had just failed with transport_error / fetch profile while the import preview was loading. The log ends while those retries are still going, which matches Continue staying on screen for many minutes.
Expected behavior
Continue finishes promptly after the contacts are saved, with visible progress while any remaining work runs. A contact whose profile lookup already failed should not be retried for the rest of the session.
Steps to Reproduce
- Use Pubky Ring with an identity that follows 61 pubkys, and a Bitkit wallet that has not saved those contacts yet.
- In Bitkit, adopt that Ring identity and wait until the import preview lists the contacts.
- Import the contacts.
- On "Let your contacts pay you", tap Continue.
- Continue stays busy for many minutes, then finishes.
Logs / Screenshots / Recordings
From bitkit_2026-10-01_15-33-29.log (device local time):
- 15:38:02
Discovered '61' contacts for import - 15:38:08–15:39:01 seven
Retrying contact profile resolutionwarnings,transport_error/fetch profile - 15:39:28
Imported '61' contacts - 15:35:49–15:57:28 sixty-eight
Failed to refresh Paykit receiver paths, alllist_paykit_receiver_paths, only those same seven pubkys
The full log could not be attached from the command line:
2026-10-01-pay-contacts-continue-hang.log
Bitkit Version
2.5.0 dev, pull request #1395 at faae845c. Confirm this was the build on the phone.
Device / OS
Samsung SM-S901B. OS version TBD. Pubky Ring 2.0 was installed from bitkit-e2e-tests/aut/pubkyring-83834e7.apk.
Reproducibility
Often (>50%)
Additional context
- One 61-contact Ring import on this build. Device log times are local. Continue was still in the receiver-path retries at the end of the exported log. The reporter later saw that step finish.
- The same seven pubkys fail both
fetch profileduring preview andlist_paykit_receiver_pathsafterward. The other follows are not in those failure lines. - Loading the contact list and avatars is separate and already covered by fix: speed up pubky profile loading #1399 and fix: speed up pubky profile loading bitkit-ios#854. This report is only the Continue / receiver-path walk.
- fix: save contacts without repeated remote lookups #1395 lists this Continue path as out of scope.
- Environment: regtest app build, staging Pubky.
- Twin: iOS issue filed in the same batch and linked after both numbers exist.
Activity
piotr-iohk commentedon Oct 1, 2026
Twin: synonymdev/bitkit-ios#861
ben-kaufman commentedon Oct 1, 2026
This matches iOS #861. Could you retest with #1401? It removes the receiver-folder discovery producing these warnings. #1395 fixes saving imported contacts, but does not address this later Continue delay. If it persists, please include logs covering the Continue tap through completion or failure.
piotr-iohk commentedon Oct 2, 2026
Checked on #1401 latest revision 5fb0193.
Test: import profile from Pubky Ring with 61 contacts.
Performance seems deteriorated from testing against #1395 - not only continue, but all 3 steps: create, import, continue.
Logs and recording (continue never finished after 15mins +):
Screen.Recording.2026-10-02.at.09.57.00.mp4
logs.zip
Jasonvdb commentedon Oct 2, 2026
@ben-kaufman, here are measurements for this issue from our 61-follow test, in case they help with #1401.
Setup: emulator, staging network, piotr's Pubky Ring follow list (61 follows, 7 of them never published). Builds:
master6ba44a4, and #1399 at 58d5c53, which already runs public reads outside the operation lock. Line numbers are at 58d5c53.Continue (tap →
setEnableddone): 339 s onmaster, 154–167 s on #1399.discoverRelevantReceiverPaths) took 93.9 s, marker selection 29.4 s andensureLinkWithPeer13.7 s.syncLists: 4.9 s.preparePrivatePaymentListReservations,PrivatePaykitRepo.kt:853). The 7 unpublished follows cost about 8 s each, 54–58 s in total; every other contact cost about 1.5 s.privateReceiverPathSelection(PaykitSdkService.kt:697).AppViewModel.kt:771) starts a second walk over the same 61 contacts (148 s). It runs alongside Continue and competes for read slots and the operation lock.After Continue, walks over all 61 contacts keep running back to back for more than 10 minutes, on both builds.
PrivatePaykitRepo.kt:94).ensureLinkWithPeerholds the operation lock during network I/O, 179 s in total (PaykitSdkService.kt:773-784).Delete Profile with 61 contacts: 551 s on #1399.
publicationMutex(PrivatePaykitRepo.kt:1360-1361, taken throughremovePublishedEndpointsForCleanup). It waited behind a 362 s foreground publish walk and a polling walk queued ahead of it.deleteAllContactswas 24 s (61 × 0.4 s).master, removing the 61 contacts right after launch took 930 s, queued on the operation lock behind profile reads.Import All → saved: 28–41 s on both builds.
saveContactdoes about 8.5 full state loads plus one full save. Each load is a RoomwalletIndexquery, a DataStore read and a Keystore AES-GCM decrypt.The 7 failing follows:
Ideas, in case they're useful (none of them are in #1399):
publicationMutex.walletIndexinKeychain.Separately, #1399 now gives each import-preview lookup a 10 s deadline, and Contacts no longer looks up every contact again on each visit.
ben-kaufman commentedon Oct 2, 2026
Addressed the blocking contact-preparation path in #1401, commit eaf6a09; matching iOS changes are in 27e921a3.
Continue now finishes public setup and starts private preparation in the background. Repeated preparations are coalesced, established links are reused, and unavailable lookups have a five-minute cooldown. Link preparation no longer holds the publication lock for the entire contact walk; cleanup invalidates the remaining work, and removed contacts are checked again before publication.
All 3,186 Android unit tests passed, including stalled lookup/cleanup, removed contacts and duplicate preparation. An in-flight SDK call can still take time, and public setup still needs the network. Please retest the 61-contact fixture on the new head; keeping this issue open until that device run confirms the Continue and Delete timings.
ben-kaufman commentedon Oct 3, 2026
Updated #1401 through 09e224c and tested a local build with Paykit #171:
Android withdrawal is still above the few-second target. In the final run the sharing gate was acquired immediately; time was spread across private-list withdrawal and registry/public cleanup. Paykit #171 reduces this work but does not make it free. It remains a separate SDK improvement to consume after merge/release; #1401 still uses rc59.
Full scope and timings. This is one saved contact on a local homeserver, not the affected staging contact set. Keep the 61-contact import, request delays and backup stalls open for retesting. The latest request payment produced a success log but was absent from Bitcoin/Electrum; it is not counted as completed. The broadcast-outcome dependency remains #1384.
3,210 unit tests passed, with no introduced lint findings. Current build/lint CI is green; device performance still needs the staging repeat.
ben-kaufman commentedon Oct 3, 2026
Both mobile PRs now use published Paykit rc60, including SDK #169/#170/#171 (Android dc73b29, iOS 10b85a1e). The Android branch also includes current master’s profile/import and prioritized public-read changes.
A fresh staging iOS-to-Android test delivered a 1,000-sat request into Android’s request list; contact-sharing withdrawal completed on both platforms. However, initial linking still took minutes and iOS sending/withdrawal still exceeded the targets tracked in synonymdev/bitkit-ios#868. This does not close the performance issue, the 61-contact case, or the full payment journey. No transaction was broadcast in this run. Further work needs timings for the remaining active shared-state operations, rather than simply increasing probe concurrency.
ben-kaufman commentedon Oct 3, 2026
Latest staging measurements used the published rc61 binaries with one linked, unfunded iOS/Android pair:
Both PRs now use published rc62. It retains the batching and polling improvements and fixes a separate SDK issue where four concurrent outbound peers could consume each other's leases while waiting on shared storage. It does not explain or establish a fix for the one-peer delay above. No rc62 staging latency result is claimed here.
Keep this open. We need timings for repository/SDK queue waits, outbound work, intake and request-list updates before attributing the remaining delay to Bitkit or the homeserver. Android also queues overlapping refreshes instead of coalescing them as iOS does; its impact in this run is unmeasured. No safety timeout was reduced.
No payment was broadcast in these unfunded-wallet runs. Full payment/unlock, the 61-contact case, cold start and backup-stall QA remain unverified. The separate wallet broadcast dependency is still tracked in iOS #844 and Android #1384.
ben-kaufman commentedon Oct 5, 2026
I profiled one linked, unfunded staging pair using a local #171 build at 13feb12a, before the later registry/endpoint optimizations. This was not the published rc62 artifact or a measurement of the final PR head.
Sharing OFF took 84.006s from the action marker to the last awaited SDK call:
Background poll/backup work interleaved, so these rows do not cover the entire critical path. Execution includes wrapper checks, not just network time. No outbound drain ran because its queue was already empty.
Sharing ON completed its public work in 31.668s; private publication finished by 68.009s. Warm request-list opening was observed within 0.9s. Both requests sent from iOS arrived, but receiver restarts prevent a clean delivery-latency claim. No payment was made.
A separate cold-start failure was reproduced and fixed in b4b21418e: wallet crypto temporarily removed a security provider needed by the TLS verifier. With that fix and published rc62, a cold launch had no verifier errors, but session restoration still took 98.9s. That remaining delay is not explained by the provider fix.
The newer SDK-only results reduce more repeated state reads. They still need a comparable mobile retest, and the performance target remains unmet.
ben-kaufman commentedon Oct 5, 2026
Tested current #171 source in Android, not published rc62: measured breakdown. One inbox refresh held the request-repository mutex for 111.7s, including 65.3s waiting for the SDK queue while receive itself took 1.8s. Presentation-marker persistence then waited 46.7s behind that refresh. The exact SDK lock holder was not tagged, so this does not establish the whole delay as one SDK call or a homeserver problem. The request was visible later; exact automatic presentation timing and full payment/unlock remain unverified. Keeping this open.
ben-kaufman commentedon Oct 5, 2026
New same-identity staging measurements with the FFI batching fix: request send 30.6s -> 15.3s; full sharing withdrawal 73.3s -> 50.0s. Warm Contact Pay still took about 10.4s SDK execution plus 3.5s queued behind backup/peer/request reads. Automatic incoming preparation took 38.2s, with 24 lock/load cycles and nine commits, so that path remains under investigation. Detailed results and limitations: pubky/paykit-rs#171 (comment) . These are individual unfunded runs, not a full payment or final-head sign-off. Keeping this open.
4 remaining items
piotr-iohk commentedon Oct 6, 2026
Android retest on 6 Oct 2026, PR #1401 at
51e26b0b710fb00edacfb7aada8bb0f5e57743fb(Bitkit 2.5.0 / 190, Pixel 6 emulator, Android 13, staging Pubky / regtest, E2E hooks enabled). Installed APK fingerprint matched the build.This run failed at Import All, before reaching the Continue step from this ticket:
Relevant log (UTC):
Later incoming-request refreshes also logged
transport_error / acquire Pubky write lockat 16:59:57.480 and 17:00:37.552 UTC.Fixture caveat: this was the same stag3 identity previously imported and then deleted on iOS (
90799f2). iOS deletion completed before Android started, but took ~13m33s and reported one failed contact cleanup. Prior shared state may be a factor; this is not a proven cause and this is not a fresh-identity baseline.I will repeat with a fresh Pubky identity with the same 62 follows on the newest PR head. At this check, #1401 still points to
51e26b0, the tested revision; no newer head was visible.Timings include automation overhead. Scoped Pubky/Paykit logs and build/timing evidence have been collected for attachment. This is a related import-path observation, not confirmation of the original Continue failure.
android-stag3-import-logs.zip
piotr-iohk commentedon Oct 6, 2026
Fresh-profile retest on Android 51e26b0: all three import steps completed successfully.
Uninstalled Bitkit and installed the verified staging/regtest APK with E2E hooks, then prepared a new empty wallet on Pixel_6 / Android 13 (
emulator-5554). Used stag4, fresh to Bitkit/Paykit, with the same 62 followed public keys as stag3.The log confirmed
Imported '62' contacts. All 62 contact row IDs were verified against the fixture, with no missing or extra contacts. Profile, Contacts and wallet Home remained usable.The earlier stag3 Import All transport-error stall did not reproduce. Both runs used the same APK/revision; this run differed in having clean Bitkit data, a fresh identity and an empty wallet, so it does not establish which factor caused the earlier failure.
One sample. Timings are automation tap-command start to polling detection of the destination, include UI-dump overhead, and exclude pauses between steps. Two profile lookups timed out during preview. Background Paykit contact preparation continued after visible completion, with warnings for contacts lacking Paykit authorization; its full completion was not verified.
The profile is retained. Profile deletion is the next test and has not been exercised in this run.
piotr-iohk commentedon Oct 6, 2026
Profile-deletion follow-up after the successful fresh stag4 import, on Android
51e26b0b710fb00edacfb7aada8bb0f5e57743fb(Pixel_6 emulator / Android 13, staging Pubky / network regtest, E2E hooks, empty wallet, 62 imported contacts).Deletion eventually completed:
Deleted all contactslogged 6m29s after confirming Yes, Delete.No contact-deletion error was found in the captured logs. Private Paykit contact preparation continued after deletion confirmation, with missing-authorization and transport-fetch warnings, so this is not an idle deletion baseline. No restart, retry or disconnect was used. Exact UI completion time and remote profile removal were not independently verified.
This observed run was faster than the earlier iOS deletion (~13m33s to session removal), but the single samples and different runtime conditions are not a controlled platform comparison. A 6–7-minute blocking deletion is still slow. We can track profile-deletion optimization in a separate follow-up rather than treat it as a critical blocker for #1401. This does not change the successful import result.
Next: repeat iOS deletion using fresh stag5 with the same 62 follows. Logs and timing evidence are collected for attachment.
ben-kaufman commentedon Oct 6, 2026
Updated #1401 in fe364a5 to published rc64, with the matching iOS fix in #856.
The per-contact deletion loop is replaced by one bulk SDK operation. Deletion stops scheduling contact preparation and batches local cleanup, while preserving subscription, lease and public-marker protections.
A fresh staging run with 62 unregistered contacts and active background preparation took 48.5s total: 18.8s withdrawal/waiting, 5.4s contact cleanup, and 24.4s remaining profile/public cleanup and sign-out. The app returned to the signed-out profile screen. iOS completed in 34.3s.
Full results and limits. These are different contacts from Stag4, not a controlled before/after comparison. The remaining sign-out time is not fully attributed, and uncertain-write safety waits are unchanged. Please repeat the 62-follow journey on this head; I haven't closed the performance issue.
piotr-iohk commentedon Oct 6, 2026
Retested the rc64 fix on Android
fe364a5cd234648081c72c5dbb38973fb464cc62, Bitkit 2.5.0 (190), Pixel_6 / emulator-5554 / Android 13, staging Pubky + regtest network backend + E2E hooks. The already installed APK matched the current-head local universal artifact (SHA25604d1c2a52c381adc86c05452a439391ea7250b30f98b14403f12ca7f09460897); no reinstall or wallet reset was performed.Fresh Stag7 (
pubkyzga7nprh39641yde48qwurekj6up3mp95e8qkijdsra17wafmyio) followed the exact same 62 contacts as our previous Stag3/Stag4/Stag5 fixture. This is the delete immediately after import, with background preparation active variant requested in the fix note.Deleted all contactslogPAYKIT_SESSIONdeleted logThe app log confirms
Imported '62' contactsat 18:53:38.398 UTC. Delete was confirmed at 18:54:10.087712 UTC, 9.947s after Profile became available, with only navigation through Edit Profile, scroll and confirmation in between. UI inspection adds overhead to that navigation delay.Preparation was still active:
contact preparationwarnings appeared at 18:54:07.961 UTC before confirmation and 18:54:11.929 UTC after it. Contact cleanup completed at 18:54:20.096 UTC, and session deletion at 18:54:29.584 UTC. No deletion error, retry, restart or connectivity intervention was observed. Two remote follow-profile fetches timed out during preview, but all 62 contacts imported.After deletion, Profile returned to the Ring chooser, Contacts showed the empty Dynamic Contacts introduction, and the wallet remained usable with 100,000 sats savings / 0 spending.
This is a substantial improvement over our earlier active-preparation Android run on
51e26b0(~400.390s to session deletion, chooser observed by 437.487s). These are individual fresh-identity runs with the same contact list, not controlled benchmarks. UI values are observed upper bounds including polling/tool overhead; log milestones provide separate cleanup timings. Remote cleanup was not independently audited. The settled/idle deletion repeat remains to be tested, and this narrow test does not sign off the full PR.piotr-iohk commentedon Oct 6, 2026
android-stag7-reimport-pending-logs.zip
Follow-up on the same Android
fe364a5cd/ Paykit rc64 build: the intended settled-deletion repeat is blocked by re-import of the just-deleted identity.After the successful fresh Stag7 import and active-preparation deletion, selected the same Stag7 identity from Ring again, with the same 62 follows. No rebuild, reinstall, wallet reset, retry or restart was used. Environment remains Pixel_6 / Android 13 / emulator-5554, Bitkit 2.5.0 (190), staging Pubky + regtest network backend + E2E hooks.
.part_002.log, contain neither a completed import nor a finalFailed to import contactserror for this attempt. Two follow-profile fetch warnings occurred during preview; no terminal transport-error result is claimed here.We therefore did not start deletion and cannot report a settled-deletion timing. The app was left running on the pending import screen when this observation window ended.
A read-only JDWP probe found the background preparation/retry jobs absent and their pending sets empty while import was still pending. That does not satisfy the settled-after-import condition: the contacts had not finished importing. The probe invoked no app methods, made no state changes and detached before ending observation.
Possible investigation point, not an established cause: this build's
saveContactsidentifies blocked peers andrestorePrivateContactsunblocks them one by one before saving the contacts. Profile deletion blocks those peers. The pending re-import may involve that restoration path; this run alone does not locate the active SDK call or establish the cause.This is a re-import after deletion observation, separate from the successful first-use import/deletion result above. Logs and screenshot are retained for attachment. A fresh identity is needed to independently exercise the intended settled-deletion case while this re-import issue is investigated.
ben-kaufman commentedon Oct 6, 2026
The re-import fix is pushed in 9083227 on #1401, using published Paykit rc65. Selected contacts are saved and unblocked in one atomic SDK operation; the per-contact write and rollback loops are gone on both platforms. A local-homeserver SDK comparison for 62 contacts took 3.019s with the loop versus 56ms with the batch, but that is not a staging/mobile result. Please repeat the same Stag7 import after deletion on this build. The reported ten-minute stall and settled-deletion measurement remain open; uncertain-write safety waits are unchanged.
ben-kaufman commentedon Oct 7, 2026
Published rc66 check on Android fd04581 with a fresh staging identity: a 5,000-sat request payment completed to iOS, with receiver credit and independently confirmed regtest transaction. The reverse payment also completed and Android showed the sending contact. This predates the newer link-preparation commit and is one run, not a matched speedup claim.
Android payment timing:
Warm Contact Pay opened Request-or-Pay within 2.19s. The initial preparation attempt was still busy at 41.3s and reached Amount by 86.6s. Send Request was busy at 9.1s and Sent by 65.9s; these are observation bounds. A backup reminder interrupted reverse-request presentation, so it is not a clean delivery-to-ready measurement.
The remaining SDK queue/lock/network breakdown is unmeasured. Linking, onboarding, cold-start and many-contact performance stay open, as do the historical Try Again and controlled-failure journeys. Transaction: 41d37956c0b3ce19dd9709d95107f351634378ad19d99088d14c4c83beba28b0, confirmed at regtest height 216161.
ben-kaufman commentedon Oct 7, 2026
Fresh paired measurements using the runtime source now in rc67: Send Request 12.11s and swipe to native wallet send 21.76s, including 5.42s in the SDK queue, 13.26s SDK execution and 1.86s pre-send wallet work. The payment confirmed on regtest. Sharing OFF completed in 42.96s (11.22s queue); fresh first-returned LINKED was about 81.2s after Save. After a separate 56.4s outage the saved session recovered without reauthorization, and Contact Pay preparation took 5.54s. One sample each, not a statistical speedup or exact UI-frame measurement. Full comparison and remaining gaps: pubky/paykit-rs#171 (comment) . The later queued-delivery fix in 90177b5 is tested but not in these timing builds. Keeping this open; the 62-contact re-import and cold-start cases are not resolved by this run.
piotr-iohk commentedon Oct 7, 2026
Android Stag8 import → delete → repeat-import checks on 804e754, published Paykit rc68 (2026-10-07).
User-installed Bitkit 2.5.0 (190), staging Pubky/regtest/E2E hooks, Pixel 6 Android 13 emulator. Installed APK SHA256
9add8f767f04c95843a56c446c23b1e889a6fdac3a0a33802dbdd28b69d36415matches the local dev universal artifact at that HEAD; no embedded commit metadata independently verified. Stag8:pubkyfhbjpghs4y6uiwni1sx5u9q3mynkr65ut66q485f4n4n45rxkymy, exact same 62 followed contacts as earlier Stag6/Stag7 fixtures.*Initial preview timing polling was interrupted by a transient Android layout parser failure; 63.20s is a recovered observation upper bound, not a precise duration. Import was not retried. All timings include UI dump polling overhead.
Immediate deletion confirmed 16.66s after profile was observed (Android UI dump/navigation/log collection overhead); preparation warnings immediately before/after prove the requested overlap. Initial import logged 62 contacts saved, with two profile fetch transport warnings (placeholder profiles), but no contact-import failure.
For the second case, the main preparation job was observed null at 08:05:40.782 UTC, then reappeared. After another 5m17.771s, preparation remained active for 62 peers / retry queue with 62 peers. Following the user's five-minute fallback instruction, deletion was confirmed 08:11:39.841 UTC, 8m59.439s after contacts saved. This is a successful delayed deletion with background work still active, not a strict settled-deletion pass. Missing signed Paykit Noise authorization for many fixture contacts and private-endpoint preparation warnings persisted. The fully idle trigger was never achieved.
After delayed deletion, Contacts showed the empty DynamicContacts intro, the profile was signed out, and the wallet retained 100,000 sats Savings / 0 Spending. Final re-import imported 62 again and leaves Stag8 imported. However, 45 contact profile resolutions timed out at 10s during preview, so many imported entries use placeholder metadata. The 103.51s preview delay and failed metadata resolution are an observation to investigate; this is not a clean metadata import pass. No payment was attempted. The previous multi-minute contact-save/rollback stall did not recur in these samples.
Evidence: Android full-cycle logs, timings and screenshots. Immediate-phase results are separately locked with SHA256. Read-only JDWP field inspection was used to verify preparation/retry state; no app methods invoked, no VM pause; forwarding removed after each probe.
Requested import/delete scope only; single samples, no full PR device coverage, no merge/release signoff. Strict idle deletion, remote-peer cleanup semantics, cold-start/restoration and payment journeys remain unverified.
The cause of the final metadata timeouts is not established: staging/network conditions and app behavior still need to be distinguished. Missing authorization may be expected for contacts without Paykit enabled; whether the ongoing retries are expected also remains to be clarified. These observations can be tracked in separate follow-up tickets if appropriate.
ben-kaufman commentedon Oct 7, 2026
Thanks, this separates the fixed contact-save/re-import stall from the remaining slow preview. The original 62-contact fixture completed all imports and both overlapping deletions on rc68, but the final 103.51s preview and 45 profile timeouts remain unresolved. Import All itself took 5.78s in that run. The evidence does not yet distinguish staging/network failures from app scheduling, so I would not attribute those timeouts to either side. Keeping metadata loading, ongoing preparation retries and strictly idle deletion open; this is not overall performance sign-off.
ovitrif commentedon Oct 14, 2026
Moved to 2.7.0. The contact-save stall is fixed (#1395), and RC1 testing showed contact payments working; what remains is the slow import preview and profile timeouts, which are not yet attributed to the app or to staging, so this does not block 2.6.0.