Yaacov Akiba Slama <y...@slamail.org> writes: > Hi Rusty, > > It seems that there are two kind of TLV fields in your proposition: > 1) LN specific fields like `num_paths` and `payment_preimage`. > 2) "Business" fields like `address1` and `currency`. > I understand the need to define and include the first category, but I > don't think that we need or can define the second category. These fields > already exists in software like crm, erp, etc.. and are well defined by > standard body. > My suggestion is to have a generic field containing well defined > structured and standardized data. See for instance > https://en.wikipedia.org/wiki/EDIFACT and/or > https://en.wikipedia.org/wiki/Universal_Business_Language.
Hi Yaacov, I've been pondering this since reading your comment on the PR! As a fan of standards, I am attracted to UBL (I've chaired an OASIS TC in the past and have great respect for them); as a fan of simplicity I am not. Forcing UBL implementation on wallet providers is simply not going to happen, whatever I were to propose. We also don't want duplication; what if the "UBL field" were to say I were selling you a bridge for $1 and the description and amount fields actually said I was selling you a coffee for $3? However, since invoices/offers and UBL are both structures, we should have an explicit mapping between the two. What fields should have their own existence in the invoice/offer and what should be in a general UBL field is a question we have to think on further. Anyway, you'll have to bear with me as I read this 172 page standard... Cheers, Rusty. > What do you think? > PS: Sorry for crossposting here and in > https://github.com/lightningnetwork/lightning-rfc/pull/694 > --yas > > On 05/11/2019 06:23, Rusty Russell wrote: >> Hi all, >> >> This took longer than I'd indicated; for that I'm sorry. >> However, this should give us all something to chew on. I've started >> with a draft "BOLT 12" (it might be BOLT 13 by the time it's finalized >> though!). >> >> I've also appended indications where we touch other BOLTs: >> 1. BOLT 7 gains a message/reply system, encoded like htlc onions and >> failure messages. >> 2. BOLT 11 gains a `q` field for quantity; this avoids changing the >> description when the user requests an invoice for more than one of >> something >> (since changing the description between offer and invoice requires user >> interaction: it's the *invoice* which you are committing to). >> >> There's definite handwaving in here; let's see if you can find it! >> >> Cheers, >> Rusty. >> >> # 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. >> >> # Table of Contents >> >> * [Offers](#offers) >> * [Encoding](#encoding) >> * [TLV Fields](#tlv-fields) >> * [Invrequests](#invrequests) >> * [Encoding](#encoding) >> * [TLV Fields](#tlv-fields) >> >> # Offers >> >> Offers supply a reader with enough information to request one or more >> invoices via the lightning network itself. >> >> ## Encoding >> >> The human-readable part of a Lightning offer is `lno`. The data part >> consists of three parts: >> >> 1. 0 or more [TLV](01-messaging.md#type-length-value-format) 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`. >> >> 1. tlvs: `offer` >> 2. types: >> 1. type: 1 (`paths`) >> 2. data: >> * [`u16`:`num_paths`] >> * [`num_paths*path`:`path`] >> 1. type: 2 (`description`) >> 2. data: >> * [`...*byte`:`description`] >> 1. type: 3 (`expiry`) >> 2. data: >> * [`tu64`:`seconds_from_epoch`] >> 1. type: 4 (`offer_identifier`) >> 2. data: >> * [`...*byte`:`id`] >> 1. type: 5 (`amount`) >> 2. data: >> * [`4*byte`:`currency`] >> * [`tu64`:`amount`] >> 1. type: 6 (`additional_data`) >> 2. data: >> * [`...*byte`:`rbits`] >> 1. type: 7 (`recurrance`) >> 2. data: >> * [`byte`:`time_unit`] >> * [`u32`:`period`] >> * [`tu32`:`number`] >> 1. type: 8 (`recurrance_base`) >> 2. data: >> * [`u32`:`basetime`] >> * [`tu32`:`paywindow`] >> 1. type: 9 (`quantity`) >> 2. data: >> * [`tu64`:`max`] >> 1. type: 10 (`refund_for`) >> 2. data: >> * [`32*byte`:`payment_hash`] >> >> 1. subtype: `path` >> 2. data: >> * [`u16`:`num_hops`] >> * [`num_hops*hop`:`hops`] >> >> 1. subtype: `hop` >> 2. 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 >> - 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 are *periodic*, 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. >> >> ## 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`!). >> >> 1. tlvs: `invoice_request_tlv` >> 2. types: >> 1. type: 1 (`tag`) >> 2. data: >> * [`...*byte`:`tag`] >> 1. type: 2 (`offer_identifier`) >> 2. data: >> * [`...*byte`:`id`] >> 1. type: 3 (`key`) >> 2. 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`. >> Sender MUST set offer_identifier to match offer. >> Sender MUST include `key` >> - SHOULD use a transient unpredictable key >> - MUST reuse key for successive recurring invoices. >> Sender MUST set `recurrence` for recurring invoices. >> >> Receiver MUST check `offer_identifier` >> Receiver MUST check `delivery_` fields. >> Receiver MUST check `recurrence`. >> Receiver MUST check `amount`. >> >> 1. tlvs: `invoice_or_error_tlv` >> 2. types: >> 1. type: 1 (`tag`) >> 2. data: >> * [`...*byte`:`tag`] >> 1. type: 3 (`omitted`) >> 2. data: >> * [`...*u64`:`omitted_fields`] >> 1. type: 4 (`invoice`) >> 2. data: >> * [`...*byte`:`invoice`] >> 1. type: 5 (`message`) >> * [`...*byte`:`message`] >> 1. type: 6 (`replacement`) >> 2. data: >> * [`signature`:`signature`] >> * [`...*byte`:`offer`] >> >> Sender: >> - MUST copy `tag` from sender. >> - MUST omit fields it does not use, and place number in order in >> `omitted_fields`. >> - if it includes `invoice`: >> - MUST not include `message` >> - MUST not include `replacement` >> - MUST [merkle fields it used](#merkle-calculation) and place that in >> invoice `s` field. >> - otherwise, if it includes `replacement`: >> - MAY include `message` >> - otherwise: >> - MUST include `message` describing the error. >> >> Receiver: >> - MUST check that `tag` matches req. >> - if `replacement`: >> - MUST fail if `signature` does not sign `offer` with same key as >> original. >> - MUST only fetch once (no double-redirects!) >> - if description or amount significantly changes, must re-ask user. >> - SHOULD note if description simply has something appended (eg "+ >> postage"). >> - within invoice: >> - MUST check that `s` matches merkle of fields, minus `omitted`. >> - MUST check that no vital fields are in `omitted`. >> - MUST check that `d` matches `description` >> - MUST check that `q` DNE if `quantity` DNE, otherwise is equal. >> - Must check valid signature, etc. >> >> ## Merkle Calculation >> >> 1. For each `invoice_req_tlv` field in ascending `tlv` type order: >> 1. If the field was omitted, it is added to `omitted_fields`. >> 2. Otherwise, the immediate parent merkle is: >> >> SHA256(SHA256(`tag` | `be64-n`) | SHA256(`tlv-value`)) >> >> Where `be64-n` is a 64-bit big-endian counter starting at 0 and >> incrementing for each leaf. >> >> 3. Order these nodes in increasing SHA256(`tag` | `be64-n`) order. >> >> 2. Create additional leaves until `be64-n` is the next power of 2: >> >> SHA256(`tag` | `be64-n`) >> >> 3. Combine adjacent leaves using SHA256(leaf1 | leaf2) until none remain. >> >> By creating adjacent leaves using the `tag` field and a counter, and >> sorting the leaves, the only significant information revealed by a >> merkle proof on a node is the depth of tree (which implies the total >> number of TLV fields). >> >> # InvRequests >> >> There are times when it makes sense to request an invoice over another >> medium, such as HTTP or a QR code. >> >> ## Encoding >> >> The human-readable part of a Lightning invrequest is `lni`. The data part >> consists of three parts: >> >> 1. 0 or more [TLV](01-messaging.md#type-length-value-format) encoded fields. >> 2. A 32-byte nodeid[1] >> 3. 64-byte signature of SHA256(hrp-as-utf8 | tlv | nodeid). >> >> ## TLV Fields >> >> 1. tlvs: `invreq` >> 2. types: >> 1. type: 1 (`paths`) >> 2. data: >> * [`u16`:`num_paths`] >> * [`num_paths*path`:`path`] >> 1. type: 2 (`description`) >> 2. data: >> * [`...*byte`:`description`] >> 1. type: 3 (`expiry`) >> 2. data: >> * [`tu64`:`seconds_from_epoch`] >> 1. type: 4 (`amount`) >> 2. data: >> * [`tu64`:`millisatoshis`] >> >> The fields `paths`, `description`, and `expiry` fields are the >> same as those for offers; the optional `amount` field describes the >> amount an invoice will be accepted for. >> >> Upon parsing and accepting an `invreq`, the node sends an >> `invoice_or_error_tlv` within an onion. The reply is empty. >> >> ## Requirements >> >> The requirements for `paths`, `description` and `expiry` are >> [described above](#requirements-for-offers-and-invrequests). >> >> FIXME: More. >> >> [1] Assuming we go for Schnorr sigs and 32-byte pubkeys. >> ---- >> Addendum: BOLT #7: P2P Node and Channel Discovery and Directed Messages >> ... >> # Directed Messages >> >> Directed messages allow peers to use existing connections to query for >> invoices (see [BOLT 12](12-offer-encoding.md)). 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`] >> >> 1. type: 384 (`directed_reply`) (`option_directed_messages`) >> 2. data: >> * [`chain_hash`:`chain_hash`] >> * [`u64`:`id`] >> * [`u16`:`len`] >> * [`len*byte`:`reply`] >> >> ## Requirements >> >> FIXME: similar to update_add_htlc and update_fail_htlc. >> FIXME: define reasonable timeout after which you can forget if not replied? >> _______________________________________________ >> Lightning-dev mailing list >> Lightning-dev@lists.linuxfoundation.org >> https://lists.linuxfoundation.org/mailman/listinfo/lightning-dev _______________________________________________ Lightning-dev mailing list Lightning-dev@lists.linuxfoundation.org https://lists.linuxfoundation.org/mailman/listinfo/lightning-dev