Repository navigation
Conversation
ovitrif
force-pushed
the
feat/breez-spark-lightning
branch
from
October 26, 2026 13:49
bf469f4 to
ef7130e
Compare
This was referenced Oct 26, 2026
ovitrif
force-pushed
the
feat/breez-spark-lightning
branch
4 times, most recently
from
October 26, 2026 17:20
01334a9 to
4ed36e0
Compare
ovitrif
force-pushed
the
fix/blocktank-sunset-sheet
branch
from
October 26, 2026 17:23
a1412d5 to
a1c333c
Compare
ovitrif
force-pushed
the
feat/breez-spark-lightning
branch
2 times, most recently
from
October 26, 2026 18:20
60e079f to
9982a55
Compare
ovitrif
force-pushed
the
feat/breez-spark-lightning
branch
from
October 26, 2026 22:47
9982a55 to
d964a5f
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1696
Twin: synonymdev/bitkit-ios#1128
This PR keeps Lightning in Bitkit after Blocktank stopped selling channels: Savings to Spending now funds a Breez SDK on Spark wallet, and Lightning sends and receives fall back to it whenever no channel can carry the payment. Existing channels keep working until they close, and ldk-node keeps running the on-chain wallet.
Description
breez_sdk_spark:bindings-android:0.27.2) withSparkServiceandSparkRepo, so that a Spark wallet starts beside the node from the same recovery phrase and passphrase. Spark derives its own keys onm/8797555', so there is no new phrase to back up.BREEZ_API_KEYbuild property, so that mainnet builds without a key keep Spark off; regtest builds use Lightspark's hosted regtest, which needs no key.ESPLORA_URLbuild property for regtest builds, which puts the node's chain source on an Esplora endpoint, plusscripts/spark-regtest-esplora.pyandscripts/spark-regtest-fund.py, so a dev build runs on Spark's regtest chain and QA can fund it and test Savings to Spending and the claim without mainnet.BREEZ_API_KEYfrom thereleaseenvironment in the release workflow and fails the release when it is missing, so Spark can't ship switched off; internal release builds pass it through when therelease-internalenvironment has it. A repo admin has to addBREEZ_API_KEYto thereleaseenvironment (and torelease-internalfor internal builds with Spark).SPARK_EXIT, encrypted like the other backups. It is exported again when Spark connects and whenever the SDK reportsUnilateralExitStateChanged, and on restore it is imported right after Spark connects, without contacting the operators, so a wallet restored on a new device can still exit on-chain while the operators are unreachable. The category shows in Backup settings only where Spark runs.Out of Scope
prepare_recover_funds/recover_fundsyet.SettingUpScreen: copy still describes a channel opening during Spark funding.Risks for review
SPARK_EXIT; upstream VSS accepts request bodies up to 1 GiB by default, but a large wallet's upload against Synonym's VSS is untested.250 × sat/vB + 750sats.CLAIM_TX_SIZE_VBYTES,deposits.rs), so it is a flat cost that weighs heavily on small transfers. The confirm screen now shows it as Claim fee and shows what reaches Spending after it.libbreez_sdk_spark_bindings.soadds about 11 MB per ABI (arm64, uncompressed).Design
N/A — no design available. Figma has no Spark or unlimited-receiving state for the Lightning Connections header, so No limit uses the same style and arrow as the spending figure beside it; @aldertnl, a design for this state is still open.
Preview
Cases 1 and 2 on a dev build without channels: the Spending invoice comes from Spark, and Transfer to Spending funds the Spark deposit address.
r1-receive2x.mp4
r3-transfer2x.mp4
Case 10 on a dev build, staged with a local hook (not in this PR) for the balance and the claimed deposit; the pending deposit is a real 35,000 sats transfer with its real confirmations.
r72x.mp4
A new dev wallet with no Spark balance: Spark shows in Lightning Connections on regtest, and its screen shows the deposit address to copy for Lightspark's regtest faucet.
r8-regtest-address2x.mp4
Case 2 confirm on a dev build: the 20,000 sat transfer shows a 99 sat claim fee (1 sat/vB × 99 on regtest) and 19,901 sats to Spending.
On a dev build with a staged 14,703 sat Spark balance: Lightning Connections and the Spark detail with the full-width bar and receiving shown as No limit, and the Spark confirm screen without the background toggle and Learn More.
QA Notes
Setup
BREEZ_API_KEY=<key>inlocal.properties, and small amounts.python3 scripts/spark-regtest-esplora.py --port 3999. It fronts Spark's regtest mempool API (the SDK's own regtest default,sdk_builder.rs) and adds the two Esplora calls it lacks and ldk-node needs:/fee-estimatesat start and/block/<hash>/statusfor Lightning sync.ESPLORA_URL=http://10.0.2.2:3999 ./gradlew assembleDevDebug, so the node's chain source is Esplora on Spark's chain instead of Electrum on Blocktank's staging chain. For a phone, run the proxy with--bind 0.0.0.0and use the host's address.FAUCET_USERNAME=… FAUCET_PASSWORD=… python3 scripts/spark-regtest-fund.py <Savings address> 100000, through Lightspark's API faucet (credentials issued by Lightspark; Breez's CI uses the same,faucet.rs). Without credentials, use the web faucet by hand.ESPLORA_URLstays on Blocktank's staging chain: Spark start-up, invoices and screens work, but its Savings to Spending deposit is never seen by Spark.Journeys
N/A — not drivable; see Manual Tests.
Manual Tests
ESPLORA_URLon Spark's regtest chain, Savings funded withspark-regtest-fund.py→ Transfer to Spending; after 1 confirmation Spark claims the deposit and Spending shows it; Lightning Connections lists it as claimed — faucet credentials.Imported Spark exit databefore Spark is used, and Spark's balance and deposits match — needs two installs.Automated Checks
SparkRepoTest.ktcases — a restored exit backup is imported once Spark connects, and one from another network is skippedBackupRepoTest.kt—SPARK_EXITuploads the exported state and restores it, uploads nothing while Spark is not connected, and uploads when Spark was ready before observing startedEditInvoiceVMTest.kt— Receive asks for no liquidity or CJIT while Spark can receiveDeriveBalanceStateUseCaseTest.kt— Spark balance counts toward Spending, caps max send, and pending deposits count as in transferLightningRepoTest.kt,WalletRepoTest.kt,TransferViewModelTest.kt,WalletViewModelTest.kt— construct the new Spark dependency, idle, so existing channel paths are unchangedEvidence
Dev build of
7aa44f6(before the rebase:a9cc9a3), Android 15 emulator, Spark's regtest:1: new wallet without channels; Spark connected on regtest from the wallet's seed. Receive shows Auto and Spending; the Spending invoice's payee is
02fe4b…, not the node03401e…, with a Spark route hint.2: funded Savings with 100,000 sats from Blocktank's staging regtest; Transfer to Spending offered 99,671 sats, confirm showed 458 sats network fee and 0 service fee without Advanced, and the swipe broadcast 50,000 sats to the Spark deposit address. No Blocktank order was created.
10 on
07a1df7(now2126254): Lightning Connections listed Spark with 20,000 sats and the 35,000 sats deposit at 1/3 confirmations read from the wallet's own transaction; the detail showed provider, trust note, pending and claimed deposits with a 312 sat claim fee, and Transfer to Savings opened the savings transfer. The Home total counted the Spark balance and the pending deposit.3 passed on mainnet (Ovi's phone,
ef7130e): 15,000 sats broadcast to the Spark deposit address showed as in transfer to Spending within a minute; Spark claimed it with the standard claim after 3 confirmations (no early claim at 1 confirmation under this PR's claim fee cap), and the Spending balance then showed the Spark funds.Regtest address on
be8df34: a new wallet with no Spark balance listed Spark in Lightning Connections, and its screen showed the deposit addressbcrt1peqm25…z0uj.Spark's chain on
5f88733(now01334a9): a dev build withESPLORA_URLthrough the proxy started its node on Spark's regtest (Esplora chain source logged), synced the on-chain and Lightning wallets to height 3,658,177 with no failed calls at the proxy, and Spark connected beside it.2 on
9a9ba8c(now4ed36e08c): confirm showed network fee 458, Claim fee 99, To spending 19,901, Total 20,458 for 20,000 sats.10 and 2 on
9383434(now60e079f): the Spark row and detail drew a full purple bar for 14,703 sats with no receiving side, the header read Receiving capacity No limit, and the Spark confirm showed Claim fee 99 and To spending 19,901 with no background toggle or Learn More.10 and 2 on
a93f3dc88(Android 15 emulator, Blocktank's staging regtest, where Spark never sees the deposit): 26 seconds after the swipe, Lightning Connections listed the 10,000 sat deposit as pending at 1/3 confirmations from the app's own broadcast, and the header showed No limit (since restyled to match the spending figure).Gaps
2695c93af: unit tests only (upload, restore, Spark ready before observing, staged import on connect); not yet driven on a device.Models used
claude-opus-5-5(reasoning:low)claude-opus-5-5(reasoning:low)