Re: [Lightning-dev] eltoo: A Simplified update Mechanism for Lightning and Off-Chain Contracts
Hi Christian, Let me know if I have summarized the paper accurately below: 1. SIGHASH_NOINPUT removes all inputs of the transaction copy before signing/verifying. 2. SIGHASH_NOINPUT can be combined with SIGHASH_SINGLE. It is dangerous to combine it with SIGHASH_NONE (as this also deletes all outputs of the transaction copy, so the signature only commits to nLockTime) and possibly pointless to combine it with SIGHASH_ANYONECANPAY (which deletes other inputs, but since there are no inputs after SIGHASH_NOINPUT, there is nothing else to delete). The BIP only mentions combining it with SIGHASH_SINGLE, in any case. 3. We have these kinds transactions: (1) funding transaction paying out to an ordinary N-of-N multisig (confirmed onchain); (2) offchain trigger transaction spending a funding transaction and paying out to the "update transaction script"; (3) offchain update transaction spending a trigger or update transaction and paying out to the "update transaction script" (the same script template as in trigger transaction); (4) offchain settlement transaction spending a trigger or update transaction and paying out to the counterparties according to the final agreed distribution of funds. Update and settlement transactions are signed with SIGHASH_NOINPUT flags. 4. The update transaction script has two branches: a CSV-encumbered N-of-N "settlement" branch, and a CLTV-encumbered N-of-N "update" branch. Crucially, we use past Unix timestamps for the CLTV encumberance on the update. The CLTV-encumberance starts with a minimum time value on the trigger transaction, and increments by 1 for every update transaction. 5. The CLTV-encumberance ensures that if a past update transaction is confirmed, we can spend it using any later update transaction, since CLTV uses `stackTop <= nLockTime`. The actual `nLockTime` we use is a past Unix timestamp so they are not actually time-encumbered, but the `OP_CLTV` still ensures that any later update transaction can be used to spend from any earlier update transaction, but not vice versa. This is actually quite clever. 6. The pubkeys on the settlement branch of each update transaction are different, and are derived using any hierarchical derivation method (trivially we can simply use the CLTV-encumberance value as the derivation index). This ensures that each settlement transaction can only spend a particular update transaction. Or in other words: There is a one-to-one correspondence between update and settlement transaction, and a settlement transaction can only confirm if the corresponding update transaction can be confirmed deeply enough without having its output re-spent. 7. The pubkeys on the update branch are all the same in all update transactions. This lets any update transaction replace any other update transaction, as long as the CLTV-encumberance (which is used for ordering rather than actual absolute locktime) is respected; together they mean that any later update transaction can replace any earlier update transaction. 8. If an old update transaction is confirmed, the settlement transaction corresponding to it is still encumbered by CSV and cannot be confirmed immediately. During this time, a later update transaction can spend it and be confirmed. If that update transaction is still not the latest available, it can be further replaced with any, later transaction (since the CLTV-encumberance and the `nLockTime` increase in lockstep with each other) until the latest update transaction is confirmed. 9. When the latest update transaction gets confirmed, no other update transaction can successfully spend its "update" branch (as their `nLockTime` is less than the CLTV-encumberance on that branch). This lets the CSV-encumbered settlement transaction be confirmable after some blocks. 10. Update transactions pay no fees! Instead, for update transactions (but not settlement transactions) we additionally give SIGHASH_SINGLE: thus, no matter what transaction they end up in, the signatures for the update transaction will always be verified against only the update transaction output that pays out to the update transaction script. We join update transactions with a spend of some onchain UTXO we control, which pays for the fees of the joined the update transactions, and may have a second output that is the remainder of the fund after paying the fees. 11. Settlement transactions can carry contracts (in much the same way that Poon-Dryja commitment transactions); however contracts that contain absolute timelock components will be affected by the CSV-encumberance of the settlement transaction. --- Some pros and cons relative to Poon-Dryja (LN-penalty) channels: - Requires more transactions in the worst-case: trigger, update, settlement. Compare to Poon-Dryja: commitment, claim. Decker-Russell-Osuntokun channels can be trigger-settlement but only in the degenerate case where the channel was never
Re: [Lightning-dev] eltoo: A Simplified update Mechanism for Lightning and Off-Chain Contracts
ZmnSCPxj writes: > Good morning Christian, > > This is very interesting indeed! > > I have started skimming through the paper. > > I am uncertain if the below text is correct? > >> Throughout this paper we will use the terms *input script* to refer to >> `witnessProgram` and `scriptPubKey`, and *output script* to refer to the >> `witness` or `scriptSig`. > > Figure 2 contains to what looks to me like a `witnessProgram`, > `scriptPubKey`, or `redeemScript` but refers to it as an "output script": > >>OP_IF >>10 OP_CSV >>2 As Bs 2 OP_CHECKMULTISIGVERIFY >>OP_ELSE >>2 Au Bu 2 OP_CHECKMULTISIGVERIFY >>OP_ENDIF >> >> Figure 2: The output script used by the on-chain update transactions. > > Regards, > ZmnSCPxj Darn last minute changes! Yes, you are right, I seem to have flipped the two definitions. I'll fix that up and push a new version. ___ Lightning-dev mailing list Lightning-dev@lists.linuxfoundation.org https://lists.linuxfoundation.org/mailman/listinfo/lightning-dev
Re: [Lightning-dev] eltoo: A Simplified update Mechanism for Lightning and Off-Chain Contracts
Excellent summary ZmnSCPxj, I'll try to address the points inline (if there is anything to add that is): ZmnSCPxj writes: > Hi Christian, > > Let me know if I have summarized the paper accurately below: > > 1. SIGHASH_NOINPUT removes all inputs of the transaction copy before > signing/verifying. It sets them to a known constant, in this case we just blank them. Removing could entail more costly serialization depending on the implementation. bitcoind serializes into a hash accumulator so it'd not make a difference there, but others may do it differently. > 2. SIGHASH_NOINPUT can be combined with SIGHASH_SINGLE. It is > dangerous to combine it with SIGHASH_NONE (as this also deletes all > outputs of the transaction copy, so the signature only commits to > nLockTime) and possibly pointless to combine it with > SIGHASH_ANYONECANPAY (which deletes other inputs, but since there are > no inputs after SIGHASH_NOINPUT, there is nothing else to delete). > The BIP only mentions combining it with SIGHASH_SINGLE, in any case. `SIGHASH_SINGLE` really is only singled out (no pun intended) because it is usefull for the fee feature that Rusty came up with. It's not our intention to exclude other uses, but you're right, it's hard to come up with a use-case for `SIGHASH_NOINPUT` in combination with `SIGHASH_NONE` or `SIGHASH_ANYONECANPAY` :-) In particular we don't want to exclude potential future sighash types, but those will have to be dealt with when they come up. > 3. We have these kinds transactions: (1) funding transaction paying > out to an ordinary N-of-N multisig (confirmed onchain); (2) offchain > trigger transaction spending a funding transaction and paying out to > the "update transaction script"; (3) offchain update transaction > spending a trigger or update transaction and paying out to the "update > transaction script" (the same script template as in trigger > transaction); (4) offchain settlement transaction spending a trigger > or update transaction and paying out to the counterparties according > to the final agreed distribution of funds. Update and settlement > transactions are signed with SIGHASH_NOINPUT flags. > > 4. The update transaction script has two branches: a CSV-encumbered > N-of-N "settlement" branch, and a CLTV-encumbered N-of-N "update" > branch. Crucially, we use past Unix timestamps for the CLTV > encumberance on the update. The CLTV-encumberance starts with a > minimum time value on the trigger transaction, and increments by 1 for > every update transaction. Really any monotonically increasing sequence of increments will work, but incrementing by 1 maximizes the number of updates we can perform, yes. > 5. The CLTV-encumberance ensures that if a past update transaction is > confirmed, we can spend it using any later update transaction, since > CLTV uses `stackTop <= nLockTime`. The actual `nLockTime` we use is a > past Unix timestamp so they are not actually time-encumbered, but the > `OP_CLTV` still ensures that any later update transaction can be used > to spend from any earlier update transaction, but not vice versa. > This is actually quite clever. Correct, the only reason to use `nLocktime` here is that we need the signature of the update transaction to commit to the state number, so we can't put it in the script itself. > 6. The pubkeys on the settlement branch of each update transaction > are different, and are derived using any hierarchical derivation > method (trivially we can simply use the CLTV-encumberance value as the > derivation index). This ensures that each settlement transaction can > only spend a particular update transaction. Or in other words: There > is a one-to-one correspondence between update and settlement > transaction, and a settlement transaction can only confirm if the > corresponding update transaction can be confirmed deeply enough > without having its output re-spent. Other bindings are possible, e.g., compare the `nLocktime` again, but binding through pubkeys minimizes the script size. > 7. The pubkeys on the update branch are all the same in all update > transactions. This lets any update transaction replace any other > update transaction, as long as the CLTV-encumberance (which is used > for ordering rather than actual absolute locktime) is respected; > together they mean that any later update transaction can replace any > earlier update transaction. > > 8. If an old update transaction is confirmed, the settlement > transaction corresponding to it is still encumbered by CSV and cannot > be confirmed immediately. During this time, a later update > transaction can spend it and be confirmed. If that update transaction > is still not the latest available, it can be further replaced with > any, later transaction (since the CLTV-encumberance and the > `nLockTime` increase in lockstep with each other) until the latest > update transaction is confirmed. Notice that the update transactions are actually paying their keep, i.e., th
Re: [Lightning-dev] eltoo: A Simplified update Mechanism for Lightning and Off-Chain Contracts
Good morning Christian, Thank you very much! > > Hi Christian, > > > > Let me know if I have summarized the paper accurately below: > > > > 1. SIGHASH_NOINPUT removes all inputs of the transaction copy before > > > > signing/verifying. > > > > It sets them to a known constant, in this case we just blank > > them. Removing could entail more costly serialization depending on the > > implementation. bitcoind serializes into a hash accumulator so it'd not > > make a difference there, but others may do it differently. Does this then mean that if the transaction has two inputs, we are still committing to the fact that there are two inputs? If so, it is probably useable together with SIGHASH_ANYONECANPAY. In fact, it seems, if we are using this to provide additional fees to update transactions, we should also use SIGHASH_ANYONECANPAY on the update UTXO path so that we can add new inputs to the transaction that will pay for the fee. > > Some pros and cons relative to Poon-Dryja (LN-penalty) channels: > > > > - Requires more transactions in the worst-case: trigger, update, > > > > settlement. Compare to Poon-Dryja: commitment, claim. > > > > Decker-Russell-Osuntokun channels can be trigger-settlement but only > > > > in the degenerate case where the channel was never updated (indeed for > > > > implementation simplicity we might rather prefer to make an initial > > > > update transaction at the start, instead of starting with a > > > > trigger-settlement). > > > > Actually the trigger can also be replaced in the cooperative case, > > meaning that the settlement in that case is just a single transaction, > > identical to LN-penalty. Yes, but the intent of this second list is to contrast it to Poon-Dryja/LN-penalty channels, so I skipped over similarities (well there is the onchannel reserve vs the onchain fee-paying reserve, but I would argue they are congruent and not exactly identical). One can argue that any viable channel-update-mechanism should be cooperatively collapsible to a single transaction on top of the funding transaction (because neither side has a good chance of contesting what has been agreed on, and collapsing them all to a single closing transaction is always an improvement due to having less data hit the blockchain), so if you did your job right in eltoo (and it looks to me like you all have) then that is already a given. > It is true that we've split the unilateral > > commitment from LN-penalty into two transactions, but we removed the > > need for a claim transaction, since funds directly go to the recipient > > and we have no our_unilateral/to_us or their_unilateral/to_them delay > > that needs to be sweeped (technically also not necessary, but all > > implementation do that if I'm not mistaken). Even if funds are not > > sweeped, the UTXO state is larger due to the bigger script that our > > simple outputs for the settlement. Ah, that is another difference I had not noticed, thank you. > > - The CSV-encumberance on settlement transactions, which are the ones > > > > which carry the contracts in the channel, affects all > > > > absolute-timelocked contracts transported on the channel. Compare to > > > > Poon-Dryja, where commitment transactions themselves are unencumbered > > > > by CSV, and we simply insert the revocation to spends of the contracts > > > > being transported (i.e. the reason why we have HTLC-success and > > > > HTLC-timeout transactions in BOLT spec). > > > > True, but as I argued in another mail, this is a fixed offset, that is > > in the same range as today's CLTV deltas for some nodes. So for the > > network of today using eltoo is roughly equivalent of adding another hop > > to our path :-) This may complicate routing decisions, however, in a mixed network where some channels are Poon-Dryja and some channels are Decker-Russell-Osuntokun; and if we deploy this as a back-compatible extension to the Lightning Network rather than a completely new network (and we should) then we will have such a mixed network. A payer routing through such a network finding two routes with identical fees and cltv-deltas, but one going through only Poon-Dryja channels and the other with one channel going through Decker-Russell-Osuntokun (and the rest still Poon-Dryja) would prefer the purely Poon-Dryja one, which does not have any additional csv-delta to impose on the route. Further, once a payer can find no purely Poon-Dryja routes, then it no longer matters how many Decker-Russell-Osuntokun channels a route hops, because csv-deltas are not additive. Unfortunately most standard routing I know of assume additive costs, meaning we would either have a two-phase routing (first we try to find only purely Poon-Dryja routes, then if there are none we accept Decker-Russell-Osuntokun channels) or hack around
Re: [Lightning-dev] eltoo: A Simplified update Mechanism for Lightning and Off-Chain Contracts
Good morning Christian, It seems to me we can remove the trigger transaction. The observation is that `nSequence`-encumbered transactions are only settlement transactions and not any update transactions. Thus, it is not needed for a trigger transaction to exist, if we can make update transactions behave as the trigger transaction for its settlement transaction or any later update transaction. So, let me propose, that the funding transaction outpoint could have the below SCRIPT: OP_IF # Settlement branch OP_FALSE OP_ELSE # Update branch. 500,000,000 is the minimum state number under Decker-Russell-Osuntokun. 5 OP_CHECKLOCKTIMEVERIFY 2 Au Bu 2 OP_CHECKMULTISIGVERIFY OP_ENDIF Thus, the funding outpoint can be spent directly by any update transaction, which has ` OP_FALSE` at its `witness` script. When creating a mutual close transaction, we simply use the update branch of the funding transaction above, again signing with ` OP_FALSE`. This does have the drawback that the mutual close transaction can be RBFed away by an update transaction (requiring more code to handle this case); but the latest update transaction can still be published in that case and we simply devolve down to the usual unilateral close branch. The drawback is that the mutual close transaction increases by 1 weight unit (the `OP_FALSE` in the `witness` script) plus the size of the more complicated funding transaction SCRIPT, which is no longer a regular 2-of-2 P2WSH and indelibly marking it as a Decker-Russel-Osuntokun mutual close. Taproot would help there by implicitly including a plain 2-of-2 fallback. Finally, we could argue that the mutual close transaction is expected to be the more common case, so increasing its size by even a small amount may outweigh the size reduction in the much rarer unilateral close case. Regards, ZmnSCPxj ___ Lightning-dev mailing list Lightning-dev@lists.linuxfoundation.org https://lists.linuxfoundation.org/mailman/listinfo/lightning-dev
[Lightning-dev] Mitigations for loop attacks
I have been thinking a lot lately about attacks on lightning routing nodes, the worst of which is the so-called Loop Attack [1]. See the mailing list thread for more details, but the basic idea is that a sender and receiver collude to create a long circuit and refuse to settle or fail the HTLC at the end until the last possible moment. The attack is particularly insidious for a few reasons. Firstly, an attack on a multi-hop route gives the attacker leverage, so that their funds locked in one channel cause funds to be locked up in every channel on the route. Second, the onion routing makes it difficult to attribute blame to a single node and defend against a repeated attack. First, I'll note that the case where a sender and recipient collude is a more active variant on an attack that any node in a route can perform. Even if a node does not initiate the payment, it can just accept offered HTLCs in the route, not forward them, and wait until the offered HTLC almost expires before failing. As noted above, the prior node in the route cannot determine whether the attacking node is at fault or one downstream from it. Even the sender may not be able to determine where in the route the payment failed because of the issue where the obfuscated error reason is not MAC'ed on every hop. There are two directions of solutions I have heard: 1) protocol support for decrypting the onion route if the HTLC is kept in-flight for too long 2) requiring fees even if the payment fails as a cost to the attacker 3) some sort of reputation system for nodes. Option 1 I'm afraid may be quite complex. Say this mechanism kicks in and all nodes in the circuit deobfuscate the route and are able to see the delays at each hop. The outcome we hope for is that there is one node clearly to blame and the prior hop in the route fails all channels with them. However, the attacker can of course control multiple successive hops in the route, one that looks innocent in front of one that looks guilty, then keep the channel alive and try again. So then all nodes need to keep a record of the full circuits and iteratively shift blame up the chain if bad HTLCs keep going through those channels. Option 2 is also problematic because it only protects against the case where the sender is colluding with the receiver, and not where a routing node is opportunistically delaying payments. This would, however, likely be successful against nodes being annoying and sending tons of payments with randomly generated payment hashes in order to "ping" a circuit. Option 3 has become my preferred solution. The idea is that that for each node that one has channels with, it only forwards payments through them if they have a good history, otherwise it fails the payment. Notably, we ignore whether the downstream hop is directly responsible for delaying a payment or whether they are simply willing to forward to another node that is intentionally delaying -- both should be considered bad behavior. In my opinion, this type of solution fits best into the Lightning model of independent, linked channels where each node has private contracts with its direct peers. It also is the simplest in the context of onion routing because if you are offered an HTLC to route, the only decision you can make is whether to forward it or fail it based on the amount, previous hop, and next hop. When I refer to "reputation" hereafter, I do not mean a global reputation that is gossiped about -- just a local view of a peer's history. For a Sybil-resistant reputation system, we can use money as a way of measuring reputation. Fees collected through routing payments across channels raise the reputation of the channel peer. You lose reputation by accepting an HTLC and not failing or fulfilling it quickly, proportional to the value of the HTLC and time to respond. What this essentially means is that if an attacker wants people to send them HTLCs to delay, they pay a certain amount in fees over time. So each node will track for each peer 1) total fees collected from forwarding on routes where that peer is the prior hop 2) total fees collect from forwarding on routes where that peer is the next hop 3) total value times time of money locked in offered HTLCs on channels to the peer. To track the third, as soon as an offered HTLC is locked in by the peer, the node starts a timer. When the HTLC is settled or failed or handled on-chain, the timer stops and the node registers the total elapsed time multiplied by the value of the HTLC. A very simple strategy would be to have two factors, R_inbound and R_outbound and calculate reputation per peer as R_inbound * total inbound fees + R_outbound * total outbound fees - total Bitcoin-seconds locked in an HTLC. When forwarding a payment, you calculate the worst case reputation loss, HTLC value * (CLTV - current block height) * 10 min / block, and fail the payment if that value is greater than their current score. Effectively, reputation scores are the maximum number of