diff --git a/README.md b/README.md index bded9af..bba6b55 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,4 @@ For more detail on the process, please read [bLIP-0001](./blip-0001.md) and | [3](./blip-0003.md) | Keysend | Valentine Wallace | Active | | [10](./blip-0010.md) | Podcasting 2.0 | Satoshis Stream | Active | | [11](./blip-0011.md) | NameDesc | Hampus Sjöberg | Active | +| [17](./blip-0017.md) | Hosted Channels | Anton Kumaigorodskiy | Active | diff --git a/blip-0002.md b/blip-0002.md index c8e8a48..1a709dc 100644 --- a/blip-0002.md +++ b/blip-0002.md @@ -43,9 +43,10 @@ Feature bits in the `0`-`255` range are reserved for BOLTs: bLIPs must use featu bLIPs may reserve feature bits by adding them to the following table: -| Bits | Name | Description | Context | Dependencies | Link | -|---------|----------------------|-------------------------------------------------|----------------|--------------------------------|--------------------------------| -| 54/55 | `keysend` | A form of spontaneous payment | N | `var_onion_optin` | [bLIP 3](./blip-0003.md) | +| Bits | Name | Description | Context | Dependencies | Link | +| --------- | ---------------------- | ------------------------------------------------- | ---------------- | -------------------------------- | -------------------------------- | +| 54/55 | `keysend` | A form of spontaneous payment | N | `var_onion_optin` | [bLIP 3](./blip-0003.md) | +| 256/257 | `hosted_channels` | This node accepts requests for hosted channels | IN | | [bLIP 17](./blip-0017.md) | ### Messages @@ -55,9 +56,20 @@ that every lightning implementation understands. Message types in the `0`-`32767` range are reserved for BOLTs: bLIPs must use message types in the `32768`-`65535` range. bLIPs may create new messages and reserve their type in the following table: -| Type | Name | Link | -|-------|-----------------------------|--------------------------------| -| 32768 | `message_name` | Link to the corresponding bLIP | +| Type | Name | Link | +| ------- | ------------------------------- | -------------------------- | +| 65535 | `invoke_hosted_channel` | [bLIP 17](./blip-0017.md) | +| 65533 | `init_hosted_channel` | [bLIP 17](./blip-0017.md) | +| 65531 | `last_cross_signed_state` | [bLIP 17](./blip-0017.md) | +| 65529 | `state_update` | [bLIP 17](./blip-0017.md) | +| 65527 | `state_override` | [bLIP 17](./blip-0017.md) | +| 65525 | `hosted_channel_branding` | [bLIP 17](./blip-0017.md) | +| 65511 | `ask_channel_branding` | [bLIP 17](./blip-0017.md) | +| 63505 | `hc_update_add_htlc` | [bLIP 17](./blip-0017.md) | +| 63503 | `hc_update_fulfill_htlc` | [bLIP 17](./blip-0017.md) | +| 63501 | `hc_updated_fail_htlc` | [bLIP 17](./blip-0017.md) | +| 63499 | `hc_update_fail_malformed_htlc` | [bLIP 17](./blip-0017.md) | +| 63497 | `hc_error` | [bLIP 17](./blip-0017.md) | ### TLV fields in BOLT messages diff --git a/blip-0017.md b/blip-0017.md new file mode 100644 index 0000000..443756e --- /dev/null +++ b/blip-0017.md @@ -0,0 +1,366 @@ +``` +bLIP: 0017 +Title: Hosted Channels +Status: Active +Author: Anton Kumaigorodski + fiatjaf +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): +* Eclair Plugin (host): +* IMMORTAN (client): +* Poncho (host): +* 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