The original link
In the last section, our rock-paper-scissors program closed a security hole! This is obviously a big step in getting the game going. In this section, we’ll focus on another important issue that is unique to decentralized applications: non-participation. Non-participation here refers to the action of one player not continuing the game. In a traditional client-server program, such as a Web server, the situation might be that the client stops sending requests to the server, or the server stops sending responses to the client. At this point, non-participation usually results in an error message on the client, which at best results in a log entry on the server. When this happens, traditional programs may need to reclaim resources such as network ports, but they also need to do so if the program ends in a normal way. In other words, there is no need for programmers to carefully consider the consequences of not participating in traditional client-side server programs. However, decentralized applications must carefully consider non-participation. In our rock-paper-scissors game, for example, what happens if After Alice places her bet, Bob never accepts it and the application stops? After this time, Alice’s network token will be locked into the contract. For example, if After Bob accepts and pays his bet, Alice stops participating and does not submit her gesture, then both of their money is locked up. If these situations occur, both parties will lose money, and their fear of such an outcome will bring additional costs to the transaction, reducing the game’s benefits to them. Maybe not so important in a game like this, but keep in mind that rock, Paper, Scissors is the epitome of a decentralized application.
Technically speaking, in the first case, Alice’s funds are not locked up when Bob fails to launch the application: Since Bob’s identity is not fixed until he sends the first message, Alice can re-enter the game as Bob and win all the money, but only at the expense of the transaction costs of the consensus network. But in the second case, neither party has a claim on the locked funds.
In the remainder of this section, we discuss how Reach solves the problem of non-participation. For a more detailed discussion, see the guide to Non-participation chapter. – In Reach, non-participation is handled by a “timeout” mechanism that causes each participant to perform a specified step if the initiator of a consensus transfer fails to publish the desired content by a specified time. We integrated this mechanism into our rock-paper-Scissors program and intentionally inserted non-participating programs into our JavaScript test program to see the results. First, we will modify the participant interface to let the front end know that a timeout has occurred. tut-6/index.rsh
.// ...
20 const Player =
21 { ...hasRandom,
22 getHand: Fun([], UInt),
23 seeOutcome: Fun([UInt], Null),
24informTimeout: Fun([], Null) }; .// ...
Copy the code
- Line 24 introduces a new method informTimeout to inform a timeout, which takes no arguments and returns no information. We call this function when a timeout occurs.
Let’s tweak the JavaScript front end a little so that it shows on the console who timed out when it receives this message (lines 30-31) : tu-6 /index.mjs
.// ...
20 const Player = (Who) = > ({
21. stdlib.hasRandom,22 getHand: () = > {
23 const hand = Math.floor(Math.random() * 3);
24 console.log(`${Who} played ${HAND[hand]}`);
25 return hand;
26 },
27 seeOutcome: (outcome) = > {
28 console.log(`${Who} saw outcome ${OUTCOME[outcome]}`);
29 },
30 informTimeout: () = > {
31 console.log(`${Who} observed a timeout`);
32 },
33}); .// ...
Copy the code
In the Reach program, we will define an identifier at the top of the program to use the deadline throughout the program. tut-6/index.rsh
.// ...
32 const DEADLINE = 10;
33 export const main =
.. // ...
Copy the code
- Line 32 defines deadline as ten time units, which is an abstraction of the basic concept of time in the consensus network. In many networks, such as Ethereum, the number is the number of blocks.
Next, at the beginning of the Reach application, we will define a helper function to notify each participant of the timeout by calling this new method. tut-6/index.rsh
.// ...
37 (A, B) => {
38 const informTimeout = () = > {
39 each([A, B], () = > {
40 interact.informTimeout(); }); };
41
42 A.only(() = >{..// ...
Copy the code
- Line 38 defines the function as an arrow expression.
- Line 39 tells each participant to perform a local step.
- Line 40 tells them to call the new method that notifies the timeout.
We won’t change Alice’s first action, because at this point her non-participation doesn’t matter: if she doesn’t start the race, then no one is in harm’s way. tut-6/index.rsh
.// ...
46 A.publish(wager, commitA)
47.pay(wager); .// ...
Copy the code
However, we will adjust Bob’s first move, because if he doesn’t participate, Alice will lose her initial bet. tut-6/index.rsh
.// ...
54 B.publish(handB)
55 .pay(wager)
56 .timeout(DEADLINE, () = >closeTo(A, informTimeout)); .// ...
Copy the code
- Line 56 adds a timeout handler to Bob’s program.
The timeout handler specifies that if Bob does not complete the action by the DEADLINE, the application will invoke the steps given by the arrow expression. In this case, this step is a call to the Reach library function closeTo, which sends Alice a message and transfers all the money in the contract to herself, and then calls the given function, informTimeout. This means that if Bob fails to reveal his cards, Alice gets her tokens back. We will add a similar timeout handler for Alice’s second action. tut-6/index.rsh
.// ...
61 A.publish(saltA, handA)
62 .timeout(DEADLINE, () = >closeTo(B, informTimeout)); .// ...
Copy the code
But in this case, if Alice doesn’t participate, Bob will be able to get all the tokens. You might think that it would be “fair” to give Alice’s token back to Alice and Bob’s token back to Bob. However, if we implement it this way, Alice can always deliberately time out when she is about to lose (and she knows because she knows her and Bob’s gestures).
This is what we need to change the Reach code to deal with non-participation perfectly: just seven lines! — Next we modify the JavaScript front end so that Bob deliberately causes a timeout when it’s his turn to accept a bet. tut-6/index.mjs
.// ...
35 await Promise.all([
36 backend.Alice(ctcAlice, {
37. Player('Alice'),
38 wager: stdlib.parseCurrency(5),
39 }),
40 backend.Bob(ctcBob, {
41. Player('Bob'),
42 acceptWager: async (amt) => { // <-- async now
43 if ( Math.random() <= 0.5 ) {
44 for ( let i = 0; i < 10; i++ ) {
45 console.log(` Bob takes his sweet time...`);
46 await stdlib.wait(1); }
47 } else {
48 console.log(`Bob accepts the wager of ${fmt(amt)}. `);
49 }
50 },
51}),..// ...
Copy the code
- Lines 42 through 50 redefine Bob’s betting method as an asynchronous function, where half the time is spent waiting 10 time units to pass, requiring at least 10 blocks over the Ethernet. We know that 10 is the DEADLINE value, so this will cause a timeout.
— Let’s run the program and see what happens:
$ ./reach run Alice played Rock Bob accepts the wager of 5. Bob played Paper Bob saw outcome Bob wins Alice saw outcome Bob wins Alice went from 10 to 4.9999. Bob went from 10 to 14.9999. $./reach run Alice played Scissors Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob played Scissors Bob observed a timeout Alice observed a timeout Alice went from 10 to 9.9999 $./reach run Alice played Paper Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob takes his sweet time... Bob played Scissors Bob observed a timeout Alice observed a timeout Alice went from 10 to 9.9999 9.9999.Copy the code
Of course, when you run it, you probably won’t end up with a timeout two out of three times.
If your version doesn’t work properly, take a look at the full version: tu-6 / index.rsh and tut.6 / index.mj, and make sure you copy everything down correctly!
The implementation of our rock-paper-Scissors game is now available to anyone involved in the game. In the next step, we will extend the application to prohibit ties and have Alice and Bob race again until there is a winner.
Do you know? :
In a decentralized application, what happens when a participant refuses to perform the next step in the program, for example, if Alice refuses to play rock-paper-scissors with Bob?
- This is impossible because the blockchain guarantees that each side performs a specific set of actions;
- The program hangs forever waiting for Alice to provide a value;
- Alice is punished and the project continues, with Bob winning by default;
- It depends on how the program is written; If a developer uses Reach, the default is (2), but a developer can include a timeout block to implement (3) behavior.
Answer: 4; Reach lets programmers design applications using the business logic they want.
2.5 Trust and Engagement
In a tie, the game continues until the winner is determined