<oembed><type>rich</type><version>1.0</version><title>ZmnSCPxj [ARCHIVE] wrote</title><author_name>ZmnSCPxj [ARCHIVE] (npub1g5…3ms3l)</author_name><author_url>https://yabu.me/npub1g5zswf6y48f7fy90jf3tlcuwdmjn8znhzaa4vkmtxaeskca8hpss23ms3l</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;Good morning Rusty, and list,&#xA;&#xA;Thank you Rusty.&#xA;Below is a quick and dirty review; I doubt I found whatever handwaves you are using.&#xA;&#xA;First, please confirm my understanding of the message flow.&#xA;Suppose I have a donation offer on my website and Rusty wants to donate to me.&#xA;Then:&#xA;&#xA;         ZmnSCPxj                      Rusty&#xA;            |                            |&#xA;            +---------- `lno` ----------&gt;+ (via non-Lightning communication channel e.g. https)&#xA;            |                            |&#xA;            +&lt;---- `invoice_request` ----+ (via a normal Rusty-&gt;ZmnSCPxj payment)&#xA;            |                            |&#xA;            +---- `invoice_or_error` ---&gt;| (by failing the above payment and embedding in the failure blob)&#xA;            |                            |&#xA;            +&lt;------- `sendpay` ---------+ (via a normal Rusty-&gt;ZmnSCPxj payment)&#xA;&#xA;Is it approximately correct?&#xA;&#xA;&gt;&#xA;&gt; BOLT #12: Offer Protocols for Lightning Payments&#xA;&gt;&#xA;&gt; =================================================&#xA;&gt;&#xA;&gt; An higher-level, QR-code-ready protocol for dealing with invoices over&#xA;&gt; Lightning. There are two simple flows supported: in one, a user gets&#xA;&gt; an offer (`lno...`) and requests an invoice over the lightning&#xA;&gt; network, obtaining one (or an error) in reply. In the other, a user&#xA;&gt; gets an invoice request (`lni...`), and sends the invoice over the&#xA;&gt; lightning network, retreiving an empty reply.&#xA;&#xA;Here are completely pointless counterproposals for the offer and invoice-request HRPs:&#xA;&#xA;* Offers:&#xA;  * `lnpayme`&#xA;  * `lnbuyit`&#xA;  * `lnforsale`&#xA;* Invoice Requests:&#xA;  * `lnpaying`&#xA;  * `lnbuying`&#xA;  * `lnshutupandtakemymoney`&#xA;&#xA;`lno` and `lni` feel wrong to me.&#xA;Their juxtaposition implies `lno` == output and `lni` == input to me, due to the use of `o` and `i`, though `lno` is where you get money in exchange for product and `lni` is the request-for-service.&#xA;&#xA;&gt;&#xA;&gt; Table of Contents&#xA;&gt;&#xA;&gt; ==================&#xA;&gt;&#xA;&gt; -   Offers&#xA;&gt;     -   Encoding&#xA;&gt;     -   TLV Fields&#xA;&gt; -   Invrequests&#xA;&gt;     -   Encoding&#xA;&gt;     -   TLV Fields&#xA;&#xA;Definite handwave, does not match the structure of the document *at all*.&#xA;&#xA;&gt; Encoding&#xA;&gt;&#xA;&gt; ---------&#xA;&gt;&#xA;&gt; The human-readable part of a Lightning offer is`lno`. The data part&#xA;&gt; consists of three parts:&#xA;&gt;&#xA;&gt; 1.  0 or more TLV encoded fields.&#xA;&gt; 2.  A 32-byte nodeid[1]&#xA;&gt; 3.  64-byte signature of SHA256(hrp-as-utf8 | tlv | nodeid).&#xA;&gt;&#xA;&gt; TLV Fields&#xA;&gt;&#xA;&gt; -----------&#xA;&gt;&#xA;&gt; The TLV fields define how to get the invoice, and what it&#39;s for.&#xA;&gt; Each offer has a unique `offer_idenfitier` so the offering node can&#xA;&gt; distinguish different invoice requests.&#xA;&gt;&#xA;&gt; Offers can request recurring payments of various kinds, and specify&#xA;&gt; what base currency they are calculated in (the actual amount will be&#xA;&gt; in the invoice).&#xA;&gt;&#xA;&gt; `additional_data` is a bitfield which indicates what information the&#xA;&gt; invoice requester should (odd) or must (even) supply:&#xA;&gt;&#xA;&gt; 1.  Bits `0/1`: include `delivery_address`&#xA;&gt; 2.  Bits `2/3`: include `delivery_telephone_number`&#xA;&gt; 3.  Bits `4/5`: include `voucher_code`&#xA;&gt; 4.  Bits `6/7`: include `refund_proof`&#xA;&gt;&#xA;&gt;     `refund_for` indicates an offer for a (whole or part) refund for a&#xA;&gt;     previous invoice, as indicated by the `payment_hash`.&#xA;&gt;&#xA;&gt; 5.  tlvs: `offer`&#xA;&gt; 6.  types:&#xA;&gt;     1.  type: 1 (`paths`)&#xA;&gt;     2.  data:&#xA;&gt;         -   [`u16`:`num_paths`]&#xA;&gt;         -   [`num_paths*path`:`path`]&#xA;&gt;     3.  type: 2 (`description`)&#xA;&gt;     4.  data:&#xA;&gt;         -   [`...*byte`:`description`]&#xA;&#xA;UTF-8?&#xA;Null-terminated?&#xA;&#xA;&gt;     5.  type: 3 (`expiry`)&#xA;&gt;     6.  data:&#xA;&gt;         -   [`tu64`:`seconds_from_epoch`]&#xA;&gt;     7.  type: 4 (`offer_identifier`)&#xA;&gt;     8.  data:&#xA;&gt;         -   [`...*byte`:`id`]&#xA;&gt;     9.  type: 5 (`amount`)&#xA;&gt;     10.  data:&#xA;&gt;         -   [`4*byte`:`currency`]&#xA;&gt;         -   [`tu64`:`amount`]&#xA;&gt;     11.  type: 6 (`additional_data`)&#xA;&gt;     12.  data:&#xA;&gt;         -   [`...*byte`:`rbits`]&#xA;&gt;     13.  type: 7 (`recurrance`)&#xA;&gt;     14.  data:&#xA;&gt;         -   [`byte`:`time_unit`]&#xA;&gt;         -   [`u32`:`period`]&#xA;&gt;         -   [`tu32`:`number`]&#xA;&gt;     15.  type: 8 (`recurrance_base`)&#xA;&gt;     16.  data:&#xA;&gt;         -   [`u32`:`basetime`]&#xA;&gt;         -   [`tu32`:`paywindow`]&#xA;&gt;     17.  type: 9 (`quantity`)&#xA;&gt;     18.  data:&#xA;&gt;         -   [`tu64`:`max`]&#xA;&gt;     19.  type: 10 (`refund_for`)&#xA;&gt;     20.  data:&#xA;&gt;         -   [`32*byte`:`payment_hash`]&#xA;&gt; 7.  subtype: `path`&#xA;&gt; 8.  data:&#xA;&gt;     -   [`u16`:`num_hops`]&#xA;&gt;     -   [`num_hops*hop`:`hops`]&#xA;&gt; 9.  subtype: `hop`&#xA;&gt; 10.  data:&#xA;&gt;     -   [`pubkey`:`nodeid`]&#xA;&gt;     -   [`short_channel_id`:`short_channel_id`]&#xA;&gt;     -   [`u16`:`flen`]&#xA;&gt;     -   [`flen*byte`:`features`]&#xA;&gt;&#xA;&gt; Requirements For Offers And Invrequests&#xA;&gt;&#xA;&gt; ----------------------------------------&#xA;&gt;&#xA;&gt; A writer of an offer or an invrequest:&#xA;&gt;&#xA;&gt; -   if it is connected only by private channels:&#xA;&gt;     -   MUST include `paths` containing a path to the node.&#xA;&gt; -   otherwise:&#xA;&gt;     -   MAY include `paths` containing a path to the node.&#xA;&gt; -   MUST describe the item(s) being offered or purpose of invoice in `description`.&#xA;&gt; -   MUST include `expiry` if the offer/invrequest will not be valid after some time.&#xA;&gt; -   if it includes `expiry`:&#xA;&gt;     -   MUST set `seconds_from_epoch` to the expiry time in seconds since 1970 UTC.&#xA;&gt;&#xA;&gt; Requirements For Offers&#xA;&gt;&#xA;&gt; ------------------------&#xA;&gt;&#xA;&gt; A writer of an offer:&#xA;&gt;&#xA;&gt; -   MUST use a unique `offer_idenfitier` for each offer.&#xA;&gt; -   MAY include `recurrence` to indicate offer should trigger time-spaced&#xA;&gt;     invoices.&#xA;&gt;&#xA;&gt; -   MUST include `amount` if it includes `recurrence`.&#xA;&gt; -   if it includes `amount`:&#xA;&gt;     -   MUST specify `currency` as the ISO 4712 or BIP-0173, padded with zero bytes if required&#xA;&#xA;I cannot find ISO 4712, but could find ISO 4217.&#xA;BIP-173 does not have a list of currencies, but refers to SLIP-0173.&#xA;Some of the listed currencies there seem to have more than 4 characters.&#xA;&#xA;Should I assume encoding is ASCII?&#xA;We will &#34;never&#34; see a non-ASCII currency code?&#xA;&#xA;&gt;     -   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;&gt; -   if it requires specific fields in the invoice:&#xA;&gt;     -   MUST set the corresponding even bits in the `additional_data` field&#xA;&gt;&#xA;&gt;         A reader of an offer:&#xA;&gt;&#xA;&gt; -   SHOULD gain user consent for recurring payments.&#xA;&gt; -   SHOULD allow user to view and cancel recurring payments.&#xA;&gt; -   SHOULD gain user consent to send `delivery_` fields.&#xA;&gt; -   if it uses `amount` to provide the user with a cost estimate:&#xA;&gt;     -   MUST warn user if amount of actual invoice differs significantly&#xA;&gt;         from that expectation.&#xA;&gt;&#xA;&gt; -   FIXME: more!&#xA;&gt;&#xA;&gt; Recurrance&#xA;&gt;&#xA;&gt; -----------&#xA;&gt;&#xA;&gt; Some offers areperiodic, such as a subscription service or monthly&#xA;&gt; dues, in that payment is expected to be repeated. There are many&#xA;&gt; different flavors of repetition, consider:&#xA;&gt;&#xA;&gt; -   Payments due on the first of every month, for 6 months.&#xA;&gt; -   Payments due on every Monday, 1pm Pacific Standard Time.&#xA;&gt; -   Payments due once a year:&#xA;&gt;     -   which must be made on January 1st, or&#xA;&gt;     -   which are only valid if started January 1st 2020, or&#xA;&gt;     -   which if paid after January 1st you (over) pay the full rate first year, or&#xA;&gt;     -   which if paid after January 1st are paid pro-rata for the first year, or&#xA;&gt;     -   which repeat from whenever you made the first payment&#xA;&gt;&#xA;&gt;         Thus, each payment has:&#xA;&gt;&#xA;&gt;&#xA;&gt; 1.  A `time_unit` defining 0 (seconds), 1 (days), 2 (months), 3 (years).&#xA;&gt; 2.  A `period`, defining how often (in `time_unit`) it has to be paid.&#xA;&gt; 3.  An optional `number` of total payments to be paid.&#xA;&gt; 4.  An optional `basetime`, defining when the first payment applies&#xA;&gt;     in seconds since 1970-01-01 UTC.&#xA;&gt;&#xA;&gt; 5.  An optional `paywindow`, defining how many seconds into the period&#xA;&gt;     a payment will be accepted: 0xFFFFFFFF being a special value meaning&#xA;&gt;     &#34;any time during the period, but you will have to pay proportionally&#xA;&gt;     to the remaining time in the period&#34;.&#xA;&gt;&#xA;&gt;     Note that the `expiry` field covers the case where an offer is no longer&#xA;&gt;     valid after January 1st 2020.&#xA;&gt;&#xA;&gt;&#xA;&gt; Default Offer&#xA;&gt;&#xA;&gt; --------------&#xA;&gt;&#xA;&gt; The &#34;default offer&#34; of a node is a nominal offer used to send&#xA;&gt; unsolicited payments. It is generally not actually sent, but can be&#xA;&gt; used by any other node as if it has been. It has the following&#xA;&gt; fields:&#xA;&gt;&#xA;&gt; -   `offer_idenfitier`: zero-length&#xA;&gt; -   `d`: any&#xA;&gt; -   `n`: the node id of the recipient.&#xA;&#xA;In essence, this is an implicitly-existing offer that never expires, and which can be used by any node at any time to construct an invoice request?&#xA;&#xA;&gt;&#xA;&gt; Invoice Request Encoding&#xA;&gt;&#xA;&gt; -------------------------&#xA;&gt;&#xA;&gt; Once it has an offer, the node can request an actual invoice using the&#xA;&gt; `invoice_req` message inside `directed`&#39;s `onion_routing_packet`. It&#xA;&gt; would expect an `invoice_or_error_tlv` inside the `directed_reply`&#xA;&gt; message.&#xA;&gt;&#xA;&gt; This includes a `tag` it can use to identify replies, the&#xA;&gt; `offer_idenfitier` from the offer, a `key` it can use to prove it was&#xA;&gt; the requester of this invoice, a `recurrence` number if this&#xA;&gt; is a payment in a recurring series, and other codes as required.&#xA;&gt;&#xA;&gt; The `refund_proof` refers to a previous invoice paid by the sender for&#xA;&gt; the specific case of a `refund_for` offer. It provides proof of&#xA;&gt; payment (the `payment_preimage` and also a signature of the&#xA;&gt; `payment_hash` from the `key` which requested the being-refunded&#xA;&gt; invoice (which does not have to be the same as this `key`!).&#xA;&#xA;An earlier requirement mentions that writers of offers or invoice request MUST have `paths` in some condition.&#xA;The below does not have `paths`, but there is a &#34;human-readable&#34; alternate encoding which *does* have `paths`.&#xA;It might be better to clarify this point.&#xA;&#xA;&gt;&#xA;&gt; 1.  tlvs: `invoice_request_tlv`&#xA;&gt; 2.  types:&#xA;&gt;&#xA;&gt;     1.  type: 1 (`tag`)&#xA;&gt;     2.  data:&#xA;&gt;         -   [`...*byte`:`tag`]&#xA;&gt;     3.  type: 2 (`offer_identifier`)&#xA;&gt;     4.  data:&#xA;&gt;         -   [`...*byte`:`id`]&#xA;&gt;     5.  type: 3 (`key`)&#xA;&gt;     6.  data:&#xA;&gt;&#xA;&gt;     -   [`32`:`key`]&#xA;&gt;&#xA;&gt;     1.  type: 4 (`recurrence`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`tu64`:`number`]&#xA;&gt;&#xA;&gt;     1.  type: 5 (`quantity`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`tu64`:`n`]&#xA;&gt;&#xA;&gt;     1.  type: 6 (`delivery_address_name`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`...*byte`:`name`]&#xA;&gt;&#xA;&gt;     1.  type: 7 (`delivery_address1`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`...*byte`:`address1`]&#xA;&gt;&#xA;&gt;     1.  type: 8 (`delivery_address2`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`...*byte`:`address2`]&#xA;&gt;&#xA;&gt;     1.  type: 9 (`delivery_city`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`...*byte`:`city`]&#xA;&gt;&#xA;&gt;     1.  type: 10 (`delivery_state_province_or_region`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`...*byte`:`state_province_or_region`]&#xA;&gt;&#xA;&gt;     1.  type: 11 (`delivery_zip_or_postal_code`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`...*byte`:`zip_or_postal_code`]&#xA;&gt;&#xA;&gt;     1.  type: 12 (`delivery_country`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`2*byte`:`country_code`]&#xA;&gt;&#xA;&gt;     1.  type: 13 (`delivery_telephone_number`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`...*byte`:`tel`]&#xA;&gt;&#xA;&gt;     1.  type: 14 (`voucher_code`)&#xA;&gt;     2.  data:&#xA;&gt;&#xA;&gt;     -   [`...*byte`:`code`]&#xA;&gt;&#xA;&gt;     1.  type: 15 (`refund_proof`)&#xA;&gt;     2.  data:&#xA;&gt;         -   [`32*byte`:`payment_preimage`]&#xA;&gt;         -   [`signature`:`signature`]&#xA;&gt;&#xA;&gt; Requirements&#xA;&gt;&#xA;&gt; -------------&#xA;&gt;&#xA;&gt; FIXME: many more&#xA;&gt; Sender MUST use ISO 3166 alpha-2 code for `delivery_country`.&#xA;&#xA;What happens when my army of non-shiny robots rise from their crypts and start annexing entire countries to enforce global peace, prosperity, and greater world optimization?&#xA;I suppose it would become immaterial then...&#xA;&#xA;&gt;&#xA;&gt; Directed Messages&#xA;&gt;&#xA;&gt; ==================&#xA;&gt;&#xA;&gt; Directed messages allow peers to use existing connections to query for&#xA;&gt; invoices (see BOLT 12). Like gossip messages,&#xA;&gt; they are not associated with a particular local channel.&#xA;&gt;&#xA;&gt; The `id` is a unique, transient identifier between the peers, used to&#xA;&gt; identify match messages and replies.&#xA;&gt;&#xA;&gt; The `directed` and `directed_reply` Messages&#xA;&gt;&#xA;&gt; ---------------------------------------------&#xA;&gt;&#xA;&gt; 1.  type: 384 (`directed`) (`option_directed_messages`)&#xA;&gt; 2.  data:&#xA;&gt;     -   [`chain_hash`:`chain_hash`]&#xA;&gt;     -   [`u64`:`id`]&#xA;&gt;     -   [`1366*byte`:`onion_routing_packet`]&#xA;&gt; 3.  type: 384 (`directed_reply`) (`option_directed_messages`)&#xA;&gt; 4.  data:&#xA;&gt;     -   [`chain_hash`:`chain_hash`]&#xA;&gt;     -   [`u64`:`id`]&#xA;&gt;     -   [`u16`:`len`]&#xA;&gt;     -   [`len*byte`:`reply`]&#xA;&#xA;This new `directed` message will be the mechanism for sending invoice requests and receiving invoice request responses?&#xA;What incentive is there for a forwarding node to actually forward a `directed` message?&#xA;&#xA;&#xA;Regards,&#xA;ZmnSCPxj</html></oembed>