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