On Sat, Mar 18, 2023 at 12:41:00AM +0000, jlspc via Lightning-dev wrote: > TL;DR
Even with Harding's optech write ups, and the optech space, I barely follow all this, so I'm going to try explaining it too as a way of understanding it myself; hopefully maybe that helps someone. Corrections welcome, obviously! I think understanding all this requires going through each of the four steps. Step 1: Tunable penalties; https://github.com/JohnLaw2/ln-tunable-penalties https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-October/003732.html This is a clever constructions that lets you do a 2-party lightning channel with existing opcodes where cheating doesn't result in you losing all your funds (or, in fact, any of your in-channel funds). It also retains the ability to do layered commit transactions, that is you can immediately commit to claiming an HTLC or that it's already timed out, even while you're waiting for the to_self_delay to expire to ensure you're not cheating. The way that it works is by separating the flow of channels funds, from the control flow. So instead of managing the channel via a single utxo, we instead manage it via 3 utxos: F (the channel funds), InA (control flow for a unilateral close by A), InB (control flow for a unilateral close by B). For each update to a new state "i", which has "k" HTLCs, we create 4 primary txs, and 8k HTLC claims. StAi which spends InA, and has k+1 outputs. The first output is used for controlling broadcast of the commitment tx, the remaining k are for controlling the resolution of each HTLC. ComAi is the commitment for the state. It spends the funding output F, and the first output of StAi. In order to spend StAi, it requires a to_self_delay (and signature by A), giving B time to object that i is a revoked state. If B does object, he is able to immediately spend the first output of StAi directly using the revocation information, and these funds form the penalty. It has k+2 outputs, one for the balance of each participant, and one for each HTLC. For each of the k HTLCs, we construct two success and two timeout transactions: (HAi-j-s, HAi-j-p); (HAi-j-t, HAi-j-r). HAi-j-s and HAi-j-t both spend the jth output of StAi, conditional either on a preimage reveal or a timeout respectively; HAi-j-p and HAi-j-r spend the output of HAi-j-s and HAi-j-t respectively, as well as the output of ComAi. (s=success commitment, t=timeout commitment, p=payment on success, r=refund) And Bob has similar versions of all of these. So if Alice is honest, the process is: * Alice publishes StAi * Alice publishes HAi-j-{s,t} for any HTLCs she is able to resolve immediately; as does Bob. * Alice waits for to_self_delay to complete * Alice publishes ComAi, and any HAi-j-{r,p} transactions she is able to, and if desired consolidates her funds. * As any remaining HTLCs resolve, those are also claimed. * Bob's InB output is available to do whatever he wants with. If Alice is dishonest, the process is: * Alice publishes StAi, and perhaps publishes some HAi-j-{s,t} transactions. * Bob spends the first output of StAi unilaterally claiming the * penalty, meaning ComAi can now never be confirmed. * Bob publishes StBi', and continues with the honest protocol. Bob only needs the usual O(log(n)) state in order to be able to reconstruct the key to spend the first output of revoked StAi txs. Because that prevents the corresponding ComAi from ever being published, no revoked HTLC-related state can make it on chain in any way that Bob needs to care about. If both Alice and Bob are dishonest (Alice tries to cheat, but Bob restored from an old backup and also publishes a revoked state) then both the StAi and StBi' may have their first output claimed by the other party, in which case the channels funds are lost (unless Alice and Bob manage to agree to a cooperative close somehow, even after all the attempts to cheat each other). While 4+8k transactions per state is a lot, I think you only actually need 2+4k signatures in advance (StAi and HAi-j-{s,t} only need to be signed when they're broadcast). Perhaps using ANYPREVOUT would let you reduce the number of HTLC states? Step 2: Efficient Factories for Lightning Channels https://lists.linuxfoundation.org/pipermail/lightning-dev/2023-January/003827.html https://github.com/JohnLaw2/ln-efficient-factories This generalizes the tunable penalties setup for more than two participants. The first part of this is a straightforward generalisation, and doesn't cover HTLCs. Where we had 2(2+4k) transactions previously, we presumably would now have P(2+4k) transactions, where P is the number of participants. The second part of this aims to avoid that factor P. It does this by introducing Trigger and Mold transactions. To do this, we first establish the number of states that the factory will support; perhaps 2**40. In that case, the trigger transaction will spend the funding tx, and have 40+1 outputs. All of them will require a P-of-P signature to be spend, with the first also requiring a relative timelock of 3*to_self_delay and containing the channel funds, while the rest contain dust-ish amounts. For any given state i, we represent i as a binary number, and the commitment tx Com_i will spend the outputs corresponding to "0" in the binary number, as well as the first output containing the channel funds. We will also have St{A..Z}_i transactions, spending In{A..Z} as previously. The sole output of StAi (etc) will be spent either by any other participant via a revoked pubkey, or else by the Mold{A..Z}_i transaction. The Mold{A..Z}_i transaction will also spend the remaining outputs of the Trigger transaction that were not spent by Com_i. In the honest scenario, this looks like: * Alice publishes Trigger * Alice publishes StA_i * Alice waits to_self_delay * Alice publishes MoldA_i * Alice waits an additional 2*to_self_delay * Alice publishes Com_i * Alice and everyone else claims their funds There are various dishonest scenarios available: * If Alice publishes StA_i before publishing the Trigger, someone else publishes the Trigger. * If Alice doesn't publish StA_i quickly, Bob publishes StB_i and continues. If multiple St{A..Z}_i's are published, whoever does not publish the Mold transaction simply reclaims their funds by the "revocation" path. * If Alice publishes an old StA_i, Bob claims the funds using the revoked key, penalising A, and then publishes the correct StB_i, and continues. * If Alice does not publish MoldA_i, Bob can publish StB_i and MoldB_i. * Once MoldB_i is confirmed, someone could attempt to broadcast an outdated Com_i' where i' < i. In that case, the outputs corresponding to all the 1's in the binary representation of i are already spent (by MoldB_i), but for any i' < i, the i' necessarily has a 0 somewhere where i had a 1 (otherwise i' >= i), so Com_i' would be double spending an input already spent by MoldB_i. In this case, you need to track P+1 outputs (F+InA..Z), you need O(P*log(N)) state, and the onchain impact of a unilateral close is ideally 4 transactions (Trigger, StAi, MoldAi, Comi) with size O(log(MaxN)), but maybe be up to 2*P+2 transactions (Trigger, St{A..Z}i, MoldAi, refund{B..Z}i, Comi). Note that this doesn't consider HTLCs at all. Step 3: Factory Optimized protocols for Lighting https://github.com/JohnLaw2/ln-factory-optimized https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-December/003782.html https://bitcoinops.org/en/newsletters/2022/12/14/#factory-optimized-ln-protocol-proposal Perhaps this should have been step 2, whoopsie. Anyway, this optimises the construction in step 1 for channels included in factories; mostly to deal with the fact that closing a factory is tedious and can take a while. I'm ignoring the Partial-Factory-Optimized step -- it's barely different to the Tunable Penalty mechanism. The Fully Factory Optimized protocol is more interesting. The main idea here is this: if a channel in a factory goes defunct while an HTLC is pending, we'd like to be able to guarantee we've resolved the HTLC to our satisfaction while deferring the decision of whether to shutdown the entire factory in order to close the channel, just in case our channel partner eventually comes back, and we can resolve everything properly. The way we do that is twofold: first, we set things up so that the default resolution of an HTLC is a refund. That immediately does away with half of our HTLC transactions, because now we only need the success/payment paths, not the timeout/refund ones. Second, we require whoever's going to receive the payment to publish their StXi transaction -- that avoids us having to do P*k success/payment paths, now we only need 1*k success/payment paths. Unfortunately, we do introduce a new "kickoff" transaction to make it a three transaction kickoff/success/payment path, rather than just two transactions. In this case your transactions are: F - the funding output, only available once the factory is closed InB - as before StBi - your state commitment, one output that will be spent by ComBi, k outputs for each pending HTLC paying to B in state i. HBi-j-k - the kickoff transaction for HTLC "j" paying to B, spends the appropriate output of StBi, conditional on revealing the HTLC preimage and B's signature. Spendable either by B after to-self-delay, or by A after the HTLC's expiry plus to-self-delay HBi-j-s - the success transaction, spendable by B HBi-j-p - the payment transaction, spends HBi-j-s and the HTLC output from ComB_i HAi-j-p - the payment transaction, spends HBi-j-s and the HTLC output from ComA_i I think there's an error in the paper here; it says as well as being spendable by H{A,B}i-j-p as above, the HTLC output in ComA_i should be spendable by A after to-self-delay. I believe it should require both to-self-delay (relative timelock) and the HTLC expiry (absolute timelock) before it can be spendable by A. Anyway, how's that work? If you want to shut the factory down immediately, it looks like: * Shut the factory down * Broadcast StBi, HBi-j-k * Wait to-self-delay * Broadcast ComBi, HBi-j-s, HBi-j-p * Done! If you were cheating, then: * Alice steals StBi's first output if i was on old state, and ComBi cannot be broadcast. Alice publishes the current StAi', ComAi', etc. * If the HTLC had timed out, Alice claims the output of HBi-j-k before to-self-delay is finished, so that HBi-j-p cannot be broadcast, then claims the output of ComBi once both timeouts are complete. However, what if you don't want to shut the factory down? In that case: * Broadcast StBi, HBi-j-k. * Wait for to_self_delay. * Spend HBi-j-k to HBi-j-s. * Wait some more. * The other guy comes online! Let's recover the channel! * Propose spending the first output of StBi and the output of HBi-j-s to a new InB2, but don't sign it. * Update all the off-chain channel data to a new state i+1 that uses InB2 instead of InB, and that acknowledges your claim to the HTLC funds. * Sign and broadcast InB2 * Continue If the other guy doesn't come online, you close the factory, immediately spend F and the first output of StBi via ComBi, and immediately spend HBi-j-s and the HTLC output of ComBi to claim your funds. While this is described as an optimisation focussed on improving channels within factories; it seems to me that the reduction in state compared to the "tunable penalties" approach in step 1 makes this a strict improvement in general. Anyway, combining this step and the previous gives us an idea how to do both factories and HTLCs. I believe that would look like: Factory funding output -- multisig of A..Z Factory In{A..Z} Factory Trigger, spends the funding output Factory St{A..Z}i spends In{A..Z} Factory Mold{A..Z}i spends St{A..Z}i and various Trigger outputs Factory Com_i spends the other Trigger outputs; its outputs are the channels in the factory. Channel F_x - an output of the Factory Com_i Channel InA, InB Channel StAi, StBi -- spends InA/InB Channel ComAi, ComBi -- spends F_x and the first output of StA/StB Channel H{A,B}i_j_{k,s,p} -- success path funds for active HTLCs So to update the channel state, the channel participants need: 3*k HTLC transactions (only 1 pre-signed) 2 commitment transactions (both pre-signed) 2 St transactions (neither pre-signed) Every time the factory updates, every channel state must also update (as the channel funding outputs will change txid). Every participant needs 1+c "In" confirmed utxos available -- one for the factory itself, and one for each channel they're involved in. Every participant needs to monitor P+c+1 outputs on chain -- the factory Funding output (which may be spent by the Trigger tx), the P St{A..Z}i outputs, and their counterparty's InX output for each channel they're participating in. That's getting pretttty complicated, so I'm not confident I've got it all in my head correctly, but I think it still works. I'm skipping over the watchtower-freedom/casual user section here. cf https://bitcoinops.org/en/newsletters/2022/10/12/#ln-with-long-timeouts-proposal Step 4: Resizing Lightning Channels Off-Chain / Hierarchial Channels https://lists.linuxfoundation.org/pipermail/lightning-dev/2023-March/003886.html https://github.com/JohnLaw2/ln-hierarchical-channels https://bitcoinops.org/en/newsletters/2023/03/29/#preventing-stranded-capital-with-multiparty-channels-and-channel-factories Hey, we made it to this thread! I'm not entirely sure of the novelty in this proposal; once you have channels in factories, lots of magic is possible, but it's all very complex. I believe the particular proposal here is something like: - Instead of just having Alice/Bob/Carol/etc as identities in lightning, let them "pair" up, so that AliceBob is considered a node, and CarolDave is also a node. - So we have a utxo where AliceBob has a channel with CarolDave, and another where CarolDave has a channel with Elizabeth, eg. - But actually the AliceBob/CarolDave utxo is a factory; and there's an internal channel between Alice and Bob, and another between Carol and Dave - Now, because we describe AliceBob and CarolDave as a channel, that means funds can move between AliceBob and CarolDave; but that is equivalent to saying the overall capacity of the internal Alice/Bob channel is actually decreasing without any on-chain activity! Neat! But... that was always the point of channel factories? And the specific structure of four participants split into a single pair of channels doesn't seem particularly compelling? I don't know, I feel like I'm missing something here. Or maybe it's just the first three steps were amazing, so merely interesting seems pedestrian by comparison? Hmm, looking at Harding's email, I see: > **Liquidity multiplexing:** Alice, Bob, Carol, and Dan each rightfully > own some portion of a UTXO. Alice and Bob expect to always be > available; Carol and Dan may sometimes be unavailable. The proposal > allows Carol and Dan to spend/receive in combination with Alice and > Bob, but also ensures Alice and Bob can spend back and forth the > entirety their portions of the UTXO even if Carol, Dan, or both of > them are unavailable. I guess I'm not entirely enthusiastic about that because in that case Alice can only send funds to Carol when Dan (and whoever else is involved in the factory) eventually come online to signoff on the factory state update. That's still useful for (slow) offchain channel reallocations, but it doesn't seem reliable/fast enough for a payment. For the case where all factory participants are reliably online (perhaps with some exceptions) I guess I could see that making sense? Then you're generally just treating it as a 4-party channel of A/B/C/D with everyone able to easily forward to anyone; but when Alice is offline for system maintenance for an hour every week, it automatically degrades to just having the Carol/Dave channel operational, with no other problems. fin Cheers, aj _______________________________________________ Lightning-dev mailing list Lightning-dev@lists.linuxfoundation.org https://lists.linuxfoundation.org/mailman/listinfo/lightning-dev