Skip to content

Instantly share code, notes, and snippets.

@zsfelfoldi
Last active July 2, 2019 08:02
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zsfelfoldi/df66b0226068761a7654d6c2bb2e3ed3 to your computer and use it in GitHub Desktop.
Save zsfelfoldi/df66b0226068761a7654d6c2bb2e3ed3 to your computer and use it in GitHub Desktop.

A payment channel protocol for LES incentivization

Author: Zsolt Felfoldi (zsfelfoldi@ethereum.org)

Special thanks to:

  • Daniel A. Nagy for the idea of the "stamper" role
  • Viktor Tron and the rest of the Swarm team for their work on the SWAP, SWEAR and SWINDLE framework

Abstract

A good incentive system for LES that doesn't encourage centralization requires an efficient many-to-many payment protocol. Preferably it should also be simple and not require complex high-level infrastructure because it serves base layer infrastructure itself. This proposal describes a simple and easy to use probabilistic payment channel system that is suitable for the requirements of LES but can be used by other incentivized protocols too. The required smart contract logic can be realized using the SWAP, SWEAR and SWINDLE framework.

One-to-one channels

There are already working solutions for setting up a channel between two parties like Swarm's SWAP protocol. This operates by committing a sum to the channel with an on-chain transaction, after which the payer can sign lots of cheques with incrementally increasing sums and the recipient can redeem any of the cheques at any point before the locked deposit expires and can be reclaimed by the owner. SWAP even supports bidirectional payments. Unfortunately creating a new edge in the payment graph costs at least two on-chain transactions (setting up, then redeeming/reclaiming). LES operates in a client/server model and the expected topology of payment directions is a bipartite graph with every client wanting to pay for many servers. Having to set up many channels per client would be expensive and raise the entry barrier for new servers.

Multi-hop payments with one-to-one channels

Both SWAP and Raiden aims at building a payment network where every new participant needs to open a few one-to-one channels only and is able to pay to anyone with a logarithmic number of steps. Such a payment network could be used by LES clients too but these developments are still in their infancy and will require complex infrastructure in order to operate in a really reliable, convenient and automatized way. Even when these solutions will be mature enough it may be a good thing to have a simpler fallback alternative. I believe right now we should find the simplest possible way to do cheap many-to-many payments without assuming an existing multi-hop payment network with a 24/7 available and healthy topology.

One-to-many channels with stampers

Opening multiple payment directions with a single locked deposit is possible if we don't commit the deposit to a specific recipient and write cheques to multiple recipients. It this case we have to find a way to prevent overspending (issuing more cheques in total than our deposit) and we are going to need a little external help for that. Let us define a role called "stamper". This can be one of the LES servers but not necessarily the recipient of the payment. The locked deposit is tied to the stamper instead of the recipient and the only thing the stamper does is that it certifies by signing the cheques that the total sum is not more than the deposit. Redeeming cheques requires signatures both from the payer and the stamper. The stamper itself has a deposit too which is burned if a cheque cannot be redeemed because of overspending. Of course the stamper may also charge a little fee for stamping and having a deposit locked.

In this setup the stamper has very little incentive to cheat. All it could do is buy a lot of LES service until being caught (which is probably not enough motivation) or try to collude with a lot of clients to help them buy LES service cheaper (in which case the risk of being instantly caught increases very quickly, especially if part of the stamper's deposit is paid to the whistleblower). In conclusion, a reasonably high deposit makes any stamper trustable for any recipient. For clients the greatest risk of committing to a previously unknown stamper is that if it is not available then the client cannot spend from that deposit until the lock expires and has to make a new deposit committed to another stamper. Since stampers lock their deposits to which their reputation is tied, they are generally interested in providing good service and making money so having a few deposits committed to different stampers sufficiently mitigates this risk. Even though the stamper is an extra third party in the process, choosing a suitable one does not require a web-of-trust or similar reputation mechanism and anyone can easily enter this market (especially because in our simple setup stampers are also light servers and paid light servers will probably also expected to be 24/7 available).

Probabilistic payments

There is a catch though even if clients can spend into multiple directions: redeeming will require a separate on-chain operation from every direction which recipients will factor into their price, therefore we still have a significant cost associated with each new edge in our bipartite graph. We can solve this problem by making the validity of cheques probabilistic. Let us assume an on-chain source of randomness (this is far from trivial but there is a good enough solution for this application until more sophisticated technologies like VDFs become available). Every random value is guaranteed to stay unknown for everyone until a certain block number randomUntil and available for everyone with a sufficiently high probability after a higher block number knownAfter. A source of randomness can generate new values periodically, individual values are identified by the source and an index.

In this case our chequebook and cheques would look like this:

  • chequebook: contract { owner, deposit, expiry, stamper, randomSource, randomIndex }
    • randomUntil(randomSource, randomIndex) < knownAfter(randomSource, randomIndex) < chequebook.expiry
  • cheque: { chequebookAddr, rangeFrom, rangeTo, recipient, payerSignature, stamperSignature }

A cheque is spendable until blockNumber <= randomUntil(cheque.chequebook.randomSource, cheque.chequebook.randomIndex) and redeemable if

  • randomValueAvailable(cheque.chequebook.randomSource, cheque.chequebook.randomIndex) is true
  • blockNumber <= chequebook.expiry
  • cheque.rangeFrom <= randomValue(cheque.chequebook.randomSource, cheque.chequebook.randomIndex) <= cheque.rangeTo
  • cheque.payerSignature matches cheque.chequebook.owner
  • cheque.stamperSignature matches cheque.chequebook.stamper

The expected value of a cheque is cheque.chequebook.deposit * (cheque.rangeTo+1 - cheque.rangeFrom) / 2**32. A stamper should remember the rangeTo field of the last cheque and only accept the next one if rangeFrom is bigger than that. It is punished if two contradicting cheques are presented to the stamper deposit contract.

Some further possible optimizations and generalizations:

Automatic renewal

If less than the entire range has been spent then there is a chance that the deposit is not taken by anyone. Deposits are expired at block chequebook.expiry after which the owner can renew the chequebook or reclaim the deposit. In order to make renewal possible without an extra transaction the chequebook contract can have an "automatic renewal" option which can be turned on or off by the owner at any time. If it is true at chequebook.expiry then the contract is automatically considered valid until a new expiry time (which is increased by the period of the random source) and tied to the next random value from the same source.

Note: time constants should be chosen so that a renewable contract is spendable at least in half of the full renewal period. This way creating two renewable chequebooks with the same period and a half-period phase offset can ensure that we can always spend money instantly and that having this option available but not using it for a longer time period does not cost us anything.

Incremental spending of a range committed and stamped for a recipient

There is a small drawback of this protocol compared to the incremental cheques of regular payment channels: the recipient has to keep all cheques until the redeem period instead of just the last one because every cheque is only valid for a disjunct range of potential random numbers. Merging cheques with existing ones is possible if the ranges are adjacent and the recipients are the same but that might not be the case because the whole point of this protocol is to be able to continually pay small amounts to many different recipients. Having to keep all the cheques might not turn out to be a serious problem but there is still a possible optimization that could reduce both the storage requirements and the number of cheques to be stamped. We can allow different data being signed by the payer and stamped by the stamper as long as the stamped ones never contradict. The redeeming rules can be loosened so that the recipient needs one cheque with a range including the random value and signed by the payer and one cheque with maybe a different range also including the random value and signed by the stamper. Of course the payer and recipient fields also have to match.

This means that the payer can commit a larger range to one recipient by stamping it, then write incrementally increasing cheques with very low cost (without always having to bother the stamper) and gradually pay the recipient. This is ideal for low-trust in-protocol payments.

Redeeming reward

Redeem periods are limited because unspent deposits need to be reusable or reclaimable. Although we usually expect paid light servers to be online 24/7 it would be nice to have some way of avoiding lost rewards due to a short service outage. One way to realize this could be a reward paid to whoever shows the cheques to the chequebook contract if it is not the recipient. This reward could grow exponentially during the redeem period, leaving the bigger part of the time window to the recipient to redeem it but at the end it would definitely be worth sending the transaction for anyone in possession of the "winner" cheque. Then it would be up to the servers to share their cheques with others (probably exchange them with other servers) just to be safe and never lose their earnings.

Source of randomness

Using block hashes alone would be risky because of the possibility of miner bribery (paying the miners to not release certain mined blocks by recipients who would lose money with that block). A simple way to have a "fairly good" source of randomness is to combine block hash randomness with a random number coming from an external actor which is committed in a hashed form before the chosen block and revealed after. Let us call this role the "randomness provider" (though in our use case it will probably be same as the stamper). The provider can also choose the actual block which is then part of the hashed and pre-committed secret, though it has to be between the previously agreed randomUntil and knownAfter blocks.

Cheques should only refer to a future value coming from a certain randomness provider after the provider has committed sha3(randomBlock, mixNumber), randomUntil and knownAfter for that value. Then the provider (or anyone else) has to do a valid reveal of the secret randomBlock and mixNumber between blocks randomBlock+1 and min(knownAfter, randomBlock+256) so that the hash of randomBlock can be queried with the BLOCKHASH opcode. After a successful reveal the generated random number will be sha3(randomBlockHash, mixNumber) and cheques written for a range including that number become redeemable. If a valid reveal does not happen until knownAfter or anyone reveals the secret before randomBlock then the security deposit of the provider is destroyed. In case of an early reveal the sender of the transaction (called the whistleblower) receives a part of the deposit as a reward.

Of course this method is also not perfect because in case of a failure of the randomness provider there is no fair way to handle the chequebook deposits committed to it so they will not be redeemable (practically they will be destroyed too). This is necessary so that no one can benefit from such a failure. Since our model is probabilistic anyway, from the payer and receiver perspective this should be calculated as a simple risk, a correction factor in estimated value that is hopefully close to one if the security deposit is high enough.

A collusion between some potential payment recipients and miners could now only be possible through the randomness provider as an intermediary. This is highly improbable to work due to several reasons:

  • it is the payer choosing the randomness provider so recipients willing to bribe can only encounter a bribe-supporting randomness provider by chance
  • if the bribe-supporting providers can be recognized by the recipients willing to bribe then they will probably be recognized by honest recipients too who will not accept them
  • bribery is also very risky because it involves sharing sensitive info with many little-known parties. Basically there are two ways, both dangerous:
    • miners share mined blocks with bribe-supporting randomness providers and wait a little bit before publicly releasing it so that they can be bribed to not do so. In this case miners waste very valuable time while waiting and even if they don't release the block it is known by many parties who can now start blackmailing the miner by threatening to release it anyway and rob it of its bribe money.
    • randomness providers share their chosen number with miners so that they know when they can apply for a reward for not releasing a mined block. In this case one of them will almost certainly claim the whistleblower reward by proving that the randomness provider released its number too early.

Security against corruption could be further improved by choosing multiple randomness providers at the cost of having a higher chance of one of them failing to release its chosen number. For the first experiment I do not believe this is necessary though. This way of generating random numbers is a temporary solution anyway which has to work until VDF technology becomes available.

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