lightning icon indicating copy to clipboard operation
lightning copied to clipboard

Unable to apply spending restrictions to a rune

Open Amperstrand opened this issue 2 years ago • 4 comments

Problem: how can you issue a rune which restricts the amount of sats that it can spend per request

There have been some loss of funds incidents that have been caused by programs like lnbits and btcpayserver being given unrestricted access to the lightning node. The impact of such incidents could be limited if they were accessing the lightning node through runes that were ratelimited in how much and how fast they could make payments.

PEER_ID=$(lightning-cli getinfo|jq -r .id)
rune=$(lightning-cli commando-rune restrictions='[["method=pay"],["pnameamountmsat<1001"]]' | jq -r .rune)
lightning-cli commando $PEER_ID pay "{\"bolt11\":\"$bolt11_invoice\", \"amount_msat\": 1000 }" $rune

The above creates a rune that can be used to pay bolt11 invoices that do not include an amount. The maximum that can be paid with this rune is 1 satoshi.

The problem is that it will only work for bolt11 invoices that do not specify the amount. If the bolt11 invoice asks for 1 satoshi, the pay command will fail because you cannot specify amount if it is encoded in the invoice:

{
   "code": -32602,
   "message": "msatoshi parameter unnecessary"
}

Possible solution 1: allow the pay command to specify the amount even when it is not necessary

If we allow the command to be used with a specified amount even when it is encoded in the invoice, I am pretty sure the rune restriction would be applied. Instead of requiring amount_msat to be None, it could be required to be either None or equal to what is encoded in the invoice. This would be the simplest way to solve this problem.

Possible solution 2: allow rune restrictions to apply to bolt11 invoice fields

Runes can look at integer fields and strings to apply restrictions. In this case, we want to make a restriction such that the amount encoded in the bolt11 invoice is less than X.

If runes allowed us to inspect the various fields of the bolt11 invoice, it would be possible to apply a restriction in this way.

Possible solution 3: use getroute and sendpay instead of pay

A possible way to restrict payment sizes would be to not issue a rune for pay but require the use of sendpay instead. With sendpay, I think amount_msat is a required field and so restricting the amount with a rune would work.

Amperstrand avatar Jan 28 '24 20:01 Amperstrand

I'm not sure if this is what you want to accomplish, but you could write a plugin that intercepts payments done with the pay command and takes a decision to complete the payments based on the amount of the invoice.

See https://lnroom.live/2023-06-08-live-0006-core-lightning-rpc-command-hook-pay-command-and-bolt11-invoice/ for an example.

tonyaldon avatar Jan 29 '24 17:01 tonyaldon

I'm not sure if this is what you want to accomplish, but you could write a plugin that intercepts payments done with the pay command and takes a decision to complete the payments based on the amount of the invoice.

Yes, this is what I want to do.

LNBits, BTCPayserver and Boltcard can all be configured to access a lightning node. If there is a bug or security incident in them, one way to restrict loss of funds is through the use of credentials that have limited access (like runes).

I like the idea of intercepting and inspecting invoices as a plugin is a good idea, but it has one disadvantage: by doing it this way, the application would be given a rune to use the pay command. If the plugin is not loaded or fails, no interception happens and it has full access. So this could set you up for a future vulnerability where you think you have a firewall in place, but after updating the node the plugin is not correctly enabled and so the restriction is lifted.

There are other ways of solving this issue then in 7cbe45b. In fact, it seems kind of redundant: if you just want to pay an invoice, you need to decode the invoice and supply the amount. It might be better if you could just specify a max amount, and then any invoice under this will be accepted.

There is already maxfeepercent and maxfee. Maybe there should be a maxamount_msat.

I want a rune that can pay any invoice up to an amount. If you issue such a rune and rate limit it to 1 time per week with an expiry of 6 days it is in effect a single use voucher much like LNURLw.

But what I really want to use it for is giving LNBits and BTCPayserver the ability to pay invoices up to a limited amount in terms of both sats and rate limit. I nearly have LNBits working using the new CLNRest api as a funding source.

Amperstrand avatar Jan 31 '24 08:01 Amperstrand

Pretty sure its not possible to take a bolt11 invoice, decode it, remove the amount, encode it as a bolt11 invoice again and then pay that. The reason for this is that I think the bolt11 invoice is signed by the issuing node(?).

So if we have a bolt11 invoice with the amount encoded in it, it is non-trivial to somehow use the pay plugin to pay it but apply a rune restriction.

Amperstrand avatar Feb 05 '24 12:02 Amperstrand

Decoding a bolt11/bolt12 inside the rune code is non-trivial, however @ShahanaFarooqui convinced me that it's worth trying: we plan on attempting to implement it to judge this week.

rustyrussell avatar Mar 03 '24 22:03 rustyrussell

https://github.com/ElementsProject/lightning/pull/7165 fixes this issue.

rune=$(lightning-cli commando-rune restrictions='[["method=pay"], ["pinvbolt11_amount<2001"]]' | jq -r .rune)

The above should only be able to pay bolt11 invoices up to and including 2 sats.

With some clever rate limiting, it can be be a great way to give an application limited ability to spend from a core lightning instance.

Now that I know this is likely to show up in a future version of core lightning I will go back to working on making lnbits use runes when communicating over CLNRest.

Thank you!

Amperstrand avatar May 13 '24 12:05 Amperstrand