Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ScorchedEarth Mechanics Using ForceMove Protocol #8

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

apbendi
Copy link
Member

@apbendi apbendi commented Jul 24, 2020

Implements the transition mechanics of the ScorchedEarth system so as to be compatible with ForceMoveApp in the Nitro Protocol, with a full test suite covering all critical rules.

Specific rules which are implemented:

  • There must be 3 entities receiving allocations: User, Suggester, Burner
  • The allocation destinations never change
  • The core parameters never change, and are:
    • How much the User pays per round
    • How much the User can burn of the Suggester's funds when punishing
    • How much the User must burn of their own funds to Punish the Suggester
  • States alternate between a Suggest Phase and a React Phase
  • In the Suggest Phase, the Suggester must provide a suggestion
  • In the the React Phase, the User must react
  • In the Suggest Phase, the Suggester must commit to their worst outcome: the User reacts by punishing them
  • In the React Phase, the User can affirm the Punish reaction, or undo it, and instead Reward the Suggester

apbendi added 9 commits July 20, 2020 12:30
* Ensures that React Phases have a Reaction while
  Suggest Phases do not
* Ensure React Phases do not have a suggestion URL while
  Suggest Phases do.
Whent the Suggester signs a Suggest phase, they configure all
allocations as if the user will choose the Burn reaction, thus
committing themselves to the possibility of this negative action.
The User, subsequently, has the ability to undo this fund burn
and instead Reward the user if they choose.
Copy link

@geoknee geoknee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @apbendi this looks great to me! I'm not sure I know enough about your application to comment on how well it covers what you want, but I have posted a couple of questions above and will try and dig in some more.

require(bytes(_data.suggestion).length == 0,
'ScorchedEarth: React Phase must not have suggestion');
} else {
require(false, 'ScorchedEarth: Invalid Phase');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would revert('msg') be a minor improvement to require(false,'msg')?

Comment on lines +85 to +88
require(
allocation.length == 3,
'ScorchedEarth: Allocation length must be 3 (Suggester, Sender, Burner)'
);
Copy link

@geoknee geoknee Jul 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear: I assume nParticipants=2, despite there being 3 destinations in the outcome? It might be worth requiring that.

Have you thought about the implications of this? I may have misunderstood, but if 2 out of 3 beneficiaries are in the channel and the remaining one is not, those two could collude to conclude the channel with whatever outcome they wished, say to cut out the 3rd beneficiary (and the rules in this contract can't prevent that, since no app logic is evaluated during a conclude). Would that be a problem, do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear: I assume nParticipants=2, despite there being 3 destinations in the outcome?

Correct. The third address is the beneficiary of any funds that are "burned" in the course of the User & Suggester's interactions.

It might be worth requiring that.

I considered this, but wasn't sure if it was critical. As long as the clients refuse to sign a state with more than 2, is there a potential for abuse here I'm not considering? Might be worth it regardless, as gas would be minimal.

if 2 out of 3 beneficiaries are in the channel and the remaining one is not, those two could collude to conclude the channel with whatever outcome they wished, say to cut out the 3rd beneficiary

Yes, this is definitely the case, and we are ok with it. Note the last line in this section of the spec. We figured that, while the two participants can always collude to unburn their funds, the very fact we're operating under the assumption of a behavioral "resentment" assumption makes this unlikely to happen. It's also not a big deal if it does, as they're not stealing funds from the Burner (the burner's initial balance is always 0).

@apbendi
Copy link
Member Author

apbendi commented Jul 24, 2020

Thanks so much for the thoughtful review @geoknee. Much appreciated!

@apbendi
Copy link
Member Author

apbendi commented Jul 24, 2020

Question from an out of band conversation with @barryWhiteHat: Could we remove the requirement that the core parameters (payment, userBurn, and suggesterBurn) remain the same for the length of the channel? This would allow future client implementations to implement negotiation of new params within the lifecycle of the client, though for now, the clients could just hardcode that they wouldn't sign a subsequent state if their counterparty changes this state in theirs.

Thinking through this question. If we remove the check in the contract, but enforce it in the clients, then the validTransition method could simply rely on the parameters in the fromState when checking if balances are allocated correctly between states. As long as the Suggester & User clients refuse to build on a state that incorrectly alters these parameters, then we should never arrive at a situation where the fromState parameters were unfairly manipulated by a bad actor.

What happens, though, in the theoretical future when clients allow these parameters to change during the lifecycle of a channel?

  • If the Suggester makes a change to these parameters during the Suggest Phase at state N, and the User accepts it, they can respond with a React Phase, State N+1, that matches the parameters.
  • For states [N, N+1], the validTransition method would correctly use the params in state N to calculate the allocations in state N+1 were correct, this would be fine
  • For states [N-1, N], however, the validTransition method would look at the params in state N-1, and determine the transition was incorrect, because the allocations would not match the rules

There's a mirror version of this problem if the User proposes the change and the Suggester accepts them.

I don't see a clear fix for this that doesn't add significant complexity. Obviously we could add a new set of Phases (Propose, Reject, Accept) and specific rules around those, but this is a considerable expansion of scope.

Thoughts @barryWhiteHat? Am I missing an easy resolution?

@barryWhiteHat
Copy link

There's a mirror version of this problem if the User proposes the change and the Suggester accepts them.

So the problem is that N to N+1 is correct but N-1 to N is not correct. And this problem would only present itself when someone tried to exit with the transition from N-1 to N.

During the dispute resolution how do we ensure that the dispute is not happening with an old state? The attempt to exit with transition from state N-1 would mean that we are in an old state ?

@apbendi
Copy link
Member Author

apbendi commented Jul 27, 2020

I'll let @geoknee weigh in here, but my current reading of the way the protocol works would suggest that it would never be a good idea to have users sign back-to-back states that don't pass the validTransition function.

The state needs to be supported by further signatures, so I believe at the Nth state, the (N-1)th state would be the last supported state a participant could use to close the channel.

@barryWhiteHat
Copy link

I think that any parameter that we want to users to be able to update as long as they agree should not be part of the transition check. There must be some space in this framework for users to cahnge this. For example when generating a random number while playing games of chance.

@geoknee
Copy link

geoknee commented Jul 27, 2020

My thoughts:

  1. If you want to change the parameters of the channel, you could consider the option of closing the current channel and spinning up a new channel. Especially in the case of virtually funded channels, this can be considered a pretty cheap operation. We made similar design choices in web3torrent in order to keep the logic (i.e.validTransition) within a single channel as simple as possible.

  2. There is another option:

my current reading of the way the protocol works would suggest that it would never be a good idea to have users sign back-to-back states that don't pass the validTransition function.

This isn't strictly true, although I can appreciate why you might have gotten that impression. Buried deep in the notes you'll find the idea of a null app. We rely on this idea heavily when updating "ledger" (i.e. non-application) channels. The relevant point here, though, is that state channel participants can escape from the strict turn-taking and validTransition checking, by countersigning states that belong to other participants.

In your example of the suggester changing the channel parameters at turn N by broadcasting a state with the new parameters: as long as all of the other participants (the User) countersign this state, then it would be supported and enforceable on chain despite it not being a validTransition from N-1. New channel history can then be safely built on top of this state, because it is possible to prove to the chain that it was supported (without submitting state N-1).

Does that make sense?

@apbendi
Copy link
Member Author

apbendi commented Jul 27, 2020

If you want to change the parameters of the channel, you could consider the option of closing the current channel and spinning up a new channel. Especially in the case of virtually funded channels, this can be considered a pretty cheap operation.

I haven't dug into understanding how virtually funded channels work. Do they require an on-chain operation?

In your example of the suggester changing the channel parameters at turn N by broadcasting a state with the new parameters: as long as all of the other participants (the User) countersign this state, then it would be supported and enforceable on chain despite it not being a validTransition from N-1

Cool, this could work too, it sounds like. So if you have a state that's supported because it's signed by both parties, can you call forceMove with ONLY that state? And presumably, if you do, it does not call validTransition (since there's no transition to evaluate)? Does this effectively mean that a state signed by everyone could have any data at all, and none of it matters (except the Outcomes)?

@geoknee
Copy link

geoknee commented Jul 27, 2020

I haven't dug into understanding how virtually funded channels work. Do they require an on-chain operation?

No, they don't; but they do require a state channel "hub" to be running as well as a more complicated sequence of operations to safely fund and defund the application you want to fund.

It's actually a lot simpler to think about "ledger" or "indirect" funding, which would give the same benefits here, and no hub required. Essentially, User + Suggester directly fund a special "ledger" channel in the usual way, with enough money to cover several different ScorchedEarth instances. Then, they can spin up and fund, complete and defund those several instances off-chain.

So if you have a state that's supported because it's signed by both parties, can you call forceMove with ONLY that state? And presumably, if you do, it does not call validTransition (since there's no transition to evaluate)? Does this effectively mean that a state signed by everyone could have any data at all, and none of it matters (except the Outcomes)?

Exactly!

@barryWhiteHat
Copy link

Seems like we don't need to validate the transition at all. I think forcing transitions is for games and things like that. In our usecase we just want to exit from the latest valid state. If users refuse to sign a state we just exit from the one before.

@apbendi
Copy link
Member Author

apbendi commented Jul 28, 2020

Seems like we don't need to validate the transition at all. I think forcing transitions is for games and things like that. In our usecase we just want to exit from the latest valid state. If users refuse to sign a state we just exit from the one before.

I think it's still worthwhile to ensure the asset allocations transition according to the agreed upon parameters in the normal case. But having both parties sign the same state does give us a way to "negotiate" new rates in a future version of the clients, without updating this contract code at all. Virtual channels sound like they'd do so as well, though I don't claim to have my head around those yet. Regardless, it seems we can likely proceed without any changes to this contract.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants