-
Notifications
You must be signed in to change notification settings - Fork 9
docs: expand concept docs and add comprehensive troubleshooting guide #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
HappySonnyDev
wants to merge
1
commit into
doc_2.0
Choose a base branch
from
doc_wsy
base: doc_2.0
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
188 changes: 151 additions & 37 deletions
188
content/docs/concept/channels/unidirectional-channel.mdx
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
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,94 +1,208 @@ | ||||||
| --- | ||||||
| title: Unidirectional Channel | ||||||
| description: Open a one-way payment channel where funds can only flow from initiator to acceptor | ||||||
| date: 2026-05-25 | ||||||
| description: Open a one-way payment channel where funds can only flow from initiator to acceptor — ideal for streaming payments and merchant receiving | ||||||
| date: 2026-06-15 | ||||||
| --- | ||||||
|
|
||||||
| import { Callout } from 'fumadocs-ui/components/callout'; | ||||||
|
|
||||||
| <Callout type="info"> | ||||||
| This page is a work in progress. Detailed operational guides and examples will be added soon. | ||||||
| </Callout> | ||||||
| ## TL;DR | ||||||
|
|
||||||
| Set `one_way: true` when opening a channel to restrict payment flow to one direction: from initiator to acceptor. One-way channels are always private and cannot route third-party payments. When the channel closes, any unspent funds are returned to the initiator. | ||||||
|
|
||||||
| A **unidirectional channel** (also called a one-way channel) is a payment channel where funds can only flow in a single direction: from the channel initiator to the acceptor. Unlike a standard bidirectional channel, the acceptor cannot send payments back through the channel. | ||||||
| ```bash | ||||||
| fnn-cli channel open_channel --pubkey <peer_pubkey> --funding-amount 49900000000 --one-way | ||||||
| ``` | ||||||
|
|
||||||
| ## When to Use a Unidirectional Channel | ||||||
| ## What Is a Unidirectional Channel | ||||||
|
|
||||||
| Unidirectional channels are useful in scenarios where: | ||||||
| A **unidirectional channel** (also called a **one-way channel**) is a payment channel where funds can only flow in a single direction — from the channel initiator to the acceptor. Unlike a standard bidirectional channel, the acceptor cannot send payments back through the same channel. | ||||||
|
|
||||||
| - **One-way streaming payments**: A subscriber pays a content provider continuously, but never expects refunds or payments back | ||||||
| - **Merchant-only receiving**: A merchant opens a channel solely to receive customer payments | ||||||
| - **Simplified channel management**: No need to worry about balanced liquidity in both directions | ||||||
| Think of it like a prepaid card: you load money onto it, and the merchant can only receive from it, never push money back. If you don't spend everything, you get the remainder back when you close the channel. | ||||||
|
|
||||||
| Under the hood, the channel's TLC (Time-Locked Contract) logic restricts the initiator to be the only sender and the acceptor to be the only receiver. | ||||||
|
|
||||||
| ## How It Works | ||||||
|
|
||||||
| In a unidirectional channel: | ||||||
|
|
||||||
| 1. The **initiator** opens the channel and provides all the funding | ||||||
| 2. The **acceptor** does not contribute any funds to the channel | ||||||
| 3. The initiator can send payments to the acceptor through off-chain TLC updates | ||||||
| 4. The acceptor **cannot** send payments back to the initiator through this channel | ||||||
| 2. The **acceptor** does not contribute any funds | ||||||
| 3. The initiator sends payments to the acceptor through off-chain TLC updates | ||||||
| 4. The acceptor **cannot** send payments back — any attempt to do so will fail at the routing level with a "Failed to build route" error | ||||||
| 5. When the channel closes, the final balance is settled on-chain | ||||||
|
|
||||||
| The direction restriction is enforced at the routing level. The node will never find a valid path for a reverse payment, so such payments are rejected before they even leave the sender. | ||||||
|
|
||||||
| <Callout type="warning"> | ||||||
| Unidirectional channels **cannot be public**. They will not be broadcast to the network graph, so they cannot be used for routing payments from other nodes. | ||||||
| One-way channels **cannot be public**. If you set both `public: true` and `one_way: true`, the node will reject the request with `"An one-way channel cannot be public"`. This also means one-way channels cannot participate in routing payments for other nodes. | ||||||
| </Callout> | ||||||
|
|
||||||
| ## Comparison with Bidirectional Channels | ||||||
|
|
||||||
| | Property | Unidirectional | Bidirectional | | ||||||
| |---|---|---| | ||||||
| | Funding | Only initiator funds | Both parties can fund | | ||||||
| | Payment direction | Initiator → Acceptor only | Both directions | | ||||||
| | Public visibility | Always private | Can be public or private | | ||||||
| | Routing for others | Cannot route third-party payments | Can route payments if public | | ||||||
| | Channel rebalancing | Not applicable | Supported via circular payments | | ||||||
| | Typical use case | Streaming, merchant receiving | General P2P payments | | ||||||
| | Channel reserve | ≥99 CKB (initiator side, default) | ≥99 CKB per side (default) | | ||||||
|
|
||||||
| ## Use Cases and Trade-offs | ||||||
|
|
||||||
| ### Good Fit | ||||||
|
|
||||||
| **Streaming payments.** A subscriber pays a content provider continuously — for example, paying per second of video watched or per API call made. The subscriber (initiator) funds the channel and streams payments to the provider (acceptor). Since payments only go one way, a unidirectional channel is a natural fit and avoids the complexity of managing liquidity in both directions. | ||||||
|
|
||||||
| **Merchant receiving.** A merchant opens a channel with a specific customer or partner so the customer can make repeated purchases without on-chain transactions each time. The customer is the initiator; the merchant is the acceptor. | ||||||
|
|
||||||
| **IoT and machine-to-machine payments.** A device that continuously pays for a service (compute, data, bandwidth) benefits from the simplified model — the device opens a one-way channel and drips payments as the service is consumed. | ||||||
|
|
||||||
| ### When to Use Bidirectional Instead | ||||||
|
|
||||||
| If you expect payments to flow in both directions — for example, two peers who may trade roles as payer and payee — use a standard bidirectional channel. A one-way channel cannot be "upgraded" to bidirectional after creation; you would need to close it and open a new one. | ||||||
|
|
||||||
| ### Limitations | ||||||
|
|
||||||
| - **No reverse payments.** If the acceptor needs to pay the initiator, they must open a separate channel. | ||||||
| - **No routing participation.** One-way channels are invisible to the network graph, so they cannot earn routing fees or help relay payments for others. | ||||||
| - **No rebalancing.** Since only one side holds funds, there is no liquidity rebalancing to manage. | ||||||
|
|
||||||
| ## Opening a Unidirectional Channel | ||||||
|
|
||||||
| Set `one_way: true` when calling `open_channel`: | ||||||
| Set `one_way: true` when calling `open_channel`. The parameter is optional and defaults to `false`. | ||||||
|
|
||||||
| ### Using fnn-cli | ||||||
|
|
||||||
| ```bash | ||||||
| fnn-cli channel open_channel --pubkey <acceptor_pubkey> --funding-amount 49900000000 --one-way | ||||||
| ``` | ||||||
|
|
||||||
| ### Using JSON-RPC | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "jsonrpc": "2.0", | ||||||
| "method": "open_channel", | ||||||
| "params": [ | ||||||
| { | ||||||
| "peer_id": "QmbLM...", | ||||||
| "funding_amount": "0x77359400", | ||||||
| "pubkey": "02abc...", | ||||||
| "funding_amount": "0xb9e459300", | ||||||
| "one_way": true | ||||||
| } | ||||||
| ], | ||||||
| "id": 1 | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Or using `fnn-cli`: | ||||||
| <Callout> | ||||||
| The `funding_amount` is in **shannons** (1 CKB = 10⁸ shannons). By default, 99 CKB is reserved for the channel reserve (98 CKB minimum cell capacity + 1 CKB default shutdown fee), so the available balance for payments is `funding_amount - 99_00000000` shannons. This default can be adjusted via the `FIBER_AUTO_ACCEPT_CHANNEL_CKB_FUNDING_AMOUNT` environment variable. | ||||||
| </Callout> | ||||||
|
|
||||||
| ## Sending Payments | ||||||
|
|
||||||
| Once the channel is in `ChannelReady` state, the initiator can send payments to the acceptor using `send_payment` as usual. No special parameters are needed — the routing engine automatically knows the channel is one-way and only allows forward-direction payments. | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "jsonrpc": "2.0", | ||||||
| "method": "send_payment", | ||||||
| "params": [ | ||||||
| { | ||||||
| "target_pubkey": "<acceptor_pubkey>", | ||||||
| "amount": "0x5f5e100", | ||||||
| "keysend": true | ||||||
| } | ||||||
| ], | ||||||
| "id": 1 | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| If the acceptor tries to send a payment back through the same channel, the routing algorithm will fail because it cannot find a valid path: | ||||||
|
|
||||||
| ``` | ||||||
| Error: Failed to build route | ||||||
| ``` | ||||||
|
|
||||||
| ## Closing and Fund Recovery | ||||||
|
|
||||||
| <Callout type="info"> | ||||||
| Although funds flow in only one direction during the channel's lifetime, closing a one-way channel works exactly the same as closing a bidirectional channel. Any unspent balance is returned to the initiator. | ||||||
| </Callout> | ||||||
|
|
||||||
| This is an important point that often causes confusion. A one-way channel restricts the **payment direction** during the channel's lifetime, but it does **not** mean the initiator forfeits unspent funds. When the channel closes — whether cooperatively or by force — the on-chain settlement distributes funds according to the current balance: | ||||||
|
|
||||||
| - **Initiator's remaining balance** goes back to the initiator's CKB address | ||||||
| - **Acceptor's received balance** goes to the acceptor's CKB address | ||||||
| - The channel reserve (99 CKB by default) is released back to the initiator | ||||||
|
|
||||||
| For example, if you open a one-way channel with 499 CKB and send 120 CKB worth of payments before closing: | ||||||
|
|
||||||
| - You (initiator) receive back: 499 - 120 - fees = **~379 CKB** (minus transaction fees) | ||||||
| - The acceptor receives: **120 CKB** | ||||||
|
|
||||||
| Either party can initiate the close: | ||||||
|
|
||||||
| ```bash | ||||||
| fnn-cli channel open_channel --peer-id QmbLM... --funding-amount 49900000000 --one-way | ||||||
| fnn-cli channel shutdown_channel --channel-id <channel_id> | ||||||
| ``` | ||||||
|
|
||||||
| ## Channel Properties | ||||||
| Or via JSON-RPC: | ||||||
|
|
||||||
| | Property | Unidirectional Channel | Bidirectional Channel | | ||||||
| |----------|----------------------|----------------------| | ||||||
| | Funding | Only initiator funds | Both parties fund | | ||||||
| | Payment direction | Initiator → Acceptor only | Both directions | | ||||||
| | Public visibility | Cannot be public | Can be public | | ||||||
| | Routing capability | Cannot route third-party payments | Can route payments | | ||||||
| | Use case | Streaming, receiving | General P2P payments | | ||||||
| ```json | ||||||
| { | ||||||
| "jsonrpc": "2.0", | ||||||
| "method": "shutdown_channel", | ||||||
| "params": [ | ||||||
| { | ||||||
| "channel_id": "0x..." | ||||||
| } | ||||||
| ], | ||||||
| "id": 1 | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| For details on the cooperative and force-close flows, see [Channel Lifecycle](/docs/concept/channels/channel-lifecycle). | ||||||
|
|
||||||
| ## Monitoring | ||||||
| ## Monitoring One-Way Channels | ||||||
|
|
||||||
| When listing channels, check `is_one_way` to verify the channel type: | ||||||
| When listing channels, the `is_one_way` field indicates whether a channel is unidirectional: | ||||||
|
|
||||||
| ```bash | ||||||
| fnn-cli channel list_channels | ||||||
| ``` | ||||||
|
|
||||||
| The response will include: | ||||||
| Response: | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "channel_id": "0x...", | ||||||
| "is_one_way": true, | ||||||
| "state": { "state_name": "ChannelReady" } | ||||||
| "channels": [ | ||||||
| { | ||||||
| "channel_id": "0x...", | ||||||
| "pubkey": "02abc...", | ||||||
| "is_public": false, | ||||||
| "is_one_way": true, | ||||||
| "is_acceptor": false, | ||||||
| "state": { "state_name": "CHANNEL_READY" }, | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| "local_balance": "0x9c16f4300", | ||||||
| "remote_balance": "0x1dcd65000" | ||||||
| } | ||||||
| ] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Combine `is_one_way` with `is_acceptor` to determine your role in the channel: | ||||||
|
|
||||||
| | `is_one_way` | `is_acceptor` | Your role | | ||||||
| |---|---|---| | ||||||
| | `true` | `false` | You are the payer (TLC sender) | | ||||||
| | `true` | `true` | You are the payee (TLC receiver) | | ||||||
| | `false` | either | Standard bidirectional channel | | ||||||
|
|
||||||
| ## Related Topics | ||||||
|
|
||||||
| - [Channel Lifecycle](/docs/guide/channels/channel-lifecycle) — full channel state transitions | ||||||
| - [Channel Rebalancing](/docs/guide/channels/channel-rebalancing) — adjust liquidity in bidirectional channels | ||||||
| - [Payment Lifecycle](/docs/guide/payments/payment-lifecycle) — how payments are processed | ||||||
| - [Channel Lifecycle](/docs/concept/channels/channel-lifecycle) — full channel state transitions and closing flows | ||||||
| - [Channel Rebalancing](/docs/concept/channels/channel-rebalancing) — liquidity management in bidirectional channels | ||||||
| - [Payment Lifecycle](/docs/concept/payments/payment-lifecycle) — how TLC payments are processed | ||||||
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
FIBER_AUTO_ACCEPT_CHANNEL_CKB_FUNDING_AMOUNTenv var controls the auto-accept funding contribution, not the channel reserve itself.