Skip to content

Instantly share code, notes, and snippets.

@ajtowns
Last active Sep 13, 2021
Embed
What would you like to do?
scaling lightning

Motivation

Find a way of making lightning (or bitcoin for payments) scale to all the world, without making unreasonable technical assumptions like gigameg blocks.

Assumptions

  • 9 billion people using lightning
  • kind-of decentralised
  • miners funded by fees - $1M/block
  • no blocksize increase (so $1/vbyte)
  • lightning routing is solved

Upgrades required

  • taproot/tapscript/schnorr
  • noinput/anyprevout
  • eltoo
  • channel factories
  • OP_COSHV / SECURETHEBAG
  • "OP_SCRIPTREPLACE"

Scheme

  • Split the population into groups of about 5000 people -- the size of a small town. Have each of these groups establish their own "bank" which will manage the onchain transactions. This will be expensive if the bank fails, but cheap so long as the bank functions correctly. It requires out of band punishment of the bank to pass on the expense of bank failures.

  • Each bank manages a single UTXO on chain which represents the "accounts" of the 5000 people using that bank. Think of this UTXO as a channel factory containing all the lightning channels for all the customers. Traditionally, if any of the customers disappeared, it wouldn't be possible to update the factory and you'd need to drop to an expensive uncooperative state update. But instead of doing that, simply commit to the channel factory via OP_COSHV in one of the tapscript scripts.

  • To make things cheap, allow the bank to do restricted updates, according to the following policy:

    • they can't update the factory state until a month has passed (to reduce updates, saving fees)
    • if they update the factory state, they have to have a one week grace period so customers can check the new state is satisfactory, and force a bank closure if not
  • To achieve this, the bank alternates between two different schemes for the utxo:

    • regular script paths are:

      • <hash of channel factory> OP_COSHV
      • <bank> OP_CHECKSIGVERIFY "1008 OP_CSV" OP_SCRIPTREPLACE 4000 OP_CSV
    • when upgrading, the SCRIPTREPLACE path is used to change the scripts to:

      • <hash of channel factory> OP_COSHV
      • 1008 OP_CSV <hash of next state> OP_COSHV
  • When upgrading, bank customers contact the bank to get the full next state, ensure their channel balances are correct, and that the hash matches the upgrading state scripts. If so, everything's fine. If not, they use the channel factory COSHV script to ensure they don't lose their funds, and use law enforcement to make the bank fail, and get reimbursed for the expense of the failure path.

Results

If things all go well, you're paying for two transactions a month, something like 147 bytes and 104 bytes, so about 250 bytes of blockspace per month; which comes to about 5c per person per month.

If things go well for everyone, then 1.8M banks can serve 9B people. Assume everyone has accounts with 4 different banks for redundancy (so 20c/month) and round up to 8M banks. At 250 bytes each, that's 2000 full blocks worth of tx data per month, out of a total of about 4400 blocks per month, or an average of about 454k vbytes per block, which leaves a fair bit of room for other uses and dealing with the occassional bank failure.

Having a global routing table for all 8M banks is probably plausible, and then invoices just need to declare the route from the user's banks to the user, and lightning's existing routing system works technically in this environment (though still is vulnerable to spam, so there's definitely open problems there)

Missing details

  • how do tx fees actually get paid?
  • how do you splice in funds from other utxos into a bank? COSHV isn't great for this, but I think can be made to work
  • this model mostly relies on on-chain tx's being expensive to prevent the bank failure case be exercised by griefers. Adding some additional trusted party to that path might be sensible to limit that attack.
  • the internal key P probably needs to be a MuSig tree, ie, take K_1_1 = MuSig(K1,K2,K3,K4,K5), K_1_2 = MuSig(K6,K7,K8,K9,K10) etc, then K_(i+1),1 = MuSig(K_i_1, K_i_2, K_i_3, K_i_4, K_i_5), etc, until you just have P=K_6_1, and can then provide ~20 keys to prove to each customer that a key path spend can only be done with their consent.
  • the actual eltoo channels and channel factories never make it on chain ideally -- they're needed to track the off-chain state to convince the bank that it's okay to move your balances around, and if the bank fails
  • depending on how they're handled, bank failures can be very expensive: expanding out 5000 tx's at say 300vB per tx and $1/vB is $1.5M -- so the above implicitly assumes banks have about that much in equity so that customer losses due to fees on bank failure can be covered. $1.5M by 8M banks is $12T which is presumably a large portion of the bitcoin market cap in this scenario.

OP_SCRIPTREPLACE takes two parameters "suffix" and "prefix" and is a tapscript opcode that requires the tx has a corresponding output to this input, that output is the same value as this input, and the scriptPubKey is taproot output Q2 = P+H(P,S2)*G; that's calculated with P as this input's internal key, and S2 is Merkle(Path, NewScript) where Path is the merkle path provided in the taproot control block, and NewScript is just prefix and suffix concatenated. Basically, this means if you have a taproot scriptpubkey made of internal key P and a merkle tree of a bunch of scripts, then if one of those scripts is

  • <P> OP_CHECKSIGVERIFY "1008 OP_CSV" OP_SCRIPTREPLACE

then you can provide witness data like

  • "DROP <Q> CHECKSIG" <Sig(P)>

and you update the utxo by replacing just that leaf with a new script 1008 OP_CSV DROP <Q> CHECKSIG -- in which case during the 1008 blocks, the utxo could be spend by one of the other script paths (or the key path), and after the CSV is satisfied, it can be spent by a signature by Q.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment