Skip to content

feat: blocktank sunset - #1696

Merged
ovitrif merged 7 commits into
masterfrom
fix/blocktank-sunset-sheet
Oct 28, 2026
Merged

ovitrif merged 7 commits into
masterfrom
fix/blocktank-sunset-sheet

Conversation

@ovitrif

@ovitrif ovitrif commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Twin: synonymdev/bitkit-ios#1127

This PR turns Blocktank channel purchases off in the app. Blocktank no longer sells channels, and its order, CJIT and gift endpoints now return 404, so every path that used to buy a channel shows the Transfer Unavailable sheet and leads to Manual Setup.

Description

  • Adds the "Transfer Unavailable" sheet: "MANUAL SETUP REQUIRED" with Learn More and Continue. Continue opens the existing Manual Setup flow, and Learn More opens https://blog.bitkit.to/ in the browser.
  • Shows the sheet on every path that used to buy a channel:
    • Savings wallet › Transfer To Spending
    • Spending wallet › transfer button
    • Hardware wallet › Transfer To Spending
    • Home › Lightning suggestion card
    • Receive › Spending › Receive Lightning Funds, and a Spending invoice edited above the wallet's inbound capacity, which no longer create a CJIT
  • Shows "Fund your spending balance" as the sunset design: Manual Setup first, Transfer from Savings, Transfer from HW wallet and Use Other Wallet disabled, and Learn More at the bottom.
  • Removes what only served buying a channel: the transfer-to-spending screens (intro, amount, receiving capacity, confirm, liquidity, setting up, funding advanced), the hardware-wallet funding screens, order and CJIT creation, fee estimates and the refund address they needed, opening and tracking channels for paid orders and their transfer-in-progress banner, the CJIT receive screens (amount, confirm, liquidity), the gift-code claim flow (Blocktank has taken its gift API down, so a scanned gift code now gets the scanner's regular unsupported-code error), the onboarding note that Bitkit offered no Lightning services in the user's country, and their settings flags, strings in every locale, tests and journeys.
  • Keeps what existing users need:
    • existing orders and CJIT entries still load in Channel Orders and activity;
    • Payment Pending offers Cancel for a Blocktank funding that never reached the network, which sends its coins back to savings so on-chain sends work again, and Retry original opens the sheet and broadcasts nothing.
  • Blocktank is no longer a trusted peer, so the wallet broadcasts and fee-bumps its own force closes.
  • Startup disconnects every stored peer with no channel and no claimable balance, which also drops it from the peer store, and skips that while a closed channel's sweep is pending.
  • When a cooperative close gives up, Bitkit offers Force Transfer for every channel, Blocktank ones included.
  • Repurposes Background Payments for Paykit notifications, since every push it relied on came from Blocktank: the settings screen, its intro, the Settings row, the Home card and "Keep Bitkit active in background" stay, reworded for subscription reminders and payment requests. The prompt shows only to users with a Paykit subscription. Proposing or accepting a subscription, or accepting an allowance, asks for notification permission if it isn't granted. The push registration with Blocktank and the handling of its pushes are removed.

Out of Scope

  • Existing Blocktank channels: closing them.
  • Refunds for paid Blocktank orders: Blocktank no longer opens channels for them; unopened orders expire within hours, and their on-chain refunds are handled manually on Blocktank's side (automatic refunds were removed in synonymdev/blocktank-lsp-ln2#285).
  • Funding option labels: the design writes "Manual setup" and "Use other wallet"; the existing "Manual Setup" and "Use Other Wallet" strings stay.

Design

Bitkit Wallet › Bitkit - Final Review v63 › Blocktank sunsetting:

Preview

Figma (left) next to the app (right):

Transfer Unavailable Fund your spending balance

Recordings of every path on the current head follow in the QA comment.

QA Notes

Setup

  • Dev build (just build, regtest, staging LSP).
  • Fresh wallet funded on-chain only: ./lsp POST /regtest/chain/deposit '{"address":"<savings address>","amountSat":100000}', then ./lsp POST /regtest/chain/mine '{"count":3}'.
  • Test 2 needs a refused Blocktank funding attempt, which staging can't produce now that it creates no orders: a local debug build tags an on-chain send as a Blocktank funding and prepares it at 0 sat/vB, so the backend refuses its real broadcast ("min relay fee not met"); the PR build then runs the steps.

Journeys

  • J1: transfer-unavailable-sheet.xml — the wallet screens' Transfer To Spending buttons open the sheet, Learn More opens https://blog.bitkit.to/, Continue opens Manual Setup, the Funding screen leads with Manual Setup with the other options disabled, and Receive's Lightning screen shows the sheet's content

Manual Tests

  • 2. With a refused Blocktank funding attempt, start a new on-chain send → Payment Pending opens; Retry original opens the sheet and broadcasts nothing; Cancel shows the transfer note and the fee prompt, then sends the coins back; a new on-chain send then goes through
  • 4. Open the sheet from Savings, Spending and the Lightning suggestion card → it opens at once, and the app log shows no Blocktank request on the way; scan bitkit://gift-abc123-3000 → the scanner's regular unsupported-code error, no sheet

Automated Checks

  • updated FundingScreenTest.kt — Manual Setup is the only enabled option
  • updated EditInvoiceVMTest.kt and ReceiveLiquidityDecisionTest.kt — a Spending invoice above inbound capacity leads to Transfer Unavailable
  • updated AppViewModelSendFlowTest.kt — Retry original on a Blocktank funding shows the sheet and broadcasts nothing
  • updated PaykitPaymentProofRepoTest.kt — a refused Blocktank funding is releasable and keeps Shop proofs
  • updated LightningRepoTest.kt, BackupRepoTest.kt and TransferRepoTest.kt — an accepted Blocktank funding finishes through the ordinary follow-up without blocking sends, and an old order-backed transfer settles once its order has no channel
  • added an AppViewModelSendFlowTest.kt case — a scanned gift code gets the regular unsupported-code toast and no sheet
  • added LightningServiceTest.kt — the node config trusts no peer for zero-conf or force-close broadcasts; only stored peers with no channel and no claimable balance count as idle, and none while a sweep is pending
  • updated LightningRepoTest.kt — start disconnects idle peers and connects to no fixed peer
  • updated NotificationsTimedSheetTest.kt — the prompt depends on Paykit subscriptions
  • removed the tests of the removed code: the purchase, hardware-funding, order, CJIT, gift and refund-address cases in TransferViewModelTest.kt, BlocktankRepoTest.kt, BlocktankTest.kt, ContentViewTest.kt, BackupRepoTest.kt and the receive tests, plus CjitQuoteValidatorTest.kt, CjitEntryDetailsTest.kt, FeesChangedStringsTest.kt and WakeNodeWorkerTest.kt
  • ran just compile, just test, just lint and compileDevDebugAndroidTestKotlin — pass

Side effects checked

  • Anchor reserve: perChannelReserveSats stays 1, so each open Blocktank channel holds 1 sat of on-chain reserve. Spendable balance is effectively unchanged.
  • Force-close broadcast: anchor commitments usually need a CPFP through the anchor, which spends a confirmed on-chain UTXO. A wallet with no confirmed savings coin retries on each new block until it has one.
  • Peer reconnects: checked at ldk-node v0.7.0-rc.77, which both apps pin. The node reconnects persisted peers, re-persists channel counterparties from the network graph after each RGS sync, and disconnect removes the peer from the store.
  • A Blocktank peer with a pending force close stays connected until its claim and sweep finish, which can take days for the timelock.

Evidence

J1 and tests 2 and 4 run on 94208b3, a dev build on a Pixel 9 emulator (Android 16) against the staging regtest LSP; all pass: QA comment. The refused funding was staged with a local debug build; every other step ran on the unmodified build.

Gaps

  • Hardware wallet Transfer To Spending was not run: no Trezor emulator. It opens the same sheet as the other buttons.

Models used

  • Planning/scoping: claude-opus-5-5 (reasoning: Not exposed)
  • Implementation: claude-opus-5-5 (reasoning: Not exposed)
  • Review: claude-sonnet-5-5 (reasoning: Not exposed)

Metadata note: the session records name the models but do not expose their reasoning settings.

@ovitrif ovitrif added this to the 3.1.0 milestone Oct 26, 2026
@ovitrif ovitrif self-assigned this Oct 26, 2026
@chatgpt-codex-connector

This comment has been minimized.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

@ovitrif
ovitrif requested review from a team, ben-kaufman and pwltr and removed request for a team October 26, 2026 13:28
@greptile-apps

This comment has been minimized.

greptile-apps[bot]

This comment was marked as outdated.

@github-actions

This comment has been minimized.

talosmachina

This comment was marked as outdated.

@ovitrif

This comment has been minimized.

@ovitrif ovitrif mentioned this pull request Oct 26, 2026
1 of 23 tasks
@ovitrif

This comment has been minimized.

@ovitrif

This comment has been minimized.

@ovitrif
ovitrif force-pushed the fix/blocktank-sunset-sheet branch from a1412d5 to a1c333c Compare October 26, 2026 17:23
@ovitrif

This comment has been minimized.

@ovitrif

This comment has been minimized.

piotr-iohk

This comment was marked as outdated.

@ovitrif

This comment has been minimized.

@ovitrif
ovitrif requested a review from piotr-iohk October 26, 2026 18:06
@ovitrif

This comment has been minimized.

piotr-iohk

This comment was marked as outdated.

@ovitrif

This comment has been minimized.

@ovitrif

This comment has been minimized.

@ovitrif

This comment has been minimized.

@ovitrif ovitrif changed the title fix: point transfers to manual setup feat: blocktank sunset Oct 27, 2026
@ovitrif

This comment has been minimized.

@ovitrif
ovitrif marked this pull request as draft October 27, 2026 16:40
@ovitrif
ovitrif force-pushed the fix/blocktank-sunset-sheet branch from 901ecd8 to 2ba664e Compare October 27, 2026 17:35
@ovitrif
ovitrif marked this pull request as ready for review October 27, 2026 17:59
@ovitrif
ovitrif requested a review from piotr-iohk October 27, 2026 17:59
@ovitrif

ovitrif commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 4eeb421: this PR is rebuilt on the latest master and now turns Blocktank channel purchases off in the app outright. Blocktank's order, CJIT and gift endpoints return 404, so every path that used to buy a channel shows the Transfer Unavailable sheet and leads to Manual Setup. The purchase screens, order and CJIT creation, the gift flow and their tests are removed. Paid-order recovery, order reads and the Cancel for a never-broadcast funding stay. The description is rewritten for the new shape. Device results on this head will follow in the QA comment.

@ovitrif
ovitrif removed the request for review from pwltr October 27, 2026 18:00
@ovitrif
ovitrif dismissed jvsena42’s stale review October 27, 2026 18:06

Dismissed per @ovitrif: this PR was rewritten (no geocheck; Blocktank purchase endpoints return 404), so this review of the old head no longer applies. @jvsena42 is off until 1 Nov; @ben-kaufman and @piotr-iohk review the new head.

@ovitrif

ovitrif commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

This PR was refactored into a proper Blocktank sunset. Blocktank no longer sells channels and its order, CJIT and gift endpoints return 404, so the app now turns channel purchases off outright: no geocheck call and no geo state remain, and every former purchase path leads to the sunset sheet and Manual Setup. The description is rewritten for the new head.

Comments and reviews from before the refactor are hidden as outdated: they reviewed the geocheck-based version, which no longer exists.

Now:

  • Head 94208b389 is pushed, rebased on master. Compile, unit tests and lint pass.
  • Since the last head: a scanned Blocktank gift code gets the scanner's regular unsupported-code error, and the recovery for paid Blocktank orders is removed, since Blocktank no longer opens their channels (see Out of Scope).
  • Device validation of the new head is running on our side; results follow here.
  • Ready for review by @ben-kaufman and @piotr-iohk.

@ovitrif

ovitrif commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

QA for 94208b3:
Tested on Pixel 9 emulator (Android 16).

🟢 Refused funding: Retry original, Cancel and a later send
A Blocktank funding whose broadcast the backend refused (`min relay fee not met, 0 < 141`, txid `836c19ab…`) leaves Savings at the full balance with no pending outgoing transaction. A new on-chain send opens Payment Pending for it; Retry original opens the Transfer Unavailable sheet and broadcasts nothing (`Stopped retrying a transfer to spending`); Cancel shows the transfer note and the fee prompt, then sends the coins back through `12fd2105…`; after one block a new on-chain send goes through (`Accepted on-chain send '9f692b7a…'`).
Refused broadcast (debug build) Blocked send, Retry original Cancel A new send
case1-stage2x.mp4
case1a2x.mp4
case1b2x.mp4
case1c2x.mp4

🟢 Every former purchase path shows the sheet
The Home Lightning card, Savings › Transfer To Spending and Spending › Transfer from Savings open the Transfer Unavailable sheet at once; Learn More opens blog.bitkit.to and Continue opens Manual Setup. Receive › Spending › Receive Lightning funds shows the same screen. Lightning Connections › add connection lists Manual Setup first and enabled, with the other three options disabled; tapping Transfer from Savings opens nothing. The app log has no request to Blocktank's purchase API on any of these paths.
Card, Savings, Spending Receive, Funding screen
case2a2x.mp4
case2b2x.mp4

🟢 A scanned gift code gets the regular unsupported-code error
Opening `bitkit://gift-abc123-3000` shows the scanner's "Unable To Read QR" warning, and no Transfer Unavailable sheet opens.
Gift code Toast
case32x.mp4

Note

Staging creates no Blocktank orders, so a local debug build tagged a 100 000-sat on-chain send as a Blocktank funding and prepared it at 0 sat/vB; the backend refused its real broadcast. The unmodified 94208b3 build was then installed over that wallet and ran every other step shown.

@ovitrif ovitrif mentioned this pull request Oct 27, 2026
3 tasks

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Re-reviewed to 94208b3 (the Blocktank sunset commits). No HIGH or MEDIUM. One LOW inline: the gift error sheet is left unused.

  • Dropping paid-order recovery doesn't put funds at risk. The sent activity and the Channel Orders entry remain, and refund detection still works, because P2WPKH monitoring is forced and the refund address was a revealed wallet address.
  • Accepted but unfinished funding attempts now complete through the ordinary follow-up.
  • The removed persisted fields still decode, thanks to ignoreUnknownKeys.
  • A scanned gift code now shows an "Unsupported" toast: no crash and no silent drop.
  • New strings are present in all 16 locales.

Device gate (partial): 94208b3, not run yet. Pending: refused funding → Cancel → a later send works; geocheck allowed → no Cancel; transfer/transfer-unavailable-sheet.xml; and the gift deep link.

@ovitrif

ovitrif commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed e7f8476: removes GiftErrorSheet.kt and GiftRoute.kt, which had no callers once the gift flow went. This answers the unused-sheet finding in the latest review. Compile, unit tests and lint pass.

@jvsena42

Copy link
Copy Markdown
Member

Device gate (partial): e7f8476 (CI APK). The only commit since my re-review, e7f8476, deletes the unused GiftErrorSheet.kt and GiftRoute.kt, so it fixes my LOW and changes no behaviour. Device: Pixel_9 emulator, fresh wallet with 200 000 sat on-chain and no channels.

  • transfer/transfer-unavailable-sheet.xml: all 11 actions pass.
    • Savings → Transfer To Spending, Spending → Transfer from Savings, and Receive → Spending all open "Transfer Unavailable" with the expected copy. Learn More opens https://blog.bitkit.to/, and Continue opens Manual Setup.
    • On Settings → Lightning Connections → Get Started, only Manual and Learn More are enabled.
  • Gift code by deep link (bitkit://gift-abc123-3000), by manual entry and by a scanned QR: each shows the "Unable To Read QR" toast, logs Received unhandled scan data 'Gift(code=abc123, amount=3000)', makes no gift or order request, doesn't crash, and leaves the balance unchanged.

Still running: refused funding → a later send is blocked → Cancel → a later send works, which needs a local Blocktank stand-in now that staging no longer creates orders.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approving. My MEDIUM is fixed and the LOW is gone in e7f8476. The device gate passed.

Device gate: e7f8476 (CI APK), Pixel_9 emulator, regtest on staging.

  1. transfer/transfer-unavailable-sheet.xml: 11 of 11 pass.
  2. Gift code by deep link, manual entry and a scanned QR: each shows the "Unable To Read QR" toast, with no sheet, no request and no crash.
  3. Refused funding:
    • Setup: a master build with a local Blocktank stand-in made the funding, and an Electrum proxy refused its broadcast. I then upgraded to this build with the data kept.
    • The next ordinary send is blocked and Payment Pending opens. Retry opens Transfer Unavailable and broadcasts nothing.
    • Cancel shows the transfer note, the 703 sat fee and the PIN, then logs Released on-chain payment 'b7ad6ac0…' through '9fc1d01c…'. A new 20 000 sat send then succeeded.

Android has no geocheck in this flow, since Cancel depends only on canBeAbandoned / isBlocktankFunding, so the iOS "geocheck allowed → no Cancel" case doesn't apply.

⚠️ Not run: Cancel refused for a funding already in the mempool. It rests on the code scan and the shared logic with synonymdev/bitkit-ios#1127, whose device run covered it.

Perf: not relevant.

Note: every blocked send logs an ERROR-level findActivityByPaymentId error for the funding that never reached the chain. It has no user effect.

@ovitrif
ovitrif merged commit 65f2082 into master Oct 28, 2026
21 checks passed
@ovitrif
ovitrif deleted the fix/blocktank-sunset-sheet branch October 28, 2026 08:29
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