State Channel Design
Last updated
Last updated
Brollup introduces a new state channel design and eliminates the need for a trusted setup by making the global state statechannel-aware. The core idea is to execute payable conditions based on state updates rather than creating new VTXOs. This decouples finality from block production and ties it directly to state updates.
This is similar to the Lightning Network; however, Lightning operates in a 2-of-2 setting, where only the involved parties are aware of the channel’s state. The channel state remains private, with outsiders gaining limited information only in the event of a force closure.
The strategy involves making channel states publicly accessible to everyone at all times. By publicly exposing channel content and ensuring accessibility at the DA layer, the global state becomes aware of individual channel states. Implementing this for all channels enables our Bitcoin Virtual Machine to verify payments between specific channels and execute payable conditions accordingly.
While one might expect storing channel content in the DA to consume a significant number of bytes, this isn’t the case. Brollup employs an efficient approach where the only overhead comes from an 8 vByte signature commitment s
by the operator. All other channel content can be derived from the calldata.
It's already possible determine the sender (msg.sender
) and receiver (as asserted in the contract logic), along with their respective channel locations. Brollup channels also avoid non-deterministic variables like in-flight HTLCs or PTLCs, allowing the entire channel content to be derived from the calldata alone. The only additional overhead is the 8 vByte signature commitment s
from the operator, given that the signature nonce R
chosen by the operator is also deterministic. This is all there is.
Brollup makes it possible to establish a virtual channel from the VTXO. To enable this, a virtual UTXO is provided with two spending paths:
The Channel path This is a 2-of-2 path used by both the operator and the owner to establish a virtual channel.
The Claim path If the operator is uncooperative in setting up a channel, the owner can access coins from this path after the timeout.
Virtual UTXOs are nested under a shared UTXO, constrained by the output's covenant construction. The shared UTXO expires in 90 days, meaning accounts must refresh their VTXOs by sending coins to themselves every 90 days. Otherwise, their coins could theoretically be taken by the operator.
When accounts refresh their coins, they send coins to themselves, thereby participating in the same covenant setup that they enforce. In this case, the covenant cannot be violated. It is entirely sufficient for msg.senders
to enforce the covenant themselves; breaking a covenant involving their own coins would be self-defeating for an owner.
Brollup channels are easy to reason about;
No revocation: Each new channel state overwrites the previous one with higher precedence.
No basepoints: It’s always the same key for to_self and to_operator. Keys are re-used without involving any point tweaking.
No assymetry: Channel state is symmetric, reproducible, and always descend from the channel root.
No middle-stages: No in-flight HTLCs or PTLCs. It is always about to_self and to_operator. Payments are linked through connectors.
Brollup channels operate as a 2-of-2 between to_self, which refers to the Brollup account itself, and to_operator. Both parties collaboratively sign state updates from the channel path to advance the channel state.
Unlike Lightning, Brollup channels do not use a revocation mechanism. Instead, each new channel state overwrites the previous one with higher precedence, rather than revoking it. This is similar to Eltoo; however, while Eltoo chains Bitcoin transactions together, we do not. A new channel state always descend from the channel root.
Brollup utilizes a degrading relative timelock scheme to ensure that each new channel state has higher precedence than the previous one. By default, we use 64 degrading periods. The virtual channel output (channel:root
) is a taptree with 64 leaves, where each tapleaf corresponds to a degrading period. Each period is a 2-of-2 between to_self and to_operator with a relative timelock, where the duration starts at 64 days and degrades by one with each subsequent period.
A user might choose to opt-in to more than 64 leaves, allowing the channel to accommodate more state updates throughout its lifetime. This comes with the tradeoff of increased waiting time in the event of unilateral closure, however, multiple layers of timelocks also serve as a means to provide everyone with a fair guarantee of unilateral exit in a mass-exit scenario, so this isn’t quite really a tradeoff. As for the on-chain footprint, the number of leaves scales logarithmically well for the control block.
A channel completes its lifetime either when 90 days have elapsed or 64 state transitions have occurred. The account then refreshes the virtual channel into a fresh, new tree. In the case where a channel has expired and not been refreshed, or the channel does not have enough liquidity, the account receives payments in the form of new VTXOs (within the onboarding tree).
Brollup incorporates two type of payments:
P2VTXO (Pay-to-vtxo) For making payments from a channel to a brand-new VTXO.
P2SCOM (Pay-to-s-commitment) For making payments from a channel to another channel.
P2VTXO (Pay-to-VTXO) involves making a payment from a channel to a VTXO. The sender swaps out their channel liquidity for a fresh new VTXO. The recipient receives a new VTXO within the onboarding tree, bound by the msg.sender
. This occurs when the intended recipient is not yet onboarded or lacks sufficient inbound liquidity in their channels.
P2VTXO is a straightforward process:
The operator creates a round transaction by allocating 0.1 BTC to the recipient and adding a connector output to the msg.sender’s channel.
The msg.sender (to_self) and the operator (to_operator) sign a state update from the channel, pushing 0.1 BTC to the operator’s side in exchange for the atomic payment in the round transaction.
The connector output provided by the operator also commits to the calldata, ensuring the authenticity and atomicity of the contract execution.
P2SCOM (Pay-to-s-commitment) involves making a payment from a channel to another channel. The recipient receives their payment directly into their channel, rather than as a brand-new VTXO. This occurs when the intended recipient is already a user and has sufficient inbound liquidity in their channels.
In contrast to Lightning, the recipient can receive payments without needing to be online. The operator simply signs a channel update in favor of the recipient, pushing the value to the recipient’s side, and provides their version of the s
commitment in the witness.
The operator, however, does not disclose the s
value outright. Instead, the operator initially commits to the s
value using a hashlock in the witness, proves with a zero-knowledge proof that the hashlock commitment is valid, and promises to reveal the actual s
value afterward. This precaution is necessary to prevent the sender and recipient from colluding to steal funds from the operator by refusing to sign the channel update after obtaining the s
value. Once the sender forfeits their channel liquidity by signing a state update, the operator reveals the s
value directly in the witness.
In the calldata output structure, the operator must now reveal the s
values (in pushdata chunks) to access their change funds; otherwise, the output can be taken by the msg.senders
:
Picking signature nonces deterministically from a well-known basepoint is not safe, as it can lead to private key recovery. However, using different keys for the same nonce is safe against private key recovery, as long as the nonce remains secret and keys are unlinked.
Brollup employs a unique approach to save 32 bytes for the signature nonce R
by pre-selecting 64,000 nonces hardcoded in the client software, and re-assigning the to_operator key from a random entropy in every state transition. This approach involves using identical nonces with varying keys, rather than using identical keys with varying nonces.
The pool of 64,000 hardcoded nonces (that is 2MB) effectively supports the potential creation of up to 1,000 new virtual channels, with each capable of accommodating a maximum of 64 state updates in each state transition.