In the program implemented above, our rock-paper-Scissors program will run until a winner is determined. In this section, we will not change the Reach program itself. Instead, we will build an interactive version on top of Reach Run that will run outside of the private developer test network (the real Ethereum network).


Previously, when we ran./ Reach Run, it created a Docker image for our Reach program that contained a temporary Node.js package that connected our JavaScript front end to the REACH library and a new instance of a private developer test network. In this section, we’ll customize it and build a non-automated version of Rock-paper-Scissors with the option to connect to the real Ethereum network. First we run $./reach scaffold which automatically generates the following files for us:

  • Package. json a node. js package file that connects index.mjs to the Reach library
  • Dockerfile a Docker image script that efficiently creates and runs package programs
  • Docker-comemage. yml A Docker compose script that links a Docker image to a new instance of the Reach private developer test network.
  • Makefile A Makefile that recompiles and runs a Docker image.

We will leave the first two files unchanged. You can see them in tu-8 /package.json and tu-8 /Dockerfile. We will customize the other two files. First, let’s look at the tut-8/ docker-comedy.yml file: tut-8/ docker-comedy.yml

1 version: '3.4' 2 X-app-base: &app-base 3 Image: reachsh/reach-app-tut-7: Latest 4 Services: 5 Ethereum-Devnet: 6 image: reachsh/ Ethereum-devnet :0.1 7 Algorand-devnet: 8 image: reachsh/algorand-devnet:0.1 9 Depends_on: 10 - algorand-postgres-db 11 environment: 12 - REACH_DEBUG 13 - POSTGRES_HOST=algorand-postgres-db 14 - POSTGRES_USER=algogrand 15 - POSTGRES_PASSWORD=indexer 16 - POSTGRES_DB=pgdb 17 ports: 18 - 9392 19 algorand-postgres-db: 20 image: postgres:11 21 environment: 22 - POSTGRES_USER=algogrand 23 - POSTGRES_PASSWORD=indexer 24 - POSTGRES_DB=pgdb 25 reach-app-tut-7-ETH-live: 26 <<: *app-base 27 environment: 28 - REACH_DEBUG 29 - REACH_CONNECTOR_MODE=ETH-live 30 - ETH_NODE_URI 31 - ETH_NODE_NETWORK 32 reach-app-tut-7-ETH-test-dockerized-geth: &default-app 33 <<: *app-base 34 depends_on: 35 - ethereum-devnet 36 environment: 37 - REACH_DEBUG 38 - REACH_CONNECTOR_MODE=ETH-test-dockerized-geth 39 - ETH_NODE_URI=http://ethereum-devnet:8545 40 reach-app-tut-7-ETH-test-embedded-ganache: 41 <<: *app-base 42 environment: 43 - REACH_DEBUG 44 - REACH_CONNECTOR_MODE=ETH-test-embedded-ganache 45 reach-app-tut-7-FAKE-test-embedded-mock: 46 <<: *app-base 47 environment: 48 - REACH_DEBUG 49 - REACH_CONNECTOR_MODE=FAKE-test-embedded-mock 50 reach-app-tut-7-ALGO-test-dockerized-algod-local: 51 <<: *app-base 52 environment: 53 - REACH_DEBUG 54 - REACH_CONNECTOR_MODE=ALGO-test-dockerized-algod 55 - ALGO_SERVER=http://host.docker.internal 56 - ALGO_PORT=4180 57 - ALGO_INDEXER_SERVER=http://host.docker.internal 58 - ALGO_INDEXER_PORT=8980 59 extra_hosts: 60 - 'host. Docker. Internal: 172.17.0.1' 61 reach - app - tut - 7 - ALGO - test - dockerized - algod: 62 < < : * the app - base 63 depends_on: 64 - algorand-devnet 65 environment: 66 - REACH_DEBUG 67 - REACH_CONNECTOR_MODE=ALGO-test-dockerized-algod 68 - ALGO_SERVER=http://algorand-devnet 69 - ALGO_PORT=4180 70 - ALGO_INDEXER_SERVER=http://algorand-devnet 71 - ALGO_INDEXER_PORT=8980 72 reach-app-tut-7-: *default-app 73 reach-app-tut-7: *default-app 74 # After this is new! 75 player: &player 76 <<: *default-app 77 stdin_open: true 78 alice: *player 79 bob: *playerCopy the code
  • Lines 2 and 3 define the services that start the application. If you stay in the same directory throughout the tutorial, line 3 shows tut instead of Tut-7.
  • Lines 5 and 6 define Ethereum’s Reach private developer test network service.
  • Lines 7 through 24 define the Reach Private Developer test network service for Algorand.
  • Lines 25 through 73 define services that allow applications to run on different networks; Line 25 is included, which defines the reach-app-tut-8-eth -live connection to an existing network.
  • We also added lines 73 through 77 to define a player, which is our application with open standard input and two instances named Alice and Bob.

With this in mind, we can run the $docker-compose run WHICH is reach-app-tut-8-eth-live in the live instance and Alice or Bob in the test instance. If we are using the Live version, then we must define the environment variable ETH_NODE_URI as the URI of our Ethereum node. We will modify tu-8 /Makefile so that the program can run: tu-8 /Makefile

. 29 .PHONY: run-live 30 run-live: 31 docker-compose run --rm reach-app-tut-7-ETH-live 32 33 .PHONY: run-alice 34 run-alice: 35 docker-compose run --rm alice 36 37 .PHONY: run-bob 38 run-bob: 39 docker-compose run --rm bobCopy the code

However, if we tried to run any of them, it would do the same thing: create test accounts for each user and randomly simulate the game. Next, let’s modify the JavaScript front ends to make them interactive.


We’re going to start from the beginning and show each line of the program again. You’ll find that this version is very similar to the previous version, but we’ll show each line for completeness. tut-8/index.mjs

 1    import { loadStdlib } from '@reach-sh/stdlib';
 2    import * as backend from './build/index.main.mjs';
 3    import { ask, yesno, done } from '@reach-sh/stdlib/ask.mjs';
 4    
 5    (async() = > {6    const stdlib = awaitloadStdlib(); .// ...
Copy the code
  • Lines 1 and 2 are the same as before: import the standard library and the back end.
  • Line 3 is a new addition, importing a library called ask.mjs from the Reach standard library for a simple console application. We’ll see how these three functions are used.

tut-8/index.mjs

.// ...
 7    
 8    const isAlice = await ask(
 9      `Are you Alice? `.10      yesno
11    );
12    const who = isAlice ? 'Alice' : 'Bob'; .// ...
Copy the code
  • Lines 8 through 11 ask the user if she participated as Alice and expect a “yes” or “no” answer. Ask displays a prompt and collects a line of input until the resulting parameters are correct. If yes/no occurs, error means ‘y’ or ‘n’ has not been received.

tut-8/index.mjs

.// ...
13    
14    console.log(`Starting Rock, Paper, Scissors! as ${who}`);
15    
16    let acc = null;
17    const createAcc = await ask(
18      `Would you like to create an account? (only possible on devnet)`.19      yesno
20    );
21    if (createAcc) {
22      acc = await stdlib.newTestAccount(stdlib.parseCurrency(1000));
23    } else {
24      const secret = await ask(
25        `What is your account secret? `.26        (x= > x)
27      );
28      acc = await stdlib.newAccountFromSecret(secret);
29}..// ...
Copy the code
  • In lines 16 through 19, the user can choose to create a test account or enter a password to import an existing account.
  • Line 21 creates the test account as before.
  • Line 27 imports the existing account.

tut-8/index.mjs

.// ...
30    
31    let ctc = null;
32    const deployCtc = await ask(
33      `Do you want to deploy the contract? (y/n)`.34      yesno
35    );
36    if (deployCtc) {
37      ctc = acc.deploy(backend);
38      const info = await ctc.getInfo();
39      console.log(`The contract is deployed as = The ${JSON.stringify(info)}`);
40    } else {
41      const info = await ask(
42        `Please paste the contract information:`.43        JSON.parse
44      );
45      ctc = acc.attach(backend, info);
46}..// ...
Copy the code
  • Lines 31 through 34 ask the participant whether to deploy the contract.
  • Lines 36 to 38 deploy it and print out public information (CTC.getInfo) that can be given to other players.
  • Lines 40 through 44 request, parse, and process this information.

tut-8/index.mjs

.// ...
47    
48    const fmt = (x) = > stdlib.formatCurrency(x, 4);
49    const getBalance = async () => fmt(await stdlib.balanceOf(acc));
50    
51    const before = await getBalance();
52    console.log(`Your balance is ${before}`);
53    
54    const interact = { ...stdlib.hasRandom };
..    // ...
Copy the code

Next, we define several helper functions and start the Participant interaction interface. tut-8/index.mjs

.// ...
55    
56    interact.informTimeout = () = > {
57      console.log(`There was a timeout.`);
58      process.exit(1);
59}; .// ...
Copy the code

First we define a timeout handler. tut-8/index.mjs

.// ...
60    
61    if (isAlice) {
62      const amt = await ask(
63        `How much do you want to wager? `.64        stdlib.parseCurrency
65      );
66      interact.wager = amt;
67    } else {
68      interact.acceptWager = async (amt) => {
69        const accepted = await ask(
70          `Do you accept the wager of ${fmt(amt)}? `.71          yesno
72        );
73        if (accepted) {
74          return;
75        } else {
76          process.exit(0);
77        }
78      };
79}..// ...
Copy the code

Next, we request the bet amount (if it is Alice) or define the acceptWager method (if it is Bob). tut-8/index.mjs

.// ...
80    
81    const HAND = ['Rock'.'Paper'.'Scissors'];
82    const HANDS = {
83      'Rock': 0.'R': 0.'r': 0.84      'Paper': 1.'P': 1.'p': 1.85      'Scissors': 2.'S': 2.'s': 2.86    };
87    interact.getHand = async() = > {88      const hand = await ask(`What hand will you play? `.(x) = > {
89        const hand = HANDS[x];
90        if ( hand == null ) {
91          throw Error(`Not a valid hand ${hand}`);
92        }
93        return hand;
94      });
95      console.log(`You played ${HAND[hand]}`);
96      return hand;
97}; .// ...
Copy the code

Next, we define the shared GEThand method. tut-8/index.mjs

.// ...
98    
99    const OUTCOME = ['Bob wins'.'Draw'.'Alice wins'];
100    interact.seeOutcome = async (outcome) => {
101      console.log(`The outcome is: ${OUTCOME[outcome]}`);
102}; .// ...
Copy the code

And finally the seeOutcome method. tut-8/index.mjs

.// ...
103    
104    const part = isAlice ? backend.Alice : backend.Bob;
105    await part(ctc, interact);
106    
107    const after = await getBalance();
108    console.log(`Your balance is now ${after}`);
109    
110    done();
111}) ();Copy the code

Finally, we select the appropriate back-end functionality and wait for it to complete.


Now we can run

$ make build

Then rebuild the image in a terminal in that directory

$ make run-alice

On another terminal in this directory:

$ make run-bob

Here is an example in action:

$ make run-alice Are you Alice? y Starting Rock, Paper, Scissors as Alice Would you like to create an account? (only possible on devnet) y Do you want to deploy the contract? (y/n) y The contract is deployed as = {"address":"0xc2a875afbdFb39b1341029A7deceC03750519Db6","creation_block":18,"args":[],"value":{"type":"BigNumber","hex": "0 x00}", "creator" : "0 x2486cf6c788890885d71667bbccd1a783131547d}" Your balance is 999.9999 How much do you want to wager? 10 What hand will you play? R You played Rock The outcome is: Bob wins Your balance is now 989.9999Copy the code

Another example

$ make run-bob Are you Alice? n Starting Rock, Paper, Scissors as Bob Would you like to create an account? (only possible on devnet) y Do you want to deploy the contract? (y/n) n Please paste the contract information: {"address":"0xc2a875afbdFb39b1341029A7deceC03750519Db6","creation_block":18,"args":[],"value":{"type":"BigNumber","hex": "0x00"},"creator":"0x2486Cf6C788890885D71667BBCCD1A783131547D"} Your balance is 1000 Do you accept the wager of 10? y What hand will you play? P You played Paper The outcome is: Bob wins Your balance is now 1009.9999Copy the code

Of course, the exact number and address at runtime may vary.


To test and run our app on Algorand instead of Ethereum, just edit Tut-8 / docker-comemage. yml and move &default-app from line 24 to line 51.


Now, our rock-paper-Scissors application is complete, and we don’t suffer from attack, timeout, tie, and we can run interactively on non-test networks. In this step, we create a command line interface for the Reach program. In the next step, we’ll go one step further and make a Web interface.

If your version doesn’t work, take a look at the full version: Tut-8 /index.rsh, tut-8/index.mjs, tut-8/package.json, tut-8/Dockerfile, tut-8/ docker-composing. Yml and tut-8/Makefile, And make sure you copy everything in its entirety!

We might also need to change line 32 of Tu-8 /index.rsh to define deadline as a larger number, such as 30. This is because Algorand does not support input-Enabled development networks and only runs rounds when a transaction occurs, so timeouts can happen unexpectedly. This often happens on machines with too much CPU load.

Do you know? :

True or false: Reach helps you build automated tests for decentralized applications, but it does not support building interactive user interfaces

Answer: Reach does not impose any restrictions on the types of front ends that can be added to Reach applications.

A tie means the game continues until the outcome is determined

Next: 2.9 Web Interaction