<oembed><type>rich</type><version>1.0</version><title>Rusty Russell [ARCHIVE] wrote</title><author_name>Rusty Russell [ARCHIVE] (npub1zw…hkhpx)</author_name><author_url>https://yabu.me/npub1zw7cc8z78v6s3grujfvcv3ckpvg6kr0w7nz9yzvwyglyg0qu5sjsqhkhpx</author_url><provider_name>njump</provider_name><provider_url>https://yabu.me</provider_url><html>📅 Original date posted:2019-11-05&#xA;📝 Original message:&#xA;Hi all,&#xA;&#xA;        This took longer than I&#39;d indicated; for that I&#39;m sorry.&#xA;However, this should give us all something to chew on.  I&#39;ve started&#xA;with a draft &#34;BOLT 12&#34; (it might be BOLT 13 by the time it&#39;s finalized&#xA;though!).&#xA;&#xA;I&#39;ve also appended indications where we touch other BOLTs:&#xA;1. BOLT 7 gains a message/reply system, encoded like htlc onions and&#xA;   failure messages.&#xA;2. BOLT 11 gains a `q` field for quantity; this avoids changing the&#xA;   description when the user requests an invoice for more than one of something&#xA;   (since changing the description between offer and invoice requires user&#xA;   interaction: it&#39;s the *invoice* which you are committing to).&#xA;&#xA;There&#39;s definite handwaving in here; let&#39;s see if you can find it!&#xA;&#xA;Cheers,&#xA;Rusty.&#xA;&#xA;# BOLT #12: Offer Protocols for Lightning Payments&#xA;&#xA;An higher-level, QR-code-ready protocol for dealing with invoices over&#xA;Lightning.  There are two simple flows supported: in one, a user gets&#xA;an offer (`lno...`) and requests an invoice over the lightning&#xA;network, obtaining one (or an error) in reply.  In the other, a user&#xA;gets an invoice request (`lni...`), and sends the invoice over the&#xA;lightning network, retreiving an empty reply.&#xA;&#xA;# Table of Contents&#xA;&#xA;  * [Offers](#offers)&#xA;    * [Encoding](#encoding)&#xA;    * [TLV Fields](#tlv-fields)&#xA;  * [Invrequests](#invrequests)&#xA;    * [Encoding](#encoding)&#xA;    * [TLV Fields](#tlv-fields)&#xA;&#xA;# Offers&#xA;&#xA;Offers supply a reader with enough information to request one or more&#xA;invoices via the lightning network itself.&#xA;&#xA;## Encoding&#xA;&#xA;The human-readable part of a Lightning offer is `lno`.  The data part&#xA;consists of three parts:&#xA;&#xA;1. 0 or more [TLV](01-messaging.md#type-length-value-format) encoded fields.&#xA;2. A 32-byte nodeid[1]&#xA;3. 64-byte signature of SHA256(hrp-as-utf8 | tlv | nodeid).&#xA;&#xA;## TLV Fields&#xA;&#xA;The TLV fields define how to get the invoice, and what it&#39;s for.&#xA;Each offer has a unique `offer_idenfitier` so the offering node can&#xA;distinguish different invoice requests.&#xA;&#xA;Offers can request recurring payments of various kinds, and specify&#xA;what base currency they are calculated in (the actual amount will be&#xA;in the invoice).&#xA;&#xA;`additional_data` is a bitfield which indicates what information the&#xA;invoice requester should (odd) or must (even) supply:&#xA;1. Bits `0/1`: include `delivery_address`&#xA;2. Bits `2/3`: include `delivery_telephone_number`&#xA;3. Bits `4/5`: include `voucher_code`&#xA;4. Bits `6/7`: include `refund_proof`&#xA;&#xA;`refund_for` indicates an offer for a (whole or part) refund for a&#xA;previous invoice, as indicated by the `payment_hash`.&#xA;&#xA;1. tlvs: `offer`&#xA;2. types:&#xA;    1. type: 1 (`paths`)&#xA;    2. data:&#xA;        * [`u16`:`num_paths`]&#xA;        * [`num_paths*path`:`path`]&#xA;    1. type: 2 (`description`)&#xA;    2. data:&#xA;        * [`...*byte`:`description`]&#xA;    1. type: 3 (`expiry`)&#xA;    2. data:&#xA;        * [`tu64`:`seconds_from_epoch`]&#xA;    1. type: 4 (`offer_identifier`)&#xA;    2. data:&#xA;        * [`...*byte`:`id`]&#xA;    1. type: 5 (`amount`)&#xA;    2. data:&#xA;        * [`4*byte`:`currency`]&#xA;        * [`tu64`:`amount`]&#xA;    1. type: 6 (`additional_data`)&#xA;    2. data:&#xA;&#x9;    * [`...*byte`:`rbits`]&#xA;    1. type: 7 (`recurrance`)&#xA;    2. data:&#xA;&#x9;    * [`byte`:`time_unit`]&#xA;&#x9;&#x9;* [`u32`:`period`]&#xA;&#x9;&#x9;* [`tu32`:`number`]&#xA;    1. type: 8 (`recurrance_base`)&#xA;    2. data:&#xA;&#x9;&#x9;* [`u32`:`basetime`]&#xA;&#x9;&#x9;* [`tu32`:`paywindow`]&#xA;    1. type: 9 (`quantity`)&#xA;    2. data:&#xA;&#x9;&#x9;* [`tu64`:`max`]&#xA;    1. type: 10 (`refund_for`)&#xA;    2. data:&#xA;        * [`32*byte`:`payment_hash`]&#xA;&#xA;1. subtype: `path`&#xA;2. data:&#xA;   * [`u16`:`num_hops`]&#xA;   * [`num_hops*hop`:`hops`]&#xA;&#xA;1. subtype: `hop`&#xA;2. data:&#xA;   * [`pubkey`:`nodeid`]&#xA;   * [`short_channel_id`:`short_channel_id`]&#xA;   * [`u16`:`flen`]&#xA;   * [`flen*byte`:`features`]&#xA;&#xA;## Requirements For Offers And Invrequests&#xA;&#xA;A writer of an offer or an invrequest:&#xA;  - if it is connected only by private channels:&#xA;    - MUST include `paths` containing a path to the node.&#xA;  - otherwise:&#xA;    - MAY include `paths` containing a path to the node.&#xA;  - MUST describe the item(s) being offered or purpose of invoice in `description`.&#xA;  - MUST include `expiry` if the offer/invrequest will not be valid after some time.&#xA;  - if it includes `expiry`:&#xA;    - MUST set `seconds_from_epoch` to the expiry time in seconds since 1970 UTC.&#xA;&#xA;## Requirements For Offers&#xA;&#xA;A writer of an offer:&#xA;  - MUST use a unique `offer_idenfitier` for each offer.&#xA;  - MAY include `recurrence` to indicate offer should trigger time-spaced&#xA;    invoices.&#xA;  - MUST include `amount` if it includes `recurrence`.&#xA;  - if it includes `amount`:&#xA;    - MUST specify `currency` as the ISO 4712 or BIP-0173, padded with zero bytes if required&#xA;&#x9;- MUST specify `amount` to the amount expected for the invoice, as the ISO 4712 currency unit multiplied by exponent, OR the BIP-0173 minimum unit (eg. `satoshis`).&#xA;  - if it requires specific fields in the invoice:&#xA;    - MUST set the corresponding even bits in the `additional_data` field&#xA;&#xA;A reader of an offer:&#xA;  - SHOULD gain user consent for recurring payments.&#xA;  - SHOULD allow user to view and cancel recurring payments.&#xA;  - SHOULD gain user consent to send `delivery_` fields.&#xA;  - if it uses `amount` to provide the user with a cost estimate:&#xA;&#x9;- MUST warn user if amount of actual invoice differs significantly&#xA;&#x9;&#x9;from that expectation.&#xA;  - FIXME: more!&#xA;&#xA;## Recurrance&#xA;&#xA;Some offers are *periodic*, such as a subscription service or monthly&#xA;dues, in that payment is expected to be repeated.  There are many&#xA;different flavors of repetition, consider:&#xA;&#xA;* Payments due on the first of every month, for 6 months.&#xA;* Payments due on every Monday, 1pm Pacific Standard Time.&#xA;* Payments due once a year:&#xA;   * which must be made on January 1st, or&#xA;   * which are only valid if started January 1st 2020, or&#xA;   * which if paid after January 1st you (over) pay the full rate first year, or&#xA;   * which if paid after January 1st are paid pro-rata for the first year, or&#xA;   * which repeat from whenever you made the first payment&#xA;&#xA;Thus, each payment has:&#xA;1. A `time_unit` defining 0 (seconds), 1 (days), 2 (months), 3 (years).&#xA;2. A `period`, defining how often (in `time_unit`) it has to be paid.&#xA;3. An optional `number` of total payments to be paid.&#xA;4. An optional `basetime`, defining when the first payment applies&#xA;   in seconds since 1970-01-01 UTC.&#xA;5. An optional `paywindow`, defining how many seconds into the period&#xA;   a payment will be accepted: 0xFFFFFFFF being a special value meaning&#xA;   &#34;any time during the period, but you will have to pay proportionally&#xA;   to the remaining time in the period&#34;.&#xA;&#xA;Note that the `expiry` field covers the case where an offer is no longer&#xA;valid after January 1st 2020.&#xA;&#xA;## Default Offer&#xA;&#xA;The &#34;default offer&#34; of a node is a nominal offer used to send&#xA;unsolicited payments.  It is generally not actually sent, but can be&#xA;used by any other node as if it has been.  It has the following&#xA;fields:&#xA;&#xA;* `offer_idenfitier`: zero-length&#xA;* `d`: any&#xA;* `n`: the node id of the recipient.&#xA;&#xA;## Invoice Request Encoding&#xA;&#xA;Once it has an offer, the node can request an actual invoice using the&#xA;`invoice_req` message inside `directed`&#39;s `onion_routing_packet`.  It&#xA;would expect an `invoice_or_error_tlv` inside the `directed_reply`&#xA;message.&#xA;&#xA;This includes a `tag` it can use to identify replies, the&#xA;`offer_idenfitier` from the offer, a `key` it can use to prove it was&#xA;the requester of this invoice, a `recurrence` number if this&#xA;is a payment in a recurring series, and other codes as required.&#xA;&#xA;The `refund_proof` refers to a previous invoice paid by the sender for&#xA;the specific case of a `refund_for` offer.  It provides proof of&#xA;payment (the `payment_preimage` and also a signature of the&#xA;`payment_hash` from the `key` which requested the being-refunded&#xA;invoice (which does *not* have to be the same as this `key`!).&#xA;&#xA;1. tlvs: `invoice_request_tlv`&#xA;2. types:&#xA;   1. type: 1 (`tag`)&#xA;   2. data:&#xA;      * [`...*byte`:`tag`]&#xA;   1. type: 2 (`offer_identifier`)&#xA;   2. data:&#xA;      * [`...*byte`:`id`]&#xA;   1. type: 3 (`key`)&#xA;   2. data:&#xA;     * [`32`:`key`]&#xA;   1. type: 4 (`recurrence`)&#xA;   2. data:&#xA;     * [`tu64`:`number`]&#xA;   1. type: 5 (`quantity`)&#xA;   2. data:&#xA;     * [`tu64`:`n`]&#xA;   1. type: 6 (`delivery_address_name`)&#xA;   2. data:&#xA;&#x9; * [`...*byte`:`name`]&#xA;   1. type: 7 (`delivery_address1`)&#xA;   2. data:&#xA;&#x9; * [`...*byte`:`address1`]&#xA;   1. type: 8 (`delivery_address2`)&#xA;   2. data:&#xA;&#x9; * [`...*byte`:`address2`]&#xA;   1. type: 9 (`delivery_city`)&#xA;   2. data:&#xA;&#x9; * [`...*byte`:`city`]&#xA;   1. type: 10 (`delivery_state_province_or_region`)&#xA;   2. data:&#xA;&#x9; * [`...*byte`:`state_province_or_region`]&#xA;   1. type: 11 (`delivery_zip_or_postal_code`)&#xA;   2. data:&#xA;&#x9; * [`...*byte`:`zip_or_postal_code`]&#xA;   1. type: 12 (`delivery_country`)&#xA;   2. data:&#xA;&#x9; * [`2*byte`:`country_code`]&#xA;   1. type: 13 (`delivery_telephone_number`)&#xA;   2. data:&#xA;&#x9; * [`...*byte`:`tel`]&#xA;   1. type: 14 (`voucher_code`)&#xA;   2. data:&#xA;&#x9; * [`...*byte`:`code`]&#xA;   1. type: 15 (`refund_proof`)&#xA;   2. data:&#xA;      * [`32*byte`:`payment_preimage`]&#xA;      * [`signature`:`signature`]&#xA;&#xA;## Requirements&#xA;&#xA;FIXME: many more&#xA;Sender MUST use ISO 3166 alpha-2 code for `delivery_country`.&#xA;Sender MUST set offer_identifier to match offer.&#xA;Sender MUST include `key`&#xA;   - SHOULD use a transient unpredictable key&#xA;   - MUST reuse key for successive recurring invoices.&#xA;Sender MUST set `recurrence` for recurring invoices.&#xA;&#xA;Receiver MUST check `offer_identifier`&#xA;Receiver MUST check `delivery_` fields.&#xA;Receiver MUST check `recurrence`.&#xA;Receiver MUST check `amount`.&#xA;&#xA;1. tlvs: `invoice_or_error_tlv`&#xA;2. types:&#xA;   1. type: 1 (`tag`)&#xA;   2. data:&#xA;     * [`...*byte`:`tag`]&#xA;   1. type: 3 (`omitted`)&#xA;   2. data:&#xA;&#x9; * [`...*u64`:`omitted_fields`]&#xA;   1. type: 4 (`invoice`)&#xA;   2. data:&#xA;     * [`...*byte`:`invoice`]&#xA;   1. type: 5 (`message`)&#xA;     * [`...*byte`:`message`]&#xA;   1. type: 6 (`replacement`)&#xA;   2. data:&#xA;     * [`signature`:`signature`]&#xA;     * [`...*byte`:`offer`]&#xA;&#xA;Sender:&#xA;- MUST copy `tag` from sender.&#xA;- MUST omit fields it does not use, and place number in order in `omitted_fields`.&#xA;- if it includes `invoice`:&#xA;  - MUST not include `message`&#xA;  - MUST not include `replacement`&#xA;  - MUST [merkle fields it used](#merkle-calculation) and place that in invoice `s` field.&#xA;- otherwise, if it includes `replacement`:&#xA;  - MAY include `message` &#xA;- otherwise:&#xA;  - MUST include `message` describing the error.&#xA;&#xA;Receiver:&#xA;- MUST check that `tag` matches req.&#xA;- if `replacement`:&#xA;  - MUST fail if `signature` does not sign `offer` with same key as original.&#xA;  - MUST only fetch once (no double-redirects!)&#xA;- if description or amount significantly changes, must re-ask user.&#xA;  - SHOULD note if description simply has something appended (eg &#34;+ postage&#34;).&#xA;- within invoice:&#xA;    - MUST check that `s` matches merkle of fields, minus `omitted`.&#xA;&#x9;- MUST check that no vital fields are in `omitted`.&#xA;&#x9;- MUST check that `d` matches `description`&#xA;    - MUST check that `q` DNE if `quantity` DNE, otherwise is equal.&#xA;    - Must check valid signature, etc.&#xA;&#xA;## Merkle Calculation&#xA;&#xA;1. For each `invoice_req_tlv` field in ascending `tlv` type order:&#xA;   1. If the field was omitted, it is added to `omitted_fields`.&#xA;   2. Otherwise, the immediate parent merkle is:&#xA;   &#xA;      SHA256(SHA256(`tag` | `be64-n`) | SHA256(`tlv-value`))&#xA;&#xA;      Where `be64-n` is a 64-bit big-endian counter starting at 0 and&#xA;      incrementing for each leaf.&#xA;&#xA;   3. Order these nodes in increasing SHA256(`tag` | `be64-n`) order.&#xA;&#xA;2. Create additional leaves until `be64-n` is the next power of 2:&#xA;&#xA;      SHA256(`tag` | `be64-n`)&#xA;&#xA;3. Combine adjacent leaves using SHA256(leaf1 | leaf2) until none remain.&#xA;&#xA;By creating adjacent leaves using the `tag` field and a counter, and&#xA;sorting the leaves, the only significant information revealed by a&#xA;merkle proof on a node is the depth of tree (which implies the total&#xA;number of TLV fields).&#xA;&#xA;# InvRequests&#xA;&#xA;There are times when it makes sense to request an invoice over another&#xA;medium, such as HTTP or a QR code.&#xA;&#xA;## Encoding&#xA;&#xA;The human-readable part of a Lightning invrequest is `lni`.  The data part&#xA;consists of three parts:&#xA;&#xA;1. 0 or more [TLV](01-messaging.md#type-length-value-format) encoded fields.&#xA;2. A 32-byte nodeid[1]&#xA;3. 64-byte signature of SHA256(hrp-as-utf8 | tlv | nodeid).&#xA;&#xA;## TLV Fields&#xA;&#xA;1. tlvs: `invreq`&#xA;2. types:&#xA;    1. type: 1 (`paths`)&#xA;    2. data:&#xA;        * [`u16`:`num_paths`]&#xA;        * [`num_paths*path`:`path`]&#xA;    1. type: 2 (`description`)&#xA;    2. data:&#xA;        * [`...*byte`:`description`]&#xA;    1. type: 3 (`expiry`)&#xA;    2. data:&#xA;        * [`tu64`:`seconds_from_epoch`]&#xA;    1. type: 4 (`amount`)&#xA;    2. data:&#xA;        * [`tu64`:`millisatoshis`]&#xA;&#xA;The fields `paths`, `description`, and `expiry` fields are the&#xA;same as those for offers; the optional `amount` field describes the&#xA;amount an invoice will be accepted for.&#xA;&#xA;Upon parsing and accepting an `invreq`, the node sends an&#xA;`invoice_or_error_tlv` within an onion.  The reply is empty.&#xA;&#xA;## Requirements&#xA;&#xA;The requirements for `paths`, `description` and `expiry` are&#xA;[described above](#requirements-for-offers-and-invrequests). &#xA;&#xA;FIXME: More.&#xA;&#xA;[1] Assuming we go for Schnorr sigs and 32-byte pubkeys.&#xA;----&#xA;Addendum: BOLT #7: P2P Node and Channel Discovery and Directed Messages&#xA;...&#xA;# Directed Messages&#xA;&#xA;Directed messages allow peers to use existing connections to query for&#xA;invoices (see [BOLT 12](12-offer-encoding.md)).  Like gossip messages,&#xA;they are not associated with a particular local channel.&#xA;&#xA;The `id` is a unique, transient identifier between the peers, used to&#xA;identify match messages and replies.&#xA;&#xA;## The `directed` and `directed_reply` Messages&#xA;&#xA;1. type: 384 (`directed`) (`option_directed_messages`)&#xA;2. data:&#xA;    * [`chain_hash`:`chain_hash`]&#xA;    * [`u64`:`id`]&#xA;    * [`1366*byte`:`onion_routing_packet`]&#xA;&#xA;1. type: 384 (`directed_reply`) (`option_directed_messages`)&#xA;2. data:&#xA;    * [`chain_hash`:`chain_hash`]&#xA;    * [`u64`:`id`]&#xA;    * [`u16`:`len`]&#xA;    * [`len*byte`:`reply`]&#xA;&#xA;## Requirements&#xA;&#xA;FIXME: similar to update_add_htlc and update_fail_htlc.&#xA;FIXME: define reasonable timeout after which you can forget if not replied?</html></oembed>