Document the configurable maximum account payment size and optional channel balance reservation check, including their defaults, fee handling, and operational limitations.
8.3 KiB
Custodial accounts
This document describes how the "accounts" feature of LiT can be used to create
custodial user accounts with their own balances on an existing lnd node.
What is an account?
An account in the LiT context is a virtual construct that provides restricted
access to an existing lnd/LiT node. An account has a virtual (off-chain only)
balance in satoshis and an optional expiration. That allows a node operator to
give someone else (or some client application, see Use cases)
restricted access to their node with the ability to only spend up to a certain
amount of the node's channel balance.
NOTE: An account's balance is purely virtual. If an account is created with an initial balance higher than the node's actual overall channel balance, that is equivalent to fractional reserve banking. Therefore, the user accepting an account-restricted access enters a trust relationship with the node operator that the promised balance of the account is actually spendable.
How do accounts work?
The accounts system is made possible thanks to the power of
macaroons
and the RPC middleware
interceptor
of lnd.
What does that mean?
It means a node operator can give another user or client application access to
their node through the default gRPC interface of lnd, which makes this access
mode fully compatible with any remote (or local) user interfaces or
apps/browser plugins, as well as any LNC (Lightning Node
Connect) connection.
What features and balance the restricted user has access to is solely controlled by the macaroon that is given to the user. So a user that wants to get restricted access to a node will receive a macaroon that is bound to an account that is defined in LiT. Because of the cryptographic setup of macaroons, that restriction cannot be removed from the macaroon by the user without invalidating the macaroon itself. Therefore, any user/application using such a restricted macaroon will trigger special rules in the RPC middleware interceptor mentioned above. See the features section below to find out what those rules are.
Features
When an account-restricted macaroon is used, the RPC middleware interceptor enforces the following rules on the RPC interface:
- Any payment made by a custodial/restricted user account is deducted from an account's virtual balance (the full amount, including off-chain routing fees).
- If a payment (or the sum of multiple in-flight payments) exceeds the account's virtual balance, it is denied.
- The on-chain balance of any RPC responses such as the
WalletBalanceRPC is always shown as0. A custodial/restricted user shouldn't be able to see what on-chain balance is available to the node operator as an account can only spend off-chain balances anyway. - The off-chain balance (e.g. the response returned by the
ChannelBalanceRPC) always reflects the account's virtual balance and not the node's overall channel balance (and any remote balances are always shown as0). - The list of active/pending/closed channels is always returned empty. The custodial/restricted user should not need to care (or even know) about channels and their internal workings.
- The list of payments and invoices is filtered to only return payments/invoices created or paid by the account.
- Invoices created by an account are mapped to that account. If/when such a mapped invoice is paid, the amount is credited to that account's virtual balance.
Operator controls
LiT provides optional account safeguards through its configuration:
-
accounts.max-payment-size-msatlimits the total amount a single account payment may debit, including fees. ForSendPaymentV2, the configured fee limit is included; forSendToRouteV2, the route's stated fee is included. The default value of0disables this limit. -
accounts.check-channel-balancerejects new accounts, administrative credits and administrative balance increases that would make the sum of all account balances exceed the node's current local channel balance. It is disabled by default. When enabled, allocations fail if LiT cannot query the Lightning client for the channel balance.Note: The channel-balance check only applies when a balance is allocated. It cannot prevent the node's local balance from subsequently falling below the allocated account balance if the operator sends non-account Lightning payments or other non-account activity.
Use cases
The following (definitely non-exhaustive) list of use cases is made possible by the accounts system:
- The "Uncle Jim" model: The tech-savvy person of the family (e.g. "Uncle Jim") operates a Lightning node. He manages the liquidity of the node and provides the capital for the channels. He can onboard his family members by creating an account, locking a macaroon to that account and then scanning a QR code with an app like Zeus on the family member's smartphone.
- The "spend up to a certain amount automatically" model: A web user has a browser extension like Alby installed and wants to allow that extension to pay invoices for paywalls automatically up to a certain amount per month. That amount could be enforced by the account so the browser extension doesn't have to keep track of its spending actions. And an account can be shared between extensions installed in different browsers.
- The "allowance" model: A parent wants to give their child their allowance in Lightning satoshis. They create an account over the allowance amount and top up the account each week/month.
HOWTO
This section describes how an account can be created and used.
Create the account
The first thing that needs to be done is to create the account with its initial
balance (and an optional expiry). This needs to be done by the node
operator, meaning access to the lit.macaroon is required.
Example:
$ litcli accounts create 50000 --save_to /tmp/accounts.macaroon
{
"account": {
"id": "d64dbc31b28edf66",
"initial_balance": "50000",
"current_balance": "50000",
"last_update": "1652353332",
"expiration_date": "0"
},
"macaroon": "020103........."
}
Account macaroon saved to /tmp/accounts.macaroon
This created a new account (ID d64dbc31b28edf66) with an initial balance of
50k satoshis and no expiration. A new macaroon was baked that contains the
correct permissions and is locked to that account. The macaroon file was stored
under /tmp/accounts.macaroon in this example.
Use the macaroon
This step is done by the user/app that should be given the restricted access. An
example could be to create a QR code with a tool like
lndconnect that can be scanned by
mobile apps to connect to the node. Or some browser extensions require the user
to upload the macaroon to the browser.
It's absolutely crucial to use the macaroon generated in the previous step here to make sure the restrictions are applied.
The permissions and restrictions of a macaroon can always be inspected by:
$ lncli printmacaroon --macaroon_file /tmp/accounts.macaroon
{
"version": 2,
"location": "lnd",
"root_key_id": "0",
"permissions": [
"info:read",
"invoices:read",
"invoices:write",
"offchain:read",
"offchain:write",
"onchain:read"
],
"caveats": [
"lnd-custom account d64dbc31b28edf66"
]
}
The important part is the lnd-custom account ... part in the caveats
section.
Example of using lncli to check the account balance (assuming integrated lnd
mode, adjust RPC server/port and TLS cert for remote mode):
$ lncli --macaroonpath=/tmp/accounts.macaroon channelbalance
{
"balance": "5000",
"pending_open_balance": "0",
"local_balance": {
"sat": "5000",
"msat": "5000000"
},
"remote_balance": {
"sat": "0",
"msat": "0"
}
...
}