pull down to refresh

Translated from https://www.bitcointrain.it/p/fermata-313-spark-una-self-custody

Last Saturday I misjudged my Glow wallet.

I had included it, along with Blockstream Swaps and Arkade Intents, among the projects that are reducing wallet dependence on a single operator. After a week of documentation, that sentence deserves correction.

Glow is the demonstration application of Breez's new SDK, built on the Spark protocol. The same protocol is already used by Wallet of Satoshi, Primal, Blink, Blitz, Cake Wallet and Xverse. The latter two report over one million and 1.6 million users respectively.

After Boltz's services were suspended, Freedomia rebuilt the flow with Breez: payment is routed through Spark and Orchestra, converted to USDC on Polygon, and credited to the card balance. Spark's business proposition is clear: instant payments, offline reception, no Lightning channels to open, no liquidity to manage, and user key control. Wallets and services present it as a self-custodial solution.

The question to be tested is equally precise: can a user bring their bitcoins back on-chain when Spark operators stop collaborating or become unreachable?
The answer has two levels. The protocol provides for true unilateral exit, but many popular wallets still depend on operators to obtain the data needed to build it.

How Bitcoins Are Controlled on Spark

A Spark deposit funds a Taproot output on the Bitcoin blockchain. The key that controls that output comes from combining two components: one belongs to the user, the other to the Spark operator group.

The mainnet configuration published in the SDKs lists three operators: Lightspark, Breez, and Flashnet. Everyone owns a share of the collective component and two are enough to produce the signature. When users and operators sign together, a normal Schnorr signature appears on the blockchain.

Operators do not own the user component and alone cannot spend the deposit. To perform normal Spark operations, the user must interact with them instead.

When Alice transfers bitcoins to Bob, the on-chain output remains unchanged. Spark modifies key components outside the blockchain: Bob gets a new personal component, and operators update their shares so that the overall public key continues to match that of the original output.

Operators must then cancel the previous quotas, those compatible with Alice's key. The official documentation defines this assumption “trust at a specific time”: if the cancellation occurs correctly during the transfer, a subsequent compromise by the operators does not allow the previous state to be reconstructed.

The erasure of a piece of data remains impossible to prove cryptographically. Alice and a sufficient number of operators who had kept the old quotas could sign a competing expenditure. Bitcoin Layers considers this eventuality to be Spark's primary finality risk.

The case remains theoretical and without documented precedent. It would require a previous owner willing to steal, the collusion of operators and the retention of shares referring to the same state.

Spark often presents its security with the formula “all it takes is an honest operator”. The published configuration, however, is 2-of-3: two old quotas are sufficient to form the collective component of the signature. The third operator may refuse to participate without preventing the other two from proceeding.

How trees and leaves work

The first statechains allowed only an entire UTXO to be transferred off the blockchain. Spark added the ability to split that value through a hierarchical structure of pre-signed transactions.

The first transaction in the structure, called the root transaction, spends the deposit output. Intermediate transactions spend outputs from the previous level and break them down into smaller amounts. The terminal nodes are called leaves and represent the individual portions of balance controlled by users.

These transactions remain outside the blockchain during ordinary operation. Operators keep its structure, status, and signatures in their databases. The wallet adds the value of the leaves assigned to the user's identity and displays a single balance.

To make a payment, the wallet selects a combination of leaves that covers the requested amount. When the available denominations do not allow you to dial the digit, the Spark Service Provider - SSP - performs an atomic swap: it receives a group of leaves and returns another group of the same value with different denominations.

Payments, receptions, swaps, renewals, and optimizations therefore modify the set of leaves owned and their position in the structure. Each leaf has a specific path composed of the root and all the intermediate transactions that precede it.

A seed imported into another wallet recreates the user's identity and keys. The new wallet then queries operators to find out the leaves assigned to that identity, their status, and the transactions that precede them.

The balance retrieved via seed therefore depends on the availability of Spark databases.

How unilateral exit really works

When a leaf is created or transferred, users and operators also sign the transactions needed to bring it on-chain in advance. The user can publish them in the future without asking for new signatures.

Each new owner receives a transaction with a shorter timelock than the previous owner. If Alice publishes an old version, Bob has an interval during which he can publish his own and assert the most recent state. Operators keep copies of these transactions and act as watchtowers. Spark recognizes that failure to intervene within the expected window would allow the old owner to complete the expense.

Cooperative exit requires user, operators and SSP. The parties sign a new transaction directed to the Bitcoin address chosen by the user. The process produces only one on-chain transaction.

Unilateral exit instead uses previously signed transactions. The wallet must retrieve the current leaf and all transactions that connect it to the root. Then he publishes them in order, from root to leaf. Each level must confirm before the next can enter the mempool.

Pre-signed transactions have zero fees, because the future cost of block space is unknown. Each contains an ephemeral anchor that allows you to add a second transaction with sufficient fees for both. The mechanism is called CPFP and requires the user to separately own at least one Bitcoin UTXO with which to fund fees.

After the intermediate transactions are confirmed, the user must wait for the leaf timelock to expire, publish the refund transaction, and finally spend the corresponding output to the desired Bitcoin address.

Blink performed this process with a 100,000 sat wallet on mainnet. The 22 leaves required a total of 253 packets. The first attempt to send them together resulted in 252 being rejected, because the mempool only allowed one unconfirmed level for each path.

At 1 sat/vB, 18 leaves containing a total of 9,888 sats had an exit cost higher than their own value. The four economically recoverable contained 90,112 sats. The process required dozens of confirmations, timelocks of up to 1,400 blocks, and the subsequent publication by a watchtower of alternative transactions already signed.

The four leaves finally arrived in output controlled by the user's seed without new operator permissions. After the last sweeps, the expected recovery was approximately 82,000 sats.

The test shows that Spark's unilateral exit works up to a point. It demonstrates, in particular, that it requires updated status, external funds for fees, tools capable of managing package relays, numerous confirmations, and timescales that can exceed a week. Above all, it can be uneconomical for smaller leaves.

Why Seed Is Not Enough

Blink had prepared a recovery bundle containing all current leaves, pre-signed transactions and their ancestors before the test. The seed allows you to derive cryptographic keys. The recovery bundle contains the dynamic state needed to determine which signed transactions belong to the user today and in what order they should be published.

A seed created months earlier does not record subsequent transfers, swaps, and reorganizations. During a complete operator blackout, it can generate user keys, while the current leaves and transactions required at the exit remain unknown.

The documentation for Breez release 0.22, released on August 13, explicitly states this: operators must be reachable when the exit is prepared, because the SDK retrieves pre-signed transactions from them.

Last Thursday, Breez merged support into the code to locally store the complete structure, automatically collect ancestors, export state, and build the offline output. The change has yet to come in a public release and then in wallets that use the SDK.

Blink and Glow's public code uses version 0.22. Primal uses 0.17.1 and Cake uses 0.14. In these versions, the full path with offline operators remains outside the app.

Blitz stores leaves and ancestors in its database and allows you to export a recovery bundle. Completing the release still requires the external tool developed by Blink to prepare fees, build and publish packages, wait for timelocks, and complete sweeps.

Wallet of Satoshi is closed source. His guide states that seed or backup to Google Drive allows you to restore your wallet to another phone. Recovery queries Spark infrastructure. There is no public procedure to complete it during a total blackout.

An independent review concluded in July examined eight consumer wallets without finding a full implementation of operator-offline exit within applications. Blitz subsequently added export, while execution remains entrusted to external instruments.

Lightspark Operational Control

Lightspark manages one of three operators, is the fixed coordinator to which the SDKs send the main requests, and controls the only SSP configured by default. Breez and Flashnet can produce the cryptographic threshold together, but applications do not automatically switch to them when the Lightspark coordinator stops responding.

The code is open source and another group can launch its own Spark Entity with different operators. The network used by the most popular wallets, however, remains permissive: the list of operators is written in the SDK configuration and any changes must be distributed to the applications.

The code also contains a control called a wallet kill switch. Each operator can use it to block operations that change the state of a specific identity, leaving reads available. The wallet receives the same message as expected for a normal authentication error and therefore cannot distinguish between the two situations.

The public registry only documents the code, and use against real users remains unverified. The presence of control nevertheless demonstrates that operators have a tool to selectively lock a wallet.

David Marcus, CEO of Lightspark, comes from PayPal, Facebook Messenger, and Libra/Diem. In 2019, he defended Facebook's attempt to create a global permissive monetary network, governed by an association of companies, before the US Congress. The project was scaled back and eventually abandoned after years of political and regulatory opposition.

Marcus's resume offers context. It shows the continuity between two goals: creating a global infrastructure for money and making it simple enough to be integrated into products used by millions of people.

The point is that Bitcoin requires skepticism by default, as the events of recent weeks have demonstrated: it requires evaluating the system in the worst-case scenario, regardless of the intentions of those who build it.

A five-year-old error caused Coldcard to generate weak seeds. Malice was superfluous. The lesson of Stop #311 also applies to Spark: bugs, compromises, and political pressure must be considered conditions under the project.

Lightspark also sells Grid, global accounts, and regulated infrastructure for fiat and stablecoins. The terms of service provide for KYC, sanctions control, denial, delay, reversal, and freezing of operations. Grid is separate from Spark, and these terms only demonstrate the company's commercial and regulatory orientation.

It remains possible to formulate a hypothetical scenario. A regulator may require identification in the future to access cooperative output or operator APIs. The SSP could apply it to the cheapest exit, servers to the state distribution. A user with a full recovery bundle would continue to use unilateral exit. A user with only the seed would depend on the infrastructure subject to the new rule.

Its technical possibility is enough to apply the correct criterion: each wallet must store and deliver to the user all the data necessary for exit before that risk materializes.

The verification you need to ask Spark wallets for is concrete: operators turned off, SSP turned off, application still capable of building the output, publishing it, and reaching a Bitcoin UTXO using a backup that the user knows they need to protect.

As long as this proof is missing, defining those self-custodial wallets means bowing your head to Lightspark marketing.

Ran the opposite stack today: phoenixd 0.9.0, on-chain swap-in, dual-funded channel with ACINQ. That is exactly the cost Spark is selling you out of — ~21k sat service on a ~50k sat open for 2M inbound.

The test in the article is the right one: if Spark/Breez/WoS operators go dark, can I get sats on-chain with only my seed? For phoenixd the answer is yes, after the channel close timeout, because the funding output is a normal LN 2-of-2 I can force-close. I still depend on ACINQ for inbound while the channel is live. That is LSP risk, not custody of the UTXO.

If a Spark wallet cannot show you a unilaterally broadcastable exit tx from the seed alone, it is not in the same category as a channel you opened. "No liquidity to manage" is the product. Unilateral exit is the property. They are not the same sentence.

reply