From 3f4eccfba58bee4ef361e1bd1d2ca2975176ac1f Mon Sep 17 00:00:00 2001 From: Barrie Byron Date: Mon, 7 Mar 2022 07:05:54 -0500 Subject: [PATCH] docs: scavenge tuto improve language and apply styles (#2134) * improve language and apply styles * edits and style * Update docs/guide/scavenge/02-game.md Co-authored-by: Sonia Singla Co-authored-by: Sonia Singla --- docs/guide/scavenge/02-game.md | 24 +++-- docs/guide/scavenge/03-scaffolding.md | 27 +++-- docs/guide/scavenge/04-messages.md | 150 +++++++++++++++++++++----- docs/guide/scavenge/05-types.md | 126 ++++++++++++++++++---- docs/guide/scavenge/06-handlers.md | 9 +- docs/guide/scavenge/07-keeper.md | 18 ++-- docs/guide/scavenge/08-cli.md | 23 ++-- docs/guide/scavenge/09-play.md | 56 +++++++--- docs/guide/scavenge/index.md | 24 +++-- 9 files changed, 353 insertions(+), 104 deletions(-) diff --git a/docs/guide/scavenge/02-game.md b/docs/guide/scavenge/02-game.md index 313e12d6cd..4a12203075 100644 --- a/docs/guide/scavenge/02-game.md +++ b/docs/guide/scavenge/02-game.md @@ -2,28 +2,34 @@ order: 2 --- -# The Scavenger Hunt Game +# Scavenger hunt game -The application you are building today can be used in many different ways but I'll be talking about building the app as a **scavenger hunt** game. Scavenger hunts are all about someone setting up tasks or questions that challenge a participant to find solutions that come with some sort of a prize. The basic mechanics of the game are as follows: +This tutorial focuses on building the app as a **scavenger hunt** game. Scavenger hunts are all about someone setting up tasks or questions that challenge a participant to find solutions that come with a prize. The basic mechanics of the game are as follows: * Anyone can post a question with an encrypted answer. * This question comes paired with a bounty of coins. * Anyone can post an answer to this question. If the answer is correct, that person receives the bounty of coins. -Something to note here is that when dealing with a public network with latency, it is possible that something like a [man-in-the-middle attack](https://en.wikipedia.org/wiki/Man-in-the-middle_attack) could take place. Instead of pretending to be one of the parties, an attacker would take the sensitive information from one party and use it for their own benefit. This scenario is actually called [Front Running](https://en.wikipedia.org/wiki/Front_running) and happens as follows: +## Safe interactions + +On a public network with latency, it is possible that something like a [man-in-the-middle attack](https://en.wikipedia.org/wiki/Man-in-the-middle_attack) could take place. Instead of pretending to be one of the parties, an attacker takes the sensitive information from one party and uses it for their own benefit. This scenario is called [Front Running](https://en.wikipedia.org/wiki/Front_running) and happens as follows: 1. You post the answer to some question with a bounty attached to it. 2. Someone else sees you posting the answer and posts it themselves right before you. 3. Since they posted the answer first, they receive the reward instead of you. -To prevent Front-Running, you will implement a **commit-reveal** scheme. A commit-reveal scheme converts a single exploitable interaction and turns it into two safe interactions. +### Prevent front running + +To prevent front running, implement a commit-reveal scheme that converts a single exploitable interaction into two safe interactions. + +The first interaction is the commit where you "commit" to posting an answer in a follow-up interaction. This commit consists of a cryptographic hash of your name combined with the answer that you think is correct. The app saves that value as a claim that you know the answer, but hasn't yet confirmed whether the answer is correct. -**The first interaction is the commit**. This is where you "commit" to posting an answer in a follow-up interaction. This commit consists of a cryptographic hash of your name combined with the answer that you think is correct. The app saves that value which is a claim that you know the answer but that it hasn't been confirmed whether the answer is correct. +The second interaction is the reveal where you post the answer in plain text along with your name. The application takes your answer and your name and cryptographically hashes them. If the result matches what you previously submitted during the commit stage, that is the proof that it is in fact you who knows the answer and not someone who is just front-running you. -**The next interaction is the reveal**. This is where you post the answer in plaintext along with your name. The application will take your answer and your name and cryptographically hash them. If the result matches what you previously submitted during the commit stage, then it will be proof that it is in fact you who knows the answer, and not someone who is just front-running you. +### Security -A system like this could be used in tandem with any kind of gaming platform in a **trustless** way. Imagine you were playing "The Legend of Zelda" and the game was compiled with all the answers to different scavenger hunts already included. When you beat a level the game could reveal the secret answer. Then either explicitly or behind the scenes, this answer could be combined with your name, hashed, submitted and subsequently revealed. Your name would be rewarded and you would have more points in the game. +A system like this commit-reveal scheme could be used in tandem with any kind of gaming platform in a **trustless** way. Imagine playing "The Legend of Zelda" and the game was compiled with all the answers to different scavenger hunts already included. When you obtain a certain level the game could reveal the secret answer. Then explicitly or behind the scenes, this answer could be combined with your name, hashed, submitted, and subsequently revealed. Your name could be rewarded so that you gain more points in the game. -Another way of achieving this level of security would be to have an Access Control List where there was an admin account that the video game company controlled. This admin account could confirm that you beat the level and then give you points. The problem with this is that it creates a ***single point of failure*** and a single target for trying to attack the system. If there is one key that rules the castle then the whole system is broken if that key is compromised. It also creates a problem with coordination if that Admin account has to be online all the time in order for players to get their points. If you use a commit reveal system then you have a more trustless architecture where you don't need permission to play. This design decision has benefits and drawbacks, but paired with a careful implementation it can allow your game to scale without a single bottle neck or point of failure. +Another way of achieving this level of security is with access-control list (ACL) managed by an admin account under control of the gaming company. This admin account could confirm that you beat the level and then give you points. The problem with this ACL approach is a ***single point of failure*** and a single target for trying to attack the system. If there is one key that rules the castle then the whole system is broken if that key is compromised. ACL security also creates a problem with coordination if that admin account is required to be online in order for players to get their points. With a commit-reveal system you have a more trustless architecture where permission is not required to play. The commit-reveal design decision has benefits and drawbacks, but when paired with a careful implementation your game can scale without a single bottleneck or point of failure. -Now that you know what you are building you can get started. +Now that you know what you are building, you can get started building your game. diff --git a/docs/guide/scavenge/03-scaffolding.md b/docs/guide/scavenge/03-scaffolding.md index 35a8b02026..a4d784bae3 100644 --- a/docs/guide/scavenge/03-scaffolding.md +++ b/docs/guide/scavenge/03-scaffolding.md @@ -2,7 +2,7 @@ order: 3 --- -# Scaffolding +# Scaffold the scavenge chain Scaffold a new Cosmos SDK blockchain using the `starport scaffold chain` command. @@ -12,7 +12,7 @@ By default a chain is scaffolded with a new empty Cosmos SDK module. Use the `-- starport scaffold chain github.com/cosmonaut/scavenge --no-module ``` -This command created a new directory `scavenge` with a brand new Cosmos SDK blockchain. This blockchain doesn't have any application-specific logic yet, but it imports standard Cosmos SDK modules, such as `auth`, `bank`, `mint` and others. +This command creates a new `scavenge` directory with a brand new Cosmos SDK blockchain. This blockchain doesn't have any application-specific logic yet, but it imports standard Cosmos SDK modules, such as `auth`, `bank`, `mint`, and others. Change the current directory to `scavenge`: @@ -20,14 +20,29 @@ Change the current directory to `scavenge`: cd scavenge ``` -Inside the project directory you can execute other Starport commands to start a blockchain node, scaffold modules, messages, types, generate code, and much more. +Inside the project directory, you execute other Starport commands to start a blockchain node, scaffold modules, messages, types, generate code, and much more. -In a Cosmos SDK blockchain, application-specific logic is implemented in separate modules. Using modules keeps code easy to understand and reuse. +In a Cosmos SDK blockchain, implement application-specific logic in separate modules. Using modules keeps code easy to understand and reuse. -Scaffold a new module called `scavenge`. Based on our design the `scavenge` module will be sending tokens between participants. Sending tokens is implemented in the standard `bank` module. Specify `bank` as a dependency using the optional `--dep` flag. +## Scaffold the scavenge module + +Scaffold a new module called `scavenge`. Based on the game design, the `scavenge` module sends tokens between participants. + +- Implement sending tokens in the standard `bank` module. +- Use the optional `--dep` flag to specify the `bank` module. ```bash starport scaffold module scavenge --dep bank ``` -A module has been created in the `x/scavenge` directory and imported into the blockchain in `app/app.go`. +This command creates the `x/scavenge` directory and imports the scavenge module into the blockchain in the `app/app.go` directory. + +## Save changes + +Before you go to the next step, you can store your project in a git commit: + +```bash +git add . +git commit -m "scaffold scavenge chain and module" +``` + diff --git a/docs/guide/scavenge/04-messages.md b/docs/guide/scavenge/04-messages.md index 9377a1fe1d..ed84682fa1 100644 --- a/docs/guide/scavenge/04-messages.md +++ b/docs/guide/scavenge/04-messages.md @@ -4,65 +4,161 @@ order: 4 # Messages -Messages are a great place to start when building a module because they define the actions that your application can make. Think of all the scenarios where a user would be able to update the state of the application in any way. These should be boiled down into basic interactions, similar to **CRUD** (Create, Read, Update, Delete). +Messages are a great place to start when building a module because messages define your application actions. Think of all the scenarios where a user would be able to update the state of the application in any way. These scenarios are the basic interactions, similar to CRUD (create, read, update, and delete) operations. Messages are objects whose end-goal is to trigger state-transitions. -The Scavenge module will have 3 messages: +For the scavenger hunt game, the scavenge module requires 3 messages: * Submit scavenge * Commit solution * Reveal solution -## Submit Scavenge Message +## Submit scavenge message -Submit scavenge message should contain all the necessary information when creating a scavenge: +The submit scavenge message must contain all the information that is required to create a scavenge: -* Description - what is the question to be solved or description of the challenge. +* Description - the question to be solved or description of the challenge. * Solution hash - the scrambled solution. -* Reward - this is the bounty that is awarded to whoever submits the answer first. +* Reward - the bounty that is awarded to whoever submits the answer first. -Use the `starport scaffold message` command to scaffold a new Cosmos SDK message for your module. The command accepts message name as the first argument and a list of fields. By default, a message is scaffolded in a module with a name that matches the name of the project, in our case `scavenge` (this behaviour can be overwritten by using a flag). +Use the `starport scaffold message` command to scaffold a new Cosmos SDK message for your module. The command accepts the message name as the first argument and a list of fields. By default, a message is scaffolded in a module with a name that matches the name of the project, in our case `scavenge`. You can use a flag to overwrite this default naming behavior. ```bash starport scaffold message submit-scavenge solutionHash description reward ``` -The command has created and modified several files. +The command creates and modifies several files: -* `proto/scavenge/tx.proto`: `MsgSubmitScavenge` and `MsgSubmitScavengeResponse` proto messages are added and a `SubmitScavenge` RPC is registered in the `Msg` service. -* `x/scavenge/types/message_submit_scavenge.go`: methods are defined to satisfy `Msg` interface. -* `x/scavenge/handler.go`: `MsgSubmitScavenge` message is registered in the module message handler. -* `x/scavenge/keeper/msg_server_submit_scavenge.go`: `SubmitScavenge` keeper method is defined -* `x/scavenge/client/cli/tx_submit_scavenge.go`: CLI command added to brodcast a transaction with a message. -* `x/scavenge/client/cli/tx.go`: CLI command is registered. -* `x/scavenge/types/codec.go`: codecs are registered. +```bash +modify app/app.go +create proto/scavenge/genesis.proto +create proto/scavenge/params.proto +create proto/scavenge/query.proto +create proto/scavenge/tx.proto +create testutil/keeper/scavenge.go +create x/scavenge/client/cli/query.go +create x/scavenge/client/cli/query_params.go +create x/scavenge/client/cli/tx.go +create x/scavenge/genesis.go +create x/scavenge/genesis_test.go +create x/scavenge/handler.go +create x/scavenge/keeper/grpc_query.go +create x/scavenge/keeper/grpc_query_params.go +create x/scavenge/keeper/grpc_query_params_test.go +create x/scavenge/keeper/keeper.go +create x/scavenge/keeper/msg_server.go +create x/scavenge/keeper/msg_server_test.go +create x/scavenge/keeper/params.go +create x/scavenge/keeper/params_test.go +create x/scavenge/module.go +create x/scavenge/module_simulation.go +create x/scavenge/simulation/simap.go +create x/scavenge/types/codec.go +create x/scavenge/types/errors.go +create x/scavenge/types/expected_keepers.go +create x/scavenge/types/genesis.go +create x/scavenge/types/genesis_test.go +create x/scavenge/types/keys.go +create x/scavenge/types/params.go +create x/scavenge/types/types.go + +🎉 Module created scavenge. +``` -In `x/scavenge/types/message_submit_scavenge.go` you can notice that the message follows the `sdk.Msg` interface. The message `struct` contains all the necessary information when creating a new scavenge: `Description`, `SolutionHash`, `Reward`, and `Creator` (which was added automatically). +The `scaffold message` command does all of these code updates for you: -The `Msg` interface requires some other methods be set, like validating the content of the `struct`, and confirming the msg was signed and submitted by the Creator. +* `proto/scavenge/tx.proto` -Now that one can submit a scavenge the only other essential action is to be able to solve it. This should be broken into two separate actions as described before: `MsgCommitSolution` and `MsgRevealSolution`. + * Adds `MsgSubmitScavenge` and `MsgSubmitScavengeResponse` proto messages + * Registers a `SubmitScavenge` RPC in the `Msg` service -## Commit Solution Message +* `x/scavenge/types/message_submit_scavenge.go` -Commit solution message needs to contain the following fields: + * Defines methods to satisfy `Msg` interface -* Solution hash - the scrambled solution. -* Solution scavenger hash - this is the hash of the combination of the solution and the person who solved it. +* `x/scavenge/handler.go` + + * Registers the `MsgSubmitScavenge` message in the module message handler + +* `x/scavenge/keeper/msg_server_submit_scavenge.go` + + * Defines the `SubmitScavenge` keeper method + +* `x/scavenge/client/cli/tx_submit_scavenge.go` + + * Adds CLI command to broadcast a transaction with a message + +* `x/scavenge/client/cli/tx.go` + + * Registers the CLI command + +* `x/scavenge/types/codec.go` + + * Registers the codecs + +In `x/scavenge/types/message_submit_scavenge.go`, you can notice that the message follows the `sdk.Msg` interface. The message `struct` automatically contains the information required to create a new scavenge: + +```go +func NewMsgSubmitScavenge(creator string, solutionHash string, description string, reward string) *MsgSubmitScavenge { + return &MsgSubmitScavenge{ + Creator: creator, + SolutionHash: solutionHash, + Description: description, + Reward: reward, + } +``` + +The `Msg` interface requires some other methods be set, like validating the content of the `struct` and confirming the message was signed and submitted by the creator. + +Now that a user can submit a scavenge, the only other essential action is to be able to solve the scavenge. As described earlier to prevent front running, use two separate actions, `MsgCommitSolution` and `MsgRevealSolution`. + +## Commit solution message + +The commit solution message requires the following fields: + +* Solution hash - the scrambled solution +* Solution scavenger hash - the hash of the combination of the solution and the person who solved it ```bash starport scaffold message commit-solution solutionHash solutionScavengerHash ``` -As you're using the same `starport scaffold message` command the set of modified and created files are the same. +Because you're using the same `starport scaffold message` command, the set of modified and created files is the same: + +```bash +modify proto/scavenge/tx.proto +modify x/scavenge/client/cli/tx.go +create x/scavenge/client/cli/tx_commit_solution.go +modify x/scavenge/handler.go +create x/scavenge/keeper/msg_server_commit_solution.go +modify x/scavenge/module_simulation.go +create x/scavenge/simulation/commit_solution.go +modify x/scavenge/types/codec.go +create x/scavenge/types/message_commit_solution.go +create x/scavenge/types/message_commit_solution_test.go + +🎉 Created a message `commit-solution`. +``` -## Reveal Solution Message +## Reveal solution message -Reveal solution message needs only one field: +The reveal solution message requires only one field: -* Solution - this is the plain text version of the solution. +* Solution - the plain text version of the solution ```bash starport scaffold message reveal-solution solution ``` -Information about the scavenger (creator of the message is automatically included) and solution hash can be deterministically derived from the solution string. +Again, because you're using the same `starport scaffold message` command, the set of modified and created files is the same for the `reveal-solution` message. + +Information about the scavenger (the creator of the message is automatically included) and the solution hash can be deterministically derived from the solution string. + +## Save changes + +Now is a good time to store your project in a git commit: + +```bash +git add . +git commit -m "add scavenge messages" +``` + diff --git a/docs/guide/scavenge/05-types.md b/docs/guide/scavenge/05-types.md index bf2fe2d02b..87e070769a 100644 --- a/docs/guide/scavenge/05-types.md +++ b/docs/guide/scavenge/05-types.md @@ -6,38 +6,115 @@ order: 5 Now that you've defined messages that trigger state transitions, it's time to implement types and methods that operate on the state. -A keeper is an abstraction that let's your blockchain app interact with the state. Functions like create, update and delete are defined as keeper methods. In the Scavenge blockchain a `scavenge` and `commit` types need to be defined, along with create and update methods. +A keeper is an abstraction that lets your blockchain app interact with the state. Functions like create, update, and delete are defined as keeper methods. In the scavenge blockchain, you need to define the `scavenge` and `commit` types along with create and update methods. -Starport has several commands that scaffold the code for CRUD functionality for a list-like data structure, a map (key-value pairs) and a single element in the state. In this example, both `scavenge` and `commit` will be stored in a map-like data structure. +Several Starport commands are available to scaffold the code for CRUD functionality for a list-like data structure, a map (key-value pairs), and a single element in the state. In this example, both `scavenge` and `commit` are stored in a map-like data structure. ## Scavenge -Use `starport scaffold map` command to scaffold the `scavenge` type and the code for creating, reading, updating, and deleting (CRUD) scavenges. The first argument is the name of the type being created (`scavenge`), the rest is list of fields. By default, generic CRUD messages are scaffolded, but since you've already created messages specifically for this blockchain, skip messages with a `--no-message` flag. +Use the `starport scaffold map` command to scaffold the `scavenge` type and the code for creating, reading, updating, and deleting (CRUD) scavenges. + +The first argument is the name of the type to create (`scavenge`), the rest is a list of fields. By default, generic CRUD messages are scaffolded. However, since you already created messages specifically for this scavenge blockchain, use the `--no-message` flag to skip message creation. ```bash starport scaffold map scavenge solutionHash solution description reward scavenger --no-message ``` -`starport scaffold map` created and mofidied several files: +The `starport scaffold map` command creates and modifies several files: + +```bash +modify proto/scavenge/genesis.proto +modify proto/scavenge/query.proto +create proto/scavenge/scavenge.proto +modify x/scavenge/client/cli/query.go +create x/scavenge/client/cli/query_scavenge.go +create x/scavenge/client/cli/query_scavenge_test.go +modify x/scavenge/genesis.go +modify x/scavenge/genesis_test.go +create x/scavenge/keeper/grpc_query_scavenge.go +create x/scavenge/keeper/grpc_query_scavenge_test.go +create x/scavenge/keeper/scavenge.go +create x/scavenge/keeper/scavenge_test.go +modify x/scavenge/module.go +modify x/scavenge/types/genesis.go +modify x/scavenge/types/genesis_test.go +create x/scavenge/types/key_scavenge.go + +🎉 scavenge added. +``` + +The `scaffold map` command does all of these code updates for you: + +* `proto/scavenge/scavenge.proto` + + * Defines the `Scavenge` type as a proto message + +* `proto/scavenge/query.proto` + + * Defines queries to get data from the blockchain as proto messages and registers the queries in the `Query` service + +* `proto/scavenge/genesis.proto` + + * Creates type for exporting the state of the blockchain (for example, during software upgrades) + +* `x/scavenge/keeper/grpc_query_scavenge.go` + + * Defines keeper methods to query the blockchain + +* `x/scavenge/keeper/grpc_query_scavenge_test.go` + + * Creates tests for query keeper methods + +* `x/scavenge/keeper/scavenge.go` + + * Defines keeper methods to get, set, and remove scavenges from the store + +* `x/scavenge/keeper/scavenge_test.go` + + * Creates tests for the keeper methods -* `proto/scavenge/scavenge.proto`: the `Scavenge` type defined as a proto message. -* `proto/scavenge/query.proto`: queries to get data from the blockchain defined as proto messages and registered in the `Query` service. -* `proto/scavenge/genesis.proto`: a type for exporting the state of the blockchain (for example, during software upgrades) -* `x/scavenge/keeper/grpc_query_scavenge.go`: keeper methods to query the blockchain. -* `x/scavenge/keeper/grpc_query_scavenge_test.go`: tests for query keeper methods. -* `x/scavenge/keeper/scavenge.go`: keper methods to get, set and remove scavenges from the store. -* `x/scavenge/keeper/scavenge_test.go`: tests for keeper methods. -* `x/scavenge/client/cli/query_scavenge.go`: CLI commands for querying the blockchain. -* `x/scavenge/client/cli/query.go`: registering CLI commands. -* `x/scavenge/client/cli/query_scavenge_test.go`: tests for CLI commands. -* `x/scavenge/types/keys.go`: a string as a prefix in the key used to store scavenges in the state. -* `x/scavenge/genesis.go`: logic for exporting and exporting the state. -* `x/scavenge/types/genesis.go`: logic for validating the genesis file. -* `x/scavenge/module.go`: registering gRPC gateway routes. +* `x/scavenge/client/cli/query_scavenge.go` -`SetScavenge` in the `keeper` package uses a key-value store using a prefix for the scavenge type (`Scavenge-value-`) encodes the `Scavenge` type (that is generated from a protocol buffer definition) and uses `store.Set` method to write a Scavenge into the store. + * Creates CLI commands for querying the blockchain -`GetScavenge` selects a store using the scavenge prefix, and uses `store.Get` to fetch a scavenge with a particular index. +* `x/scavenge/client/cli/query.go` + + * Registers the CLI commands + +* `x/scavenge/client/cli/query_scavenge_test.go` + + * Createstests for the CLI commands + +* `x/scavenge/types/keys.go` + + * Creates a string as a prefix in the key used to store scavenges in the state + +* `x/scavenge/genesis.go` + + * Creates logic for exporting and exporting the state + +* `x/scavenge/types/genesis.go` + + * Createslogic for validating the genesis file + +* `x/scavenge/module.go` + + * Registers the gRPC gateway routes + +Review the `x/scavenge/keeper/scavenge.go` file to see the `SetScavenge` updates that were made in the `keeper` package, like the `store.Set` method that writes a Scavenge into the store: + +```go +// SetScavenge set a specific scavenge in the store from its index +func (k Keeper) SetScavenge(ctx sdk.Context, scavenge types.Scavenge) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.ScavengeKeyPrefix)) + b := k.cdc.MustMarshal(&scavenge) + store.Set(types.ScavengeKey( + scavenge.Index, + ), b) +} +``` + +Review the update for `GetScavenge` that selects a store using the scavenge prefix and uses `store.Get` to fetch a scavenge with a particular index. ## Commit @@ -46,3 +123,12 @@ Use `starport scaffold map` to create the same logic for a `commit` type. ```bash starport scaffold map commit solutionHash solutionScavengerHash --no-message ``` + +## Save changes + +Now is a good time to store your project in a git commit: + +```bash +git add . +git commit -m "add scavenge types" +``` diff --git a/docs/guide/scavenge/06-handlers.md b/docs/guide/scavenge/06-handlers.md index 29b4c99ae3..22c668450f 100644 --- a/docs/guide/scavenge/06-handlers.md +++ b/docs/guide/scavenge/06-handlers.md @@ -4,12 +4,15 @@ order: 6 # Handlers -In order for a **Message** to reach a **Keeper**, it has to go through a **Handler**. This is where logic can be applied to either allow or deny a `Message` to succeed. If you're familiar with [Model View Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) (MVC) architecture, the `Keeper` is a bit like the **Model** and the `Handler` is a bit like the **Controller**. If you're familiar with [React/Redux]() or [Vue/Vuex](https://en.wikipedia.org/wiki/Vue.js) architecture, the `Keeper` is a bit like the **Reducer/Store** and the `Handler` is a bit like **Actions**. +For a message to reach a keeper, it has to go through a handler. A handler is where you can apply logic to allow or deny a message to succeed. -Module-wide message handler is defined in `x/scavenge/handler.go`. Three message types were added to the handler: +* If you're familiar with the [Model-view-controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) (MVC) software architecture, the keeper is a bit like the model, and the handler is a bit like the controller. +* If you're familiar with [React]() or [Vue](https://en.wikipedia.org/wiki/Vue.js) architecture, the keeper is a bit like the reducer store and the handler is a bit like actions. + +The module-wide message handler is defined in `x/scavenge/handler.go`. Three message types were automatically added to the handler: * `MsgSubmitScavenge` * `MsgCommitSolution` * `MsgRevealSolution` -Each message, when handled, calls the appropriate keeper method, responsible for committing changes to the store. +Each message, when handled, calls the appropriate keeper method that is responsible for committing changes to the store. diff --git a/docs/guide/scavenge/07-keeper.md b/docs/guide/scavenge/07-keeper.md index d9c6052e2d..72ea969924 100644 --- a/docs/guide/scavenge/07-keeper.md +++ b/docs/guide/scavenge/07-keeper.md @@ -4,12 +4,14 @@ order: 7 # Keeper -## Create Scavenge +Keepers are a Cosmos SDK abstraction whose role is to manage access to the subset of the state defined by various modules. -Create scavenge method should do the following: +## Create scavenge + +Make the required changes in the `x/scavenge/keeper/msg_server_submit_scavenge.go` file so the create scavenge method can manage the following: * Check that a scavenge with a given solution hash doesn't exist -* Send tokens from scavenge creator account to a module account +* Send tokens from the scavenge creator account to a module account * Write the scavenge to the store ```go @@ -64,9 +66,11 @@ func (k msgServer) SubmitScavenge(goCtx context.Context, msg *types.MsgSubmitSca } ``` -Notice the use of `moduleAcct`. This account is not controlled by a public key pair, but is a reference to an account that is owned by this actual module. It is used to hold the bounty reward that is attached to a scavenge until that scavenge has been solved, at which point the bounty is paid to the account who solved the scavenge. +Notice the use of `moduleAcct`. This account is not controlled by a public key pair, but is a reference to an account that is owned by this actual module. `moduleAcct` is used to hold the bounty reward that is attached to a scavenge until that scavenge has been solved, at which point the bounty is paid to the account who solved the scavenge. + +`SubmitScavenge` uses the `SendCoins` method from the `bank` module. When you scaffolded the scavenge module, you used `--dep bank` to specify a dependency between the `scavenge` and `bank` modules. This dependency automatically created an `expected_keepers.go` file with a `BankKeeper` interface. -`SubmitScavenge` uses `SendCoins` method from the `bank` module. In the beginning when scaffolding a module you used `--dep bank` to specify a dependency between the `scavenge` and `bank` modules. This created an `expected_keepers.go` file with a `BankKeeper` interface. Add `SendCoins` to be able to use it in the keeper methods of the `scavenge` module. +To use the `BankKeeper` interface in the keeper methods of the `scavenge` module, add `SendCoins` to the `x/scavenge/types/expected_keepers.go` file: ```go // x/scavenge/types/expected_keepers.go @@ -81,7 +85,7 @@ type BankKeeper interface { ## Commit Solution -Commit solution method should do the following: +Make the required changes in the `x/scavenge/keeper/msg_server_commit_solution.go` file so the commit solution method can manage the following: * Check that commit with a given hash doesn't exist in the store * Write a new commit to the store @@ -119,7 +123,7 @@ func (k msgServer) CommitSolution(goCtx context.Context, msg *types.MsgCommitSol ## Reveal Solution -Reveal solution method should do the following: +Make the required changes in the `x/scavenge/keeper/msg_server_reveal_solution.go` file so the reveal solution method can manage the following: * Check that a commit with a given hash exists in the store * Check that a scavenge with a given solution hash exists in the store diff --git a/docs/guide/scavenge/08-cli.md b/docs/guide/scavenge/08-cli.md index 11140717dd..4f4f87a6ab 100644 --- a/docs/guide/scavenge/08-cli.md +++ b/docs/guide/scavenge/08-cli.md @@ -4,15 +4,24 @@ order: 8 # CLI -A Command Line Interface (CLI) will help you interact with our app once it is running on a machine somewhere. Each Module has it's own namespace within the CLI that gives it the ability to create and sign Messages destined to be handled by that module. It also comes with the ability to query the state of that module. When combined with the rest of the app, the CLI will let you do things like generate keys for a new account or check the status of an interaction you already had with the application. +A command line interface (CLI) lets you interact with your app after it is running on a machine somewhere. Each module has its own namespace within the CLI that gives it the ability to create and sign messages that are destined to be handled by that module. -The CLI for our module is broken into two files called `tx.go` and `query.go` which are located in `./x/scavenge/client/cli/`. One file is for making transactions that contain messages which will ultimately update our state. The other is for making queries which will give us the ability to read information from our state. Both files utilize the [Cobra](https://github.com/spf13/cobra) library. +The CLI also comes with the ability to query the state of that module. When combined with the rest of the app, the CLI lets you do things like generate keys for a new account or check the status of an interaction you already had with the application. -## The tx.go File +The CLI for the scavenge module is present in the `tx.go` and `query.go` files in the `./x/scavenge/client/cli/` directory. -The `tx.go` file contains `GetTxCmd` which is a standard method within the Cosmos SDK. It is referenced later in the `module.go` file which describes exactly which attributes a modules has. This makes it easier to incorporate different modules for different reasons at the level of the actual application. After all, you want to focus on a module at this point, but later you will create an application that utilizes this module as well as other modules which are already available within the Cosmos SDK. +- The `tx.go` file is for making transactions that contain messages that will ultimately update the state. +- The `query.go` file is for making queries let you read information from the state. -## Commit Solution +Both files use the [Cobra](https://github.com/spf13/cobra) library. + +## The tx.go file + +The `tx.go` file contains the `GetTxCmd` standard method that is used in the Cosmos SDK. This method is referenced later in the `module.go` file that describes exactly which attributes a modules has. + +This method makes it easier to incorporate different modules for different reasons at the level of the actual application. You are focused on a module now, but later you create an application that uses this module and other modules that are already available within the Cosmos SDK. + +## Commit solution ```go // x/scavenge/client/cli/tx_commit_solution.go @@ -67,9 +76,9 @@ func CmdCommitSolution() *cobra.Command { } ``` -Note that this file makes use of the `sha256` library for hashing the plain text solutions into the scrambled hashes. This activity takes place on the client side so the solutions are never leaked to any public entity which might want to sneak a peak and steal the bounty reward associated with the scavenges. You can also notice that the hashes are converted into hexadecimal representation to make them easy to read as strings (which is how they are ultimately stored in the keeper). +Note that this file makes use of the `sha256` library for hashing the plain text solutions into the scrambled hashes. This activity takes place on the client side so the solutions are never leaked to any public entity that might want to sneak a peak and steal the bounty reward associated with the scavenges. You can also notice that the hashes are converted into hexadecimal representation to make them easy to read as strings. Hashes are ultimately stored as hexadecimal representations in the keeper. -## Submit Scavenge +## Submit scavenge ```go // x/scavenge/client/cli/tx_submit_scavenge.go diff --git a/docs/guide/scavenge/09-play.md b/docs/guide/scavenge/09-play.md index 8dbaeaf2b5..7d09234210 100644 --- a/docs/guide/scavenge/09-play.md +++ b/docs/guide/scavenge/09-play.md @@ -2,9 +2,11 @@ order: 9 --- -# Play With Your Blockchain +# Play with your blockchain -## Starting the Blockchain +Now that you have made the required updates to you new chain, it's time to test it. + +## Start the blockchain To start your blockchain in development, run the following command: @@ -12,20 +14,28 @@ To start your blockchain in development, run the following command: starport chain serve ``` -`serve` will build the chain's binary, initialize a data directory and start a node in development. Keep it running in one terminal window and open another one to execute commands. +The `serve` command builds the chain binary file, initializes a data directory, and starts a node in development. + +For all subsequent commands, you use a terminal window that is different from the window you started the chain in. + +## Create a scavenge -## Creating a Scavenge +Follow the instructions and submit a new scavenge. -Let's follow the instructions and submit a new scavenge. The first parameter you need is the `solution`, but probably you should also know what the actual question is that your solution solves (the `description`). How about the challenge question be something family friendly like: `What's brown and sticky?`. Of course the only solution to this question is: `A stick`. +The first parameter is the `solution`, but you should also know what the actual question is that your solution solves (the `description`). -Next you should specify the `reward`. Let's give away `100token` as a reward for solving the scavenge. +You can make the challenge question something family-friendly like `What's brown and sticky?`. Of course, the only solution to this question is `A stick`. -Now you have all the pieces needed to create our message. Piece them all together, adding the flag `--from` so the CLI knows who is sending it: +Next, you must specify the `reward`. Give away `100token` as a reward for solving the scavenge. + +Now you have all the pieces needed to create your message. Piece them all together in the command and add the flag `--from` so the CLI knows who is sending it: ```bash scavenged tx scavenge submit-scavenge "A stick" "What's brown and sticky?" 100token --from alice ``` +The results: + ```json { "body": { @@ -99,12 +109,14 @@ scavenged tx scavenge submit-scavenge "A stick" "What's brown and sticky?" 100to } ``` -Replace the `txhash` with your `txhash` from the previous output. +Replace the `txhash` with your `txhash` from the previous output: ```bash scavenged q tx CE401E1F95FC583355BF6ABB823A4655185E2983CACE7C430E22CC7B573152DD --output json ``` +The results: + ```json { "height": "229", @@ -186,12 +198,14 @@ scavenged q tx CE401E1F95FC583355BF6ABB823A4655185E2983CACE7C430E22CC7B573152DD } ``` -## Querying For a List of Scavenges +## Query for a list of scavenges ```bash scavenged q scavenge list-scavenge --output json ``` +The results: + ```json { "Scavenge": [ @@ -209,7 +223,7 @@ scavenged q scavenge list-scavenge --output json } ``` -## Committing a solution +## Commit a solution Follow the instructions and submit the answer as a commit on behalf of `bob`: @@ -217,6 +231,8 @@ Follow the instructions and submit the answer as a commit on behalf of `bob`: scavenged tx scavenge commit-solution "A stick" --from bob ``` +The results: + ```json { "body": { @@ -241,12 +257,14 @@ scavenged tx scavenge commit-solution "A stick" --from bob } ``` -## Querying For a List of Commits +## Query for a list of commits ```bash scavenged q scavenge list-commit --output json ``` +The results: + ```json { "Commit": [ @@ -263,14 +281,16 @@ scavenged q scavenge list-commit --output json You don't need to put the `solutionHash` because it can be generated by hashing the actual solution. -Since all you need is the solution again, send and confirm the final message: +Since all you need is the solution again, you can send and confirm the final message: -## Revealing a Solution +## Reveal a solution ```bash scavenged tx scavenge reveal-solution "A stick" --from bob ``` +The results: + ```json { "body": { @@ -294,12 +314,14 @@ scavenged tx scavenge reveal-solution "A stick" --from bob } ``` -## Querying For a List of solved Scavenges +## Query for a list of solved scavenges ```bash scavenged q scavenge list-scavenge --output json ``` +The results: + ```json { "Scavenge": [ @@ -317,12 +339,14 @@ scavenged q scavenge list-scavenge --output json } ``` -## Committing a Solution Again, Expect To Get an Error +## Commit a solution again, expect to get an error ```bash scavenged tx scavenge commit-solution "A stick" --from bob ``` +The results: + ```json { "height": "665", @@ -340,4 +364,4 @@ scavenged tx scavenge commit-solution "A stick" --from bob } ``` -Congratulations! You have built a scavenge hunt blockchain! +Congratulations, you have built and tested a scavenge hunt blockchain! diff --git a/docs/guide/scavenge/index.md b/docs/guide/scavenge/index.md index 911bf04e4e..06d6d2228c 100644 --- a/docs/guide/scavenge/index.md +++ b/docs/guide/scavenge/index.md @@ -5,27 +5,33 @@ parent: order: 5 --- -# Introduction +# Scavenger hunt game -In this tutorial, you will build a blockchain for a scavenger hunt game. In the process you will learn how to: +In this tutorial, you will build a blockchain for a scavenger hunt game and learn how to: * Implement custom logic in the CLI commands * Use an escrow account to store tokens This tutorial was first presented as a workshop at GODays 2020 Berlin by [Billy Rennekamp](https://twitter.com/billyrennekamp). -The goal of this session is to get you thinking about what is possible when developing applications that have access to **digital scarcity as a primitive**. The easiest way to think of scarcity is as money; If money grew on trees it would stop being _scarce_ and stop having value. We have a long history of software which deals with money, but it's never been a first class citizen in the programming environment. Instead, money has always been represented as a number or a float, and it has been up to a third party merchant service or some other process of exchange where the _representation_ of money is swapped for actual cash. If money were a primitive in a software environment, it would allow for **real economies to exist within games and applications**, taking one step further in erasing the line between games, life and play. +This session aims to get you thinking about what is possible when developing applications that have access to **digital scarcity as a primitive**. The easiest way to think of scarcity is as money; If money grew on trees it would stop being _scarce_ and stop having value. -You will be working today with the Go framework [Cosmos SDK](https://github.com/cosmos/cosmos-sdk). This framework makes it easy to build **deterministic state machines**. A state machine is simply an application that has a state and explicit functions for updating that state. You can think of a light bulb and a light switch as a kind of state machine: the state of the "application" is either `light on` or `light off`. There is one function in this state machine: `flip switch`. Every time you trigger `flip switch` the state of the application goes from `light on` to `light off` or vice versa. +Although a long history of software deals with money, the representation of money has not been a first-class citizen in the programming environment. Instead, money has historically been represented as a number or a float. It has been left up to a third party merchant service or process of exchange to swap the _representation_ of money for actual cash. If money were a primitive in a software environment, it would allow for **real economies to exist within games and applications**. Money as a primitive takes one more step in erasing the line between games, life, and play. -A **deterministic** state machine is a state machine in which an accumulation of actions, taken together and replayed, will have the same outcome. So if you were to take all the `switch on` and `switch off` actions of the entire month of January for some room and replay them in August, you should have the same final state of `light on` or `light off`. There should be nothing about January or August that changes the outcome (of course a _real_ room might not be deterministic if there were things like power shortages or maintenance that took place during those periods). +This tutorial uses the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) framework that makes it possible to build **deterministic state machines**. A state machine is simply an application that has a state and explicit functions for updating that state. You can think of a light bulb and a light switch as a kind of state machine: the state of the "application" is either `light on` or `light off`. There is one function in this state machine: `flip switch`. Every time you trigger `flip switch`, the state of the application goes from `light on` to `light off` or vice versa. -What is nice about deterministic state machines is that you can track changes with **cryptographic hashes** of the state, just like version control systems like `git`. If there is agreement about the hash of a certain state, it is unnecessary to replay every action from genesis to ensure that two repos are in sync with each other. These properties are useful when dealing with software that is run by many different people in many different situations, just like git. +## Deterministic state machine -Another nice property of cryptographically hashing state is that it creates a system of **reliable dependencies**. I can build software that uses your library and reference a specific state in your software. That way if you change your code in a way that breaks my code, I don't have to use your new version but can continue to use the version that I reference. This same property of knowing exactly what the state of a system (as well as all the ways that state can update) makes it possible to have the necessary assurances that allow for digital scarcity within an application. _If I say there is only one of some thing within a state machine and you know that there is no way for that state machine to create more than one, you can rely on there always being only one._ +A **deterministic** state machine is a state machine in which an accumulation of actions, taken together and replayed, have the same outcome. So if you were to take all the `switch on` and `switch off` actions of the entire month of January for some room and replay them in August, you have the same final state of `light on` or `light off`. Nothing about the metaphorical months of January or August changes the outcome. Of course, a _real_ room is not deterministic if things like power shortages or maintenance occurred during those months. -You might have guessed by now that what I'm really talking about are **blockchains**. These are deterministic state machines which have very specific rules about how state is updated. Blockchains checkpoint state with cryptographic hashes and use asymmetric cryptography to handle **access control**. There are different ways that different Blockchains decide who can make a checkpoint of state. These entities can be called **Validators**. Some of them are chosen by an electricity-intensive game called **proof-of-work** in tandem with something called the longest chain rule or **Nakamoto Consensus** on blockchains like Bitcoin or Ethereum. +A strong feature of deterministic state machines lets you track changes with **cryptographic hashes** of the state, similar to version control systems like `git`. If there is agreement about the hash of a certain state, it is unnecessary to replay every action from genesis to ensure that two repos are in sync with each other. These properties are useful when dealing with software that is run by many different people in many different situations, just like git. -The state machine you are building will use an implementation of **proof-of-stake** called **Tendermint**, which is energy efficient and can consist of one or many validators, either trusted or byzantine. When building a system that handles _real_ scarcity, the integrity of that system becomes very important. One way to ensure that integrity is by sharing the responsibility of maintaining it with a large group of independently motivated participants as validators. +Another nice property of cryptographically hashing state is the system of **reliable dependencies**. For example, a developer can build software that uses your library and references a specific state in your software. That way if your code changes in a way that breaks code in a specific state, developers are not required to use your new version but can continue to use the referenced version. This same property of knowing exactly what the state of a system (as well as all the ways that state can update) makes it possible to have the necessary assurances that allow for digital scarcity within an application. _If I say there is only one of some thing within a state machine and you know that there is no way for that state machine to create more than one, you can rely on there always being only one._ + +You might have guessed by now that we're talking about **blockchains**. Blockchains are deterministic state machines that have very specific rules about how state is updated. Blockchains checkpoint state with cryptographic hashes and use asymmetric cryptography to handle **access control**. There are different ways that different blockchains decide who can make a checkpoint of state. These entities are called **validators**. On blockchains like Bitcoin or Ethereum, validators are chosen by an electricity-intensive process called Proof-of-Work (PoW) in tandem with something called the longest chain rule or the Nakamoto consensus. Nakamoto solved the permissionless consensus problem with a remarkably simple but powerful scheme that uses only basic cryptographic primitives (hash functions and digital signatures). + +## Proof-of-Stake (PoS) + +The state machine you build with this tutorial uses the energy-efficient Proof-of-Stake (PoS) consensus that can consist of one or many validators, either trusted or byzantine. When a system handles _real_ scarcity, the integrity of that system becomes very important. One way to ensure integrity is by sharing the responsibility of maintaining the integrity with a large group of independently motivated participants as validators. So, now that you know a little more about **why** you might build an app like this, start to dive into the game itself.