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