mirror of
https://github.com/lightning/blips.git
synced 2024-11-19 00:50:02 +01:00
367 lines
16 KiB
Markdown
367 lines
16 KiB
Markdown
```
|
|
bLIP: 0017
|
|
Title: Hosted Channels
|
|
Status: Active
|
|
Author: Anton Kumaigorodski <akumaigorodski@gmail.com>
|
|
fiatjaf <fiatjaf@gmail.com>
|
|
Created: 2022-07-07
|
|
License: CC0
|
|
```
|
|
|
|
## Abstract
|
|
|
|
Hosted Channels is a specification for auditable Lightning-like channels backed
|
|
only by trust from a client in a host.
|
|
|
|
This bLIP serves to document what is already supported by some wallets and daemons
|
|
(see [Reference Implementations](#reference-implementations)) and to encourage new
|
|
implementations.
|
|
|
|
## Copyright
|
|
|
|
This bLIP is licensed under the CC0 license.
|
|
|
|
## Motivation
|
|
|
|
Hosted Channels are an improvement over the traditional custodial Lightning wallet
|
|
model. They describe the roles of "host" and "client", the client being the peer
|
|
that is trusting the host to allow them to spend from the channel according to their
|
|
balance.
|
|
|
|
Like custodial wallets, hosted channel clients do not have to have any previous
|
|
Bitcoin UTXO or touch the chain in any way; they gain the ability to receive
|
|
payments immediately; and they do not have to keep track of a channel state history
|
|
and all the pitfalls involved on that, or keep track of Bitcoin transactions or fees.
|
|
|
|
Unlike custodial wallets, hosted channel clients are not "accounts" in a central
|
|
server, they communicate through signed messages over the Lightning protocol and are
|
|
otherwise anonymous to the hosts; they perform client-side routing and their hosts only
|
|
ever see onions to forward, never the final destination of a payment; they can open
|
|
multiple channels to different hosts and send MPP payments over these channels, further
|
|
improving their anonymity; and their channel state is signed by both parties, so
|
|
they are immune to hosts that may tamper with balances from their actual state on
|
|
purpose or by mistake. All this also means clients must be online to send and receive,
|
|
as they must sign state updates.
|
|
|
|
## Specification
|
|
|
|
The protocol consists of messages being passed through [bolt01][bolt01] connections and
|
|
mimics [bolt02][bolt02], with different messages that serve analogous purposes.
|
|
|
|
Importantly, it defines two distinct roles in the channel: that of the HOST and that of
|
|
the CLIENT. The CLIENT being the party who trusts the HOST with its funds when it requests
|
|
a hosted channel and receives payments into it.
|
|
|
|
### Channel Establishment
|
|
|
|
This is a diagram that illustrates the entire channel establishment process.
|
|
|
|
C is the CLIENT, H is the HOST.
|
|
|
|
+-------+ +-------+
|
|
| |--(1)--- invoke_hosted_channel -->| |
|
|
| |<-(2)--- init_hosted_channel -----| |
|
|
| C | | H |
|
|
| |--(3)------ state_update -------->| |
|
|
| |<-(4)------ state_update ---------| |
|
|
+-------+ +-------+
|
|
|
|
The following sections about the messages will detail this process further.
|
|
|
|
|
|
### The `invoke_hosted_channel` Message
|
|
|
|
This message is sent by the CLIENT when it wants to request a new hosted channel from a
|
|
HOST, but also every time it establishes a connection to the HOST, to activate that
|
|
channel and check if the two parties are in agreement with regards to the channel state.
|
|
|
|
1. type: 65535 (`invoke_hosted_channel`)
|
|
2. data:
|
|
* [`chain_hash`:`chain_hash`]
|
|
* [`u16`:`len`]
|
|
* [`len*byte`:`refund_scriptpubkey`]
|
|
* [`u16`:`len`]
|
|
* [`len*byte`:`secret`]
|
|
|
|
The `refund_scriptpubkey` is similar to `scriptpubkey` in [bolt02][bolt02-scriptpubkey]
|
|
and must be provided in case HOST wants to terminate the channel but cannot contact the
|
|
CLIENT.
|
|
|
|
The `secret` is an optional blob that can be used by HOST to tweak channel parameters
|
|
in multiple ways: offer an initial balance, tweak the capacity, only allow cliets with
|
|
a given secret etc.
|
|
|
|
Upon receiving an `invoke_hosted_channel` message, the HOST, if willing to establish the
|
|
channel, must reply with an `init_hosted_channel` message. If the CLIENT already has a
|
|
channel established, the HOST must instead reply with a `last_cross_signed_state`.
|
|
|
|
### The `init_hosted_channel` Message
|
|
|
|
This is sent by the HOST after a new CLIENT requests a channel using the
|
|
`invoke_hosted_channel` message.
|
|
|
|
1. type: 65533 (`init_hosted_channel`)
|
|
2. data:
|
|
* [`u64`:`max_htlc_value_in_flight_msat`]
|
|
* [`u64`:`htlc_minimum_msat`]
|
|
* [`u16`:`max_accepted_htlcs`]
|
|
* [`u64`:`channel_capacity_msat`]
|
|
* [`u64`:`initial_client_balance_msat`]
|
|
* [`u16`:`len`]
|
|
* [`len*byte`:`features`]
|
|
|
|
It contains the parameters of the hosted channel offered from HOST to CLIENT.
|
|
|
|
### The `last_cross_signed_state` Message
|
|
|
|
This message essentially represents the _state of the channel_ and the only
|
|
piece of data that must be stored by peers. It is different for each peer or rather
|
|
each peer sees the reverse of what the other peer sees, but it includes a signature
|
|
by each peer of the other peer's view, thus the name "cross-signed".
|
|
|
|
1. type: 65531 (`last_cross_signed_state`)
|
|
2. data:
|
|
* [`bool`: `is_host`]
|
|
* [`u16`:`len`]
|
|
* [`len*byte`:`last_refund_scriptpubkey`]
|
|
* [[`init_hosted_channel`](#the-init_hosted_channel-message):`init_hosted_channel`]
|
|
* [`u32`:`block_day`]
|
|
* [`u64`:`local_balance_msat`]
|
|
* [`u64`:`remote_balance_msat`]
|
|
* [`u32`:`local_updates`]
|
|
* [`u32`:`remote_updates`]
|
|
* [`u16`:`num_incoming_htlcs`]
|
|
* [`num_incoming_htlcs`*[`update_add_htlc`][update_add_htlc]:`incoming_htlcs`]
|
|
* [`u16`:`num_outgoing_htlcs`]
|
|
* [`num_outgoing_htlcs`*[`update_add_htlc`][update_add_htlc]:`outgoing_htlcs`]
|
|
* [`signature`:`remote_sig_of_local`]
|
|
* [`signature`:`local_sig_of_remote`]
|
|
|
|
The `is_host` property must be set to either `true` or `false` depending on the side
|
|
that is signing it.
|
|
|
|
The `block_day` is the current Bitcoin block height divided by 144 and rounded down.
|
|
|
|
The `last_refund_scriptpubkey` is the value of `refund_scriptpubkey` received in the
|
|
last `invoke_hosted_channel` sent by CLIENT.
|
|
|
|
`local_updates` and `remote_updates` are the number of updates each party has added to
|
|
the channel over the time, these numbers increase every time a peer sends an
|
|
`update_add_htlc`, `update_fail_htlc`, `update_fail_malformed_htlc` or `update_fulfill_htlc`.
|
|
|
|
Upon receiving this message the CLIENT must check the signatures and proceed as follows:
|
|
|
|
1. if the number of `local_updates` + `remote_updates` is smaller than what they have
|
|
stored, the CLIENT must ignore the message and send their own `last_cross_signed_state`.
|
|
In this case the HOST must accept the state as sent by the peer, **reverse** it and
|
|
override their own state with the state received from CLIENT.
|
|
2. if the number of updates is the same that means CLIENT and HOST are on agreement.
|
|
The CLIENT must still sent their `last_cross_signed_state` so the HOST will acknowledge
|
|
that and the channel can become "active".
|
|
3. if the number of updates is greater than what the CLIENT has stored, they must
|
|
**reverse** it and replace their local state with that, then send their local updated
|
|
`last_cross_signed_state` to HOST so they can acknowledge that. From the point of view
|
|
of the HOST there is no difference between cases 2 and 3.
|
|
|
|
If the signatures are bad the peer must send an [`error`](#the-error-message) and put
|
|
the channel in an "errored" state until it can be fixed manually.
|
|
|
|
To **reverse** a `last_cross_signed_state` means to see it as the channel peer would, i.e.
|
|
change `is_host` from `true` to `false` and vice-versa; to set `local_updates` to
|
|
`remote_updates` and vice-versa; to to set `local_balance_msat` to `remote_balance_msat`
|
|
and vice-versa, to set `incoming_htlcs` to `outgoing_htlcs` and vice-versa and finally
|
|
(although this part is not relevant when signing a `state_update`) to set
|
|
`local_sig_of_remote` to `remote_sig_of_local` and vice-versa.
|
|
|
|
### The `state_update` Message
|
|
|
|
After receiving an `init_hosted_channel` the CLIENT should check if it likes its params,
|
|
then send this message.
|
|
|
|
Upon receiving it, the HOST will then send its own `state_update` and from the two
|
|
`state_update`s
|
|
combined both parties should be able to construct their `last_cross_signed_state`
|
|
independently, which are the effective channel "state" that both parties must store and
|
|
keep track of.
|
|
|
|
1. type: 65529 (`state_update`)
|
|
2. data:
|
|
* [`u32`:`block_day`]
|
|
* [`u32`:`local_updates`]
|
|
* [`u32`:`remote_updates`]
|
|
* [`signature`:`local_sig_of_remote`]
|
|
|
|
The initial value for `local_updates` and `remote_updates` is `0`.
|
|
|
|
The `local_sig_of_remote` is a signature over the **reverse** of the current value for
|
|
`last_cross_signed_state` known to the signer, i.e. the peer's view of the channel
|
|
state. The calculation of the hash to be signed can be seen [in this piece of source code][hosted_sighash].
|
|
|
|
### Normal Operation
|
|
|
|
The normal channel operation consists in peers offering the default [bolt02][bolt02]
|
|
"update messages" to each other, `update_add_htlc`, `update_fail_htlc`,
|
|
`update_fail_malformed_htlc` and `update_fulfill_htlc`, then following them with a
|
|
`state_update` representing what will be the next state of the channel after the updates
|
|
are accepted by the peer.
|
|
|
|
The peer must respond with their own `state_update` after applying the received update
|
|
to their local state view.
|
|
|
|
There is no distinction between HOST and CLIENT here.
|
|
|
|
+-------+ +-------+
|
|
| |--(1)---- update_add_htlc ---->| |
|
|
| |--(2)---- update_add_htlc ---->| |
|
|
| |--(3)---- state_update ------->| |
|
|
| |<-(4)---- state_update --------| |
|
|
| | | |
|
|
| A |<-(5)-- update_fulfill_htlc ---| B |
|
|
| |<-(6)-- state_update ----------| |
|
|
| |--(7)-- state_update --------->| |
|
|
| | | |
|
|
| |<-(8)--- update_fail_htlc -----| |
|
|
| |<-(9)--- state_update ---------| |
|
|
| |-(10)--- state_update -------->| |
|
|
+-------+ +-------+
|
|
|
|
To construct the `state_update` message, for each update sent or received, the peers must
|
|
increase the `local_updates` or `remote_updates` count of their next (still uncommitted,
|
|
unsigned) state (`last_cross_signed_state`), change the balances accordingly, then sign the
|
|
**reverse** of that.
|
|
|
|
`state_update` messages with `local_updates` or `remote_updates` counts smaller than the
|
|
current view of a peer's next state must be ignored, and only when their counts and signature
|
|
matches a the receiver's current view of the next state that state can be considered
|
|
committed, i.e. cross-signed.
|
|
|
|
### The `update_add_htlc` Message
|
|
|
|
This is the same [`update_add_htlc` message][update_add_htlc] from bolt01,
|
|
but using the type `63505`.
|
|
|
|
### The `update_fulfill_htlc` Message
|
|
|
|
This is the same [`update_fulfill_htlc` message][update_fulfill_htlc] from bolt01,
|
|
but using the type `63503`.
|
|
|
|
### The `update_fail_htlc` Message
|
|
|
|
This is the same [`update_fail_htlc` message][update_fail_htlc] from bolt01,
|
|
but using the type `63501`.
|
|
|
|
### The `update_fail_malformed_htlc` Message
|
|
|
|
This is the same [`update_fail_malformed_htlc` message][update_fail_malformed_htlc] from bolt01,
|
|
but using the type `63499`.
|
|
|
|
### The `error` Message
|
|
|
|
This is the same [`error` message][error] from bolt02, but using the type `63497`.
|
|
|
|
### Dealing with problems
|
|
|
|
If a peer receives a preimage from a payment sent upstream, but can't contact their hosted
|
|
peer to send the `update_fulfill_htlc` and effectively update the state, they can opt to
|
|
publish the preimage to the Bitcoin chain in an `OP_RETURN` output. The output scriptPubKey
|
|
must take the form of either `OP_RETURN <32-byte-preimage>` or `OP_RETURN <32-byte-preimage>
|
|
<32-byte-preimage>` (for publishing two preimages in the same output). Peers should also
|
|
monitor the blockchain in search for preimages that may have been published for their
|
|
in-flight HTLCs, as the presence of these preimages onchain effectively proves the payment,
|
|
even if the peer hasn't sent an `update_fulfill_htlc` update through the normal means.
|
|
|
|
If an HTLC that is incoming through a hosted channel times out and there is no sight of its
|
|
preimage anywhere, it is considered failed, and the peer who is holding that must send an
|
|
[`error`](#the-error-message) and put the channel in an "errored" state until it can be
|
|
fixed manually.
|
|
|
|
When the state is "errored" it must not accept new updates _except_ `update_fail_htlc` and
|
|
`update_fulfill_htlc` for non-expired pending HTLCs, as good HTLCs may still be resolved
|
|
peacefully independent of the conflict caused by others.
|
|
|
|
After a channel reaches an "errored" state peers can agree through extra-protocol means to
|
|
reactivate it under a certain balance distribution. Once that happens the HOST must send
|
|
an `state_override` message and the CLIENT must accept it.
|
|
|
|
### The `state_override` Message
|
|
|
|
1. type: 65527 (`state_override`)
|
|
2. data:
|
|
* [`u32`:`block_day`]
|
|
* [`u32`:`local_updates`]
|
|
* [`u32`:`remote_updates`]
|
|
* [`signature`:`local_sig_of_remote`]
|
|
|
|
After receiving a `state_override`, at any point a CLIENT may send a `state_update` that
|
|
matches the `last_cross_signed_state` implied by the `state_override` parameters. That will
|
|
put the channel back in an active state.
|
|
|
|
### Optional branding
|
|
|
|
Channels can be "branded", i.e. they can have a URL for human contact, a color and an image
|
|
logo.
|
|
|
|
Upon establishing a connection to a HOST, a CLIENT can at anytime send a `ask_branding_info`
|
|
message and a HOST may or may not reply with a `hosted_channel_branding` message.
|
|
|
|
### The `ask_branding_info` Message
|
|
|
|
1. type: 65511 (`ask_branding_info`)
|
|
2. data:
|
|
* [`chain_hash`:`chain_hash`]
|
|
|
|
### The `hosted_channel_branding` Message
|
|
|
|
1. type: 65525 (`hosted_channel_branding`)
|
|
2. data:
|
|
* [`3*byte`:`rgb_color`]
|
|
* [`bool`:`has_png`]
|
|
* [`if has_png: u16`:`len`]
|
|
* [`len*byte`:`png_icon`]
|
|
* [`u16`:`len`]
|
|
* [`len*byte`:`contact_info`]
|
|
|
|
The `png_icon` must have at most 65535 bytes, and `contact_info` must be a valid URL.
|
|
|
|
## Rationale
|
|
|
|
The protocol for hosted channels was created based on real-world experience maintaining
|
|
the first standalone Lightning mobile wallet, [BLW][blw], and improved upon the
|
|
[initial version of the protocol][rfc1] that was deployed on that in 2019.
|
|
|
|
It introduces some new messages with numbers picked from the end of the range of
|
|
available numbers and a set of messages related to HTLC adding and removing that are
|
|
exactly like their BOLT equivalents, but wrapped so they don't interfere with the normal
|
|
protocol and can be implemented on plugins that allow custom messages to be sent and
|
|
received from nodes like Eclair, CLN and LND.
|
|
|
|
## Universality
|
|
|
|
Most nodes are probably not interested in using hosted channels, but that is fine since
|
|
for the protocol to be useful we only ever need two parties that are interested in
|
|
establishing such a channel to support it, and they connect to each other directly.
|
|
|
|
## Backwards Compatibility
|
|
|
|
This does not have backwards compatibility concerns.
|
|
|
|
## Reference Implementations
|
|
|
|
* Simple Bitcoin Wallet (client): <https://github.com/btcontract/wallet>
|
|
* Eclair Plugin (host): <https://github.com/btcontract/plugin-hosted-channels>
|
|
* IMMORTAN (client): <https://github.com/fiatjaf/immortan>
|
|
* Poncho (host): <https://github.com/fiatjaf/poncho>
|
|
* scoin (types and codecs): [https://github.com/fiatjaf/scoin](https://github.com/fiatjaf/scoin/blob/master/shared/src/main/scala/hc/HostedChannelCodecs.scala)
|
|
|
|
[blw]: https://archive.is/iMVJj
|
|
[rfc1]: https://github.com/btcontract/hosted-channels-rfc/tree/b9d53d5ec9e5238273cead742730cf15824ef48f
|
|
[bolt01]: https://github.com/lightning/bolts/blob/master/01-messaging.md
|
|
[bolt02]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md
|
|
[bolt02-scriptpubkey]: https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md#closing-initiation-shutdown
|
|
[hosted_sighash]: https://github.com/fiatjaf/scoin/blob/315be232a70e9a9caa648ac384b679e6eb563ad7/shared/src/main/scala/hc/HostedChannelMessages.scala#L77-L117
|
|
[error]: https://github.com/lightning/bolts/blob/master/01-messaging.md#the-error-and-warning-messages
|
|
[update_add_htlc]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#adding-an-htlc-update_add_htlc
|
|
[update_fulfill_htlc]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#removing-an-htlc-update_fulfill_htlc-update_fail_htlc-and-update_fail_malformed_htlc
|
|
[update_fail_htlc]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#removing-an-htlc-update_fulfill_htlc-update_fail_htlc-and-update_fail_malformed_htlc
|
|
[update_fail_malformed_htlc]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#removing-an-htlc-update_fulfill_htlc-update_fail_htlc-and-update_fail_malformed_htlc
|