diff --git a/auction/README.md b/auction/README.md index dc0659b..740d502 100644 --- a/auction/README.md +++ b/auction/README.md @@ -5,7 +5,7 @@ A first-price sealed-bid auction in Leo. ## Summary -A first-price sealed-bid auction (or blind auction) is a type of auction in which each participant submits a bid without knowing the bids of the other participants. +A first-price sealed-bid auction (or blind auction) is a type of auction in which each participant submits a bid without knowing the bids of the other participants. The bidder with the highest bid wins the auction. In this model, there are two parties: the auctioneer and the bidders. @@ -20,45 +20,140 @@ We make following assumptions about the auction: Under this model, we require that: - Bidders do not learn any information about the value of other bids. -### Auction Flow +## Auction Flow + The auction is conducted in a series of stages. - **Bidding**: In the bidding stage, bidders submit bids to the auctioneer. They do so by invoking the `place_bid` function. - **Resolution**: In the resolution stage, the auctioneer resolves the bids in the order they were received. The auctioneer does so by invoking the `resolve` function. The resolution process produces a single winning bid. - **Finishing**: In this stage, the auctioneer finishes the auction by invoking the `finish` function. This function returns the winning bid to the bidder, which the bidder can then use to claim the item. - ## Language Features and Concepts - `record` declarations - `assert_eq` - record ownership -## Running the Program +## How to Run + +Follow the [Leo Installation Instructions](https://developer.aleo.org/leo/installation). + +This auction program can be run using the following bash script. Locally, it will execute Leo program functions to conduct, bid, and close a three party auction. + +```bash +cd auction +./run.sh +``` + +The `.env` file contains a private key and address. This is the account that will be used to sign transactions and is checked for record ownership. When executing programs as different parties, be sure to set the `private_key` field in `.env` to the appropriate value. You can check out how we've set things up in `./run.sh` for a full example of how to run the program as different parties. -Leo provides users with a command line interface for compiling and running Leo programs. -Users may either specify input values via the command line or provide an input file in `inputs/`. +## Walkthrough -### Configuring Accounts -The `.env` file contains a private key. -This is the account that will be used to sign transactions and is checked for record ownership. -When executing programs as different parties, be sure to set the `PRIVATE_KEY` field in `.env` to the appropriate values. -See `./run.sh` for an example of how to run the program as different parties. +* [Step 0: Initializing the Auction](#step0) +* [Step 1: The First Bid](#step1) +* [Step 2: The Second Bid](#step2) +* [Step 3: Select the Winner](#step3) +## Step 0: Initializing the Auction -The [Aleo SDK](https://github.com/AleoHQ/leo/tree/testnet3) provides an interface for generating new accounts. -To generate a new account, navigate to [aleo.tools](https://aleo.tools). +The three parties we'll be emulating are as follows: +```markdown +First Bidder Private Key: +APrivateKey1zkpG9Af9z5Ha4ejVyMCqVFXRKknSm8L1ELEwcc4htk9YhVK +First Bidder Address: +aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke + +Second Bidder Private Key: +APrivateKey1zkpAFshdsj2EqQzXh5zHceDapFWVCwR6wMCJFfkLYRKupug +Second Bidder Address: +aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4 + +Auctioneer Private Key: +APrivateKey1zkp5wvamYgK3WCAdpBQxZqQX8XnuN2u11Y6QprZTriVwZVc +Auctioneer Address: +aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh +``` + +## Step 1: The First Bid + +Have the first bidder place a bid of 10. + +Swap in the private key and address of the first bidder to `.env`. -### Providing inputs via the command line. -1. Run ```bash -leo run ... +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpG9Af9z5Ha4ejVyMCqVFXRKknSm8L1ELEwcc4htk9YhVK +" > .env ``` -See `./run.sh` for an example. +Call the `place_bid` program function with the first bidder and `10u64` arguments. -### Using an input file. -1. Modify `inputs/auction.in` with the desired inputs. -2. Run ```bash -leo run +leo run place_bid aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke 10u64 ``` + +## Step 2: The Second Bid + +Have the second bidder place a bid of 90. + +Swap in the private key of the second bidder to `.env`. + +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpAFshdsj2EqQzXh5zHceDapFWVCwR6wMCJFfkLYRKupug +" > .env +``` + +Call the `place_bid` program function with the second bidder and `90u64` arguments. + +```bash +leo run place_bid aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4 90u64 +``` + +## Step 3: Select the Winner + +Have the auctioneer select the winning bid. + +Swap in the private key of the auctioneer to `.env`. + +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp5wvamYgK3WCAdpBQxZqQX8XnuN2u11Y6QprZTriVwZVc +" > .env +``` + +Provide the two `Bid` records as input to the `resolve` transition function. + +```bash +leo run resolve "{ + owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, + bidder: aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke.private, + amount: 10u64.private, + is_winner: false.private, + _nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public +}" "{ + owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, + bidder: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4.private, + amount: 90u64.private, + is_winner: false.private, + _nonce: 5952811863753971450641238938606857357746712138665944763541786901326522216736group.public +}" +``` + +## Step 4: Finish the Auction + +Call the `finish` transition function with the winning `Bid` record. + +```bash +leo run finish "{ + owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, + bidder: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4.private, + amount: 90u64.private, + is_winner: false.private, + _nonce: 5952811863753971450641238938606857357746712138665944763541786901326522216736group.public +}" +``` + +Congratulations! You've run a private auction. We recommend going to [aleo.tools](https://aleo.tools) to generate new accounts and trying the same commands with those addresses. \ No newline at end of file diff --git a/auction/inputs/auction.in b/auction/inputs/auction.in deleted file mode 100644 index c3471d4..0000000 --- a/auction/inputs/auction.in +++ /dev/null @@ -1,39 +0,0 @@ -// The program input for auction/src/main.leo -[place_bid] -// Note that `bidder` must have the same address as the caller (refer to `program.json`). -// Swapping the below lines, will result in an error. -// bidder: address = aleo1y7065c2jxkra5yzu9jfxq55klweqev0zas89lt9nxmfrqrafmq9qw2ktdg; -bidder: address = aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh; -amount: u64 = 90u64; - -[resolve] -first: Bid = Bid { - owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh, - bidder: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh, - amount: 10u64, - is_winner: false, - _nonce: 6480683131255842390406647532838179519970794442201387718334686863304493823461group, -}; -second: Bid = Bid { - owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh, - bidder: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh, - amount: 90u64, - is_winner: false, - _nonce: 1635474322959998727812727786860193861506102794050195384593971440884677453921group, -}; - -[finish] -bid: Bid = Bid { - owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh, - bidder: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh, - amount: 90u64, - is_winner: false, - _nonce: 4195536711021629817871261453343069023119119274215827022954896024118351555272group, -}; - - - - - - - diff --git a/auction/run.sh b/auction/run.sh index 560c345..ea7251d 100755 --- a/auction/run.sh +++ b/auction/run.sh @@ -6,26 +6,28 @@ then exit fi -# The private key and address of the first bidder. -# Swap these into program.json, when running transactions as the first bidder. -# "private_key": "APrivateKey1zkpG9Af9z5Ha4ejVyMCqVFXRKknSm8L1ELEwcc4htk9YhVK" -# "address": aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke +echo " +We will be playing the role of three parties. -# The private key and address of the second bidder. -# Swap these into program.json, when running transactions as the second bidder. -# "private_key": "APrivateKey1zkpAFshdsj2EqQzXh5zHceDapFWVCwR6wMCJFfkLYRKupug" -# "address": aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4 +The private key and address of the first bidder. +private_key: APrivateKey1zkpG9Af9z5Ha4ejVyMCqVFXRKknSm8L1ELEwcc4htk9YhVK +address: aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke -# The private key and address of the auctioneer. -# Swap these into program.json, when running transactions as the auctioneer. -# "private_key": "APrivateKey1zkp5wvamYgK3WCAdpBQxZqQX8XnuN2u11Y6QprZTriVwZVc", -# "address": "aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh" +The private key and address of the second bidder. +private_key: APrivateKey1zkpAFshdsj2EqQzXh5zHceDapFWVCwR6wMCJFfkLYRKupug +address: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4 +The private key and address of the auctioneer. +private_key: APrivateKey1zkp5wvamYgK3WCAdpBQxZqQX8XnuN2u11Y6QprZTriVwZVc +address: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh +" echo " +Let's start an auction! + ############################################################################### ######## ######## -######## STEP 0: Initialize a new 2-party auction ######## +######## Step 0: Initialize a new 2-party auction ######## ######## ######## ######## ------------------------------- ######## ######## | OPEN | A | B | ######## @@ -35,17 +37,33 @@ echo " ######## ######## ############################################################################### " -# Swap in the private key and address of the first bidder to .env. + +echo " +Let's take the role of the first bidder - we'll swap in the private key and address of the first bidder to .env. + +We're going to run the transition function "place_bid", slotting in the first bidder's public address and the amount that is being bid. The inputs are the user's public address and the amount being bid. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpG9Af9z5Ha4ejVyMCqVFXRKknSm8L1ELEwcc4htk9YhVK +' > .env + +leo run place_bid aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke 10u64 +" + +# Swap in the private key of the first bidder to .env. echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpG9Af9z5Ha4ejVyMCqVFXRKknSm8L1ELEwcc4htk9YhVK " > .env # Have the first bidder place a bid of 10. +leo run place_bid aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke 10u64 + echo " ############################################################################### ######## ######## -######## STEP 1: The first bidder places a bid of 10 ######## +######## Step 1: The first bidder places a bid of 10 ######## ######## ######## ######## ------------------------------- ######## ######## | OPEN | A | B | ######## @@ -55,19 +73,31 @@ echo " ######## ######## ############################################################################### " -leo run place_bid aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke 10u64 || exit -# Swap in the private key and address of the second bidder to .env. +echo " +Now we're going to place another bid as the second bidder, so let's switch our keys to the second bidder and run the same transition function, this time with the second bidder's keys, public address, and different amount. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpAFshdsj2EqQzXh5zHceDapFWVCwR6wMCJFfkLYRKupug +' > .env + +leo run place_bid aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4 90u64 +" + +# Swap in the private key of the second bidder to .env. echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpAFshdsj2EqQzXh5zHceDapFWVCwR6wMCJFfkLYRKupug " > .env # Have the second bidder place a bid of 90. +leo run place_bid aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4 90u64 + echo " ############################################################################### ######## ######## -######## STEP 2: The second bidder places a bid of 90 ######## +######## Step 2: The second bidder places a bid of 90 ######## ######## ######## ######## ------------------------------- ######## ######## | OPEN | A | B | ######## @@ -77,19 +107,55 @@ echo " ######## ######## ############################################################################### " -leo run place_bid aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4 90u64 || exit -# Swap in the private key and address of the auctioneer to .env. +echo " +Now, let's take the role of the auctioneer, so we can determine which bid wins. Let's swap our keys to the auctioneer and run the resolve command on the output of the two bids from before. The resolve command takes the two output records from the bids as inputs and compares them to determine which bid wins. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp5wvamYgK3WCAdpBQxZqQX8XnuN2u11Y6QprZTriVwZVc +' > .env + +leo run resolve '{ + owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, + bidder: aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke.private, + amount: 10u64.private, + is_winner: false.private, + _nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public + }' '{ + owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, + bidder: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4.private, + amount: 90u64.private, + is_winner: false.private, + _nonce: 5952811863753971450641238938606857357746712138665944763541786901326522216736group.public + }' +" + +# Swaps in the private key of the auctioneer to .env. echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkp5wvamYgK3WCAdpBQxZqQX8XnuN2u11Y6QprZTriVwZVc " > .env # Have the auctioneer select the winning bid. +leo run resolve "{ + owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, + bidder: aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke.private, + amount: 10u64.private, + is_winner: false.private, + _nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public + }" "{ + owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, + bidder: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4.private, + amount: 90u64.private, + is_winner: false.private, + _nonce: 5952811863753971450641238938606857357746712138665944763541786901326522216736group.public + }" + echo " ############################################################################### ######## ######## -######## STEP 3: The auctioneer selects the winning bidder ######## +######## Step 3: The auctioneer determines the winning bidder ######## ######## ######## ######## ------------------------------- ######## ######## | OPEN | A | → B ← | ######## @@ -99,25 +165,32 @@ echo " ######## ######## ############################################################################### " -leo run resolve "{ + +echo " +Keeping the key environment the same since we're still the auctioneer, let's finalize the auction and label the winning output as the winner. The finish transition takes the winning output bid as the input and marks it as such. + +leo run finish '{ owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, - bidder: aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke.private, - amount: 10u64.private, + bidder: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4.private, + amount: 90u64.private, is_winner: false.private, - _nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public - }" "{ + _nonce: 5952811863753971450641238938606857357746712138665944763541786901326522216736group.public + }' +" + +# Have the auctioneer finish the auction. +leo run finish "{ owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, bidder: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4.private, amount: 90u64.private, is_winner: false.private, _nonce: 5952811863753971450641238938606857357746712138665944763541786901326522216736group.public - }" || exit + }" -# Have the auctioneer finish the auction. echo " ############################################################################### ######## ######## -######## STEP 3: The auctioneer completes the auction. ######## +######## The auctioneer completes the auction. ######## ######## ######## ######## ------------------------------- ######## ######## | CLOSE | A | → B ← | ######## @@ -127,16 +200,3 @@ echo " ######## ######## ############################################################################### " -leo run finish "{ - owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, - bidder: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4.private, - amount: 90u64.private, - is_winner: false.private, - _nonce: 5952811863753971450641238938606857357746712138665944763541786901326522216736group.public - }" || exit - - - - - - diff --git a/basic_bank/README.md b/basic_bank/README.md index 961b37a..a4fe922 100644 --- a/basic_bank/README.md +++ b/basic_bank/README.md @@ -33,39 +33,87 @@ Can you find any others? - mappings - finalize -## Running the Program +## How to Run -Leo provides users with a command line interface for compiling and running Leo programs. -Users may either specify input values via the command line or provide an input file in `inputs/`. +Follow the [Leo Installation Instructions](https://developer.aleo.org/leo/installation). -### Configuring Accounts -The `.env` file contains a private key. -This is the account that will be used to sign transactions and is checked for record ownership. -When executing programs as different parties, be sure to set the `PRIVATE_KEY` field in `.env` to the appropriate values. -See `./run.sh` for an example of how to run the program as different parties. +This basic bank program can be run using the following bash script. Locally, it will execute Leo program functions to issue, deposit, and withdraw tokens between a bank and a user. +```bash +cd basic_bank +./run.sh +``` + +The `.env` file contains a private key and address. This is the account that will be used to sign transactions and is checked for record ownership. When executing programs as different parties, be sure to set the `private_key` field in `.env` to the appropriate value. You can check out how we've set things up in `./run.sh` for a full example of how to run the program as different parties. -The [Aleo SDK](https://github.com/AleoHQ/leo/tree/testnet3) provides an interface for generating new accounts. -To generate a new account, navigate to [aleo.tools](https://aleo.tools). +## Walkthrough +* [Step 0: Issue Tokens](#issue) +* [Step 1: Deposit Tokens](#deposit) +* [Step 2: Wait](#wait) +* [Step 3: Withdraw Tokens](#withdraw) + +## Issue Tokens + +We will be playing the role of two parties. -### Providing inputs via the command line. -1. Run ```bash -leo run ... +The private key and address of the bank. +private_key: APrivateKey1zkpHtqVWT6fSHgUMNxsuVf7eaR6id2cj7TieKY1Z8CP5rCD +address: aleo1t0uer3jgtsgmx5tq6x6f9ecu8tr57rzzfnc2dgmcqldceal0ls9qf6st7a + +The private key and address of the user. +private_key: APrivateKey1zkp75cpr5NNQpVWc5mfsD9Uf2wg6XvHknf82iwB636q3rtc +address: aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg ``` -See `./run.sh` for an example. +Let's make some bank transactions. We'll take the role of the bank and issue 100 tokens to the user. We swap the private key into `.env` and run the `issue` transition function. The inputs are simply the recipient of the issuance and the amount. -### Using an input file. -1. Modify `inputs/auction.in` with the desired inputs. -2. Run ```bash -leo run +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpHtqVWT6fSHgUMNxsuVf7eaR6id2cj7TieKY1Z8CP5rCD +" > .env + +leo run issue aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg 100u64 ``` -For example, + +## Deposit Tokens + +Now, let's have the user deposit 50 of their tokens with the bank. We'll take the role of the user and call the deposit function, having the user use the output record that was issued to them by the bank. The inputs are the output record from the `issue` transition and the amount the user wishes to deposit. + ```bash -leo run issue -leo run deposit -leo run withdraw +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp75cpr5NNQpVWc5mfsD9Uf2wg6XvHknf82iwB636q3rtc +" > .env + +leo run deposit "{ + owner: aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg.private, + amount: 100u64.private, + _nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public +}" 50u64 ``` + +You'll see that the output contains a new private record belonging to the user with 50 credits, and a finalize `deposit` function taking the arguments (bank address, amount) that will update a public mapping with 50 credits. This information is queryable on-chain. + +## Wait + +With the 50 token deposit, let's say 15 periods of time pass with compounding interest at a rate of 12.34% on the principal amount. + +You can run the calculation yourself, it comes out to 266 tokens accrued using those numbers. + +## Withdraw Tokens + +Now, let's have the bank withdraw all tokens after 15 periods. Let's switch to the bank role, and call the `withdraw` transition function. The inputs are the recipient's address, amount, rate, and periods. + +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpHtqVWT6fSHgUMNxsuVf7eaR6id2cj7TieKY1Z8CP5rCD +" > .env + +leo run withdraw aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg 50u64 1234u64 15u64 +``` + +You'll see here the withdrawal function creates a new private record for the user containing all 266 withdrawn tokens, and then calls the finalize `withdraw` function with arguments (address, amount), which will update the public balance of the bank back to 0. The public mapping will be queryable on-chain. \ No newline at end of file diff --git a/basic_bank/build/main.aleo b/basic_bank/build/main.aleo index f049880..b8274fa 100644 --- a/basic_bank/build/main.aleo +++ b/basic_bank/build/main.aleo @@ -6,8 +6,8 @@ record Token: mapping balances: - key left as field.public; - value right as u64.public; + key as field.public; + value as u64.public; function issue: input r0 as address.private; @@ -22,9 +22,9 @@ function deposit: input r1 as u64.private; sub r0.amount r1 into r2; cast r0.owner r2 into r3 as Token.record; - hash.bhp256 r0.owner into r4 as field; output r3 as Token.record; - - finalize r4 r1; + hash.bhp256 r0.owner into r4 as field; + async deposit r4 r1 into r5; output r3 as Token.record; + output r5 as basic_bank.aleo/deposit.future; finalize deposit: input r0 as field.public; @@ -547,11 +547,11 @@ function withdraw: input r2 as u64.private; input r3 as u64.private; assert.eq self.caller aleo1t0uer3jgtsgmx5tq6x6f9ecu8tr57rzzfnc2dgmcqldceal0ls9qf6st7a; - hash.bhp256 r0 into r4 as field; call calculate_interest r1 r2 r3 into r5; + hash.bhp256 r0 into r4 as field; + call calculate_interest r1 r2 r3 into r5; cast r0 r5 into r6 as Token.record; - output r6 as Token.record; - - finalize r4 r1; + async withdraw r4 r1 into r7; output r6 as Token.record; + output r7 as basic_bank.aleo/withdraw.future; finalize withdraw: input r0 as field.public; diff --git a/basic_bank/inputs/basic_bank.in b/basic_bank/inputs/basic_bank.in deleted file mode 100644 index 6942984..0000000 --- a/basic_bank/inputs/basic_bank.in +++ /dev/null @@ -1,21 +0,0 @@ -// Inputs for the `issue` function. -[issue] -owner: address = aleo1t0uer3jgtsgmx5tq6x6f9ecu8tr57rzzfnc2dgmcqldceal0ls9qf6st7a; -amount: u64 = 1234u64; - -// Inputs for the `deposit` function. -[deposit] -token: Token = Token { - owner: aleo1t0uer3jgtsgmx5tq6x6f9ecu8tr57rzzfnc2dgmcqldceal0ls9qf6st7a, - amount: 1234u64, - _nonce: 0group, -}; -amount: u64 = 321u64; - -// Inputs for the `withdraw` function. -[withdraw] -recipient: address = aleo1t0uer3jgtsgmx5tq6x6f9ecu8tr57rzzfnc2dgmcqldceal0ls9qf6st7a; -amount: u64 = 321u64; -rate: u64 = 1500u64; -periods: u64 = 10u64; - diff --git a/basic_bank/run.sh b/basic_bank/run.sh index e4894a0..1ad624f 100755 --- a/basic_bank/run.sh +++ b/basic_bank/run.sh @@ -6,27 +6,42 @@ then exit fi -# The private key and address of the bank. -# Swap these into program.json, when running transactions as the first bidder. -# "private_key": "APrivateKey1zkpHtqVWT6fSHgUMNxsuVf7eaR6id2cj7TieKY1Z8CP5rCD", -# "address": "aleo1t0uer3jgtsgmx5tq6x6f9ecu8tr57rzzfnc2dgmcqldceal0ls9qf6st7a" +echo " +We will be playing the role of two parties. + +The private key and address of the bank. +private_key: APrivateKey1zkpHtqVWT6fSHgUMNxsuVf7eaR6id2cj7TieKY1Z8CP5rCD +address: aleo1t0uer3jgtsgmx5tq6x6f9ecu8tr57rzzfnc2dgmcqldceal0ls9qf6st7a + +The private key and address of the user. +private_key: APrivateKey1zkp75cpr5NNQpVWc5mfsD9Uf2wg6XvHknf82iwB636q3rtc +address: aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg +" + +echo " +Let's make some bank transactions. We'll take the role of the bank and issue 100 tokens to the user. We swap the private key into .env and run the issue transition function. The inputs are simply the recipient of the issuance and the amount. -# The private key and address of the user. -# Swap these into program.json, when running transactions as the second bidder. -# "private_key": "APrivateKey1zkp75cpr5NNQpVWc5mfsD9Uf2wg6XvHknf82iwB636q3rtc" -# "address": "aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg" +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpHtqVWT6fSHgUMNxsuVf7eaR6id2cj7TieKY1Z8CP5rCD +' > .env + +leo run issue aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg 100u64 +" -# Swap in the private key and address of the bank to .env. +# Swap in the private key of the bank to .env. echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpHtqVWT6fSHgUMNxsuVf7eaR6id2cj7TieKY1Z8CP5rCD " > .env # Have the bank issue 100 tokens to the user. +leo run issue aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg 100u64 + echo " ############################################################################### ######## ######## -######## STEP 1: Initialize 100 tokens for aleo1zeklp...v46ngg ######## +######## Step 0: Initialize 100 tokens for aleo1zeklp...v46ngg ######## ######## ######## ######## ----------------------------------------- ######## ######## | ACTION | AMOUNT | ######## @@ -47,7 +62,7 @@ echo " ######## ----------------------------------------- ######## ######## ######## ######## ----------------------------------------- ######## -######## | BANK | aleo1zeklp...v46ngg | ######## +######## | BANK | aleo1t0uer...f6st7a | ######## ######## ----------------------------------------- ######## ######## ----------------------------------------- ######## ######## | Balance | 0 | ######## @@ -63,19 +78,43 @@ echo " ######## ######## ############################################################################### " -leo run issue aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg 100u64 || exit -# Swap in the private key and address of the user to .env. +echo " +Now, let's have the user deposit 50 of their tokens with the bank. We'll take the role of the user and call the deposit function, having the user use the output record that was issued to them by the bank. The inputs are the output record from the issue transition and the amount the user wishes to deposit. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp75cpr5NNQpVWc5mfsD9Uf2wg6XvHknf82iwB636q3rtc +' > .env + +leo run deposit '{ + owner: aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg.private, + amount: 100u64.private, + _nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public +}' 50u64 +" + +# Swap in the private key of the user to .env. echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkp75cpr5NNQpVWc5mfsD9Uf2wg6XvHknf82iwB636q3rtc " > .env # Have the user deposit 50 tokens into the bank. +leo run deposit "{ + owner: aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg.private, + amount: 100u64.private, + _nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public +}" 50u64 + +echo " +You'll see that the output contains a new private record belonging to the user with 50 credits, and a finalize deposit function taking the arguments (bank address, amount) that will update a public mapping with 50 credits. This information is queryable on-chain. +" + echo " ############################################################################### ######## ######## -######## STEP 2: aleo1zeklp...v46ngg deposits 50 tokens ######## +######## Step 1: aleo1zeklp...v46ngg deposits 50 tokens ######## ######## ######## ######## ----------------------------------------- ######## ######## | ACTION | AMOUNT | ######## @@ -96,7 +135,7 @@ echo " ######## ----------------------------------------- ######## ######## ######## ######## ----------------------------------------- ######## -######## | BANK | aleo1zeklp...v46ngg | ######## +######## | BANK | aleo1t0uer...f6st7a | ######## ######## ----------------------------------------- ######## ######## ----------------------------------------- ######## ######## | Balance | 50 | ######## @@ -112,16 +151,13 @@ echo " ######## ######## ############################################################################### " -leo run deposit "{ - owner: aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg.private, - amount: 100u64.private, - _nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public -}" 50u64 || exit echo " +With the 50 token deposit, let's say 15 periods of time pass with compounding interest at a rate of 12.34% on the principal amount. + ############################################################################### ######## ######## -######## STEP 3: Wait 15 periods ######## +######## Step 2: Wait 15 periods ######## ######## ######## ######## ----------------------------------------- ######## ######## | ACTION | AMOUNT | ######## @@ -142,7 +178,7 @@ echo " ######## ----------------------------------------- ######## ######## ######## ######## ----------------------------------------- ######## -######## | BANK | aleo1zeklp...v46ngg | ######## +######## | BANK | aleo1t0uer...f6st7a | ######## ######## ----------------------------------------- ######## ######## ----------------------------------------- ######## ######## | Balance | 266 | ######## @@ -159,17 +195,34 @@ echo " ############################################################################### " -# Swap in the private key and address of the bank to .env. +echo " +Now, let's have the bank withdraw all tokens after 15 periods. Let's switch to the bank role, and call the withdraw transition function. The inputs are the recipient's address, amount, rate, and periods. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpHtqVWT6fSHgUMNxsuVf7eaR6id2cj7TieKY1Z8CP5rCD +' > .env + +leo run withdraw aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg 50u64 1234u64 15u64 + +" +# Swap in the private key of the bank to .env. echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpHtqVWT6fSHgUMNxsuVf7eaR6id2cj7TieKY1Z8CP5rCD " > .env # Have the bank withdraw all of the user's tokens with compound interest over 15 periods at 12.34%. +leo run withdraw aleo1zeklp6dd8e764spe74xez6f8w27dlua3w7hl4z2uln03re52egpsv46ngg 50u64 1234u64 15u64 + +echo " +You'll see here the withdrawal function creates a new private record for the user containing all 266 withdrawn tokens, and then calls the finalize withdraw function with arguments (address, amount), which will update the public balance of the bank back to 0. The public mapping will be queryable on-chain. +" + echo " ############################################################################### ######## ######## -######## STEP 4: Withdraw tokens of aleo1zeklp...v46ngg w/ interest ######## +######## Step 3: Withdraw tokens of aleo1zeklp...v46ngg w/ interest ######## ######## ######## ######## ----------------------------------------- ######## ######## | ACTION | AMOUNT | ######## @@ -190,7 +243,7 @@ echo " ######## ----------------------------------------- ######## ######## ######## ######## ----------------------------------------- ######## -######## | BANK | aleo1zeklp...v46ngg | ######## +######## | BANK | aleo1t0uer...f6st7a | ######## ######## ----------------------------------------- ######## ######## ----------------------------------------- ######## ######## | Balance | 0 | ######## @@ -206,5 +259,3 @@ echo " ######## ######## ############################################################################### " -leo run withdraw aleo1t0uer3jgtsgmx5tq6x6f9ecu8tr57rzzfnc2dgmcqldceal0ls9qf6st7a 50u64 1234u64 15u64 || exit - diff --git a/battleship/.env b/battleship/.env index 6cd3948..aca2ca4 100644 --- a/battleship/.env +++ b/battleship/.env @@ -1,4 +1,4 @@ - NETWORK=testnet3 - PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH - +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm + diff --git a/battleship/README.md b/battleship/README.md index 11f79cc..ed0edc5 100644 --- a/battleship/README.md +++ b/battleship/README.md @@ -1,6 +1,8 @@ workshop/battleship +## Contents + - [Summary](#summary) - [Build](#how-to-build) - [Run](#how-to-run) @@ -23,9 +25,7 @@ - [Ensuring Valid Moves](#enforce-constraints-on-valid-moves-and-force-the-player-to-give-their-opponent-information-about-their-opponents-previous-move-in-order-to-continue-playing) - [Winning](#winning-the-game) -# Summary - -workshop/battleship +## Summary Battleship is a game where two players lay their ships into secret configurations on their respective 8x8 grids, and then take turns firing upon each other's board. @@ -33,50 +33,49 @@ The game ends when one player has sunk all of the other player's ships. This application was translated into Leo from the [zk-battleship](https://github.com/demox-labs/zk-battleship) example written by the Aleo community - show them some love! -## How to Build +## How to Run + +Follow the [Leo Installation Instructions](https://developer.aleo.org/leo/installation). + +This battleship program can be run using the following bash script. Locally, it will execute Leo program functions to create the board, place ships, and play a game of battleship. + -To compile this Leo program, run: ```bash -leo build +cd battleship +./run.sh ``` -## How to Run -
Commands and Playing the Game +The `.env` file contains a private key and address. This is the account that will be used to sign transactions and is checked for record ownership. When executing programs as different parties, be sure to set the `private_key` field in `.env` to the appropriate value. You can check out how we've set things up in `./run.sh` for a full example of how to run the program as different parties. + +## 1. Initializing the Players +In order to play battleship, there must be two players with two boards. Players will be represented by their Aleo address. -### 1. Initializing the Players -In order to play battleship, there must be two players with two boards. -Players will be represented by their Aleo address. -You can use the provided player accounts or [generate your own](https://aleohq.github.io/aleo/). -```markdown -Player 1: - Private Key APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm - View Key AViewKey1fSyEPXxfPFVgjL6qcM9izWRGrhSHKXyN3c64BNsAjnA6 - Address aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy +We will be playing the role of these two parties: -Player 2: - Private Key APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH - View Key AViewKey1hh6dvSEgeMdfseP4hfdbNYjX4grETwCuTbKnCftkpMwE - Address aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry +```bash +The private key and address of Player 1. +private_key: APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +address: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy + +The private key and address of Player 2. +private_key: APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +address: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry ``` -Save the keys and addresses. Set the `.env` private_key and address to one of the newly created aleo accounts. We'll refer to this address as Player 1, and the remaining address as Player 2. +## 2. Player 1 Places Ships on the Board +Now, we need to make a board as Player 1. See the [modeling the boards and ships](#modeling-the-board-and-ships) section for information on valid ship bitstrings and placements on the board. -```text +With Player 1's private key, they initialize the board with the placement of 4 ships and the opponent's public address. + +```bash +echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm -``` - -### 2. Player 1 Places Ships on the Board -Now, we need to make a board as Player 1. -See the [modeling the boards and ships](#modeling-the-board-and-ships) section for information on valid ship bitstrings and placements on the board. -For this example, we will be using sample valid inputs. -Initialize a new board as Player 1 with valid ship inputs and Player 2's address: `leo run initialize_board ship_5_bitstring ship_4_bitstring ship_3_bitstring ship_2_bitstring player_2_address` +" > .env -**Run** -``` leo run initialize_board 34084860461056u64 551911718912u64 7u64 1157425104234217472u64 aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry ``` -**Output** + ```bash ➡️ Output @@ -88,15 +87,14 @@ leo run initialize_board 34084860461056u64 551911718912u64 7u64 1157425104234217 player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, game_started: false.private, - _nonce: 3887646704618532506963887075433683846689834495661101507703164090915348189037group.public + _nonce: 605849623036268790365773177565562473735086364071033205649960161942593750353group.public } -✅ Executed 'battleship.aleo/initialize_board' +Leo ✅ Finished 'battleship.aleo/initialize_board' ``` The output is a board_state record owned by Player 1. -Notice that the `game_started` flag is false, as well as the composite ship configuration `ships`. -1157459741006397447u64 to a binary bitstring becomes `0001000000010000000111111000000010000000100000001000000000000111`, +Notice that the `game_started` flag is false, as well as the composite ship configuration `ships`. 1157459741006397447u64 to a binary bitstring becomes `0001000000010000000111111000000010000000100000001000000000000111`, or laid out in columns and rows: ``` 0 0 0 1 0 0 0 0 @@ -109,11 +107,11 @@ or laid out in columns and rows: 0 0 0 0 0 1 1 1 ``` -### 3: Player 1 Passes The Board To Player 2 -Now, we can offer a battleship game to player 2. Run `leo run offer_battleship 'board_state.record'` with the record you just created: -**Run** +## 3: Player 1 Passes The Board To Player 2 +Now, we can offer a battleship game to Player 2. Run `offer_battleship` with the record you just created: + ``` -leo run offer_battleship '{ +leo run offer_battleship "{ owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, hits_and_misses: 0u64.private, played_tiles: 0u64.private, @@ -121,11 +119,10 @@ leo run offer_battleship '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, game_started: false.private, - _nonce: 3887646704618532506963887075433683846689834495661101507703164090915348189037group.public -}' + _nonce: 605849623036268790365773177565562473735086364071033205649960161942593750353group.public +}" ``` -**Output** ```bash ➡️ Outputs @@ -137,7 +134,7 @@ leo run offer_battleship '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, game_started: true.private, - _nonce: 6563064852163330630334088854834332804417910882908622526775624018226782316843group.public + _nonce: 5443521912126792569907060514335205174032013684291524549930033539632156136027group.public } • { owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, @@ -145,35 +142,27 @@ leo run offer_battleship '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, prev_hit_or_miss: 0u64.private, - _nonce: 4374626042494973146987320062571809401151262172766172816829659487584978644457group.public + _nonce: 6986401140057557061321899375524513841643724821230599181456639629979203966487group.public } -✅ Executed 'battleship.aleo/offer_battleship' +Leo ✅ Finished 'battleship.aleo/offer_battleship' ``` -The first output record is the udpated board_state.record. -Notice the `game_started` flag is now true. -This board cannot be used to offer any other battleship games or accept any battleship game offers. -Player 1 would need to initialize a new board and use that instead. -The second output record is a dummy move.record -- -there are no fire coordinates included to play on Player 2's board, -and no information about any previous Player 2 moves (Player 2 has not made any moves yet). -This move.record is owned by Player 2, who must use that in combination with their own board_state.record to accept the game. Let's do that now. +The first output record is the updated `board_state.record`. Notice the `game_started` flag is now true. This board cannot be used to offer any other battleship games or accept any battleship game offers. Player 1 would need to initialize a new board and use that instead. The second output record is a dummy `move.record` - there are no fire coordinates included to play on Player 2's board, and no information about any previous Player 2 moves (Player 2 has not made any moves yet). This `move.record` is owned by Player 2, who must use that in combination with their own `board_state.record` to accept the game. Let's do that now. + +## 4: Player 2 Places Ships On The Board + +We switch our .env to Player 2's private key and similarly run initialize_board to create a new and different board for player two. -### 4: Player 2 Places Ships On The Board -We must run the program as Player 2 now, so switch the `.env` file to use Player 2's keys: -```text +```bash +echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH -``` +" > .env -We'll create a new and different board for Player 2, and make sure to include Player 1's address as the opponent: -**Run** -```bash leo run initialize_board 31u64 2207646875648u64 224u64 9042383626829824u64 aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy ``` -**Output** ```bash ➡️ Output @@ -185,7 +174,7 @@ leo run initialize_board 31u64 2207646875648u64 224u64 9042383626829824u64 aleo1 player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, game_started: false.private, - _nonce: 1549419609469324182591325047490602235361156298832591378925133482196483208807group.public + _nonce: 677929557867990662961068737825412945684193990901139603462104629310061710321group.public } ✅ Executed 'battleship.aleo/initialize_board' @@ -202,11 +191,12 @@ Note, the output ships here is 9044591273705727u64, which in a bitstring is: 1 1 1 1 1 1 1 1 ``` -### 5: Passing The Board Back To Player 1 -Now, we can accept Player 1's offer. Run `leo run start_battleship 'board_state.record' 'move.record'`: -**Run** +## 5: Passing The Board Back To Player 1 + +Now, we can accept Player 1's offer. Run `start_battleship`: + ```bash -leo run start_battleship '{ +leo run start_battleship "{ owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, hits_and_misses: 0u64.private, played_tiles: 0u64.private, @@ -214,18 +204,17 @@ leo run start_battleship '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, game_started: false.private, - _nonce: 1549419609469324182591325047490602235361156298832591378925133482196483208807group.public -}' '{ + _nonce: 677929557867990662961068737825412945684193990901139603462104629310061710321group.public +}" "{ owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, incoming_fire_coordinate: 0u64.private, player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, prev_hit_or_miss: 0u64.private, - _nonce: 4374626042494973146987320062571809401151262172766172816829659487584978644457group.public -}' + _nonce: 6306786918362462465996698473371289503655844751914031374264794338640697795225group.public +}" ``` -**Outputs** ```bash ➡️ Outputs @@ -237,7 +226,7 @@ leo run start_battleship '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, game_started: true.private, - _nonce: 6222383571142756260765569201308836492199048237638652378826141459336360362251group.public + _nonce: 499506036017893504519951074816367233238764881167148207158107765834843789278group.public } • { owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, @@ -245,23 +234,25 @@ leo run start_battleship '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, prev_hit_or_miss: 0u64.private, - _nonce: 3742551407126138397717446975757978589064777004441277005584760115236217735495group.public + _nonce: 7551593771072417773015833444631669906818701068612998340960968556531564726874group.public } ✅ Executed 'battleship.aleo/start_battleship' ``` -Notice the outputs here are similar to `offer_battleship`. -A dummy move.record is owned by Player 1, and Player 2 gets a board_state.record with the `game_started` flag updated. -However, now that Player 1 has a move.record and a started board, they can begin to play. +Notice the outputs here are similar to `offer_battleship`. A dummy `move.record` is owned by Player 1, and Player 2 gets a `board_state.record` with the `game_started` flag updated. However, now that Player 1 has a `move.record` and a started board, they can begin to play. + +## 6: Player 1 Takes The 1st Turn -### 6: Player 1 Takes The 1st Turn -**Switch** `.env`'s keys back to Player 1's. -Player 1 now makes the first real move: `leo run play 'board_state.record' 'move.record' fire_coordinate` +We switch the .env back to Player 1, and we run the transition function play. -**Run** ```bash -leo run play '{ +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +" > .env + +leo run play "{ owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, hits_and_misses: 0u64.private, played_tiles: 0u64.private, @@ -269,17 +260,17 @@ leo run play '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, game_started: true.private, - _nonce: 6563064852163330630334088854834332804417910882908622526775624018226782316843group.public -}' '{ + _nonce: 6313341191294792052861773157032837489809107102476040695601777954897783350080group.public +}" "{ owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, incoming_fire_coordinate: 0u64.private, player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, prev_hit_or_miss: 0u64.private, - _nonce: 3742551407126138397717446975757978589064777004441277005584760115236217735495group.public -}' 1u64 + _nonce: 2798663115519921626400765401803177719929914180089719334947022448579691220488group.public +}" 1u64 ``` -**Outputs** + ```bash ➡️ Outputs @@ -291,7 +282,7 @@ leo run play '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, game_started: true.private, - _nonce: 1474170213684980843727833284550698461565286563122422722760769547002894080093group.public + _nonce: 5833516448655036599597838063894464861371198938108460526636526325286738488235group.public } • { owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, @@ -299,24 +290,25 @@ leo run play '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, prev_hit_or_miss: 0u64.private, - _nonce: 5481529266389297320813092061136936339861329677911328036818179854958874588416group.public + _nonce: 4383078917685812690935470339923943658033179718952229417171392956492546325808group.public } ✅ Executed 'battleship.aleo/play' ``` -Player 1 has an updated board_state.record -- they have a new `played_tiles` bitstring, -which corresponds to the fire coordinate they just sent to Player 2. -You can see that the `incoming_fire_coordinate` in the move.record owned by Player 2 matches exactly the input given by Player 1. -Player 2 can now play this move tile and respond with a fire coordinate of their own, -and they will also let Player 1 know whether their fire coordinate hit or miss Player 2's ships. +Player 1 has an updated `board_state.record` - they have a new `played_tiles` bitstring, which corresponds to the fire coordinate they just sent to Player 2. You can see that the `incoming_fire_coordinate` in the `move.record` owned by Player 2 matches exactly the input given by Player 1. Player 2 can now play this move tile and respond with a fire coordinate of their own, and they will also let Player 1 know whether their fire coordinate hit or miss Player 2's ships. + +## 7: Player 2 Takes The 2nd Turn -### 7: Player 2 Takes The 2nd Turn -**Switch** `.env` to Player 2's keys. Player 2 makes their move: +We switch the .env back to Player 2, and we run the transition function play. -**Run** ```bash -leo run play '{ +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +" > .env + +leo run play "{ owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, hits_and_misses: 0u64.private, played_tiles: 0u64.private, @@ -324,19 +316,17 @@ leo run play '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, game_started: true.private, - _nonce: 6222383571142756260765569201308836492199048237638652378826141459336360362251group.public -}' '{ + _nonce: 6864275139988909612799168784231775829713739147830284979332684562641318182923group.public +}" "{ owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, incoming_fire_coordinate: 1u64.private, player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, prev_hit_or_miss: 0u64.private, - _nonce: 5481529266389297320813092061136936339861329677911328036818179854958874588416group.public -}' 2048u64 - + _nonce: 8420474443174402614458578667801578345975509805478103542095622903412594983971group.public +}" 2048u64 ``` -**Outputs** ```bash ➡️ Outputs @@ -348,7 +338,7 @@ leo run play '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, game_started: true.private, - _nonce: 5254963165391133332409074172682159033621708071536429341861038147524454777097group.public + _nonce: 6284479302801058138006361960649628992876976428745392660731784830148359328839group.public } • { owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, @@ -356,33 +346,30 @@ leo run play '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, prev_hit_or_miss: 1u64.private, - _nonce: 5851606198769770675504009323414373017067582072428989801313256693053765675198group.public + _nonce: 8217837260140600949756911248177622179381338760298068527463640818659709985441group.public } ✅ Executed 'battleship.aleo/play' ``` -Player 2 now has an updated board_state.record which includes their newly updated `played_tiles`, -only containing the fire coordinate they just sent to Player 1. -Player 1 now owns a new move.record which includes the `hits_and_misses` field. -This contains only the result of Player 1's previous fire coordinate they had sent to Player 2. -It will always be a single coordinate on the 8x8 grid if it's a hit. A miss is 0u64 (8x8 grid of 0s), -whereas a hit is the u64 equivalent of their previous fire coordinate in bitstring form. -If you check Player 2's ships configuration, you'll note their entire bottom row is covered by two ships, -so sample valid hits on the bottom row would be: 1u64, 2u64, 4u64, 8u64, 16u64, 32u64, 64u64, and 128u64. -Since Player 1's first fire coordinate (1u64) was a hit, the `hits_and_misses` field is also 1u64. +Player 2 now has an updated `board_state.record` which includes their newly updated `played_tiles`, only containing the fire coordinate they just sent to Player 1. Player 1 now owns a new `move.record` which includes the `hits_and_misses` field. +This contains only the result of Player 1's previous fire coordinate they had sent to Player 2. It will always be a single coordinate on the 8x8 grid if it's a hit. A miss is 0u64 (8x8 grid of 0s), whereas a hit is the u64 equivalent of their previous fire coordinate in bitstring form. -Player 1's next move will consume this move.record, which will update Player 1's board with the hit-or-miss, -as well as figure out the result of Player 2's fire coordinate. -Now that Player 1 has some `played_tiles`, they can no longer choose an alread-played fire coordinate. -For example, running `aleo run play 'board_state.record' 'move.record' 1u64` will fail, because 1u64 has already been played. +If you check Player 2's ships configuration, you'll note their entire bottom row is covered by two ships, so sample valid hits on the bottom row would be: 1u64, 2u64, 4u64, 8u64, 16u64, 32u64, 64u64, and 128u64. Since Player 1's first fire coordinate (1u64) was a hit, the `hits_and_misses` field is also 1u64. -### 8: Player 1 Takes The 3rd Turn -**Switch** `.env` to use Player 1's keys. +Player 1's next move will consume this `move.record`, which will update Player 1's board with the hit-or-miss, as well as figure out the result of Player 2's fire coordinate. Now that Player 1 has some `played_tiles`, they can no longer choose an alread-played fire coordinate. For example, running `aleo run play 'board_state.record' 'move.record' 1u64` will fail, because 1u64 has already been played. + +## 8: Player 1 Takes The 3rd Turn + +We switch the .env back to Player 1, and we run the transition function play. -**Run** ```bash -leo run play '{ +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +" > .env + +leo run play "{ owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, hits_and_misses: 0u64.private, played_tiles: 1u64.private, @@ -390,18 +377,17 @@ leo run play '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, game_started: true.private, - _nonce: 1474170213684980843727833284550698461565286563122422722760769547002894080093group.public -}' '{ + _nonce: 1962122153746742645258971561783872712461616481157617568489391338473028502271group.public +}" "{ owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, incoming_fire_coordinate: 2048u64.private, player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, prev_hit_or_miss: 1u64.private, - _nonce: 5851606198769770675504009323414373017067582072428989801313256693053765675198group.public -}' 2u64 + _nonce: 1204008848449868423802652577996848559012797694551224583683080100053831915439group.public +}" 2u64 ``` -**Outputs** ```bash ➡️ Outputs @@ -413,7 +399,7 @@ leo run play '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, game_started: true.private, - _nonce: 853278652528988609827041334083853520436225751739504321439524466875699631772group.public + _nonce: 5338125050531864311985370830280952305688629865354830939402745656578990650505group.public } • { owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, @@ -421,14 +407,13 @@ leo run play '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, prev_hit_or_miss: 0u64.private, - _nonce: 710336412388939616658264778971886770861024495941253598683184288448156545822group.public + _nonce: 7971995563631235472540847437984726419106193784727086463494463811056252801811group.public } ✅ Executed 'battleship.aleo/play' ``` -As before, both a board_state.record and move.record are created. -The board_state.record now contains 3u64 as the `played_tiles`, which looks like this in bitstring form: +As before, both a `board_state.record` and `move.record` are created. The `board_state.record` now contains 3u64 as the `played_tiles`, which looks like this in bitstring form: ``` 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -440,14 +425,19 @@ The board_state.record now contains 3u64 as the `played_tiles`, which looks like 0 0 0 0 0 0 1 1 ``` -The board_state.record `hits_and_misses` field has also been updated with the result of their previous move. The new move.record owned by Player 2 now contains information about whether Player 2's previous move was a hit or miss, as well as Player 1's new fire coordinate. +The `board_state.record` `hits_and_misses` field has also been updated with the result of their previous move. The new `move.record` owned by Player 2 now contains information about whether Player 2's previous move was a hit or miss, as well as Player 1's new fire coordinate. + +## 9: Player 2 Takes The 4th Turn -### 9: Player 2 Takes The 4th Turn -**Switch** `.env`'s keys to Player 2. Player 2 makes their next move: +We switch the .env back to Player 2, and we run the transition function play. -**Run** ```bash -leo run play '{ +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +" > .env + +leo run play "{ owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, hits_and_misses: 0u64.private, played_tiles: 2048u64.private, @@ -455,18 +445,17 @@ leo run play '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, game_started: true.private, - _nonce: 5254963165391133332409074172682159033621708071536429341861038147524454777097group.public -}' '{ + _nonce: 591128247205636061702123861968396246163831838278146623498909560875485861872group.public +}" "{ owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, incoming_fire_coordinate: 2u64.private, player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, prev_hit_or_miss: 0u64.private, - _nonce: 710336412388939616658264778971886770861024495941253598683184288448156545822group.public -}' 4u64 + _nonce: 4871574741887919250014604645502780786361650856453535231083359604148337116539group.public +}" 4u64 ``` -**Outputs** ```bash ➡️ Outputs @@ -478,7 +467,7 @@ leo run play '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, game_started: true.private, - _nonce: 1145182747531998766752104305052328886102707397061849372000385383229513301534group.public + _nonce: 4866144015676673398767235148516158177034901439767024502676546368462039477864group.public } • { owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, @@ -486,17 +475,15 @@ leo run play '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, prev_hit_or_miss: 2u64.private, - _nonce: 5958326936461495382488152485080596366937963499216527548334225566230682598418group.public + _nonce: 5304512645876453228434639693756897952439730718508628026257897445388710294282group.public } ✅ Executed 'battleship.aleo/play' ``` -### 10. Who Wins? -Play continues back and forth between Player 1 and Player 2. -When one player has a total of 14 flipped bits in their `hits_and_misses` field on their board_state.record, -they have won the game. -
+## 10. Who Wins? + +Play continues back and forth between Player 1 and Player 2. When one player has a total of 14 flipped bits in their `hits_and_misses` field on their `board_state.record`, they have won the game. ## ZK Battleship Privacy @@ -528,7 +515,7 @@ Most battleship representations in programs use a 64 character string or an arra 0 0 0 0 0 0 0 0 ``` -Battleship is played with 4 different ship types -- a ship of length 5, length 4, length 3, and length 2. Some versions of battleship have an extra length 3 ship or another extra ship type, however, we will stick to the most basic version for this project. In order to be a valid ship placement, a ship must be placed vertically or horizontally (no diagonals). On a physical board, a ship cannot break across rows or intersect with another ship, but ships are allowed to touch one another. +Battleship is played with 4 different ship types - a ship of length 5, length 4, length 3, and length 2. Some versions of battleship have an extra length 3 ship or another extra ship type, however, we will stick to the most basic version for this project. In order to be a valid ship placement, a ship must be placed vertically or horizontally (no diagonals). On a physical board, a ship cannot break across rows or intersect with another ship, but ships are allowed to touch one another. Similar to how we represent a board with a u64 bitstring, we can represent a ship horizontally as a bitstring. We "flip" the bits to represent a ship: | Length | Bitstring | u64 | @@ -548,7 +535,7 @@ We can also represent a ship vertically as a bitstring. To show this, we need 7 With a board model and ship bitstring models, we can now place ships on a board. -
Examples of valid board configurations: +### Examples of valid board configurations: 17870284429256033024u64 ``` @@ -586,9 +573,7 @@ With a board model and ship bitstring models, we can now place ships on a board. 0 0 0 0 0 0 0 1 ``` -
- -
Examples of invalid board configurations: +### Examples of invalid board configurations: Ships overlapping the bottom ship: 67503903u64 @@ -628,7 +613,6 @@ Ships splitting across rows and columns: 0 0 0 1 0 0 1 0 0 0 0 1 0 0 1 0 ``` -
Given these rules, our strategy will be to validate each individual ship bitstring placement on a board, and then, if all the ships are valid, compose all the positions onto a board and validate that the board with all ships are valid. If each individual ship's position is valid, then all the ships together should be valid unless any overlapping occurs. @@ -647,18 +631,20 @@ If vertical: If a ship is valid vertically or horizontally, then we know the ship is valid. We just need to check for the bit count, the adjacency of those bits, and make sure those bits do not split a row/column. However, we can't loop through the bit string to count bits, or to make sure those bits don't break across columns. We'll need to turn to special bitwise operations and hacks. -
Bit Counting +### Bit Counting See the "c_bitcount" closure to follow along with the code. 50 years ago, MIT AI Laboratory published HAKMEM, which was a series of tricks and hacks to speed up processing for bitwise operations. https://w3.pppl.gov/~hammett/work/2009/AIM-239-ocr.pdf We turned to HAKMEM 169 for bitcounting inspiration, although we've tweaked our implementation to be (hopefully) easier to understand. Before diving into details, let's build some intuition. -Let a,b,c,d be either 0 or 1. Given a polynomial 8a + 4b + 2c + d, how do we find the summation of a + b + c + d? -If we subtract subsets of this polynomial, we'll be left with the summation. +Let a,b,c,d be either 0 or 1. Given a polynomial 8a + 4b + 2c + d, how do we find the summation of a + b + c + d? If we subtract subsets of this polynomial, we'll be left with the summation. + Step 1: 8a + 4b + 2c + d Step 2: -4a - 2b - c Step 3: -2a - b Step 4: - a -Step 5: = a + b + c + d -This polynomial is basically a bitwise representation of a number, so given a 4 bit number, e.g. 1011 or 13u64, we can follow these instructions to get the bit count. Step 2 is just subtracting the starting number but bit shifted to the right (equivalent to dividing by 2). Step 3 bit shifts the starting number to the right twice and is subtracted, and Step 4 bit shifts thrice and is subtracted. Put another way: Start with a 4-digit binary number A. A - (A >> 1) - (A >> 2) - (A >> 3) = B. +Step 5: = a + b + c + d + +This polynomial is basically a bitwise representation of a number, so given a 4 bit number, e.g. 1011 or 13u64, we can follow these instructions to get the bit count. Step 2 is just subtracting the starting number but bit shifted to the right (equivalent to dividing by 2). Step 3 bit shifts the starting number to the right twice and is subtracted, and Step 4 bit shifts thrice and is subtracted. Put another way: Start with a 4-digit binary number A. A - (A >> 1) - (A >> 2) - (A >> 3) = B. + Step 1: 1101 = 13u64 Step 2: -0110 = 6u64 Step 3: -0011 = 3u64 @@ -667,6 +653,7 @@ Step 5: =0011 = 3u64 To make this process work for any bit-length number, where the sum of the bits is left in groups of 4 bits, we'll need to use some bit-masking, so that the sum of one group of 4 does not interfere with the next group of 4. With a larger starting number, like 1111 0001 0111 0110, we will need the following bit maskings: + ``` For A >> 1, we'll use 0111 0111 0111 .... (in u64, this is 8608480567731124087u64) For A >> 2, we'll use 0011 0011 0011 .... (in u64, this is 3689348814741910323u64) @@ -674,6 +661,7 @@ For A >> 3, we'll use 0001 0001 0001 .... (in u64, this is 1229782938247303441u6 ``` For example, finding the sums of groups of 4 with a 16-bit number we'll call A to yield the bit sum number B: + ``` A: 1111 0001 0111 0110 A>>1: 0111 1000 1011 1011 @@ -698,6 +686,7 @@ B: 0100 0001 0011 0010 ``` The next step is to combine the summation of each of those 4-bit groups into sums of 8-bit groups. To do this, we'll use another bit trick. We will shift this number B to the right by 4 (B >> 4), and add that back to B. Then, we'll apply a bit masking of 0000 1111 0000 1111 .... (in u64, this is 1085102592571150095u64) to yield the sums of bits in groups of 8, a number we'll call C. + ``` B: 0100 0001 0011 0010 B>>4: 0000 0100 0001 0011 @@ -714,14 +703,15 @@ C: 0000 0101 0000 0101 At this point, we've gone from a bit sum in groups of 4 to bit sums in groups of 8. That's great, but ultimately we want the total sum of bits in the original binary number. The final bit trick is to modulo C by 255. This is 2^8 - 1. For a bit of intuition, consider the number 1 0000 0001. If we take 1 0000 0001 mod 256, we're left with 1. If we take 1 0000 0001 mod 255, we're left with 2. Modding by 255 gives us the amount of bits _beyond_ the first 255 numbers, as 255 is the largest number that can be represented with 8 bits. A full summary of abbreviated steps to get the bit count, starting with a 64 bit integer A (closely following the c_bitcount closure in the verify.aleo code): + +``` let A = 64 unsigned bit integer let B = A - (A>>1 & 8608480567731124087u64) - (A>>2 & 3689348814741910323u64) - (A>>3 & 1229782938247303441u64) let C = (B - B>>4) & 1085102592571150095u64 bit count = C mod 255u64 +``` -
- -
Adjacency Check +### Adjacency Check Given a ship's placement on the board and its bitstring representation (horizontally or vertically), we can determine if the bits are adjacent. Follow the c_adjacency_check closure in verify.aleo. Given the ship of length 2, we know it's horizontal bitstring is 11 (3u64) and it's vertical bitstring is 100000001 (257u64). If on the board, the ship starts at the bottom right corner, its horizontal ship placement string would be: 3u64 @@ -804,12 +794,14 @@ We can make the observation that the original bitstring is always shifted by a p To ensure that the remaining number is a power of 2, we can use a bit trick. See the bit trick for ensuring a bitstring is a power of 2 section. In the code, you'll notice one extra step. Dividing a ship placement bitstring by a ship bitstring representation could result in 0, and then subtracting by 1 will result in an underflow. In that case, we know the ship placement is not valid, so we can set a number which is gauranteed to not be a power of 2. -
-
Splitting a row or column + +### Splitting a row or column + Follow the c_horizontal_check closure in verify.aleo to follow the code. Assume all the bits are adjacent (see the adjacency check section). The column case is trivial. We can be certain that if a ship bitstring splits columns, the division of that ship placement bitstring by its ship bitstring representation will not yield a power of 2, and it would have failed the adjacency check. The horizontal case must be checked because a split row bitstring could still contain a ship with adjacent bits. To make this check easier, we will condense the 64 bitstring into an 8 bitstring by taking it modulo 255. If we assume that a bitstring is not splitting a row, then taking the ship placement bitstring modulo 255 will yield an 8 bit valid bitstring. If the original ship placement bitstring is not valid, then we will have an invalid 8 bit bitstring. E.g.: + ``` 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -836,9 +828,7 @@ mod 255 = 11000001 (invalid) How do we know the 8 bit bitstring is valid or not? We can simply do an adjacency check, as before. -
- -
Ensuring a bitstring is a power of 2 +### Ensuring a bitstring is a power of 2 Any power of 2 will have a single bit flipped. If we subtract 1 from that number, it will result in a complementary bitstring that, bitwise-anded with the original, will always result in 0. @@ -853,8 +843,6 @@ E.g. 7&6: 0110 != 0 ``` -
- ## Validating all ships together in a single board Give individual valid ship position bitstrings, we can combine all these together into a single board using bitwise or operators. See the create_board function in verify.aleo to follow the code. Once all ships are on the board, we can count the total number of bits, which should be 14 exactly for a ship of length 5, 4, 3, and 2. diff --git a/battleship/build/imports/board.aleo b/battleship/build/imports/board.aleo index fac2e70..90212b4 100644 --- a/battleship/build/imports/board.aleo +++ b/battleship/build/imports/board.aleo @@ -13,7 +13,7 @@ record board_state: function new_board_state: input r0 as u64.private; input r1 as address.private; - cast self.caller 0u64 0u64 r0 self.caller r1 false into r2 as board_state.record; + cast self.signer 0u64 0u64 r0 self.signer r1 false into r2 as board_state.record; output r2 as board_state.record; diff --git a/battleship/build/imports/move.aleo b/battleship/build/imports/move.aleo index ed5247d..cd848b4 100644 --- a/battleship/build/imports/move.aleo +++ b/battleship/build/imports/move.aleo @@ -20,5 +20,5 @@ function create_move: function start_game: input r0 as address.private; - cast r0 0u64 self.caller r0 0u64 into r1 as move.record; + cast r0 0u64 self.signer r0 0u64 into r1 as move.record; output r1 as move.record; diff --git a/battleship/imports/board.leo b/battleship/imports/board.leo index 9489780..954fd76 100644 --- a/battleship/imports/board.leo +++ b/battleship/imports/board.leo @@ -26,11 +26,11 @@ program board.aleo { opponent: address, ) -> board_state { return board_state { - owner: self.caller, + owner: self.signer, hits_and_misses: 0u64, played_tiles: 0u64, ships, - player_1: self.caller, + player_1: self.signer, player_2: opponent, game_started: false, }; diff --git a/battleship/imports/move.leo b/battleship/imports/move.leo index 4a75699..6df54f2 100644 --- a/battleship/imports/move.leo +++ b/battleship/imports/move.leo @@ -37,7 +37,7 @@ program move.aleo { return move { owner: player_2, incoming_fire_coordinate: 0u64, - player_1: self.caller, + player_1: self.signer, player_2: player_2, prev_hit_or_miss: 0u64, }; diff --git a/battleship/inputs/battleship.in b/battleship/inputs/battleship.in deleted file mode 100644 index a621184..0000000 --- a/battleship/inputs/battleship.in +++ /dev/null @@ -1,4 +0,0 @@ -// The program input for battleship/src/main.leo -[main] -public a: u32 = 1u32; -b: u32 = 2u32; diff --git a/battleship/run.sh b/battleship/run.sh index 8c8ef6b..e6eb267 100755 --- a/battleship/run.sh +++ b/battleship/run.sh @@ -5,15 +5,30 @@ then echo "leo is not installed." exit fi -# Follow along in the README.md for a detailed explanation of each step. -# 1: Initializing Player 1 +echo " +We will be playing the role of these two parties: + +The private key and address of Player 1. +private_key: APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +address: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy + +The private key and address of Player 2. +private_key: APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +address: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry +" + echo " ############################################################################### ######## ######## ######## STEP 1: Initializing Player 1 ######## ######## ######## ############################################################################### + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +' > .env " echo " @@ -21,29 +36,29 @@ NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm " > .env -echo "✅ Successfully initialized Player 1." - -# 2: Player 1 Places Ships On The Board echo " ############################################################################### ######## ######## ######## STEP 2: Player 1 Places Ships On The Board ######## ######## ######## ############################################################################### + +With Player 1's private key, they initialize the board with the placement of 4 ships and the opponent's public address. + +leo run initialize_board 34084860461056u64 551911718912u64 7u64 1157425104234217472u64 aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry " -leo run initialize_board 34084860461056u64 551911718912u64 7u64 1157425104234217472u64 aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry || exit -echo " -✅ Successfully initialized Player 1's board." +leo run initialize_board 34084860461056u64 551911718912u64 7u64 1157425104234217472u64 aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry -# 3: Player 1 Passes The Board To Player 2 echo " ############################################################################### ######## ######## ######## STEP 3: Player 1 Passes The Board To Player 2 ######## ######## ######## ############################################################################### -" + +Player 1 "passes the board" as an offer to play battleship with Player 2. + leo run offer_battleship '{ owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, hits_and_misses: 0u64.private, @@ -52,31 +67,45 @@ leo run offer_battleship '{ player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, game_started: false.private, - _nonce: 3887646704618532506963887075433683846689834495661101507703164090915348189037group.public -}' || exit -echo " -✅ Successfully passed the board to Player 2." + _nonce: 605849623036268790365773177565562473735086364071033205649960161942593750353group.public +}' +" + +leo run offer_battleship "{ + owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + hits_and_misses: 0u64.private, + played_tiles: 0u64.private, + ships: 1157459741006397447u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + game_started: false.private, + _nonce: 605849623036268790365773177565562473735086364071033205649960161942593750353group.public +}" -# 4: Player 2 Places Ships On The Board echo " ############################################################################### ######## ######## ######## STEP 4: Player 2 Places Ships On The Board ######## ######## ######## ############################################################################### + +We switch our .env to Player 2's private key and similarly run initialize_board to create a new and different board for player two. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +' > .env + +leo run initialize_board 31u64 2207646875648u64 224u64 9042383626829824u64 aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy " -( - echo " - NETWORK=testnet3 - PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH - " > .env - leo run initialize_board 31u64 2207646875648u64 224u64 9042383626829824u64 aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy || exit -) echo " -✅ Successfully initialized Player 2's board." +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +" > .env + +leo run initialize_board 31u64 2207646875648u64 224u64 9042383626829824u64 aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy -# 5: Passing The Board Back To Player 1 echo " ############################################################################### ######## ######## @@ -84,7 +113,8 @@ echo " ######## ######## ############################################################################### -" +Player 2 accepts Player 1's offer to play and starts a game of battleship. + leo run start_battleship '{ owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, hits_and_misses: 0u64.private, @@ -93,156 +123,260 @@ leo run start_battleship '{ player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, game_started: false.private, - _nonce: 1549419609469324182591325047490602235361156298832591378925133482196483208807group.public + _nonce: 677929557867990662961068737825412945684193990901139603462104629310061710321group.public }' '{ owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, incoming_fire_coordinate: 0u64.private, player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, prev_hit_or_miss: 0u64.private, - _nonce: 4374626042494973146987320062571809401151262172766172816829659487584978644457group.public -}' || exit -echo " -✅ Successfully passed the board back to Player 1." + _nonce: 6306786918362462465996698473371289503655844751914031374264794338640697795225group.public +}' +" + +leo run start_battleship "{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + hits_and_misses: 0u64.private, + played_tiles: 0u64.private, + ships: 9044591273705727u64.private, + player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + game_started: false.private, + _nonce: 677929557867990662961068737825412945684193990901139603462104629310061710321group.public +}" "{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + incoming_fire_coordinate: 0u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + prev_hit_or_miss: 0u64.private, + _nonce: 6306786918362462465996698473371289503655844751914031374264794338640697795225group.public +}" -# 6: Player 1 Takes The 1st Turn echo " ############################################################################### ######## ######## ######## STEP 6: Player 1 Takes The 1st Turn ######## ######## ######## ############################################################################### + +We switch the .env back to Player 1, and we run the transition function play. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +' > .env + +leo run play '{ + owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + hits_and_misses: 0u64.private, + played_tiles: 0u64.private, + ships: 1157459741006397447u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + game_started: true.private, + _nonce: 6313341191294792052861773157032837489809107102476040695601777954897783350080group.public +}' '{ + owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + incoming_fire_coordinate: 0u64.private, + player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + prev_hit_or_miss: 0u64.private, + _nonce: 2798663115519921626400765401803177719929914180089719334947022448579691220488group.public +}' 1u64 " -( - echo " - NETWORK=testnet3 - PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm - " > .env - - leo run play '{ - owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - hits_and_misses: 0u64.private, - played_tiles: 0u64.private, - ships: 1157459741006397447u64.private, - player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - game_started: true.private, - _nonce: 6563064852163330630334088854834332804417910882908622526775624018226782316843group.public - }' '{ - owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - incoming_fire_coordinate: 0u64.private, - player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - prev_hit_or_miss: 0u64.private, - _nonce: 3742551407126138397717446975757978589064777004441277005584760115236217735495group.public - }' 1u64 || exit -) -echo " -✅ Successfully executed Player 1's turn." - -# 7: Player 2 Takes The 2nd Turn + +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +" > .env + +leo run play "{ + owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + hits_and_misses: 0u64.private, + played_tiles: 0u64.private, + ships: 1157459741006397447u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + game_started: true.private, + _nonce: 6313341191294792052861773157032837489809107102476040695601777954897783350080group.public +}" "{ + owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + incoming_fire_coordinate: 0u64.private, + player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + prev_hit_or_miss: 0u64.private, + _nonce: 2798663115519921626400765401803177719929914180089719334947022448579691220488group.public +}" 1u64 + echo " ############################################################################### ######## ######## ######## STEP 7: Player 2 Takes The 2nd Turn ######## ######## ######## ############################################################################### + +We switch the .env back to Player 2, and we run the transition function play. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +' > .env + +leo run play '{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + hits_and_misses: 0u64.private, + played_tiles: 0u64.private, + ships: 9044591273705727u64.private, + player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + game_started: true.private, + _nonce: 6864275139988909612799168784231775829713739147830284979332684562641318182923group.public +}' '{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + incoming_fire_coordinate: 1u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + prev_hit_or_miss: 0u64.private, + _nonce: 8420474443174402614458578667801578345975509805478103542095622903412594983971group.public +}' 2048u64 " -( - - echo " - NETWORK=testnet3 - PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH - " > .env - - leo run play '{ - owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - hits_and_misses: 0u64.private, - played_tiles: 0u64.private, - ships: 9044591273705727u64.private, - player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - game_started: true.private, - _nonce: 6222383571142756260765569201308836492199048237638652378826141459336360362251group.public - }' '{ - owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - incoming_fire_coordinate: 1u64.private, - player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - prev_hit_or_miss: 0u64.private, - _nonce: 5481529266389297320813092061136936339861329677911328036818179854958874588416group.public - }' 2048u64 || exit -) -echo " -✅ Successfully executed Player 2's turn." - -# 8: Player 1 Takes The 3rd Turn + +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +" > .env + +leo run play "{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + hits_and_misses: 0u64.private, + played_tiles: 0u64.private, + ships: 9044591273705727u64.private, + player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + game_started: true.private, + _nonce: 6864275139988909612799168784231775829713739147830284979332684562641318182923group.public +}" "{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + incoming_fire_coordinate: 1u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + prev_hit_or_miss: 0u64.private, + _nonce: 8420474443174402614458578667801578345975509805478103542095622903412594983971group.public +}" 2048u64 + echo " ############################################################################### ######## ######## ######## STEP 8: Player 1 Takes The 3rd Turn ######## ######## ######## ############################################################################### + +We switch the .env back to Player 1, and we run the transition function play. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +' > .env + +leo run play '{ + owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + hits_and_misses: 0u64.private, + played_tiles: 1u64.private, + ships: 1157459741006397447u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + game_started: true.private, + _nonce: 1962122153746742645258971561783872712461616481157617568489391338473028502271group.public +}' '{ + owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + incoming_fire_coordinate: 2048u64.private, + player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + prev_hit_or_miss: 1u64.private, + _nonce: 1204008848449868423802652577996848559012797694551224583683080100053831915439group.public +}' 2u64 " -( - echo " - NETWORK=testnet3 - PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm - " > .env - - leo run play '{ - owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - hits_and_misses: 0u64.private, - played_tiles: 1u64.private, - ships: 1157459741006397447u64.private, - player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - game_started: true.private, - _nonce: 1474170213684980843727833284550698461565286563122422722760769547002894080093group.public - }' '{ - owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - incoming_fire_coordinate: 2048u64.private, - player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - prev_hit_or_miss: 1u64.private, - _nonce: 5851606198769770675504009323414373017067582072428989801313256693053765675198group.public - }' 2u64 || exit -) -echo " -✅ Successfully executed Player 1's turn." - -# 9: Player 2 Takes The 4th Turn + +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +" > .env + +leo run play "{ + owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + hits_and_misses: 0u64.private, + played_tiles: 1u64.private, + ships: 1157459741006397447u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + game_started: true.private, + _nonce: 1962122153746742645258971561783872712461616481157617568489391338473028502271group.public +}" "{ + owner: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + incoming_fire_coordinate: 2048u64.private, + player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + prev_hit_or_miss: 1u64.private, + _nonce: 1204008848449868423802652577996848559012797694551224583683080100053831915439group.public +}" 2u64 + echo " ############################################################################### ######## ######## ######## STEP 9: Player 2 Takes The 4th Turn ######## ######## ######## ############################################################################### + +We switch the .env back to Player 2, and we run the transition function play. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +' > .env + +leo run play '{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + hits_and_misses: 0u64.private, + played_tiles: 2048u64.private, + ships: 9044591273705727u64.private, + player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + game_started: true.private, + _nonce: 591128247205636061702123861968396246163831838278146623498909560875485861872group.public +}' '{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + incoming_fire_coordinate: 2u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + prev_hit_or_miss: 0u64.private, + _nonce: 4871574741887919250014604645502780786361650856453535231083359604148337116539group.public +}' 4u64 " -( - - echo " - NETWORK=testnet3 - PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH - " > .env - - leo run play '{ - owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - hits_and_misses: 0u64.private, - played_tiles: 2048u64.private, - ships: 9044591273705727u64.private, - player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - game_started: true.private, - _nonce: 5254963165391133332409074172682159033621708071536429341861038147524454777097group.public - }' '{ - owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - incoming_fire_coordinate: 2u64.private, - player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, - player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, - prev_hit_or_miss: 0u64.private, - _nonce: 710336412388939616658264778971886770861024495941253598683184288448156545822group.public - }' 4u64 || exit -) -echo " -✅ Successfully executed Player 2's turn." + +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp86FNGdKxjgAdgQZ967bqBanjuHkAaoRe19RK24ZCGsHH +" > .env + +leo run play "{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + hits_and_misses: 0u64.private, + played_tiles: 2048u64.private, + ships: 9044591273705727u64.private, + player_1: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + player_2: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + game_started: true.private, + _nonce: 591128247205636061702123861968396246163831838278146623498909560875485861872group.public +}" "{ + owner: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + incoming_fire_coordinate: 2u64.private, + player_1: aleo15g9c69urtdhvfml0vjl8px07txmxsy454urhgzk57szmcuttpqgq5cvcdy.private, + player_2: aleo1wyvu96dvv0auq9e4qme54kjuhzglyfcf576h0g3nrrmrmr0505pqd6wnry.private, + prev_hit_or_miss: 0u64.private, + _nonce: 4871574741887919250014604645502780786361650856453535231083359604148337116539group.public +}" 4u64 + +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpGKaJY47BXb6knSqmT3JZnBUEGBDFAWz2nMVSsjwYpJmm +" > .env \ No newline at end of file diff --git a/tictactoe/README.md b/tictactoe/README.md index 2f425d6..498e483 100644 --- a/tictactoe/README.md +++ b/tictactoe/README.md @@ -1,7 +1,9 @@ workshop/tictactoe -A standard game of Tic-Tac-Toe in Leo. +## Summary + +We can play a standard game of Tic-Tac-Toe in Leo. ⭕ ❕ ⭕ ❕ ❌ @@ -15,30 +17,68 @@ A standard game of Tic-Tac-Toe in Leo. ## Representing State Leo allows users to define composite data types with the `struct` keyword. -The game board is represented by a struct called `Board`, which contains three `Row`s. -An alternative representation would be to use an array, however, these are not yet supported in Leo. +The game board is represented by a struct called `Board`, which contains three `Row`s. An alternative representation would be to use an array, however, these are not yet supported in Leo. ## Language Features - `struct` declarations - conditional statements - early termination. Leo allows users to return from a function early using the `return` keyword. -## Running the Program +## How to Run + +Follow the [Leo Installation Instructions](https://developer.aleo.org/leo/installation). + +This tictactoe program can be run using the following bash script. Locally, it will execute Leo program functions to create and play a game of Tic Tac Toe. + +```bash +cd tictactoe +./run.sh +``` + +## Walkthrough + +* [Step 0: Create a new board.](#step0) +* [Step 1: Player 1 makes the first move.](#step1) +* [Step 2: Player 2 makes the second move.](#step2) +* [Step 3: Player 1 makes the third move.](#step3) +* [Step 4: and so on...](#step4) + +## Create a new board. + +We generate the board, and then the player take turns executing the transition function make_move. + +The inputs to the function are the player number, row position, column position, and the previous state of the board. + +The output provided is the new state of the board and an evaluation of who won the game. 0u8 as the evaluation output means a draw if the board is complete or that the game is not yet over. -Leo provides users with a command line interface for compiling and running Leo programs. -Users may either specify input values via the command line or provide an input file in `inputs/`. +```bash +leo run new +``` +## Player 1 makes a move. + +Have Player 1 make the first move. + +```bash +leo run make_move 1u8 1u8 1u8 "{ r1: { c1: 0u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" +``` + +## Player 2 makes a move. + +Have Player 2 make the second move. -### Providing inputs via the command line. -1. Run ```bash -leo run ... +leo run make_move 2u8 2u8 2u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" ``` -See `./run.sh` for an example. +## Player 1 makes a move. + +Have Player 1 make the third move. -### Using an input file. -1. Modify `inputs/tictactoe.in` with the desired inputs. -2. Run ```bash -leo run +leo run make_move 1u8 3u8 1u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 2u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" ``` + +## and so on... + +If you follow the run script till the end, you'll see the players make a draw, with an output of `0u64`. + diff --git a/tictactoe/inputs/tictactoe.in b/tictactoe/inputs/tictactoe.in deleted file mode 100644 index 76c9aa7..0000000 --- a/tictactoe/inputs/tictactoe.in +++ /dev/null @@ -1,18 +0,0 @@ -// The `new` function does not take any inputs. -[new] - -// Inputs for the `make_move` function. -// - `player` : A u8 representing the player making the move. 1 for player 1, 2 for player 2. -// - `row` : A u8 representing the row to make the move in. -// - `column` : A u8 representing the column to make the move in. -// - `board` : A representation of the board state. -[make_move] -player: u8 = 1u8; -row: u8 = 1u8; -col: u8 = 1u8; -board: Board = Board { - r1: Row { c1: 0u8, c2: 1u8, c3: 1u8 }, - r2: Row { c1: 2u8, c2: 2u8, c3: 0u8 }, - r3: Row { c1: 0u8, c2: 0u8, c3: 0u8 }, -}; - diff --git a/tictactoe/run.sh b/tictactoe/run.sh index 0c57c04..93a0443 100755 --- a/tictactoe/run.sh +++ b/tictactoe/run.sh @@ -5,7 +5,25 @@ then echo "leo is not installed." exit fi -# Create a new game. + +echo " +Let's play a game of tic tac toe. + +We generate the board, and then the player take turns executing the transition function make_move. + +The inputs to the function are the player number, row position, column position, and the previous state of the board. + +The output provided is the new state of the board and an evaluation of who won the game. 0u8 as the evaluation output means a draw if the board is complete or that the game is not yet over. +" + +echo " +Create a new game. + +leo run new +" + +leo run new + echo " ############################################################################### ######## ######## @@ -17,9 +35,15 @@ echo " ######## ######## ############################################################################### " -leo run new || exit -# Have the Player 1 make a move. +echo " +Have Player 1 make the first move. + +leo run make_move 1u8 1u8 1u8 '{ r1: { c1: 0u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }' +" + +leo run make_move 1u8 1u8 1u8 "{ r1: { c1: 0u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" + echo " ############################################################################### ######## ######## @@ -31,9 +55,15 @@ echo " ######## ######## ############################################################################### " -leo run make_move 1u8 1u8 1u8 "{ r1: { c1: 0u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" || exit -# Have the Player 2 make a move. +echo " +Have Player 2 make the second move. + +leo run make_move 2u8 2u8 2u8 '{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }' +" + +leo run make_move 2u8 2u8 2u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" + echo " ############################################################################### ######## ######## @@ -45,9 +75,15 @@ echo " ######## ######## ############################################################################### " -leo run make_move 2u8 2u8 2u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" || exit -# Have the Player 1 make a move. +echo " +Have Player 1 make the third move. + +leo run make_move 1u8 3u8 1u8 '{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 2u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }' +" + +leo run make_move 1u8 3u8 1u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 2u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" + echo " ############################################################################### ######## ######## @@ -59,9 +95,15 @@ echo " ######## ######## ############################################################################### " -leo run make_move 1u8 3u8 1u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 2u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" || exit -# Have the Player 2 make a move. +echo " +Have Player 2 make the fourth move. + +leo run make_move 2u8 2u8 1u8 '{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 2u8, c3: 0u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }' +" + +leo run make_move 2u8 2u8 1u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 2u8, c3: 0u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" + echo " ############################################################################### ######## ######## @@ -73,9 +115,15 @@ echo " ######## ######## ############################################################################### " -leo run make_move 2u8 2u8 1u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 2u8, c3: 0u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" || exit -# Have the Player 1 make a move. +echo " +Have Player 1 make the fifth move. + +leo run make_move 1u8 2u8 3u8 '{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 0u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }' +" + +leo run make_move 1u8 2u8 3u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 0u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" + echo " ############################################################################### ######## ######## @@ -87,9 +135,15 @@ echo " ######## ######## ############################################################################### " -leo run make_move 1u8 2u8 3u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 0u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" || exit -# Have the Player 2 make a move. +echo " +Have Player 2 make the sixth move. + +leo run make_move 2u8 1u8 2u8 '{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }' +" + +leo run make_move 2u8 1u8 2u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" + echo " ############################################################################### ######## ######## @@ -101,9 +155,15 @@ echo " ######## ######## ############################################################################### " -leo run make_move 2u8 1u8 2u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" || exit -# Have the Player 1 make a move. +echo " +Have Player 1 make the seventh move. + +leo run make_move 1u8 3u8 2u8 '{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }' +" + +leo run make_move 1u8 3u8 2u8 "{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" + echo " ############################################################################### ######## ######## @@ -115,9 +175,16 @@ echo " ######## ######## ############################################################################### " -leo run make_move 1u8 3u8 2u8 "{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" || exit -# Have the Player 2 make a move. + +echo " +Have Player 2 make the eighth move. + +leo run make_move 2u8 3u8 3u8 '{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 1u8, c3: 0u8 } }' +" + +leo run make_move 2u8 3u8 3u8 "{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 1u8, c3: 0u8 } }" + echo " ############################################################################### ######## ######## @@ -129,7 +196,14 @@ echo " ######## ######## ############################################################################### " -leo run make_move 2u8 3u8 3u8 "{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 1u8, c3: 0u8 } }" || exit + +echo " +Have Player 1 make the ninth move. + +leo run make_move 1u8 1u8 3u8 '{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 1u8, c3: 2u8 } }' +" + +leo run make_move 1u8 1u8 3u8 "{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 1u8, c3: 2u8 } }" echo " ############################################################################### @@ -142,9 +216,10 @@ echo " ######## ######## ############################################################################### " -leo run make_move 1u8 1u8 3u8 "{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 1u8, c3: 2u8 } }" || exit echo " +Output is still 0u8 at the end. Draw! + ############################################################################### ######## ######## ######## Game Complete! Players 1 & 2 Tied ######## diff --git a/token/.env b/token/.env index e86f465..f39588b 100644 --- a/token/.env +++ b/token/.env @@ -1,4 +1,4 @@ NETWORK=testnet3 -PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR +PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF diff --git a/token/README.md b/token/README.md index cd91e70..4187f95 100644 --- a/token/README.md +++ b/token/README.md @@ -1,18 +1,139 @@ workshop/token +## Summary + A transparent & shielded custom token in Leo. -## Run Guide +## How to Run + +Follow the [Leo Installation Instructions](https://developer.aleo.org/leo/installation). + +This token program can be run using the following bash script. Locally, it will execute Leo program functions to mint and transfer tokens publicly and privately. + + +```bash +cd token +./run.sh +``` + +The `.env` file contains a private key and address. This is the account that will be used to sign transactions and is checked for record ownership. When executing programs as different parties, be sure to set the `private_key` field in `.env` to the appropriate value. You can check out how we've set things up in `./run.sh` for a full example of how to run the program as different parties. + +## Walkthrough + +* [Step 0: Public Mint](#step0) +* [Step 1: Private Mint](#step1) +* [Step 2: Public Transfer](#step2) +* [Step 3: Private Transfer](#step3) +* [Step 4: Public to Private Transfer](#step4) +* [Step 5: Private to Public Transfer](#step5) + +We'll be conducting a transfer between two parties. + +```bash +The private key and address of Alice. +private_key: APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR +address: aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q + +The private key and address of Bob. +private_key: APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF +address: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z +``` + +## Public Mint + +Let's play Alice. Swap in her private key and publicly mint 100 tokens. + +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR +" > .env + +leo run mint_public aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 100u64 +``` + +You can see the output of the finalize function of `mint_public`, which takes the arguments Alice's address and the amount of tokens to mint publicly. This information is shown on-chain and can be queried on a network. + +## Private Mint + +Now let's privately mint 100 tokens for Bob. Switch to Bob's private key and privately mint 100 tokens for Bob. + +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF +" > .env + +leo run mint_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 100u64 +``` + +The output is a private record. + +## Public Transfer + +Let's publicly transfer 10 tokens from Alice to Bob. Swap the private key back to Alice and call the public transfer transition. -To run this program, run: ```bash -leo run main +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR +" > .env + +leo run transfer_public aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 10u64 ``` -## Execute Guide +Again, we see the arguments used for the finzalize function of `transfer_public` - Alice's address, Bob's address, and the amount to transfer. The public mapping will be queryable on-chain. + +## Private Transfer + +Let's privately transfer 20 tokens from Bob to Alice. Switch to Bob's private key and call the private transfer transition. -To execute this program, run: ```bash -leo execute main +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF +" > .env + +leo run transfer_private "{ + owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private, + amount: 100u64.private, + _nonce: 6586771265379155927089644749305420610382723873232320906747954786091923851913group.public +}" aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 20u64 ``` + +The output of `transfer_private` is a record owned by Bob less the 20 tokens he privately transferred to Alice, and a record owned by Alice with the 20 tokens Bob transferred to Alice. + +## Public to Private Transfer + +Let's convert 30 of Alice's public tokens into 30 private tokens for Bob. Switch the private key back to Alice. + +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR +" > .env + +leo run transfer_public_to_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 30u64 +``` + +When calling `transfer_public_to_private`, we see the finalize function with the arguments to modify Alice's public token mapping (address, amount), and a private record created that's owned by Bob and contains 110 tokens. + +## Private to Public Transfer + +Let's convert 40 of Bob's private tokens into 40 public tokens for Alice. Switch the private key back to Bob. + +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF +" > .env + +leo run transfer_private_to_public "{ + owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private, + amount: 80u64.private, + _nonce: 1852830456042139988098466781381363679605019151318121788109768539956661608520group.public +}" aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 40u64 +``` + +When we call `transfer_private_to_public`, we take Bob's private record that contains 110 tokens, and outputs a record owned by Bob with 70 tokens, and calls the finalize function under `transfer_private_to_public` with Alice's address and 40 tokens as arguments. This changes the public mapping under Alice's address to contain 100 public tokens. Again, public mappings are queryable on-chain. diff --git a/token/build/main.aleo b/token/build/main.aleo index ec32c95..8f45b49 100644 --- a/token/build/main.aleo +++ b/token/build/main.aleo @@ -6,14 +6,13 @@ record token: mapping account: - key left as address.public; - value right as u64.public; + key as address.public; + value as u64.public; function mint_public: input r0 as address.public; input r1 as u64.public; - - finalize r0 r1; + async mint_public r0 r1 into r2; output r2 as token.aleo/mint_public.future; finalize mint_public: input r0 as address.public; @@ -33,8 +32,7 @@ function mint_private: function transfer_public: input r0 as address.public; input r1 as u64.public; - - finalize self.caller r0 r1; + async transfer_public self.caller r0 r1 into r2; output r2 as token.aleo/transfer_public.future; finalize transfer_public: input r0 as address.public; @@ -65,9 +63,8 @@ function transfer_private_to_public: input r2 as u64.public; sub r0.amount r2 into r3; cast r0.owner r3 into r4 as token.record; - output r4 as token.record; - - finalize r1 r2; + async transfer_private_to_public r1 r2 into r5; output r4 as token.record; + output r5 as token.aleo/transfer_private_to_public.future; finalize transfer_private_to_public: input r0 as address.public; @@ -81,9 +78,8 @@ function transfer_public_to_private: input r0 as address.public; input r1 as u64.public; cast r0 r1 into r2 as token.record; - output r2 as token.record; - - finalize self.caller r1; + async transfer_public_to_private self.caller r1 into r3; output r2 as token.record; + output r3 as token.aleo/transfer_public_to_private.future; finalize transfer_public_to_private: input r0 as address.public; diff --git a/token/inputs/token.in b/token/inputs/token.in deleted file mode 100644 index 5426929..0000000 --- a/token/inputs/token.in +++ /dev/null @@ -1,34 +0,0 @@ -// The program input for token/src/main.leo -[mint_public] -receiver: address = aleo1ht2a9q0gsd38j0se4t9lsfulxgqrens2vgzgry3pkvs93xrrzu8s892zn7; -amount: u64 = 100u64; - -[mint_private] -receiver: address = aleo1ht2a9q0gsd38j0se4t9lsfulxgqrens2vgzgry3pkvs93xrrzu8s892zn7; -amount: u64 = 100u64; - -[transfer_public] -receiver: address = aleo1mgfq6g40l6zkhsm063n3uhr43qk5e0zsua5aszeq5080dsvlcvxsn0rrau; -amount: u64 = 50u64; - -[transfer_private] -sender: token = token { - owner: aleo1pvmhrzm5tevxg7rrj65z6txpvm9stfgex05v4lfs05677n7e0cxqdk46jr, - amount: 100u64, - _nonce: 0group, -}; -receiver: address = aleo1mgfq6g40l6zkhsm063n3uhr43qk5e0zsua5aszeq5080dsvlcvxsn0rrau; -amount: u64 = 50u64; - -[transfer_private_to_public] -sender: token = token { - owner: aleo1pvmhrzm5tevxg7rrj65z6txpvm9stfgex05v4lfs05677n7e0cxqdk46jr, - amount: 100u64, - _nonce: 0group, -}; -receiver: address = aleo1mgfq6g40l6zkhsm063n3uhr43qk5e0zsua5aszeq5080dsvlcvxsn0rrau; -amount: u64 = 50u64; - -[transfer_public_to_private] -receiver: address = aleo1mgfq6g40l6zkhsm063n3uhr43qk5e0zsua5aszeq5080dsvlcvxsn0rrau; -amount: u64 = 50u64; diff --git a/token/run.sh b/token/run.sh index 18344e6..99e76d9 100755 --- a/token/run.sh +++ b/token/run.sh @@ -6,27 +6,42 @@ then exit fi -# The private key and address of the Alice. -# Swap these into program.json, when running transactions as the first bidder. -# "private_key": "APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR", -# "address": "aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q" +echo " +We'll be conducting a transfer between two parties. + +The private key and address of Alice. +private_key: APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR +address: aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q + +The private key and address of Bob. +private_key: APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF +address: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z +" + +echo " +Let's play Alice. Swap in her private key and publicly mint 100 tokens. -# The private key and address of the Bob. -# Swap these into program.json, when running transactions as the second bidder. -# "private_key": "APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF" -# "address": "aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z" +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR +' > .env + +leo run mint_public aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 100u64 +" -# Swap in the private key of Alice. echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR " > .env -# Publicly mint 100 tokens for Alice. +leo run mint_public aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 100u64 + echo " +You can see the output of the finalize function of mint_public, which takes the arguments Alice's address and the amount of tokens to mint publicly. This information is shown on-chain and can be queried on a network. + ############################################################################### ######## ######## -######## STEP 1: Publicly mint 100 tokens for Alice ######## +######## STEP 0: Publicly mint 100 tokens for Alice ######## ######## ######## ######## ----------------------------------------- ######## ######## | PUBLIC BALANCES | ######## @@ -48,19 +63,31 @@ echo " ######## ######## ############################################################################### " -leo run mint_public aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 100u64 -# Swap in the private key of Bob. +echo " +Now let's privately mint 100 tokens for Bob. Switch to Bob's private key and privately mint 100 tokens for Bob. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF +' > .env + +leo run mint_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 100u64 +" + echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF " > .env -# Privately mint 100 tokens for Bob. +leo run mint_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 100u64 + echo " +The output is a private record. + ############################################################################### ######## ######## -######## STEP 2: Privately mint 100 tokens for Bob ######## +######## STEP 1: Privately mint 100 tokens for Bob ######## ######## ######## ######## ----------------------------------------- ######## ######## | PUBLIC BALANCES | ######## @@ -82,19 +109,31 @@ echo " ######## ######## ############################################################################### " -leo run mint_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 100u64 -# Swap in the private key of Alice. +echo " +Let's publicly transfer 10 tokens from Alice to Bob. Swap the private key back to Alice and call the public transfer transition. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR +' > .env + +leo run transfer_public aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 10u64 +" + echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR " > .env -# Publicly transfer 10 tokens from Alice to Bob. +leo run transfer_public aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 10u64 + echo " +Again, we see the arguments used for the finzalize function of transfer_public - Alice's address, Bob's address, and the amount to transfer. The public mapping will be queryable on-chain. + ############################################################################### ######## ######## -######## STEP 3: Publicly transfer 10 tokens from Alice to Bob ######## +######## STEP 2: Publicly transfer 10 tokens from Alice to Bob ######## ######## ######## ######## ----------------------------------------- ######## ######## | PUBLIC BALANCES | ######## @@ -116,19 +155,39 @@ echo " ######## ######## ############################################################################### " -leo run transfer_public aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 10u64 -# Swap in the private key of Bob. +echo " +Let's privately transfer 20 tokens from Bob to Alice. Switch to Bob's private key and call the private transfer transition. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF +' > .env + +leo run transfer_private '{ + owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private, + amount: 100u64.private, + _nonce: 6586771265379155927089644749305420610382723873232320906747954786091923851913group.public +}' aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 20u64 +" + echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF " > .env -# Privately transfer 20 tokens from Bob to Alice. +leo run transfer_private "{ + owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private, + amount: 100u64.private, + _nonce: 6586771265379155927089644749305420610382723873232320906747954786091923851913group.public +}" aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 20u64 + echo " +The output of transfer_private is a record owned by Bob less the 20 tokens he privately transferred to Alice, and a record owned by Alice with the 20 tokens Bob transferred to Alice. + ############################################################################### ######## ######## -######## STEP 4: Privately transfer 20 tokens from Bob to Alice ######## +######## STEP 3: Privately transfer 20 tokens from Bob to Alice ######## ######## ######## ######## ----------------------------------------- ######## ######## | PUBLIC BALANCES | ######## @@ -150,23 +209,31 @@ echo " ######## ######## ############################################################################### " -leo run transfer_private "{ - owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private, - amount: 100u64.private, - _nonce: 6586771265379155927089644749305420610382723873232320906747954786091923851913group.public - }" aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 20u64 -# Swap in the private key of Alice. +echo " +Let's convert 30 of Alice's public tokens into 30 private tokens for Bob. Switch the private key back to Alice. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR +' > .env + +leo run transfer_public_to_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 30u64 +" + echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR " > .env -# Convert 30 public tokens from Alice into 30 private tokens for Bob. +leo run transfer_public_to_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 30u64 + echo " +When calling transfer_public_to_private, we see the finalize function with the arguments to modify Alice's public token mapping (address, amount), and a private record created that's owned by Bob and contains 110 tokens. + ############################################################################### ######## ######## -######## STEP 5: Convert 30 public tokens from Alice into 30 ######## +######## STEP 4: Convert 30 public tokens from Alice into 30 ######## ######## private tokens for Bob. ######## ######## ######## ######## ----------------------------------------- ######## @@ -189,19 +256,39 @@ echo " ######## ######## ############################################################################### " -leo run transfer_public_to_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 30u64 -# Swap in the private key of Bob. +echo " +Let's convert 40 of Bob's private tokens into 40 public tokens for Alice. Switch the private key back to Bob. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF +' > .env + +leo run transfer_private_to_public '{ + owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private, + amount: 80u64.private, + _nonce: 1852830456042139988098466781381363679605019151318121788109768539956661608520group.public +}' aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 40u64 +" + echo " NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF " > .env -# Convert 40 private tokens from Bob into 40 public tokens for Alice. +leo run transfer_private_to_public "{ + owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private, + amount: 80u64.private, + _nonce: 1852830456042139988098466781381363679605019151318121788109768539956661608520group.public +}" aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 40u64 + echo " +When we call transfer_private_to_public, we take Bob's private record that contains 110 tokens, and outputs a record owned by Bob with 70 tokens, and calls the finalize function under transfer_private_to_public with Alice's address and 40 tokens as arguments. This changes the public mapping under Alice's address to contain 100 public tokens. Again, public mappings are queryable on-chain. + ############################################################################### ######## ######## -######## STEP 6: Convert 40 private tokens from Bob into 40 ######## +######## STEP 5: Convert 40 private tokens from Bob into 40 ######## ######## public tokens for Alice. ######## ######## ######## ######## ----------------------------------------- ######## @@ -224,16 +311,3 @@ echo " ######## ######## ############################################################################### " -leo run transfer_private_to_public "{ - owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private, - amount: 80u64.private, - _nonce: 1852830456042139988098466781381363679605019151318121788109768539956661608520group.public - }" aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 40u64 - - -# Swap in the private key of Alice. -# This is done to ensure that program.json is the same after every execution of ./run.sh. -echo " -NETWORK=testnet3 -PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR -" > .env diff --git a/vote/.env b/vote/.env index c106cc9..27c5826 100644 --- a/vote/.env +++ b/vote/.env @@ -1,2 +1,4 @@ + NETWORK=testnet3 -PRIVATE_KEY=APrivateKey1zkpBDEpwdFWqxe1NdB9fxZxiX9LahJ3CdqchSs7FGZVggNw \ No newline at end of file +PRIVATE_KEY=APrivateKey1zkp6NHwbT7PkpnEFeBidz5ZkZ14W8WXZmJ6kjKbEHYdMmf2 + diff --git a/vote/README.md b/vote/README.md index 1b470c5..a5f3fd6 100644 --- a/vote/README.md +++ b/vote/README.md @@ -5,9 +5,7 @@ `vote.leo` is a general vote program. -Anyone can issue new proposals, -proposers can issue tickets to the voters, -and voters can vote without exposing their identity. +Anyone can issue new proposals, proposers can issue tickets to the voters, and voters can vote without exposing their identity. This example is inspired by the [aleo-vote](https://github.com/zkprivacy/aleo-vote) example written by the Aleo community. @@ -18,76 +16,123 @@ Proposal information and voting results are revealed using the public `mapping` ## How to Run -To compile this Leo program, run: +Follow the [Leo Installation Instructions](https://developer.aleo.org/leo/installation). + +This vote program can be run using the following bash script. Locally, it will execute Leo program functions to create proposals, create tickets, and make votes. + ```bash -leo run +cd vote +./run.sh ``` -Make changes to `vote/inputs/vote.in` before running each command. +The `.env` file contains a private key and address. This is the account that will be used to sign transactions and is checked for record ownership. When executing programs as different parties, be sure to set the `private_key` field in `.env` to the appropriate value. You can check out how we've set things up in `./run.sh` for a full example of how to run the program as different parties. + +## Walkthrough + +* [Functions](#functions) +* [Step 0: Create a Proposal](#step0) +* [Step 1: Voter 1 issues a ticket and makes a vote](#step1) +* [Step 2: Voter 2 issues a ticket and makes a vote](#step2) +* [Step 3: How votes are tallied](#step3) + +## Functions ### Propose Anyone can issue a new proposal publicly by calling `propose` function. -Run `propose`: +### Create Ticket -``` -leo run propose -``` +Proposers can create new tickets for proposed proposals. -Output sample: +Ticket is a record with `owner` and `pid`, it can be used to vote for the specific proposal - `pid`, and can only be used(voted) by the ticket `owner`. -``` - { - owner: aleo1kkk52quhnxgn2nfrcd9jqk7c9x27c23f2wvw7fyzcze56yahvcgszgttu2.private, - id: 2805252584833208809872967597325381727971256629741137995614832105537063464740field.private, - info: { - title: 2077160157502449938194577302446444field.private, - content: 1452374294790018907888397545906607852827800436field.private, - proposer: aleo1kkk52quhnxgn2nfrcd9jqk7c9x27c23f2wvw7fyzcze56yahvcgszgttu2.private - }, - _nonce: 1639660347839832220966145410710039205878572956621820215177036061076060242021group.public -} -``` +### Vote -### Create Ticket +A ticket owner can use their ticket record to vote `agree` / `disagree` with the specific proposal - `pid`. Since the ticket record can be used as an input privately, the voter's privacy is protected. -Proposers can create new tickets for proposed proposals. +## Create a Proposal + +We will be playing the role of three parties. -Ticket is a record with `owner` and `pid`, -it can be used to vote for the specific proposal - `pid`, -and can only be used(voted) by the ticket `owner`. +```bash +The private key and address of the proposer. +private_key: APrivateKey1zkp8wKHF9zFX1j4YJrK3JhxtyKDmPbRu9LrnEW8Ki56UQ3G +address: aleo1rfez44epy0m7nv4pskvjy6vex64tnt0xy90fyhrg49cwe0t9ws8sh6nhhr -Run `new_ticket`: +The private key and address of voter 1. +private_key: APrivateKey1zkpHmSu9zuhyuCJqVfQE8p82HXpCTLVa8Z2HUNaiy9mrug2 +address: aleo1c45etea8czkyscyqawxs7auqjz08daaagp2zq4qjydkhxt997q9s77rsp2 +The private key and address of voter 2. +private_key: APrivateKey1zkp6NHwbT7PkpnEFeBidz5ZkZ14W8WXZmJ6kjKbEHYdMmf2 +address: aleo1uc6jphye8y9gfqtezrz240ak963sdgugd7s96qpuw6k7jz9axs8q2qnhxc ``` -leo run new_ticket +Let's propose a new ballot. Take on the role of the proposer and run the propose transition function. We've provided the necessary information as inputs to the `propose` function. + +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp8wKHF9zFX1j4YJrK3JhxtyKDmPbRu9LrnEW8Ki56UQ3G +" > .env + +leo run propose "{ + title: 2077160157502449938194577302446444field, + content: 1452374294790018907888397545906607852827800436field, + proposer: aleo1rfez44epy0m7nv4pskvjy6vex64tnt0xy90fyhrg49cwe0t9ws8sh6nhhr +}" ``` -Output sample: +You'll see that the output generates a new record with the proposal information and sets a public mapping with the proposal id as an argument input. The public mapping will be queryable on-chain. + +## Voter 1 makes a vote + +Let's create a new private ticket to make a vote. Take on the role of voter 1 and run the `new_ticket` transition. The inputs take a unique ticket ID and the voter's public address. + +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpHmSu9zuhyuCJqVfQE8p82HXpCTLVa8Z2HUNaiy9mrug2 +" > .env +leo run new_ticket 2264670486490520844857553240576860973319410481267184439818180411609250173817field aleo1c45etea8czkyscyqawxs7auqjz08daaagp2zq4qjydkhxt997q9s77rsp2 ``` -{ - owner: aleo1kkk52quhnxgn2nfrcd9jqk7c9x27c23f2wvw7fyzcze56yahvcgszgttu2.private, + +You'll see a new private ticket created belonging to the owner, and a public mapping in the vote program to track the ID of that ticket. + +Voter 1 can now vote privately on their ticket. Call the agree or disagree transition function, which takes the voter's ticket output as the input. + +```bash +leo run agree "{ + owner: aleo1c45etea8czkyscyqawxs7auqjz08daaagp2zq4qjydkhxt997q9s77rsp2.private, pid: 2264670486490520844857553240576860973319410481267184439818180411609250173817field.private, - _nonce: 1637267040221574073903539416642641433705357302885235345311606754421919550724group.public -} + _nonce: 1738483341280375163846743812193292672860569105378494043894154684192972730518group.public +}" ``` -### Vote - -A ticket owner can use their ticket record to vote `agree` / `disagree` with the specific proposal - `pid`. +## Voter 2 makes a vote -Since the ticket record can be used as an input privately, the voter's privacy is protected. +Let's create a new private ticket for voter 2. Take on the role of voter 1 and run the `new_ticket` transition. The inputs take a unique ticket ID and the voter's public address. -Run `agree`: +```bash +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp6NHwbT7PkpnEFeBidz5ZkZ14W8WXZmJ6kjKbEHYdMmf2 +" > .env -``` -leo run agree +leo run new_ticket 2158670485494560943857353240576760973319410481267184429818180411607250143681field aleo1uc6jphye8y9gfqtezrz240ak963sdgugd7s96qpuw6k7jz9axs8q2qnhxc ``` -Run `disagree`: +Voter 2 can now vote privately on their ticket. Call the agree or disagree transition function, which takes the voter's ticket output as the input. +```bash +leo run disagree "{ + owner: aleo1uc6jphye8y9gfqtezrz240ak963sdgugd7s96qpuw6k7jz9axs8q2qnhxc.private, + pid: 2158670485494560943857353240576760973319410481267184429818180411607250143681field.private, + _nonce: 6511154004161574129036815174288926693337549214513234790975047364416273541105group.public +}" ``` -leo run disagree -``` + +## How votes are tallied + +Votes on the ticket are private. But the sum total of the agreements and disagreements are shown on-chain in the public mapping. You can query this data on-chain. \ No newline at end of file diff --git a/vote/build/main.aleo b/vote/build/main.aleo index fdaafdb..82cfdf4 100644 --- a/vote/build/main.aleo +++ b/vote/build/main.aleo @@ -16,31 +16,31 @@ record Ticket: mapping proposals: - key left as field.public; - value right as ProposalInfo.public; + key as field.public; + value as ProposalInfo.public; mapping tickets: - key left as field.public; - value right as u64.public; + key as field.public; + value as u64.public; mapping agree_votes: - key left as field.public; - value right as u64.public; + key as field.public; + value as u64.public; mapping disagree_votes: - key left as field.public; - value right as u64.public; + key as field.public; + value as u64.public; function propose: input r0 as ProposalInfo.public; assert.eq self.caller r0.proposer; - hash.bhp256 r0.title into r1 as field; cast self.caller r1 r0 into r2 as Proposal.record; - output r2 as Proposal.record; - - finalize r1; + hash.bhp256 r0.title into r1 as field; + cast self.caller r1 r0 into r2 as Proposal.record; + async propose r1 into r3; output r2 as Proposal.record; + output r3 as vote.aleo/propose.future; finalize propose: input r0 as field.public; @@ -51,9 +51,8 @@ function new_ticket: input r0 as field.public; input r1 as address.public; cast r1 r0 into r2 as Ticket.record; - output r2 as Ticket.record; - - finalize r0; + async new_ticket r0 into r3; output r2 as Ticket.record; + output r3 as vote.aleo/new_ticket.future; finalize new_ticket: input r0 as field.public; @@ -64,8 +63,7 @@ finalize new_ticket: function agree: input r0 as Ticket.record; - - finalize r0.pid; + async agree r0.pid into r1; output r1 as vote.aleo/agree.future; finalize agree: input r0 as field.public; @@ -76,8 +74,7 @@ finalize agree: function disagree: input r0 as Ticket.record; - - finalize r0.pid; + async disagree r0.pid into r1; output r1 as vote.aleo/disagree.future; finalize disagree: input r0 as field.public; diff --git a/vote/inputs/vote.in b/vote/inputs/vote.in deleted file mode 100644 index bebf867..0000000 --- a/vote/inputs/vote.in +++ /dev/null @@ -1,25 +0,0 @@ -// The program input for vote/src/main.leo -[propose] -info: ProposalInfo = ProposalInfo { - title: 2077160157502449938194577302446444field, - content: 1452374294790018907888397545906607852827800436field, - proposer: aleo1kkk52quhnxgn2nfrcd9jqk7c9x27c23f2wvw7fyzcze56yahvcgszgttu2, -}; - -[new_ticket] -pid: field = 2264670486490520844857553240576860973319410481267184439818180411609250173817field; -voter: address = aleo1kkk52quhnxgn2nfrcd9jqk7c9x27c23f2wvw7fyzcze56yahvcgszgttu2; - -[agree] -ticket: Ticket = Ticket { - owner: aleo1kkk52quhnxgn2nfrcd9jqk7c9x27c23f2wvw7fyzcze56yahvcgszgttu2, - pid: 2264670486490520844857553240576860973319410481267184439818180411609250173817field, - _nonce: 1637267040221574073903539416642641433705357302885235345311606754421919550724group -}; - -[disagree] -ticket: Ticket = Ticket { - owner: aleo1kkk52quhnxgn2nfrcd9jqk7c9x27c23f2wvw7fyzcze56yahvcgszgttu2, - pid: 2264670486490520844857553240576860973319410481267184439818180411609250173817field, - _nonce: 1637267040221574073903539416642641433705357302885235345311606754421919550724group -}; diff --git a/vote/run.sh b/vote/run.sh index 9f5d458..aa17819 100755 --- a/vote/run.sh +++ b/vote/run.sh @@ -6,40 +6,160 @@ then exit fi +echo " +We will be playing the role of three parties. + +The private key and address of the proposer. +private_key: APrivateKey1zkp8wKHF9zFX1j4YJrK3JhxtyKDmPbRu9LrnEW8Ki56UQ3G +address: aleo1rfez44epy0m7nv4pskvjy6vex64tnt0xy90fyhrg49cwe0t9ws8sh6nhhr + +The private key and address of voter 1. +private_key: APrivateKey1zkpHmSu9zuhyuCJqVfQE8p82HXpCTLVa8Z2HUNaiy9mrug2 +address: aleo1c45etea8czkyscyqawxs7auqjz08daaagp2zq4qjydkhxt997q9s77rsp2 + +The private key and address of voter 2. +private_key: APrivateKey1zkp6NHwbT7PkpnEFeBidz5ZkZ14W8WXZmJ6kjKbEHYdMmf2 +address: aleo1uc6jphye8y9gfqtezrz240ak963sdgugd7s96qpuw6k7jz9axs8q2qnhxc +" + +echo " +Let's propose a new ballot. Take on the role of the proposer and run the propose transition function. We've provided the necessary information as inputs to the propose function. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp8wKHF9zFX1j4YJrK3JhxtyKDmPbRu9LrnEW8Ki56UQ3G +' > .env + +leo run propose '{ + title: 2077160157502449938194577302446444field, + content: 1452374294790018907888397545906607852827800436field, + proposer: aleo1rfez44epy0m7nv4pskvjy6vex64tnt0xy90fyhrg49cwe0t9ws8sh6nhhr +}' +" + +# swaps in the private key of the proposer to .env +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp8wKHF9zFX1j4YJrK3JhxtyKDmPbRu9LrnEW8Ki56UQ3G +" > .env + +# runs the propose transition function with proposal info as the input +leo run propose "{ + title: 2077160157502449938194577302446444field, + content: 1452374294790018907888397545906607852827800436field, + proposer: aleo1rfez44epy0m7nv4pskvjy6vex64tnt0xy90fyhrg49cwe0t9ws8sh6nhhr +}" + +echo " +You'll see that the output generates a new record with the proposal information and sets a public mapping with the proposal id as an argument input. The public mapping will be queryable on-chain. +" + echo " ############################################################################### ######## ######## -######## STEP 0: Compile the vote program ######## +######## Step 0: Propose a new ballot ######## +######## ######## +######## --------------------------- ######## +######## | | Yes | No | ######## +######## --------------------------- ######## +######## | Votes | | | ######## +######## --------------------------- ######## ######## ######## ############################################################################### " -# Build the Leo vote program. -( - leo build || exit -) + +echo " +Let's create a new private ticket to make a vote. Take on the role of voter 1 and run the new_ticket transition. The inputs take a unique ticket ID and the voter's public address. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpHmSu9zuhyuCJqVfQE8p82HXpCTLVa8Z2HUNaiy9mrug2 +' > .env + +leo run new_ticket 2264670486490520844857553240576860973319410481267184439818180411609250173817field aleo1c45etea8czkyscyqawxs7auqjz08daaagp2zq4qjydkhxt997q9s77rsp2 +" + +# Swap in the private key of voter 1 to .env. +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkpHmSu9zuhyuCJqVfQE8p82HXpCTLVa8Z2HUNaiy9mrug2 +" > .env + +# Runs the new_ticket transition function with unique id and voter address as inputs. +leo run new_ticket 2264670486490520844857553240576860973319410481267184439818180411609250173817field aleo1c45etea8czkyscyqawxs7auqjz08daaagp2zq4qjydkhxt997q9s77rsp2 + +echo " +You'll see a new private ticket created belonging to the owner, and a public mapping in the vote program to track the ID of that ticket. +" echo " ############################################################################### ######## ######## -######## STEP 1: Propose a new ballot ######## +######## Step 1: Voter 1 issues a new ballot ticket ######## ######## ######## ######## --------------------------- ######## ######## | | Yes | No | ######## ######## --------------------------- ######## -######## | Votes | | | ######## +######## | Votes | 0 | 0 | ######## ######## --------------------------- ######## ######## ######## ############################################################################### " -# Run the `propose` function -( - leo run propose || exit -) + +echo " +Voter 1 can now vote privately on their ticket. Call the agree or disagree transition function, which takes the voter's ticket output as the input. + +leo run agree '{ + owner: aleo1c45etea8czkyscyqawxs7auqjz08daaagp2zq4qjydkhxt997q9s77rsp2.private, + pid: 2264670486490520844857553240576860973319410481267184439818180411609250173817field.private, + _nonce: 1738483341280375163846743812193292672860569105378494043894154684192972730518group.public +}' +" + +leo run agree "{ + owner: aleo1c45etea8czkyscyqawxs7auqjz08daaagp2zq4qjydkhxt997q9s77rsp2.private, + pid: 2264670486490520844857553240576860973319410481267184439818180411609250173817field.private, + _nonce: 1738483341280375163846743812193292672860569105378494043894154684192972730518group.public +}" echo " ############################################################################### ######## ######## -######## STEP 2: Issue a new ballot ticket ######## +######## Voter 1 votes 'yes' on their ballot ticket ######## +######## ######## +######## --------------------------- ######## +######## | | Yes | No | ######## +######## --------------------------- ######## +######## | Votes | 1 | 0 | ######## +######## --------------------------- ######## +######## ######## +############################################################################### +" + +echo " +Let's create a new private ticket for voter 2. Take on the role of voter 1 and run the new_ticket transition. The inputs take a unique ticket ID and the voter's public address. + +echo ' +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp6NHwbT7PkpnEFeBidz5ZkZ14W8WXZmJ6kjKbEHYdMmf2 +' > .env + +leo run new_ticket 2158670485494560943857353240576760973319410481267184429818180411607250143681field aleo1uc6jphye8y9gfqtezrz240ak963sdgugd7s96qpuw6k7jz9axs8q2qnhxc +" + +# Swap in the private key of voter 2 to .env. +echo " +NETWORK=testnet3 +PRIVATE_KEY=APrivateKey1zkp6NHwbT7PkpnEFeBidz5ZkZ14W8WXZmJ6kjKbEHYdMmf2 +" > .env + +# Run the new_ticket transition function with unique id and voter address as inputs. +leo run new_ticket 2158670485494560943857353240576760973319410481267184429818180411607250143681field aleo1uc6jphye8y9gfqtezrz240ak963sdgugd7s96qpuw6k7jz9axs8q2qnhxc + +echo " +############################################################################### +######## ######## +######## Step 2: Voter 2 issues a new ballot ticket ######## ######## ######## ######## --------------------------- ######## ######## | | Yes | No | ######## @@ -49,26 +169,49 @@ echo " ######## ######## ############################################################################### " -# Run the `new_ticket` function -( - leo run new_ticket || exit -) + +echo " +Voter 2 can now vote privately on their ticket. Call the agree or disagree transition function, which takes the voter's ticket output as the input. + +leo run disagree '{ + owner: aleo1uc6jphye8y9gfqtezrz240ak963sdgugd7s96qpuw6k7jz9axs8q2qnhxc.private, + pid: 2158670485494560943857353240576760973319410481267184429818180411607250143681field.private, + _nonce: 6511154004161574129036815174288926693337549214513234790975047364416273541105group.public +}' +" + +leo run disagree "{ + owner: aleo1uc6jphye8y9gfqtezrz240ak963sdgugd7s96qpuw6k7jz9axs8q2qnhxc.private, + pid: 2158670485494560943857353240576760973319410481267184429818180411607250143681field.private, + _nonce: 6511154004161574129036815174288926693337549214513234790975047364416273541105group.public +}" echo " ############################################################################### ######## ######## -######## STEP 3: Vote 'Yes' on the ballot ticket ######## +######## Voter 2 votes 'no' on their ballot ticket ######## ######## ######## ######## --------------------------- ######## ######## | | Yes | No | ######## ######## --------------------------- ######## -######## | Votes | 1 | 0 | ######## +######## | Votes | 0 | 1 | ######## +######## --------------------------- ######## +######## ######## +############################################################################### +" + +echo " +Votes on the ticket are private. But the sum total of the agreements and disagreements are shown on-chain in the public mapping. You can query this data on-chain. + +############################################################################### +######## ######## +######## Step 3: Tallying the Votes ######## +######## ######## +######## --------------------------- ######## +######## | | Yes | No | ######## +######## --------------------------- ######## +######## | Votes | 1 | 1 | ######## ######## --------------------------- ######## ######## ######## ############################################################################### " -# Run the `agree` or `disagree` function -( - leo run agree || exit - # leo run disagree || exit -)