Skip to content

chore: prepare Bitkit 3.1.1 - #1878

Closed
piotr-iohk wants to merge 11 commits into
masterfrom
release-3.1.1
Closed

piotr-iohk wants to merge 11 commits into
masterfrom
release-3.1.1

Conversation

@piotr-iohk

@piotr-iohk piotr-iohk commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

Twin: synonymdev/bitkit-ios#1298

Prepares Bitkit 3.1.1 (build 197) from v3.1.0, with the version bump and collected hotfix changelog.

Description

Design

No separate release design; profile behavior and QA details are in the linked backports.

Preview

See the profile backport for the feature preview.

QA Notes

Setup

The runs below test ce4d5cb25704d19d42860380251b57a0b7e6caff on release-3.1.1 using the E2E repository’s main branch. Migration uses the archived native 3.1.0 build and includes the extended RN 1.1.6 cases.

Journeys

  • J1: new edit-pubky-app-profile.xml — profile changes are shared with pubky.app.
  • J2: new request-note-too-long.xml — an oversized Paykit note is rejected before submission.
  • J3: new grant-signup-rejected-code.xml — a rejected signup code gets a clear error.

Automated Checks

Evidence

Local backend APK assembled and verified; 722 targeted unit tests passed. Detekt and Android Lint passed.

Gaps

Full E2E and migration results are tracked in the linked runs. Device journeys are separate from the local unit tests.

Models used

  • Planning/scoping: GPT-6 (reasoning: Not exposed)
  • Implementation: GPT-6 (reasoning: Not exposed)
  • Review: Not performed

The session exposes the model family but not its variant or reasoning setting.

jvsena42 and others added 8 commits November 12, 2026 08:12
fix: run pubky work on its own service queue
(cherry picked from commit ebe9bc0)
fix: adopt paykit 0.1.1-rc6
(cherry picked from commit 816f08e)
feat: use the pubky.app profile directly
(cherry picked from commit e062e86)
fix: save the profile without waiting for an unchanged copy
(cherry picked from commit 831c4d2)
chore: bump ldk-node and vss client
(cherry picked from commit 7d57966)
chore: bump ldk-node to 0.7.0-rc.80
(cherry picked from commit 428ed54)
chore: bump paykit to 0.1.1-rc8
(cherry picked from commit 8e86d30)
ci: pass Blocktank regtest auth to E2E jobs
(cherry picked from commit 1764309)
@piotr-iohk

piotr-iohk commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator Author

After 3.1.1 ships from this branch, close this PR without merging. A separate follow-up PR will bump the version and update the 3.1.1 changelog on master.

Validation after the profile-tag fix.

Results before the tag-preservation fix

The first migration run hit HTTP 401 in all six wallet funding scenarios. The first staging run hit the same error in both hardware wallet funding cases. The job logs and device artifacts show the failures occurred at the Blocktank regtest deposit call.

Android #1820 is now backported in 47d05d808 and supplies the existing regtest credential to staging and migration jobs. The E2E suite uses main, which includes the auth helper from E2E #304. Native migration still uses the archived 3.1.0 app.

Check Earlier run
Local E2E Run with E2E main · ✅ 39 cases passed with the shipped 3.1.0 suite
Staging E2E ✅ Passed · 3 hardware wallet and 6 Pubky/Paykit cases passed on attempt 1; transfer and multi-address had no matching cases
Migration ✅ Passed · 4/4 standard cases on attempt 1
CI build and unit tests ✅ Build and 4,891 unit tests passed
Compose UI 253 passed, 3 failed · same three failures and assertions as 3.1.0
Device storage integration ✅ All 7 cases passed · run separately after Compose UI stopped the combined workflow

The authenticated migration run passed native 3.1.0 upgrade and restore and RN 1.1.6 upgrade and restore. Its optional RN sweep failed in legacy-wallet setup after funding worked, before the native app was installed. The standard rerun uses the four upgrade and restore cases. Extended run evidence

The E2E-main local run reproduced two Android send behaviors from 3.1.0 on all three attempts. The BIP21 case expected the review route added by Android #1763, and the Lightning amount case reproduced the stale spending limit fixed by Android #1808. The comparison run pins E2E to 611509ee2d6dc44094276b567ac51949d63760f9, the suite that tested the shipped Android 3.1.0 build. 3.1.0 control

The Android Compose UI failures are amountShowsNumberPadAndContinue (node count), hardwareEditorReturnsToTrezorTab (touch injection), and providerRefundShowsSourceNetworkAndLocalizedAmountAndHonorsEligibility (localized USDT amount). All three also failed with the same assertions on the shipped 3.1.0 commit.

Original evidence: staging and migration.

@piotr-iohk
piotr-iohk marked this pull request as ready for review November 12, 2026 08:50
@greptile-apps

greptile-apps Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[High impact] The changes reach public profiles and payment-related workflows across Bitkit.

The PR appears safe to merge, with a non-blocking improvement needed for profile link input.

Findings

  1. P2 Accepted links can prevent saving ▶

Summary

Prepares Bitkit 3.1.1, build 197.

  • Bitkit profiles now share their public details with pubky.app.
  • Payment requests now stop oversized notes before sending.
  • USDT transfers from before a request no longer match it.
  • Rejected Pubky signup codes now show a clear next step.
  • Bitkit is prepared as version 3.1.1, build 197.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Edit[Edit profile] --> Shared[Write pubky.app profile at loaded revision]
  Shared --> Tags{Tags changed?}
  Tags -->|Yes| Wait[Wait for Paykit copy write]
  Tags -->|No| Background[Write Paykit copy in background]
  Wait --> Result[Show saved profile or tag error]
  Background --> Result
  Payment[Prepare payment] --> Size{Largest proof fits?}
  Size -->|Yes| Continue[Continue payment]
  Size -->|No| Stop[Stop before payment]
Loading

Reviews (1) · Last reviewed commit: "Merge pull request #1820 from mdozhdev/c..." · Reviewed by Greptile

Comment on lines +1293 to +1297
val links = fitted?.links ?: links.map {
PubkyProfileLink(
label = it.label.singleLine(),
url = it.url.toPubkyAppLinkUrl() ?: it.url
)

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.

P2 Accepted links can prevent saving

Existing-profile edits skip the link limits used during migration. AddLinkSheet accepts any nonblank label and URL, while this branch neither limits labels to 100 code points nor rejects URLs that fail toPubkyAppLinkUrl(). Instead, ?: it.url sends the rejected value unchanged.

Check these limits before enabling Save and show which link needs fixing, so one invalid link does not make the whole profile save fail.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Confirmed. The form accepts links that the pubky.app publisher can reject, which blocks saving the profile. The link validation bug is tracked in #1882, with the matching iOS issue at synonymdev/bitkit-ios#1301. The 3.1.1 branch now has the separate restore fix that preserves saved profile tags.

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

No findings. Prepares Bitkit 3.1.1 (versionCode 197) on release-3.1.1 from v3.1.0: seven backports, the version bump and the collected changelog. Reviewed 47d05d8, full tier, static.

What I checked, and 3 candidates I ruled out

Backports: each of #1798, #1803, #1801, #1865, #1824, #1872, #1873 and #1820 carries the same changed lines as its merge on master (compared line by line against the master merge commit, 0 lines differ).
Version commit ce4d5cb: versionCode 196 to 197, versionName 3.1.1, the four consumed fragments match the CHANGELOG 3.1.1 entries.
Dependency pins: paykit 0.1.1-rc8, ldk-node 0.7.0-rc.80, vss-client 0.5.26-rc2, the same pins as master.
CI on the release branch: CI, E2E, E2E Staging and E2E Migration green on 47d05d8; Android Instrumented Tests still running when I read it.
Build: not built here. On the Bitkit Android track the review is reasoned from the code and CI, since Gradle would run the branch's own build scripts.

Ruled out

  • Fragments collected twice after the merge to master: the merge brings back master's copies of 1798/1801/1803/1870 into changelog.d/next. Ran scripts/collect-changelog.sh --target next on the merged tree: they land under [Unreleased], because the script only dedupes inside that section. Same pattern the 3.0.1 hotfix already went through, so a release-process choice, not this diff's defect.
  • Merge to master conflicts: PaykitExceptionExt.kt and libs.versions.toml conflict against master (GitHub shows the PR as conflicting). Both are master moving ahead of the same backports; resolving to master's side is correct for both.
  • CI secret exposure: BLOCKTANK_REGTEST_AUTH is added only to E2E job env in two workflows already on master from #1820.

Merge confidence: 4/5, no findings, but reviewed statically and the instrumented suite had not finished.

Reviewer model: claude-opus-5-5
Reasoning effort: Not exposed (the run metadata names the model only)

@jvsena42

Copy link
Copy Markdown
Member

@piotr-iohk this conflicts with master; please rebase. I'll review it again once it merges cleanly.

@piotr-iohk

Copy link
Copy Markdown
Collaborator Author

@jvsena42 Thanks for flagging the conflicts. This PR is for reviewing 3.1.1, which is based on v3.1.0 with selected backports. Rebasing onto master would pull in newer changes and require validating a different release candidate. Please review the patch against its release base. 3.1.1 patch diff

After 3.1.1 ships from this branch, we’ll close this PR without merging and open a separate PR on master for the version bump and changelog.

@piotr-iohk

piotr-iohk commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator Author

3.1.1 validation after the profile-tag fix

The release branch now includes the tag-preservation fix in signed commit 9eb6a8896. ✅ The local unit suite passed all 4,892 tests, including the new restore regression. Patch against v3.1.0

Check on 9eb6a8896 Run
Build and unit tests ✅ Passed
Device storage integration ✅ All 7 passed
Compose UI 253 passed, 3 failed · same three cases as the earlier candidate and 3.1.0
Local E2E with the shipped 3.1.0 suite ✅ All 39 cases passed · E2E commit 611509ee2
Staging E2E ✅ 9 cases passed · 3 hardware wallet and 6 Pubky/Paykit cases on attempt 1 · transfer and multi-address had no matching cases
Standard migration ✅ 4/4 cases passed · RN restore passed on attempt 2 after an RN 1.1.6 transaction broadcast timeout during fixture setup

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

Verdict: ⛔️ Request changes. Saved tags can still be lost outside the restore form. The picks, the bitkit-core pin, the version bump and the restore fix itself are correct.

Findings:

  • HIGH: Adding a tag or saving an edit after an unreadable load can wipe the stored tags. app/src/main/java/to/bitkit/repositories/PubkyRepo.kt:1418

    • loadOwnProfile sets the global tagsUnavailable as soon as it reads the Paykit copy. It does this before the pubky.app read, and before loadProfile applies the profile to _profile (:883-891).
    • If the pubky.app read then fails, _profile is left as it was (:894-902) while the flag says the tags are readable.
    • Saves take their unread flag from that global at save time: the profile screen's tag sheet (ProfileViewModel.kt:236 → profileEditBase(), :1136) and the edit screen (EditProfileViewModel.kt:64).

    How to replicate (flaky network):

    1. Open Profile. The Paykit copy read fails, so the profile shows with no tags and tagsUnavailable = true.
    2. Open Profile again (ProfileViewModel.kt:76 loads on each open). This time the copy read succeeds, so the flag goes false, but the pubky.app read fails. Or the user acts while that read is still in flight: the profile body and Add Tag stay enabled during a load (ProfileScreen.kt:142-150, :318-319).
    3. Add tag x. copyUnread is false (:1299), so storedCopy skips the read and keptTags = [x]. The copy write at :1366 replaces stored [a, b] with [x] and reports success.
    4. Or: open Edit, change the name and save. keptTags = [] equals baseTags, so the background copy write (:1359-1363) stores no tags, and the screen shows "Profile saved".

    This is the same root cause as the restore bug 9eb6a8896 fixes: a load clears the flag before the shown profile catches up. The fix only covers the restore form.

    Suggested fix: set tagsUnavailable where the load's profile is applied (loadProfile's onSuccess, together with _profile), not at the read. Or carry tagsUnread on the profile the way iOS does: on iOS, ProfileView and EditProfileView pass profile.tagsUnread from the profile they show. The same code is on master.

  • LOW: A new profile is created without its tags, with no message, when the copy write fails. app/src/main/java/to/bitkit/repositories/PubkyRepo.kt:1080

    • publishIdentityProfile logs tagError and drops it.
    • When the Paykit copy write fails, writeProfileOnce returns the profile with baseTags (:1370-1376).
    • The create screen then reports success, and the tags the user typed are gone. No stored tags are lost: on a restore the copy is left as it was.
    • In shipped 3.1.0, the same failure failed the whole create.

QA for 9eb6a88:

Check: The picks match the 3.1.1 list ✅ The picks are #1798, #1803, #1801, #1865, #1824, #1872, #1873 and #1820, in that order, each identical to its master merge.
  • Checked with git log --first-parent v3.1.0..9eb6a88. Each pick's changed lines were diffed against its master merge, and 0 lines differ.
  • Three more commits are direct: d13769269 (the 3.1.0 changelog line now links #1706; the same commit is on master), the version commit ce4d5cb25, and the tag fix 9eb6a8896.
  • Nothing else is in the patch. No bitkit-core rc15 (#1876) and no Spark/Breez code: the added lines have 0 matches.
Check: Dependency pins ✅ bitkit-core stays at `0.8.0-rc11`. ldk-node is `0.7.0-rc.80`, vss-client `0.5.26-rc2` and Paykit `0.1.1-rc8`.

git diff v3.1.0...9eb6a88 -- gradle/libs.versions.toml changes only the ldk-node, vss-client and Paykit lines.

Check: Restoring a profile keeps its tags ✅ Without the fix, a restore save erases the stored tags. With it, they are kept. The tag sheet and the edit screen are still exposed (HIGH above).

The bug at 47d05d808, without the fix:

  1. fetchRemoteProfile cannot read the Paykit copy, so the restore form opens with empty tags (PubkyRepo.kt:954-957).
  2. A later loadOwnProfile reads the copy and sets tagsUnavailable = false (:1417-1418).
  3. On Save, publishIdentityProfile passes no unread flag, so copyUnread is false (:1299). storedCopy then skips the read, so keptTags is the form's empty list.
  4. baseTags is also empty, so the copy write publishes no tags and the stored tags are gone.

The fix: 9eb6a8896 stores tagsUnread in RestoreBase and passes it to writeProfile (:957, :1073). storedCopy then re-reads the copy and merges its tags with the form's. If that read fails, keptTags is null and the copy is not written.

The other save paths:

  • The edit screen takes profileEditBase() when the form opens (EditProfileViewModel.kt:64), and writeProfileOnce ORs that flag with the live one. That covers a load that marks the tags unreadable after the form opened. It does not cover a form opened while the flag is already wrong (HIGH).
  • The tag sheet on the profile screen reads the flag when the save starts (ProfileViewModel.kt:236). It is exposed in the same way (HIGH).
  • A retry after PubkyProfileTagsNotSavedError carries tagsUnread = copyUnread (:1373).
  • The background copy refresh in loadOwnProfile runs only after a successful read and writes the tags it just read (:1448-1449).
  • The migration and the avatar move write only to pubky.app, never to the Paykit copy.

The commit: it matches #1884's code commit line for line. Only the changelog fragment is dropped, which is right: this bug never shipped. @jvsena42 reviewed #1884 on master with no findings.

Check: Version and release notes ✅ versionCode goes 196 → 197 and versionName to `3.1.1`. The four fragments are collected into `[3.1.1] - 2026-11-12`, and the compare links are updated.

These notes do not block the release:

  • The Security line names only the Lightning node. It does not mention the VSS client and Paykit rustls updates (#1824, #1873).
  • The collected section leaves five blank lines before ## [3.1.0].
Check: CI at 9eb6a88 ✅ CI, E2E (shipped 3.1.0 suite), E2E Staging and E2E Migration all passed.

UI Tests show 253 passed and 3 failed. The failures are amountShowsNumberPadAndContinue, hardwareEditorReturnsToTrezorTab and providerRefundShowsSourceNetworkAndLocalizedAmountAndHonorsEligibility. All three also fail on shipped 3.1.0.

Considerations:

  • A fresh-install restore whose lookup fails: the form opens empty with no error (CreateProfileViewModel.kt:97-100). Saving then cannot overwrite an existing pubky.app profile, because Paykit refuses a write with no revision when a profile exists (PubkyProfileChangedError). A Paykit-only profile would still be overwritten, as it already is in shipped 3.1.0. This is unchanged by this PR.

No device cases were driven here, and I could not show that the new journeys J1–J3 ran at this head. Local E2E used the shipped 3.1.0 suite (611509ee2), and staging used e2e main (0ca01e0a2). The runs come from @piotr-iohk's validation after the tag fix. No E2E case covers the HIGH above. The review covered the full v3.1.0...9eb6a88 patch, statically.

Twin: synonymdev/bitkit-ios#1298 has the same picks and the same tag fix. Its extras are explained: no #1798 on iOS, plus a test commit for the staging fixtures.


Reviewed by claude-opus-5-5

@piotr-iohk

piotr-iohk commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator Author

Validation after Ovi’s review

Android release head 0f23fc253 includes both profile fixes and the VSS client and Paykit security notes.

@piotr-iohk

Copy link
Copy Markdown
Collaborator Author

@ovi-reviewer review

@piotr-iohk

Copy link
Copy Markdown
Collaborator Author

@ovitrif @jvsena42 Could you review the Android 3.1.1 release candidate at 0f23fc253 and approve it if it looks good? Both profile-tag findings are fixed, and CI and staging E2E passed. Validation results

ovi-reviewer[bot]
ovi-reviewer Bot previously approved these changes Nov 12, 2026

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

Verdict: ✅ Approve. The reported profile defects are fixed.

Findings: No new findings. The previously accepted link-validation issue remains tracked in bitkit-android#1882.

QA for 0f23fc2:

Built the reviewed head and drove the listed journeys on Android 15 with disposable regtest wallets.

Edit the public Pubky profile ✅ J1: the public-profile notice is visible, and the saved name remains visible after relaunch.

J1 journey. Changed the name to Edited In Bitkit. A separate public read of the pubky.app profile also returned {"name":"Edited In Bitkit"} with HTTP 200.

j1-edit-profile-2x.2x.mp4

Profile notice, save and relaunch at 2× speed.

Reject an oversized payment request note ✅ J2: the oversized note is refused, while Coffee sends and reaches the payer’s 1,000-sat confirmation.

J2 journey. Pasted the 256-heart fixture. The app showed “Shorten the note and try again.” and kept Send available; no request appeared on the payer. Replacing the note with Coffee sent the request successfully. The payer received it and showed Coffee with 1,000 sats on confirmation. I closed confirmation without paying.

The payer needed regtest funds to reach confirmation; its initial unfunded preparation was a setup limitation.

Note validation and delivery: requester left, payer right Funded payer confirmation
j2-note-comparison-2x.2x.mp4
j2-review-2x.2x.mp4

Both recordings are at 2× speed; the paired recording is aligned in time.

Explain a rejected Pubky signup code ✅ J3: the specific error appears, authorization remains retryable, and no identity is created.

J3 journey. Opened an ordinary signup grant through Scan → Paste using an invalid token for the reachable staging homeserver. The app showed “The homeserver did not accept this signup code. Try again or use another signup link.” Authorize became available again. Canceling returned to the wallet’s profile introduction. The device log also reported signup_code_rejected.

j3-rejected-signup-2x.2x.mp4

Rejected token, retryable authorization and absent identity at 2× speed.

Check: Profile regressions and release validation ✅ The focused profile tests pass, and current-head CI and staging E2E are green.

PubkyRepoTest, CreateProfileViewModelTest, ProfileViewModelTest and EditProfileViewModelTest passed at this head. They cover the held/failed refresh, unread-tag preservation, partial creation warning and navigation.

Fixed findings:

  • Tag preservation: the profile, revision and unread status are installed together; tag actions use one edit-base snapshot.
  • Creation warning: setup retains the active identity and surfaces the partial-save warning.

Current-head reports:

Earlier full E2E, migration and native UI evidence remains at its original head in the earlier validation report. I did not rerun those suites.

Covered the full release diff (d137692..0f23fc2), using the common ancestor with the PR base as requested for this release branch. All listed journeys were driven. Dependency updates, USDT receipt matching, migration and injected profile failure/race paths were not driven as device cases; they were reviewed statically, with the profile paths additionally checked by the focused regression tests.

Twin: bitkit-ios#1298 chore: prepare Bitkit 3.1.1 matches the release changes and tag-preservation behavior; this Android head also fixes its own snapshot and creation-warning findings.


Reviewed by gpt-6.1-sol-high

ovitrif
ovitrif previously approved these changes Nov 12, 2026

@ovitrif ovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

utAck

@ovitrif
ovitrif dismissed stale reviews from ovi-reviewer[bot] and themself November 12, 2026 13:34

Release PR: reviewed and ready to release, but kept unapproved so it can't merge before the store release.

@ovitrif

ovitrif commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

✅ Ready to release: review at 0f23fc2, no open findings.

I dismissed both approvals on this PR: a release PR stays unapproved until after the store release, so it can't be merged by accident.

This branch was successfully deployed

1 active deployment
release — 0f23fc25 Deployed Nov 12, 2026 by piotr-iohk via build-release #21
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.

4 participants