Study & contribute to bitcoin and lightning open source
Interactive AI chat to learn about bitcoin technology and its history
Technical bitcoin search engine
Daily summary of key bitcoin tech development discussions and updates
Engaging bitcoin dev intro for coders using technical texts and code challenges
Review technical bitcoin transcripts and earn sats
Make Me An Offer: Next Level Invoicing
The Lightning Conference Day 1 (Stage 1)
BOLT 11: https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md
BOLT 12 PR: https://github.com/lightningnetwork/lightning-rfc/pull/798
bolt12.org: https://bolt12.org/
Hi. My name is Rusty Russell and this is actually my official job title at Blockstream, code contributor. It is generic, it is flexible, it is probably a little too humble maybe but that is me. Before we begin the main part of this talk I want to tell you a story. Once upon a time when Bitcoin was young there was an enterprise called Satoshi Dice and the happy Bitcoiners frolicked in the blocks and they would send their Bitcoin to Satoshi Dice and receive either a multiple in return or a single satoshi consolation prize. Into our happy tale come the evil Core developers who introduce the dreaded dust limit, destroyed the business model of Satoshi Dice and the brave entrepreneurs. I believe they were mainly poor orphans and adorable puppy dogs in fact. I can see you looking horrified, “Rusty why are you telling me this awful and heartrending tale?”. Because like the Bitcoin network the Lightning Network will change. It will come from this happy place that it is now and it will have to adapt as the environment that we are in becomes more adversarial. It is going to have to develop defenses against various forms of behavior and spam. I feel, perhaps so that nobody is giving a talk like this about me in 5 years time, we should give you some warning about things that generally the developers agree will happen to the network in the next some mumble number of years.
The first is that despite what Jameson said, fees will rise. There are two real reasons for this. The first is that the default fees that dominate the network today are approximately zero. That has proven insufficient to maintain robust connectivity. We are already seeing people increase fees and we expect that to continue to some level. But there is a second reason and that is defending against some kinds of spam on the network requires us to introduce some upfront fees. The problem is that these upfront fees have to be large enough to prevent spam but small enough compared to the current success based fees so that we are not incentivizing failure. Because if we start allowing nodes to just steal the money then users will fall back on reputation systems to figure out what nodes to route through. As far as we can tell reputation systems are inherently centralizing so we want to avoid that. If you are relying on fees staying around one part per million like today you should be rethinking your plans for the future.
The other thing that will happen is very slow payments will have to be defended against. Anything that takes more than maybe a minute or two is going to have to have some solution. That’s because really slow payments are trivial to weaponize on the network. The network will have to defend itself the same way that the Bitcoin network had to defend itself against dust. There are two obvious ways to do this. One is to charge a much larger upfront fee for things that self identify as slow payments. The second thing is to aggressively close channels when delays happen. There is a proposal for that which nobody likes but that seems to be the best thing on the table. If your business model relies on payments that are taking minutes or blocks or days that is fine but make sure you have a Plan B. There are things in the protocol that we can do to alleviate a lot of these things that we’re doing today. This is what I want to emphasize, there is nothing wrong with doing any of these things at the moment, that’s fantastic, but as a public service announcement I do want to tell you that things are going to have to change eventually. That’s the end of the PSA talk part of my talk. Now I am going to talk about what I came here to talk about. I am a protocol level person, I am happy with bits and bytes. There are a whole heap of cool things happening in the protocol that I could have talked about and there are some amazing talks on the schedule talking about future stuff that is coming up. But I dug out something that we’ve talked about for a while because for this audience I really want your feedback on something.
I wrote BOLT 11 and I probably am a bigger fan of invoices than anyone you’ll ever meet who is not actually an accountant. The oldest piece of writing that we have is actually a Babylonian receipt or maybe an invoice. The reason is that when there is no intermediary in a transaction an invoice or receipt is key to keeping the vendor honest. As we grow the Lightning Network into something that supports real commerce between people you don’t know and trust you will definitely thank me that we have cryptographic invoices. But invoices also suck for a whole range of use cases, things that we really want to do. They say that every problem in computer science can be solved by adding another layer of indirection. That is exactly what I’ve done. I do want to caution you that I wrote literally some of this draft BOLT on the plane on the way here so I made arbitrary choices some of which are indefensible and will no doubt change, but I want to present it to you now so that we can tear it apart and put it back together as something that we can live with. None of these decisions that I present here as if they are in carved in stone actually really matter and any number of them could change. But this is what it might look like, a lot like a BOLT 11 invoice except it starts with lno…
. You might turn it into a QR code. It gets into the payer’s hands somehow. Then they send through the Lightning Network itself a request to the node that signed the offer. The node replies with the real invoice. From that point the payer then pays the invoice like it normally would. Probably but not necessarily along the same path that it used to request the invoice in the first place.
If we break up the offer it starts with lno1…
. I know roasbeef and Conner will be really happy with this, it is TLV data inside the invoice. I am using a standard TLV format, not the boutique one that we use in BOLT11. At the end it has a signature from the node that it came from or maybe a node ID and signature if we are going to be fancy and go for 32 byte pubkeys and Schnorr and everything else that people want these days.
The TLV data contains the offer itself. It has some obvious things, it has an offer_id
so when I ask the node for the offer it knows which one I’m tallking about. It has a Description
which says what you are offering, a cup of coffee or whatever it maybe. It has a Expiry (optional: default never)
but unlike BOLT 11 expiry if there is no expiry it means it doesn’t expire. These things can stay around for a long time. It may have some Paths (optional)
hints. We have these hints already in BOLT 11. They tell you how to get to the node, if it is hard to reach or if it is through private channels. Or even just a hint about capacity. This is new. It has a Max quantity (optional)
. For some things you want to offer but it makes sense for someone to order 10 of them at once. We’ve added a quantity optionally to the offer. Of course it contains an Amount
which is the amount you will pay for the item that is being offered multiplied by the quantity if you have one. But the amount also contains a currency code. That’s weird. It turns out that offers can stick around for a long time and generally this currency code could be in Bitcoin if you are a Bitcoin maximalist but for a lot of places their actual underlying costs are in a different currency. They want to reflect that in the offer.
That is going to seem a little bit strange to you but it particularly comes home when you have an offer with recurrence. An offer might be a simple cup of coffee or it might be something that happens every 60 seconds that you keep paying or it could be every 7 days, every month or even every year (Period
). There are a lot of things that this applies to where you want this recurrence. It could also have a limited Number (optional)
. It could be 60 payments, it could be something that is naturally limited to 12 or something. It could also have a Basetime (optional)
. Sometimes you pay and then you pay again in 60 seconds. Sometimes you pay on the first of the month and you pay every month. If you specify a base time, that anchors where that payments occurs and there may be a Paywindow
. Perhaps you have to pay within the first 3 days of the month. There is a special value here that says maybe if you pay halfway though the month you only pay half, you pay proportionally to the remaining amount in that period. I didn’t go all out on this. You cannot say “I want you to pay me on the first Tuesday of the month or the last weekend of the month”. But this seems to offer enough flexibility for the moment at least with the minimum intersection of complexity.
We also have additional data. You may also say “I also want you to send me a delivery address”. For some things that makes perfect sense, you are ordering something, I need to send it to you. I want to use a specified delivery address. And maybe a telephone number if it is credit for example, like Bitrefill doing phone credit. It could be a voucher code, this conference had a voucher code. A lot of places have a voucher code field. If you supply a voucher code perhaps that gives you a discounted invoice. We can add additional ones because this uses our standard bit encoding to say what you need. The Lightning protocol at the low level has this rule that says “It is ok to be odd”. That means that if you don’t understand something and it is odd that is ok, you can ignore it. But if you don’t understand something and it is even that means you are in trouble, you need to go upgrade or something.
This also provides unsolicited payments, sometimes called donations. If an offer is zero or empty offer_id
that is special, that means I haven’t seen an offer, I just want to give you money.
This message when we request an invoice is onion encrypted like a normal HTLC payment through the Lightning Network so no one can tell where it is going etc. It contains the offer_id
that says “Here is the offer I am asking for”. And it contains a nonce, a (transient) pubkey that the payer chooses and of course the quantity and amount if it was a straight donation, the recurrence number if it is one of a series, the delivery info and stuff like that if that is something that is required.
The invoice_reply
message could be an error message. “I don’t know what offer that is” or “That has expired, what are you doing?” It could be a replacement offer, “That has expired but here is a new one”. That has to be signed by the key in the original. If Foo Corp buys out Bar Corp and you use a Foo Corp invoice it might be replaced by a Bar Corp invoice for the same thing but it has to be signed by the original Foo Corp. But normally the reply will just be the invoice, that’s how it is supposed to work.
Invoices as of 2 weeks ago or something have a s
field, a secret field that you are supposed to send through with the payment that proves that you saw the invoice. In the case of an invoice that we’ve requested the secret is actually a Merkle tree of the invoice_req
. You basically take the tag that they provided in the invoice request and you build up a Merkle tree like this. Make sure it is a power of 2, then you SHA everything and then you sort them by the SHAs, you do the Merkle thing and you end up with a single secret.
For those of you who completely didn’t follow that this gives you the ability to prove to third parties various things about the invoice and thus the payment. I can prove any part of the delivery info, I can prove that I paid this and I had it shipped to Australia. Or I had it shipped to my postal code without revealing anything else about the payment. I can prove to you that they screwed me over because they didn’t deliver it and here’s the proof that I ordered it. You can also prove that you are the one who paid for it. Or at least you are the one who requested that specific invoice because it has that transient key in it that we sent as part of the invoice request. I can prove to you what that key is and I can prove to you that I control the key by producing a signature. Now that’s pretty cool.
An offer says “Ask me for an invoice” and you can request it over Lightning. But also sometimes you just want the invoice request. I want to tell you “Give me an invoice please”. So we came up with another encoding lni…
. It is basically an invoice request that we would normally send over the Lightning Network except in encoded form.
It looks the same but it also has a description that says what you are invoicing me for. This is the use case of an exchange withdrawal for example. It would push this lni
thing through to you, maybe it is a barcode, maybe through your browser to say “Give me an invoice so I can pay you”. It would say what the payment is for, it may have some paths on how to reach them. It will have the amount.
But most importantly this can be used for refunds. I want the user to present me with an invoice so I can pay them a refund. Because remember the invoice secret commits to the payer key that they gave me originally, that transient key that they gave me. They can use that to prove that they are the ones who paid and they are the ones who are entitled to the refund. One of the problems with refunds generally or any kind of reverse flow is that in Lightning you generally know who you are paying but they don’t know where the payment is coming from. If you tell them to pay you are telling them who you are, or at least your node ID. But we have another proposal coming which is rendezvous routing and that preserves recipient privacy. When you combine these technologies you can get your refund without actually revealing what node you are on the network.
So what do offers give us? They give us static invoices. An offer is a static invoice. You can print out these QR codes and you can slap them on things. Somebody can come along and scan them and pay that way. You can slap it on your website because in order to securely use a BOLT 11 invoice you have to generate a fresh one for every payment and not reuse them. And ideally a BOLT 11 invoice should have the description that is specific enough to identify that particular payment. Not just a cup of coffee but a cup of coffee that I delivered at this time to this person at this address or whatever. Because otherwise I can prove that I paid for a cup of coffee but I can’t prove anything useful about it. They can say “We delivered a cup of coffee” but that wasn’t my coffee. How do you know? This gives you static invoices, it really does. It gives you user controlled recurring payments. Vendors love recurring payments with credit cards. They love them. They want you to sign up for free trial periods so they can put you on recurring payments. Recurring payments, when it is a pull model like that, are evil and that’s pretty clear. It is incredibly painful to cancel them. User controlled recurring payments however bring back some of that sovereignty over your payments. You control your recurring payments. You flip across the tab in your wallet and you can see all your recurring payments. You cancel them, done. Because you are the one pushing them out every 60 seconds or every month or whatever the schedule is. And you are the one who agreed to that. That’s incredibly important and it is useful. I have no problem with recurring payments for some things as long as I consent to them and I can cancel them at any time. It gives you spontaneous donations with a receipt. At the moment there are various hacks you can use to pay a node but you don’t have any evidence that you actually paid it or that they actually got it. Or the amount, if you have an invoice without an amount there is no evidence that you actually paid that amount. You can have an invoice that doesn’t specify an amount and will accept anything but then you have no way to prove that you gave them more than 1 satoshi or technically more than 1 millisatoshi. This gives you spontaneous donations with a receipt. And of course it allows for full or partial refunds which is pretty cool as well.
So my question particularly to this crowd here is “Is this useful? Is this something that you want? What can’t this do? Are there plans that you have, things that you are building that would be really great to do that this doesn’t quite make it?” Please come and talk to me, send me an email or join the discussion so we can make sure that we have as much coverage as possible of these ideas.
I want to throw myself forward a little bit and look at the future and what I might like to see with this kind of model. Somehow, it could be a billboard, it could be on an ad, it could be pushed through to my browser, whatever, I see this offer for a weekly delivery of coffee beans and it is 2 euros per 100 grams. Except my wallet doesn’t show me that, it shows me in Australian dollars, 3 dollars 50 per 100 grams weekly. How many do I want? That looks like a great deal, I will do that. I order 10 of them, 1 kilo. My wallet says they want a delivery address, I select my work delivery address. Underneath my wallet has now used that information, it has got approval from me to spend this money, it goes out, it fetches the real invoice and it comes back, they’ve modified the description. The description no longer says “Weekly coffee beans” it now says “Weekly coffee beans plus postage”. Now it says “They added plus postage and they also changed the amount from what we expected.” Of course the amounts are actually in Bitcoin. It says “They’ve charged you an extra 20 Australian dollars” because postage to Australia is really expensive. At that point I either go “No cancel that. I am going to have it delivered to my remailer” or I go “I really want that coffee” and I hit Yes. Of course if everything had come back within the parameters that it had originally offered me it wouldn’t have prompted me again to say “The deal changed”. What is cool is that I signed up for this once a week so next week it automatically goes back and uses the offer, requests the new invoice. Remember it has already got approval from me to spend this money, 35 Australian dollars in this case. Internally it does the conversion and if the price is within say 5 percent it doesn’t bother reprompting me. But if the price has changed, it looks at the Bitcoin amount that is in the invoice and it has moved too much it will reprompt me and say “Hold on. The deal has changed.” It knows from the original offer that the thing was in euros so it may even be able to tell me that the euro has moved and that is probably why. Or it could just be we are using different price bases for how much Bitcoin is worth or it could be they are trying to screw me. At that point it will stop and go “Hold on the price has gone up”. If the price has gone down I probably don’t care. It will require reapproval. But normally it will just continue.
This emphasizes two things. Firstly I want my wallet to be my agent to deal with this stuff. I want it to check with me and get approval but once it has got approval I want it to just go and do it. But the other thing is that when I want to buy coffee or whatever it is that I’m after I don’t want to go to your website and I don’t want to click around. I don’t want to find the thing and I don’t want to subscribe to your newsletter and I don’t want to create an account. I just want you to shut up and take my money. In theory this kind of flow allows exactly that to happen. Scan, or get pushed the thing, hit Yes, wallet please do that and we are done. We don’t have a relationship. I’ve just bought something from you. That is what I would like to see.
Q - I want to check that I understand how this works. Basically you request an invoice and then you generate an invoice with a refund in order to generate a generic invoice every single time? You don’t need to receive a specific… every time? Is the process generating a generic invoice that could be generated anytime?
A - Let’s look at the way it happens at the moment. If someone wants to offer you a refund over the Lightning Network they say “Send me an invoice so I can pay you”. They say it in words, over Twitter or email or whatever. Then you generate an invoice, make sure you get the amount right and you invoice it to and all those things. I just want to automate that flow. A standard message that says “Generate me an invoice for this” and your wallet will generate an invoice. Or it will say to you “They want me to generate an invoice. Are you ok with that?” You say “Yes”. Then it will send the invoice to them and then they will pay it. I just want to automate that flow that is currently done manually and of course is very prone to mistakes. I am in a different timezone and the invoice default timeout used to be 1 hour. By the time they woke up the invoice had expired and they could no longer send me my refund. All that kind of cr*p goes away if we automate the refund flow. It makes sense if we’ve got this mechanism to communicate and ask for invoices through the Lightning Network we can also repurpose it to do this refund flow.
Q - I recently made an issue on the LNURL repository about doing a similar thing. You have thought it through much more on the various scenarios, the automation of the workflows. It seems like your idea to do it in the BOLT directly may be better especially because it has built in onion routing so we can protect privacy. Another idea that I had before is to categorize the fields that you are selling. If you have a supermarket you probably already have some kind of system that categorizes whether this is food, some tools etc. Your wallet could make you report on how you are spending your money without having to fill it manually and without screwing your privacy. For instance my bank offers me this possibility to have nice charts and whatever. I was amazed but at the same time horrified. This is what they know about me. This would be really cool to not have to get rid of privacy or comfort.
A - That is something I hadn’t thought of before which is great. It is exactly why I’m here to present, to throw this out here. Categorization would be definitely something that would be useful. We just have to come up with a spec for it. To be fair I was without my notes here but I did forget to credit LNURL who have done a lot of experimentation in this space and I love what they’ve done with invoice requests, stuff like that, building those layers on top. I just want more, more of that. I forgot to mention them but the LNURL spec is a ragbag of things that you need to bring Lightning up a level. We want that and we want it at the BOLT level. I think having individual implementations do it is cool for experimentation and eventually I hope to implement this in c-lightning but the important thing is that we get it spec’ed out. We get a whole heap of eyes on it, we get a whole heap of people to play with it, we get commonality so that everyone can use it. I really do think addressing it at the standards level is the way to go.
Q - You said something about payment secrets with a Merkle root. I know that there are overpay attacks where someone overpays, you can intercept the payment and steal fees. With donation invoices you can steal all the money. How does the secret actually get to the recipient so they can verify it?
A - The secret is sent through embedded in the onion at the moment. In the spec that we agreed on yesterday, I haven’t edited the spec yet to make the changes. We will be putting that in through the onion packets so it will come along with the payment itself. At the moment it is a random nonce but with this proposal it will actually be a full Merkle tree. You’ll be able to prove things about it.
Community-maintained archive to unlocking knowledge from technical bitcoin transcripts