Skip to content

Latest commit

 

History

History
366 lines (278 loc) · 16.5 KB

blip-0017.md

File metadata and controls

366 lines (278 loc) · 16.5 KB
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) 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 connections and mimics 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 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 clients 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: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:incoming_htlcs]
  • [u16:num_outgoing_htlcs]
  • [num_outgoing_htlcs*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 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_updates 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.

Normal Operation

The normal channel operation consists in peers offering the default 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 from bolt01, but using the type 63505.

The update_fulfill_htlc Message

This is the same update_fulfill_htlc message from bolt01, but using the type 63503.

The update_fail_htlc Message

This is the same update_fail_htlc message from bolt01, but using the type 63501.

The update_fail_malformed_htlc Message

This is the same update_fail_malformed_htlc message from bolt01, but using the type 63499.

The error Message

This is the same error message 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 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, and improved upon the initial version of the protocol 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