xavierlepretre / corda-slot-machine

CordaCon distributed slot machine
1 stars 1 forks source link

Define (document) the algorithm or high-level design #12

Open cwellsx opened 4 years ago

cwellsx commented 4 years ago

Perhaps we ought to document the design a bit -- i.e. summarise the discussion which we had (or are having) via email. Markdown would do, diagrams possibly being even better. If we've already understood the design, I don't know whether we need this ourselves -- or perhaps it's a nice-to-have for posterity, so we instead might focus on only-coding for now. We might "deliver" a talk at CordaCon, so maybe ought to consider what documentation if any should go with that.

xavierlepretre commented 4 years ago

We could draw CDLs, Corda Design Language diagrams.

cwellsx commented 4 years ago

On Tue, Aug 18, 2020 at 3:16 AM Christopher Wells wrote:

Can we discuss or define details of our commit-reveal? Because I don't yet have a solution which mitigates all the risks, but I'd like to be clear on what we're implementing. I guess it's something like the following.

There's a state called Bet, governed by contract, which is initiated by a player and which both parties then add to.

The associated commands or successive partial states are:

The input and output tokens are standard (i.e. fungible tokens with a current owner).

I'm not sure how/where each party privately stores its random number before the reveal -- in a custom SQL table (associated with the hash), or more simply in a local variable in the suspendable flow session? <<<

Casino cheats

The above ("commit-reveal") solves the following risk:

IMO this is the biggest risk because it could happen a lot and happen unobserved (e.g. if there are 1000s of automated bets per day). So even only being able to address this risk is already a win for Corda.

Other risks

The above isn't enough to solve these other risks, however:

IMO if these risks happen then it's easy to detect that they happened -- unlike the casino secretly cheating, these risks don't happen without the player noticing. If they happen then perhaps it's a disagreement which lawyers and the courts could fix. Still, it would be nice to have a software fix.

Parties overspend

To solve the first two risks (tokens already spent when it's time to settle the bet), Xavier suggested adding a new "locked" state to each token. So the tokens associated with a bet would be locked (so they can't be transferred nor used in another bet while they're locked). I suppose that makes this kind of token non-fungible. Doesn't the user have to trust the issuer of these custom tokens (e.g. trust that they'll redeem them for something worthwhile) -- and is that ultimately any different from trusting the casino not to overspend -- and if not then is it worth the bother to implement locking? In a real casino the user may buy tokens from the casino, then trusts the casino to redeem them.

Or instead of locking with a custom token type, another option could be to move the standard/fungible tokens to a temporary account, from which they're paid when the bet is resolved. The problem with that is that the owner of that account might steal the money and/or not sign it over when the bet is completed.

Parties stall

To solve the last two or three risks (i.e. a party refuses to continue an already-started bet), a strategy might be to write the contract so that the counter-party can abend the bet after a timeout. For example if the player refuses to continue then the Casino can end the bet.

But I don't know how to implement this safely -- for example how do you prevent the casino's not sending the bet to the user, pretending that the user didn't respond in time, then abending the bet and collecting? Is there any proof that one party attempted to deliver to the other party? Is this a standard problem in Corda -- e.g. "insurance company doesn't pay up" -- how is that normally handled if at all?

Regards, Chris

On Tue, Aug 18, 2020 at 12:24 PM Xavier Lepretre wrote:

Your "Create Bet" and "Accept Bet" have to be in a single flow that creates a single transaction. It has to be signed before we move onto the reveal stage.

Storing the random number between commit and reveal. That is a good point, I did not really think about it, but it is easily solved. We have:

So no need for SQL, the ephemeral random numbers are all saved as part of the flow checkpointing.

Risks:

At first I thought we could use a time-window so that the aggrieved party could get something after the time-window's deadline. Now I am not so sure. My other thought would be that there is a "judge" node that receives pure reveal transactions from either casino or player or both within the time-window. After the time-window, the aggrieved party(ies) could contact the judge and ask for redress. The judge would look at which party(ies) revealed in time and redress accordingly, for a fee perhaps. Not fantastic.

Now, I think that the most elegant way is to have each party reveal in turn, so we have 2 transactions signed by each in turn. Casino reveals first within the first time-window, Player reveals within the second time-window. There is a second TW so that the Casino cannot cheat by revealing ever so close to the end of the TW, leaving no physical time for the Player to reveal. If the Casino did not reveal within the first TW, then the player can seek redress after the first TW. If the Player did not reveal with the second TW, then the casino can seek redress after the second TW. "Redress" is coded in the contract. I would make the TW generous, like 10 minutes, to not unduly punish the casino that may be genuinely overwhelmed. For it to be effective, the Commit tx also needs a TW.

Trust in redeeming lockable tokens. In the grand scheme of things, these lockable tokens are not Casino tokens, they are a new Fungible currency with an exchange rate that is left to the market. As long as you can find willing buyers / sellers you can get them and redeem them. But yes, the issuer of these tokens could be a third party, not the Casino. Perhaps the Casino regulator? I think we should wave our hands with this theory for now.

Temporary account: That will be possible with SGX, or enclave-computing. The enclave would be the only machine capable of decrypting the private key and use it to sign away conventional tokens. When this is implementable, and we know how to do it, then a portion of the workflow could run on any computer to sign away these tokens according to strict rules. I do not follow R3's dev in this regard, but I think here again we could wave our hands.

Parties stall: In my reveal-by-turn flow, each party knows the signature transaction id and content when asked to sign. And both signatures are needed. So we can protect parties by not waiting to receive the finalised transaction, which is really just the same tx plus the notary's signature, but instead by starting the reveal right from the unfinalised but signed transaction. I would have to look at how to do it in practice, although our overarching Game flow will help. I think it would really only concern the Casino starting to reveal at the start of its time-window after signing, and the Player seeking redress after the time-window. I don't know of a standard way of handling that.

Xavier

On Tue, Aug 18, 2020 at 4:19 PM Christopher Wells wrote: (and Xavier commented on this inline, marked using XL)

 Game flow that is an overarching and Initiating one

Yes -- but it was the Casino's side of that, that I wasn't sure of.

I guessed it might be a suspendable local variable iff the Casino side can be a single function which implements the whole transaction, rather than being split into several subflows?

XL -- The commit flow at both ends is started by an enclosing flow. This enclosing flow is Initiating on the Player and InitiatedBy on the Casino. It is the enclosing flow(s) that create the random numbers. As long as the enclosing flows are not completed, the random number is there, as a local variable. There is a single flow session throughout. The session is created when the enclosing Initiating flow is started. The enclosing flow(s) pass the random number to the constructor(s) of the Commit and the Reveal flows on both ends.

 The commit tx also includes the max payout and the player's coin, so we have no "I can't pay" risks

That is so, iff we go with your idea of "locking tokens" while they're in play, and/or moving ownership/control of standard "fungible tokens" to a trusted escrow account while they're in play.

XL -- Mmh, the locked tokens concept is not compatible with a trusted escrow model. The trusted escrow can reuse known patterns but would require SGX to be trustless. 

 If the Casino did not reveal within the first TW, then the player can seek redress after the first TW.

Sorry I'm naive but how does (for example) the Casino prove that it revealed during the first TW?

XL -- The commit (hash) state of the Casino is consumed (i.e. the notary says so) when the Casino reveals during the first TW. The notary will not let the Casino reveal after the TW. The Player cannot later reuse that consumed commit state to seek redress. 

The proof is needed, to prevent:

Ideally the proof should work even if the player and/or casino nodes are trying to cheat. I guess the proof involves a notary, i.e. the Casino's revelation is signed by the notary within a time window. Does the player node see the revelation before or after the notary signs it? Which node (i.e. Casino or Notary) delivers the revelation to the client? If the delivery happens after notarisation can the Casino node prevent its delivery? If the delivery happens before notarisationcan the player node prevent its notarisation?

XL -- If we code as usual, the other node receives the revelation after notarisation. And yes, delivery can be prevented by using a modified FinalityFlow. I don't see why the node would reveal to the other node but prevent notarisation. But just to cover this case, we could make the reveal independent of who reveals as long as it matches. XL -- To prevent reveal notarisation and not delivering to the other node, I think what we could do is both nodes reveal in parallel during the first time-window, then, the combination of these 2 reveals can be used at any time in the future to unlock the tokens. The casino could also refuse to commit new transactions with a given player that has a pending reveal. XL -- I still have no idea how to force a delivery or a notarised proof of delivery. If we implement a second TW far in the future where either party can seek redress, and both have (notary-) revealed and one has not delivered, the aggrieved party cannot seek redress because according to the notary, the cheating party has revealed. And in this "long game", the Player has the upper hand because it can grieve the Casino of (max payout - 1) locked tokens at the cost of 1 of its own locked tokens. 

 Trust in redeeming lockable tokens  Temporary account

IMO either of these would do, and they're alternatives i.e. we want one or the other. Do you know which you prefer, and why? Ideally we might want another/separate node therefore -- i.e. a "Bank", which will issue tokens -- either, lockable tokens -- or, if they're normal (unlockable) tokens, then the "Bank" node could also own the temporary escrow account for the amounts in play (which it releases automatically when presented with a game result).

XL -- If we use standard tokens, I would prefer a simple "Bank" node too, whose additional signature is required on the reveal transaction. Additional benefit of the separate Bank is that it could act as the judge of the reveals.

Optionally the Bank could be the Casino.

 in my reveal-by-turn flow ... we can protect parties by not waiting to receive the finalised transaction

I hope you're right but I haven't seen/understood that flow of yours defined in enough detail -- who signs what when, and what do they see before they sign, and what if they refuse to sign after seeing, and can the counter-party ever prove that the other side has seen but has not signed? -- so I can't inspect/confirm yet.

XL -- In detail it is the responder flow that signs the transaction and that can already give the transaction in full. We get it again in the responder flow of FinalityFlow if the initiating FinalityFlow does its job. Some code should confirm the possibility and make it clearer. 

 I would have to look at how to do it in practice

Could you? Before starting to code it? I don't see a robust solution unless there's a trusted intermediary node (e.g. notary) which timestamps the reveals and which then guarantees to attempt the delivery of the reveal to the counter-party. Can you make the notary do that, in Corda? Or we could expect the "Bank" node to do that at run-time -- but that's just expecting everyone to trust the bank node, instead of trusting the casino and/or players nodes. Otherwise this seems like a special case of the "Two generals' problem", in which the network is semi-reliable but the generals themselves are not (i.e. may not reliably initiate the sending of a message).

Thank you, Chris

On Tue, Aug 18, 2020 at 5:47 PM Xavier Lepretre wrote:

Comments inlined (marked using XL).

Xavier

On Tue, Aug 18, 2020 at 9:34 PM Christopher Wells wrote:

  As long as the enclosing flows are not completed, the random number is there, as a local variable.

So you're saying there might be only one InitiatedBy flow on the Casino (which contains the hash and random number as a local variable) -- and not several independent InitiatedBy (e.g. one for commit and another for reveal).If that's feasible then yes.

 Mmh, the locked tokens concept is not compatible with a trusted escrow model.

Yes -- so let's pick either one or the other.Do you clearly prefer one over the other, for some reason?

 If we use standard tokens, I would prefer a simple "Bank" node too, whose additional signature is required on the reveal transaction.

I think this is your agreeing that either would do (either special tokens, or standard tokens with an escrow account).

 Additional benefit of the separate Bank is that it could act as the judge of the reveals.

I assume you mean judging the timing and delivery. It would do as a solution IMO, yet I feel it's a slightly disappointing cheat of a solution -- because it simply requires the nodes to trust the bank at run-time, instead of trusting each other -- that's like sweeping dirt under the carpet and hoping no-one will notice. So a more decentralised solution would be preferable, iff we can design a robust one. 

 I think what we could do is both nodes reveal in parallel during the first time-window

Alright then, so I think the idea is -- and please, correct me if I'm wrong (because I'm just making this up as I go along):

A counter-party which hasn't revealed yet and which has already received the other's reveal, might know that it is going to lose -- in which case it might choose not to reveal. In that case we want the winning party to be able to foreclose the state after a second timeout ("T2") -- and the contract should allow this foreclosure to succeed, iff the counter-party hasn't already revealed. To implement the above, I guess that the final "committed" state should become (i.e. the final output of the commit transaction should include) two what-I-call "stub" instances -- one of which is to be updated by the Casino and the other of which to be updated by the Player -- so that either party can update (i.e. reveal using, i.e. consume) its own copy of the state independently. Each stub is I guess a Linear state.

The foreclosure command should succeed, iff the counter-party hasn't updated. So a party's inputs to the foreclosure transaction should be the party's stub (showing it revealed) and the counter-party's stub (showing it hadn't revealed).

 I still have no idea how to force a delivery or a notarised proof of delivery.  ... the aggrieved party cannot seek redress because according to the notary, the cheating party has revealed. 

Yes we agree on this (i.e. I arrived at the same conclusion, immediately above). Might a node ever fetch what's been notarised from the notary, or does it always depend on the counterparty to deliver what was notarised? If this is a real problem is it endemic to every Corda transaction -- i.e. is our problem no worse than any/every other CorDapp's?

Anyway I guess the above (i.e. symmetric reveal) is nearly a bullet-proof solution. The only attack I see remaining is if the node which notarises the commit (i.e. the player) then doesn't deliver it -- in which case (non-delivery) the other party (i.e. the Casino) can't begin its reveal in time -- do you see any solution to that? Aprt from that is this design too complicated for our purposes -- or is it simply what's necessary? Sorry I'm being so wordy via email -- I hope I'm being clear, at least.

Chris

On Wed, Aug 19, 2020 at 12:52 PM Christopher Wells wrote:

 The only attack I see remaining is if the node which notarises the commit (i.e. the player) then doesn't deliver it -- in which case (non-delivery) the other party (i.e. the Casino) can't begin its reveal in time -- do you see any solution to that?

Do you agree this is a problem? And maybe the last problem, if we agreed on everything else? How about the following -- would this solve it? Our previous version of the algorithm was:

My new proposal is as follows:

The "threat" here is if the casino notarises but doesn't deliver its reveal -- which causes the player node to never reveal, which allows the casino to foreclose. To mitigate this threat I propose one new step, i.e. one new bit of logic:

I guess -- am I right? -- that there's never an automated solution (except "retry") to the problem of "a node fails to deliver what's been notarised". For example if you buy a house from me, I can notarise our transaction, begin to spend your money, and not deliver to you your copy of our transaction (so the house isn't available to you). And all Corda can do in this case, is ensure that somewhere (e.g. on the notary) there's a reliable single source of truth, which can eventually be distributed somehow and/or investigated manually/forensically -- is this so? So my intention in proposing this "private reveal" is that at least the notary will know whether the player revealed in time -- and know what the player's random number was -- if or assuming that it is a validating notary, does the notary store the whole transaction state i.e. including the "random number" payload? What I described above conceptually as a "private reveal" and a "public reveal" might be simplified using a custom flow on the player side:

Is this all right/agreeable? It's partly based on guesswork on my part, about Corda and how/whether people (node operators) can recover after non-delivery of notarised copies.

Chris

On Thu, Aug 20, 2020 at 3:33 PM Xavier Lepretre wrote:

I like this casino reveals first, and the player reveals privately too. Like you said, griefing by the player remains possible. One way we could mitigate it is by having the player locking the same amount of tokens as the casino, although only 1 goes into the winning pool, and the player retrieves the amount - 1 in full no questions asked on resolution of the game.

Xavier

On Thu, Aug 20, 2020 at 9:06 PM Christopher Wells wrote:

Well great then -- I think we have, you or I at least, a consensus -- a design spec. It's a pity it takes so many words -- how do you usually do this? I would have guessed we'd be using a whiteboard and a sequence diagram. At least email provides lots of time to think.

Are Awie and Rajesh OK with this? And I hope it isn't too ambitious to implement. And Martin said someone else might want to join our team -- Erik De Graaf -- if we don't have too many already?

One more thing -- you didn't mention whether you prefer for some reason a custom non-fungible "lockable token" type, or, a standard fungible type with an escrow account whose third-party owner ("the bank") signs automatically when each game finishes. IMO either would do -- and I'd prefer whichever the judges (CordaCon participants) prefer, but I don't which that is.

Chris

On Thu, Aug 20, 2020 at 7:41 PM Xavier Lepretre wrote:

I don't know which token type the "judges" would prefer, but my preference goes to the locked tokens as it showcases tokens controlled by code.

Xavier

On Thu, Aug 20, 2020 at 8:11 PM Christopher Wells wrote:

Ok that's a good-enough reason, and emulates the "real world" of a casino's issuing physical tokens, which have a state e.g. "in play" (defined by their location).