State Channels for Dummies: Part 6
Stale-State Griefing with Pisa
Note: This post is greatly aided by some background knowledge of state channels. If you have not already, I advise going through the series from the beginning to provide more context for this article.
In state / payment channels, participants exchange messages between each other until they are ready to settle / close the channel. In the ideal case, all participants have 100% uptime and when they want to settle the channel, they:
- Agree to the latest state
- Deploy their off-chain contracts (if there are any)
- Submit the latest state to these contracts
- Wait for the challenge period to elapse (where any party can submit a later state to the contract to contest the one previously submitted)
- If there are off-chain contracts that had been deployed, submit a conditional payment transaction to their multisignature wallet (which pays out according to some information on the newly deployed smart contracts)
- If there were no off-chain contracts, submit the latest state to the multisignature wallet, directly.
In the case that one of these parties is offline (Bob), the other (Alice) can submit an earlier state which would resolve to a more favorable outcome for her than the latest state. Given Bob’s unavailability, Alice can submit earlier state, unchallenged, and ‘grief’ Bob. Below is an example presented in part one of this series where Alice attempts to grief Bob, but fails to do so:
In this article, we will be reviewing Pisa and other strategies to account for the times where Bob is not available to contest Alice’s fraudulent transactions.
Assuming that people are not always online and monitoring the channels that they have open, a strategy for mitigating any sort of griefing is highly desirable. In the above example, if Alice had succeeded in submitting an earlier state, Bob would have been cheated out of 5 ETH.
Everything in channels is contextual — when constructing a payment / state channel, it is crucial to understand both the duration that the channel will stay open for (deadline) as well as the process for settling / closing a channel. Without these being well understood, you leave yourself open to the possibility of having a channel which is open indefinitely or potentially a channel in which parties are unable to dispute what the other had submitted as the most recent state.
Deadline / Expiration
Different channels call for different time durations :
- A channel containing a game of Risk that you have open with some soon-to-not-be friends, might be open for a few days.
- A channel open with your roommates where you each pay the others back for rent, utilities, groceries, etc… might be open for months.
The point of putting a deadline on a contract is to ensure that if nothing occurs within a certain of time that parties can withdraw the money that they had initially deposited. Here is an example of a simple one-way payment that allows for an expiration to be set in the constructor to ensure that the creator of the channel is able to withdraw their funds after a certain period of time (in the event that the recipient doesn’t respond / take action). If the participants of a channel agree, they should also be able to extend the deadline to better suit their needs (maybe that game of Risk is taking longer than they expected).
Within state channels, challenge periods define an interval during which a state submitted on-chain can be disputed. Disputes are prioritized based on their sequence number / nonce — if the dispute nonce is greater than the nonce which was previously submitted, the contract state is updated and a new challenge period begins. Any dispute which is in progress should be able to be canceled with unanimous consent from the participants of the channel.
A great example of each of these pieces of functionality can be seen in Counterfactual’s StateChannel.sol.
Note: All state updates submitted to the smart contract must be signed by all participants to be considered valid (aside from on-chain state updates with a turn-based framework).
There are a few different solutions to stale state griefing in the community, these chiefly include monitors and watchtowers. Both of these solutions have their shortcomings when it comes to storage requirements, channel-size limitations, and accountability of monitors / watchtowers. This is where an alternative solution, Pisa, comes in — Pisa’s protocol goals include the following:
- State Privacy: only hashed state is provided to the custodian.
- Fair Exchange: payment is always exchanged for a signed receipt.
- Recourse as a Deterrent: receipt can punish a dishonest custodian.
- Non-Frameability: a full collusion of all channel parties cannot attack an honest custodian.
- Efficiency: storage requirement is O(1).
In Pisa, a ‘customer’ hires a ‘custodian’ for an ‘appointment’. When approaching the custodian, the customer makes it known which contract needs to be watched. After analyzing the contract and understanding how to settle a dispute on it, the custodian sets up a payment channel between themselves and the customer with a large security deposit that can be used as collateral in case he / she cheats. This payment channel also details the duration and cost for each appointment.
If the customer is satisfied with the size of the security deposit as well as the cost / duration for each appointment, they can then make a deposit into the contract that they can use to pay with a payment channel.
Members of the state channel would then proceed as normal, aside from the fact that they would be passing around signed ‘blinded state’ as well as plaintext channel state. Blinded state is the state nonce as well as a hash of the state — this allows for this to be shared to other parties without divulging any state-specific information.
Note: Channel members can exchange plaintext channel state using public key cryptography, symmetric cryptography, unencrypted, etc… — this is dependent upon their messaging infrastructure.
This is what a few offchain transactions would look like with this setup:
With Pisa, after receiving new state, a customer would forward it to the custodian through the following process:
- Customer sends blinded state (hashed state plus nonce)
- Custodian validates signatures
- Custodian gives a signed receipt (Appointment start / end time, signed hashed state, random hash number r)
- Customer returns conditional payment with random hash r based on the pre-image of r
- Customer waits to receive the pre-image of r (either offchain or through the custodian redeeming the conditional payment on-chain)
What this process looks like:
At this point in time, if an earlier state is submitted to the smart contract and the customer is successfully griefed, they are able to submit their signed receipt from the custodian (as well as the hash pre-image) to the payment channel and get paid out the entire security deposit (30 ETH).
In the happy case of this protocol, if the customer goes offline and the other parties attempt to grief them, the custodian will then submit the latest state to the smart contract, preventing the customer from being successfully griefed.
There will also be the case where a custodian could be malicious — this would happen if they were guaranteed by the other members of the state channel to gain more by colluding than the value of their security deposit. To mitigate this, it is recommended that the custodian put up a large security deposit.
There are many benefits to this approach which are listed above in the protocol goals, but there are also areas where the protocol can be further improved for a more fair and robust system.
In Pisa, payments between a custodian and customer are facilitated through a payment channel. Before any payment is issued to a custodian, they must create a payment channel with a prospective customer — this prospect will then review the security deposit on the smart contract as well as the cost and duration for each appointment.
If the customer decides not to move forward with the custodian, the custodian has wasted gas on the smart contract to construct the payment channel and may have their security deposit subject to an escrow period defined by the smart contract before being able to withdraw and allocate it elsewhere. And even if the customer does decide to move forward with the custodian, they may not engage with the custodian, causing the custodian’s security deposit to again be tied up for an implementation-specific amount of time.
Large security deposits also limit the impact of custodians to just a few channels given their overcapitalization.
Custodians should logically always act in their best financial interest. In the case where they stand to earn more from malicious activity than from being a good actor, they should behave maliciously. This leaves them open to collusion with other members of the channel where everyone except for the party being griefed benefits — leaving us at square one, really.
Payment for Inaction
In Pisa, even if there is never any griefing which occurs in a channel, the customer pays the custodian. This seems inefficient for the customer as they are burning capital for no true added benefit (the custodian could be malicious and still benefit!).
Given that Pisa depends on appointment windows where custodians are only held accountable for the time duration of the window that they signed up for, members in the channel could collude to proceed slowly so that any challenge would be outside of the appointment window.
Single Point of Failure
A number of people like to point to this model and say that it could be susceptible to a DDoS attack or that the custodian might experience some natural disaster which takes them offline for an extended period of time. The former case is very hard to justify without knowing the value stored in the state channel — if the custodian was an XXL AWS box, it would take an immense amount of capital to bring that thing down, and the effort would only slow it down, honestly. The latter is a legitimate concern, custodians could certainly set up multiple servers located around the world to mitigate this possibility.
An alternative approach that I have been working makes the tradeoff of accountability provided by the security deposit to address each of these shortcomings.
This alternative model would be one in which all blinded state is made highly available and broadcasted to a network of custodians. The reward model would also be based on the submission of a successful dispute, whereby the reward would be issued from the contract. In order to provide this reward to the contract, every dispute would have to be submitted with a required ‘dispute bond’. This would cause the griefing party to have to pay for the dispute (instead of the party being griefed) and award the disputing party (custodian) on the basis of successfully challenging a dispute.
With this network in place, the participants in a channel would have to bribe many more parties to prevent one from submitting the latest state to the smart contract. Take the following example:
- There are 100 custodians in the network.
- The channel has a dispute bond of 0.5 ETH + gas costs
- Channel participants have 28 ETH that they gain from successfully griefing one party.
In order to prevent one of the custodians from successfully disputing, the participants would need to offer all of them something greater than 0.5 ETH. Given that they only have 28 ETH to work with, the max they could offer is 0.28 ETH (which would leave them with no benefit for having griefed one party in the first place). Given that custodians should act in their best financial interest, this makes it impossible for channel participants to collude with the network.
Who is Working on This?
I am not sure who is currently working on an implementation or if this is still in the research phase, but I have linked to a presentation on Pisa given by Surya as well as the whitepaper, below.
I highly encourage everyone to check out Surya Bakshi’s presentation from Binary District’s Master Workshop and to read their whitepaper.
Interested in Being Featured?
If you have any interesting projects that the community would benefit from leveraging / understanding, I would love to see your implementation and how you’re approaching things.
If there’s something about this that I can improve, please don’t hesitate to let me know in the comments section, below. And if you’ve gained something from this article, please spam the clap button and share it (these simple actions really make a huge difference). Here’s the link to do so: