free web page hit counter
Technical: More channel mechanisms!

Technical: More channel mechanisms!

Bitcoin Reddit

Reddit / Bitcoin Reddit 28 Views

This is a followup of my older post about the history of payment channel mechanisms.

The "modern" payment channel system is Lightning Network, which uses bidirectional indefinite-lifetime channels, using HTLCs to trustlessly route through the network.

However, at least one other payment channel mechanism was developed at roughly the same time as Lightning, and there are also further proposals that are intended to replace the core payment channel mechanism in use by Lightning.

Now, in principle, the "magic" of Lightning lies in combining two ingredients:

  1. Offchain updateable systems.
  2. HTLCs to implement atomic cross-system swaps.

We can replace the exact mechanism implementing an offchain updateable system. Secondly we can replace the use of HTLCs with another atomic cross-system swap, which is what we would do when we eventually switch to payment points and scalars from payment hashes and preimages.

So let's clarify what I'll be discussing here:

  • I will be discussing mechanisms for the offchain updateable system, which are generally called "payment channel mechanisms". The exact contracts that can be transported across such systems, such as HTLCs, the Scriptless-Script point-based variant, and Discrete Log Contracts, will have to wait another post.
  • Payment channel mechanisms are designed to be trust-minimized. They might not achieve this design goal (consider the broken Satoshi sequence numbers, or the pre-SegWit Spilman, which I still class as "payment channel mechanism"), but mechanisms which invoke trust in one participant or other as inherent parts of their design are not true payment channels. Such constructions might be of interest, but I will not discuss them here.

Now I might use "we" here to refer to what "we" did to the design of Bitcoin, but it is only because "we" are all Satoshi, except for Craig Steven Wright.

So, let's present the other payment channel mechanisms. But first, a digression.

Digression: the new nSequence and OP_CHECKSEQUENCEVERIFY

The new relative-timelock semantics of nSequence.

Last time we used nSequence, we had the unfortunate problem that it would be easy to rip off people by offering a higher miner fee for older state where we own more funds, then convince the other side of the channel to give us goods in exchange for a new state with tiny miner fees, then publish both the old state and the new state, then taunt the miners with "so which state is gonna earn you more fees huh huh huh?".

This problem, originally failed by Satoshi, was such a massive facepalm that, in honor of miners doing the economically-rational thing in the face of developer and user demands when given a non-final nSequence, we decided to use nSequence as a flag for the opt-in replace-by-fee.

Basically, under opt-in replace-by-fee, if a transaction had an nSequence that was not 0xFFFFFFFF or 0xFFFFFFFE, then it was opt-in RBF (BIP125). Because you'd totally abuse nSequence to bribe miners in order to steal money from your bartender, especially if your bartender is not a werebear.

Of course, using a 4-byte field for a one-bit flag (to opt-in to RBF or not) was a massive waste of space, so when people started proposing relative locktimes, the nSequence field was repurposed.

Basically, in Bitcoin as of the time of this writing (early 2020) if nSequence is less than 0x80000000 it can be interpreted as a relative timelock. I'll spare you the details here, BIP68 has them, but basically nSequence can indicate (much like nLockTime) either a "real world" relative lock time (i.e. the output must have been confirmed for X seconds before it can be spent using a transaction with a non-zero nSequence) or the actual real world, which is measured in blocks (i.e. the output must have been confirmed for N blocks before it can be spent using a transaction with a non-zero nSequence). Of course, this is the Bitcoin universe and "seconds" is a merely human delusion, so we will use blocks exclusively.

And similarly to OP_CHECKLOCKTIMEVERIFY, we also added OP_CHECKSEQUENCEVERIFY in BIP112. This ensures that the nSequence field is a relative-locktime (i.e. less than 0x80000000) and that it is the specified type (block-based or seconds-based) and that it is equal or higher to the specified minimum relative locktime.

It is important to mention the new, modern meaning of nSequence, because it is central to many of the modern payment channel mechanisms, including Lightning Poon-Dryja.

Lessons learned?

  • Poetic justice is a thing. Go go new nSequence!

Decker-Wattenhofer "Duplex Micropayment Channels"

Mechanisms-within-mechanisms for a punishment-free bidirectional indefinite-lifetime payment channel.

The Decker-Wattenhofer paper was published in 2015, but the Poon-Dryja "Lightning Network" paper was published in 2016. However, the Decker-Wattenhofer paper mentions the Lightning mechanism, specifically mentioning the need to store every old revocation key (i.e. the problem I mentioned last time that was solved using /u/RustyReddit shachains). Maybe Poon-Dryja presented the Lightning Network before making a final published paper in 2016, or something. Either that or /u/cdecker is the Bitcoin time traveler.

It's a little hard to get an online copy now, but as of late 2019 this seems to work: copy

Now the interesting bit is that Decker-Wattenhofer achieves its goals by combining multiple mechanisms that are, by themselves, workable payment channel mechanisms already, except each has some massive drawbacks. By combining them, we can minimize the drawbacks.

So let's go through the individual pieces.

Indefinite-lifetime Spilman channels

As mentioned before, Spilman channels have the drawback that they have a limited lifetime: the lock time indicated in the backoff transaction or backoff branch of the script. However, instead of an absolute lock time, we can use a relative locktime.

In order to do so, we use a "kickoff" transaction, between the backoff transaction and the funding transaction. Our opening ritual goes this way, between you and our gender-neutral bartender-bancho werebear:

  1. First, you compute the txid for the funding transaction and the kickoff transaction. The funding transaction takes some of your funds and puts it into a 2-of-2 between you and the bartender, and the kickoff is a 1-input 1-output transaction that spends the funding transaction and outputs to another 2-of-2 between you and the bartender.
  2. Then, you generate the backoff transaction, which spends the kickoff transaction and returns all the funds to you. The backoff has a non-zero nSequence, indicating a delay of a number of blocks agreed between you, which is a security/convenience tradeoff parameter
  3. You sign the backoff transaction, then send it to the bartender.
  4. The bartender signs the backoff, and gives back the fully-signed transaction to you.
  5. You sign the kickoff transaction, then send it to the bartender.
  6. The bartender signs the kickoff, and gives it back to you fully signed.
  7. You sign and broadcast the funding transaction, and both of you wait for the funding transaction to be deeply confirmed.

The above setup assumes you're using SegWit, because transaction malleability fix.

At any time, either you or the bartender can broadcast the kickoff transaction, and once that is done, this indicates closure of the channel. You do this if you have drunk enough alcoholic beverages, or the bartender could do this when he or she is closing the bar.

Now, to get your drinks, you do:

  1. Sign a transaction spending the kickoff, and adding more funds to the bartender, to buy a drink. This transaction is not encumbered with an nSequence.
  2. Hand the signed transaction to the bartender, who provides you with your next drink.

The channel is closed by publishing the kickoff transaction. Both of you have a fully-signed copy of the kickoff, so either of you can initiate the close.

On closure (publication and confirmation of the kickoff transaction), there are two cases:

  1. You fail to pick up any chicks at the bar (I prefer female humans of optimum reproductive age myself rather than nestling birds, but hey, you do you) so you didn't actually spend for drinks at all. In this case, the bartender is not holding any transactions that can spend the kickoff transaction. You wait for the agreed-upon delay after the kickoff is confirmed, and then publish the backoff transaction and get back all the funds that you didn't spend.
  2. You spend all your money on chicks and end up having to be kicked into a cab to get back to your domicile, because even juvenile birds can out-drink you, you pushover. The bartender then uses the latest transaction you gave (the one that gives the most money to him or her --- it would be foolish of him or her to use an earlier version with less money!), signs it, and broadcasts it to get his or her share of the money from the kickoff transaction.
  • Pro: Number of updates is limited only by the amount of money you have in the "payer" side of the channel.
  • Pro: no lifetime limit. You can keep the channel open indefinitely if you don't transact over it.
  • Pro: The delay can be very small.
  • Con: Unidirectional.

Decrementing nSequence channels

Enforcing order by reducing relative locktimes.

I believe this to be novel to the Decker-Wattenhofer mechanism, though I might be missing some predecessor.

This again uses the new relative-locktime meaning of nSequence. As such, it also uses a kickoff transaction like the above indefinite-lifetime Spilman channel. Set up is very similar to the setup of the above indefinite-lifetime Spilman channel, except that because this is bidirectional, we can actually have both sides put money into the initial starting backoff transaction.

We also rename the "backoff" transaction to "state" transaction. Basically, the state transaction indicates how the money in the channel is divided up between the two participants. The "backoff" we sign during the funding ritual is now the first state transaction. Both sides keep track of the current state transaction (which is initialized to the first state transaction on channel establishment).

Finally, the starting nSequence of the first state transaction is very large (usually in the dozens or low hundreds of blocks).

Suppose one participant wants to pay the other. The ritual done is then:

  1. A new version of the current state transaction is created with more money in the payee side.
  2. This new version has nSequence that is one block lower than the current state transaction (in practice it should be a few blocks lower, not just one, because sometimes miners find blocks in quick succession).
  3. Both sides exchange signatures for the new state transaction.
  4. Both sides set the new state transaction as the current state transaction that will be the basis for the next payment.

When the channel is closed by publication of the kickoff transaction, then the transaction with the lowest nSequence becomes valid earlier than the other state transactions. This is enough to enforce that the most recent state transaction (the one with the lowest nSequence, and thus the first to become valid) is published.

  • Pro: bidirectional.
  • Pro: indefinite lifetime, at least if no updates are done.
  • Pro: it shows that life is not without a sense of irony. The original design for nSequence replacement required an incrementing nSequence using the original Satoshi's Vision interpretation of nSequence (which doesn't work). But this channel mechanism instead uses a decrementing nSequence using the new Bitcoin Core interpretation of nSequence as a relative timelock (which does, in fact, work).
  • Con: Number of updates is limited by the starting maximum nSequence delay. Increasing this delay increases the encumbrance if the channel is closed without any activity, but reducing this delay reduces the number of payments in either direction you can use before you have to close the channel and recreate it. For example, let's have a maximum of 144 blocks of delay. Each update, we decrement the nSequence by 4, because that handles up to the very rare case where up to 3 blocks arrive in very close succession to each other. That only gives us 36 updates for a worst-case of one day of delay, a very bad tradeoff.
  • Con: You can only be safely offline for a number of blocks equal to the "step", but the maximum delay you may incur is the product of the step times the number of updates you want to make. So you want a small step (because you don't want your worst-case lock time to be large) but you want a big step (because you want to still be safe even if you go offline for a long time).

Mechanism-within-mechanism

Combining the ingredients of the Decker-Wattenhofer Duplex Micropayment Channels concoction.

Of note is that we can "chain" these mechanisms together in such a way that we strengthen their strengths while covering their weaknesses.

A note is that both the indefinite-lifetime nSequence Spilman variant, and the above decrementing nSequence mechanism, both have "kickoff" transactions.

However, when we chain the two mechanisms together, it turns out that the final transaction of one mechanism also serves as the kickoff of the next mechanism in the chain.

So for example, let's chain two of those decrementing nSequence channels together. Let's make them 144 blocks maximum delay each, and decrement in units of 4 blocks, so each of the chained mechanisms can do 37 updates each.

We start up a new channel with the following transactions:

  1. A funding transaction paying to a 2-of-2, confirmed deeply onchain. All other transactions are offchain until closure.
  2. A kickoff transaction spending the funding transaction output, paying to a 2-of-2.
  3. A "stage 1" decrementing nSequence state transaction, spending the kickoff, with current nSequence 144, paying to a 2-of-2.
  4. A "stage 2" decrementing nSequence state transaction, spending the stage 1, with current nSequence 144, paying to the initial state of the channel.

When we update this channel, we first update the "stage 2" state transaction, replacing it with an nSequence lower by 4 blocks. So after one update our transactions are:

  1. A funding transaction paying to a 2-of-2, confirmed deeply onchain. All other transactions are offchain until closure.
  2. A kickoff transaction spending the funding transaction output, paying to a 2-of-2.
  3. A "stage 1" decrementing nSequence state transaction, spending the kickoff, with current nSequence 144, paying to a 2-of-2.
  4. A "stage 2" decrementing nSequence state transaction, spending the stage 1, with current nSequence 140, paying to the second state of the channel.

The first 3 transactions are the same, only the last one is replaced with a state transaction with lower `nSequence.

Things become interesting when we reach the "stage 2" having nSequence 0. On the next update, we create a new "stage 1", with an nSequence that is 4 lower, and "reset" the "stage 2" back to an nSequence of 144.

This is safe because even though we have a "stage 2" with shorter nSequence, that stage 2 spends a stage 1 with an nSequence of 144, and the stage 1 with nSequence of 140 would beat it to the blockchain first.

This results in us having, not 36 + 36 updates, but instead 36 * 36 updates (1296 updates). 1296 updates is still kinda piddling, but that's much better than just a single-stage decrementing nSequence channel.

The number of stages can be extended indefinitely, and your only drawback would be the amount of blockchain space you'd spend for a unilateral close. Mutual cooperative closes can always shortcut the entire stack of staged transactions and cut it to a single mutual cooperative close transaction.

But that's not all! You might be wondering about the term "duplex" in the name "Duplex Micropayment Channels".

That's because the last decrementing nSequence stage does not hold the money of the participants directly. Instead, the last stage holds two indefinite-lifetime Spilman channels. As you might remember, Spilman channels are unidirectional, so the two Spilman channels represent both directions of the channel. Thus, duplex.

Let's go back to you and your favorite werebear bartender. If you were using a Decker-Wattenhofer Duplex Micropayment Channel, you'd have several stages of decrementing nSequence, terminated in two Spilman channels, a you-to-bartender channel and a bartender-to-you channel.

Suppose that, while drinking, the bartender offers you a rebate on each drink if you do some particular service for him or her. Let us not discuss what service this is and leave it to your imagination. So you pay for a drink, decide you want to get the rebate, and perform a service that the bartender finds enjoyable. So you transfer some funds on the you-to-bartender direction, and then later the bartender transfers some funds in the bartender-to-you channel after greatly enjoying your service.

Suppose you now exhaust the you-to-bartender direction. However, you note that the rebates you've earned are enough to buy a few more drinks. What you do instead is to update the staged decrementing nSequence mechanisms, and recreate the two Spilman directions such that the you-to-bartender direction contains all your current funds and the bartender-to-you direction contains all the bartender's funds. With this, you are now able to spend even the money you earned from rebates. At the same time, even if the staged decrementing nSequence mechanisms only have a few hundred thousand updates, you can still extend the practical number of updates as long as you don't have to reset the Spilman channels too often.

  • Pro: chaining allows more possible updates!
  • Pro: no "toxic waste"! That is, old backups of your channel state database won't cause you to lose funds automatically.
  • Con: unilateral closes have long lock times, due to the chaining of decrementing-nSequence mechanisms.
  • Con: unilateral closes put a lot of transactions onchain, due to the chaining of multiple nested mechanisms.
  • Con: HTLCs are affected by the total nSequence delay needed by the mechanism. This is because HTLCs have an absolute timelock in their contract, and this can only be enforced onchain. However, the existence of nSequence delays means that absolute timelocks need to trigger unilateral closes several blocks before the absolute timelock, by the nSequence total delta of all the stacked mechanisms. In Poon-Dryja you can safely keep a channel open until just before the absolute timelock expires.
  • Con: It's not clear to me if the cancellable HTLCs used by Lightning can be hosted by Spilman channels. The HTLCs used in Lightning are "cancellable" because of a nifty ability of every offchain update mechanism: every contract has an additional clause "... or if every signer of the offchain update mechanism agrees, we can ignore this contract and place its funds wherever we agree on". This is not a degradation of security since the HTLCs in a channel are between the two users of the channel, so both of them need to agree anyway in order to accept such a cancellation. This ability is used to propagate forwarding failures back to the payer: instead of waiting for the HTLCs to time out, the node just says to the sender "between you and me, this HTLC won't propagate anyway, because 'insert some reason here', so let's just put the money in it back to you". However, this seems unsafe with Spilman channels, as a cancelled HTLC will still be available on older states of the Spilman channel, and potentially claimable by the payee end up until the timelock. Removing the Spilman channels at the end would remove this issue, but now you are limited to a few hundred thousand updates even with lots of decrementing-nSequence layers.

Burchert-Decker-Wattenhofer Channel Factories

Because you like channels so much, you put channels inside channels so you could pay while you pay. I N C E P T I O N

The Decker-Wattenhofer Duplex Micropayment Channels introduced the possibility of nesting a channel mechanism inside another channel mechanism. For example, it suggests nesting a decrementing-nSequence mechanism inside another decrementing-nSequence mechanism, and having as well an unlimited-lifetime Spilman channel at the end. In the Decker-Wattenhofer case, it is used to support the weakness of one mechanism with the strength of another mechanism.

One thing to note is that while the unlimited-lifetime Spilman channel variant used is inherently two-participant (there is one payer and one payee), the decrementing-nSequence channel mechanism can be multiparticipant.

Another thing of note is that nothing prevents one mechanism from hosting just one inner mechanism, just as it is perfectly fine for a Lightning Network channel to have multiple HTLCs in-flight, plus the money in your side, plus the money in the counterparty's side. As these are "just" Bitcoin-enforceable contracts, there is no fundamental difference between an HTLC, and a payment channel mechanism.

Thus the most basic idea of the Burchert-Decker-Wattenhofer Channel Factories paper is simply that we can have a multiparticipant update mechanism host multiple two-party update mechanisms. The outer multiparticipant update mechanism is called a "channel factory" while the inner two-party update mechanisms are called "channels".

The exact mechanism used in the Burchert-Decker-Wattenhofer paper uses several decrementing-nSequence mechanisms to implement the factory, and Decker-Wattenhofer Duplex Micropayment Channels to implement the channel layer.

However, as noted before, there is no fundamental difference between a Poon-Dryja channel and an HTLC. So it is in fact possible to have chained Decker-Wattenhofer decrementing-nSequence mechanisms to implement the factory level, while the channels are simply Poon-Dryja channels.

Conclusion

So this concludes for now an alternative mechanism to the classic Poon-Dryja that Lightning uses. The tradeoffs are significantly different between Decker-Wattenhofer vs Poon-Dryja:

  • Decker-Wattenhofer: No toxic waste: old data stolen from you, or which you inadvertently use, is not going to lose all your funds.
  • Decker-Wattenhofer: Multiple participants in a single offchain mechanism, enabling things like Channel Factories.
  • Poon-Dryja: Doesn't have ridiculously long lock times in the unilateral close case.
  • Poon-Dryja: Supports HTLCs for trustless forwarding (not clear if Decker-Wattenhofer fully supports this without sacrificing the duplexed indefinite-lifetime Spilman channels at the end).

Copyright

Copyright 2020 Alan Manuel K. Gloria. Released under CC-BY.

submitted by /u/almkglor
[link] [comments]

Comments