├── .github
├── CODEOWNERS
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── lint.yml
│ ├── md-link-checker.yml
│ └── misspell.yml
├── .gitignore
├── .golangci.yml
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── banner.png
├── go.work.example
├── scripts
└── go-lint-all.bash
└── tutorials
├── base
├── Makefile
├── app
│ ├── app.go
│ ├── app.yaml
│ ├── export.go
│ └── params
│ │ ├── config.go
│ │ └── encoding.go
├── cmd
│ └── exampled
│ │ ├── cmd
│ │ ├── commands.go
│ │ └── root.go
│ │ └── main.go
├── go.mod
├── go.sum
└── scripts
│ └── init.sh
├── nameservice
├── base
│ ├── Makefile
│ ├── app
│ │ ├── app.go
│ │ ├── app.yaml
│ │ ├── export.go
│ │ ├── genesis.go
│ │ └── params
│ │ │ ├── config.go
│ │ │ └── encoding.go
│ ├── cmd
│ │ └── exampled
│ │ │ ├── cmd
│ │ │ ├── cmd.go
│ │ │ └── commands.go
│ │ │ └── main.go
│ ├── go.mod
│ ├── go.sum
│ ├── scripts
│ │ └── init.sh
│ └── x
│ │ └── auction
│ │ ├── Makefile
│ │ ├── abci
│ │ ├── proposal.go
│ │ ├── types.go
│ │ └── vote_ext.go
│ │ ├── api
│ │ ├── module
│ │ │ └── module.pulsar.go
│ │ └── v1
│ │ │ ├── event.pulsar.go
│ │ │ ├── genesis.pulsar.go
│ │ │ ├── query.pulsar.go
│ │ │ ├── query_grpc.pb.go
│ │ │ ├── tx.pulsar.go
│ │ │ ├── tx_grpc.pb.go
│ │ │ └── types.pulsar.go
│ │ ├── codec.go
│ │ ├── errors.go
│ │ ├── event.pb.go
│ │ ├── expected_keepers.go
│ │ ├── flags.go
│ │ ├── genesis.go
│ │ ├── genesis.pb.go
│ │ ├── keeper
│ │ ├── genesis.go
│ │ ├── genesis_test.go
│ │ ├── keeper.go
│ │ ├── migrator.go
│ │ ├── msg_server.go
│ │ ├── msg_server_test.go
│ │ ├── nameservice.go
│ │ ├── query_server.go
│ │ └── query_server_test.go
│ │ ├── keys.go
│ │ ├── mempool
│ │ ├── mempool.go
│ │ └── testutil.go
│ │ ├── module
│ │ ├── autocli.go
│ │ ├── cli.go
│ │ ├── depinject.go
│ │ └── module.go
│ │ ├── proto
│ │ ├── buf.gen.gogo.yaml
│ │ ├── buf.gen.pulsar.yaml
│ │ ├── buf.lock
│ │ ├── buf.yaml
│ │ └── cosmos
│ │ │ └── auction
│ │ │ ├── module
│ │ │ └── module.proto
│ │ │ └── v1
│ │ │ ├── event.proto
│ │ │ ├── genesis.proto
│ │ │ ├── query.proto
│ │ │ ├── tx.proto
│ │ │ └── types.proto
│ │ ├── provider
│ │ └── provider.go
│ │ ├── query.pb.go
│ │ ├── query.pb.gw.go
│ │ ├── scripts
│ │ ├── configure.sh
│ │ ├── list-beacon-keys.sh
│ │ ├── protocgen.sh
│ │ ├── query-beacon-status.sh
│ │ ├── reserve.sh
│ │ ├── single_node
│ │ │ ├── frontrun.sh
│ │ │ └── setup.sh
│ │ ├── stop.sh
│ │ ├── vars.sh
│ │ └── whois.sh
│ │ ├── testutils
│ │ └── encoding.go
│ │ ├── tx.pb.go
│ │ └── types.pb.go
└── docs
│ ├── 00-getting-started.md
│ ├── 01-understanding-frontrunning.md
│ ├── 02-mitigating-front-running-with-vote-extesions.md
│ └── 03-demo-of-mitigating-front-running.md
└── oracle
├── base
├── Makefile
├── app
│ ├── app.go
│ ├── app.yaml
│ ├── export.go
│ └── params
│ │ ├── config.go
│ │ └── encoding.go
├── cmd
│ └── exampled
│ │ ├── cmd
│ │ ├── commands.go
│ │ └── root.go
│ │ └── main.go
├── go.mod
├── go.sum
├── scripts
│ └── init.sh
└── x
│ └── oracle
│ ├── Makefile
│ ├── abci
│ ├── proposal.go
│ ├── provider_aggregator.go
│ └── vote_extensions.go
│ ├── api
│ ├── module
│ │ └── v1
│ │ │ └── module.pulsar.go
│ └── v1
│ │ ├── query.pulsar.go
│ │ ├── query_grpc.pb.go
│ │ ├── tx.pulsar.go
│ │ ├── tx_grpc.pb.go
│ │ └── types.pulsar.go
│ ├── codec.go
│ ├── errors.go
│ ├── genesis.go
│ ├── integration
│ ├── go.mod
│ ├── go.sum
│ └── integration_test.go
│ ├── keeper
│ ├── genesis.go
│ ├── genesis_test.go
│ ├── keeper.go
│ ├── keeper_test.go
│ ├── migrator.go
│ ├── msg_server.go
│ ├── msg_server_test.go
│ ├── query_server.go
│ └── query_server_test.go
│ ├── keys.go
│ ├── mockprovider
│ └── provider.go
│ ├── module
│ ├── autocli.go
│ ├── depinject.go
│ └── module.go
│ ├── proto
│ ├── buf.gen.gogo.yaml
│ ├── buf.gen.pulsar.yaml
│ ├── buf.lock
│ ├── buf.yaml
│ └── cosmos
│ │ └── oracle
│ │ ├── module
│ │ └── v1
│ │ │ └── module.proto
│ │ └── v1
│ │ ├── query.proto
│ │ ├── tx.proto
│ │ └── types.proto
│ ├── query.pb.go
│ ├── query.pb.gw.go
│ ├── scripts
│ └── protocgen.sh
│ ├── tx.pb.go
│ └── types.pb.go
└── docs
├── 00-getting-started.md
├── 01-what-is-an-oracle.md
├── 02-implementing-vote-extensions.md
└── 03-testing-oracle.md
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # CODEOWNERS:
2 |
3 | @samricotta
4 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 | Closes: #XXXX
4 |
5 |
7 |
8 | ---
9 |
10 | ## Author Checklist
11 |
12 | *All items are required. Please add a note to the item if the item is not applicable and
13 | please add links to any relevant follow up issues.*
14 |
15 | I have...
16 |
17 | * [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
18 | * [ ] confirmed `!` in the type prefix if API or client breaking change
19 | * [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting))
20 | * [ ] provided a link to the relevant issue or specification
21 | * [ ] reviewed "Files changed" and left comments if necessary
22 | * [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing)
23 | * [ ] added a changelog entry to `CHANGELOG.md`
24 | * [ ] updated the relevant documentation or specification, including comments for [documenting Go code](https://blog.golang.org/godoc)
25 | * [ ] confirmed all CI checks have passed
26 |
27 | ## Reviewers Checklist
28 |
29 | *All items are required. Please add a note if the item is not applicable and please add
30 | your handle next to the items reviewed if you only reviewed selected items.*
31 |
32 | I have...
33 |
34 | * [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
35 | * [ ] confirmed all author checklist items have been addressed
36 | * [ ] reviewed state machine logic, API design and naming, documentation is accurate, tests and test coverage
37 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 | on:
3 | push:
4 | branches:
5 | - main
6 | - release/**
7 | pull_request:
8 | merge_group:
9 | permissions:
10 | contents: read
11 | jobs:
12 | golangci:
13 | name: golangci-lint
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v4
17 | - uses: DeterminateSystems/nix-installer-action@main
18 | - uses: DeterminateSystems/magic-nix-cache-action@main
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: "1.21"
22 | check-latest: true
23 | - name: run linting (long)
24 | if: env.GIT_DIFF
25 | id: lint_long
26 | run: |
27 | make lint
28 |
--------------------------------------------------------------------------------
/.github/workflows/md-link-checker.yml:
--------------------------------------------------------------------------------
1 | name: Check Markdown links
2 | on:
3 | pull_request:
4 | jobs:
5 | markdown-link-check:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v4
9 | - uses: gaurav-nelson/github-action-markdown-link-check@1.0.15
10 |
--------------------------------------------------------------------------------
/.github/workflows/misspell.yml:
--------------------------------------------------------------------------------
1 | name: Fix typos nightly
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v4
11 | - name: Run codespell
12 | continue-on-error: true
13 | run: |
14 | sudo apt-get install codespell -y
15 | codespell -w --skip="*.pulsar.go,*.pb.go,*.pb.gw.go,*.cosmos_orm.go,*.json,*.git,*.js,crypto/keys,fuzz,*.h,proto/tendermint,*.bin" --ignore-words=.github/.codespellignore
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/exampled
2 | build/
3 | data/
4 | config/
5 | Library/
6 | go.work
7 | go.work.sum
8 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | run:
2 | tests: true
3 | timeout: 15m
4 | allow-parallel-runners: true
5 | skip-files:
6 | - ".*\\.pb\\.go$"
7 | - ".*\\.pb\\.gw\\.go$"
8 | - ".*\\.pulsar\\.go$"
9 |
10 | linters:
11 | disable-all: true
12 | enable:
13 | - dogsled
14 | - errcheck
15 | - exportloopref
16 | - gci
17 | - goconst
18 | - gocritic
19 | - gofumpt
20 | - gosec
21 | - gosimple
22 | - govet
23 | - ineffassign
24 | - misspell
25 | - nakedret
26 | - nolintlint
27 | - revive
28 | - staticcheck
29 | - stylecheck
30 | - thelper
31 | - typecheck
32 | - unconvert
33 | - unused
34 |
35 | issues:
36 | exclude-rules:
37 | - text: "Use of weak random number generator"
38 | linters:
39 | - gosec
40 | - text: "ST1003:"
41 | linters:
42 | - stylecheck
43 | # FIXME: Disabled until golangci-lint updates stylecheck with this fix:
44 | # https://github.com/dominikh/go-tools/issues/389
45 | - text: "ST1016:"
46 | linters:
47 | - stylecheck
48 | - text: "leading space"
49 | linters:
50 | - nolintlint
51 | max-issues-per-linter: 10000
52 | max-same-issues: 10000
53 |
54 | linters-settings:
55 | gci:
56 | custom-order: true
57 | sections:
58 | - standard # Standard section: captures all standard packages.
59 | - default # Default section: contains all imports that could not be matched to another section type.
60 | - prefix(cosmossdk.io github.com/cosmos/cosmos-sdk)
61 | - prefix(github.com/cosmos/sdk-tutorials)
62 | revive:
63 | rules:
64 | - name: redefines-builtin-id
65 | disabled: true
66 |
67 | gosec:
68 | # To select a subset of rules to run.
69 | # Available rules: https://github.com/securego/gosec#available-rules
70 | # Default: [] - means include all rules
71 | includes:
72 | # - G101 # Look for hard coded credentials
73 | - G102 # Bind to all interfaces
74 | - G103 # Audit the use of unsafe block
75 | - G104 # Audit errors not checked
76 | - G106 # Audit the use of ssh.InsecureIgnoreHostKey
77 | - G107 # Url provided to HTTP request as taint input
78 | - G108 # Profiling endpoint automatically exposed on /debug/pprof
79 | - G109 # Potential Integer overflow made by strconv.Atoi result conversion to int16/32
80 | - G110 # Potential DoS vulnerability via decompression bomb
81 | - G111 # Potential directory traversal
82 | - G112 # Potential slowloris attack
83 | - G113 # Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772)
84 | - G114 # Use of net/http serve function that has no support for setting timeouts
85 | - G201 # SQL query construction using format string
86 | - G202 # SQL query construction using string concatenation
87 | - G203 # Use of unescaped data in HTML templates
88 | - G204 # Audit use of command execution
89 | - G301 # Poor file permissions used when creating a directory
90 | - G302 # Poor file permissions used with chmod
91 | - G303 # Creating tempfile using a predictable path
92 | - G304 # File path provided as taint input
93 | - G305 # File traversal when extracting zip/tar archive
94 | - G306 # Poor file permissions used when writing to a new file
95 | - G307 # Deferring a method which returns an error
96 | - G401 # Detect the usage of DES, RC4, MD5 or SHA1
97 | - G402 # Look for bad TLS connection settings
98 | - G403 # Ensure minimum RSA key length of 2048 bits
99 | - G404 # Insecure random number source (rand)
100 | - G501 # Import blocklist: crypto/md5
101 | - G502 # Import blocklist: crypto/des
102 | - G503 # Import blocklist: crypto/rc4
103 | - G504 # Import blocklist: net/http/cgi
104 | - G505 # Import blocklist: crypto/sha1
105 | - G601 # Implicit memory aliasing of items from a range statement
106 | misspell:
107 | locale: US
108 | gofumpt:
109 | extra-rules: true
110 | dogsled:
111 | max-blank-identifiers: 6
112 | maligned:
113 | suggest-new: true
114 | nolintlint:
115 | allow-unused: false
116 | require-explanation: true
117 | require-specific: false
118 | gosimple:
119 | checks: ["all"]
120 | gocritic:
121 | disabled-checks:
122 | - regexpMust
123 | - appendAssign
124 | - ifElseChain
125 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Cosmos SDK Tutorials repo
2 |
3 | This repository contains the code and content for the published along side the [Cosmos SDK documentation](https://docs.cosmos.network/).
4 |
5 | ## Most Tutorials are Version-Specific
6 |
7 | If the tutorial instructs you to download a specific version, that means that the code and the docs for each tutorial are tested, supported, and based on a specific version of the software. Be sure to download and use the right version.
8 |
9 | ## Contributing
10 |
11 | Thank you for helping us to create and maintain this awesome developer portal.
12 |
13 | ### Language and Style
14 |
15 | We welcome contributions to the tutorials. Our technical content follows the Google developer documentation style guide:
16 |
17 | * [Google developer documentation style guide](https://developers.google.com/style)
18 | * [Highlights](https://developers.google.com/style/highlights)
19 | * [Word list](https://developers.google.com/style/word-list)
20 | * [Style and tone](https://developers.google.com/style/tone)
21 | * [Writing for a global audience](https://developers.google.com/style/translation)
22 | * [Cross-references](https://developers.google.com/style/cross-references)
23 | * [Present tense](https://developers.google.com/style/tense)
24 |
25 | The Google guidelines include more material than is listed here, and are used as a guide that enables easy decision making about proposed content changes.
26 |
27 | Other useful resources:
28 |
29 | * [Google Technical Writing Courses](https://developers.google.com/tech-writing)
30 | * [GitHub guides on mastering markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)
31 |
32 | ### Pull Request
33 |
34 | When you submit your PR, please use the default [Pull Request template](./.github/PULL_REQUEST_TEMPLATE.md).
35 |
36 | ### Folder structure
37 |
38 | The published content currently lives in a few separate folders:
39 |
40 | * `tutorials` contains specific tutorials on different topics
41 |
42 | ## Images
43 |
44 | **Please note that images _must_ be linked using an absolute path!**
45 |
46 | ## Who works on the tutorials?
47 |
48 | Meet the people [behind the Cosmos SDK and contributors](https://github.com/cosmos/sdk-tutorials/graphs/contributors).
49 |
50 | ## Viewing Example Builds
51 |
52 | There are two ways to see what your changes will look like in production before the updated pages are published:
53 |
54 | * When a PR is ready for review, you can see a preview deployment.
55 | * While a PR is in DRAFT mode, you can preview a local build.
56 |
57 | ### Pull Requests
58 |
59 | After the PR is created CI will kick off and run. All of ci must pass in order to merge the PR. Once the PR is merged, the changes will be deployed to the production site.
60 |
61 | ```sh
62 | npm run serve
63 | ```
64 |
65 | to start the local server.
66 |
67 | A successful client compile looks like: `> VuePress dev server listening at http://localhost:8080/ ✔ Client Compiled successfully in 280.71ms success [12:06:28] Build 03d41f finished in 283 ms! ( http://localhost:8080/ )`
68 |
69 | You can now view the tutorial build on a local web browser at `http://localhost:8080/`.
70 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | ### Linting ###
3 | ###############################################################################
4 |
5 | golangci_version=v1.55.0
6 |
7 | lint-install:
8 | @echo "--> Installing golangci-lint $(golangci_version)"
9 | @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
10 |
11 | lint:
12 | @echo "--> Running linter"
13 | $(MAKE) lint-install
14 | @./scripts/go-lint-all.bash --timeout=15m
15 |
16 | lint-fix:
17 | @echo "--> Running linter"
18 | $(MAKE) lint-install
19 | @./scripts/go-lint-all.bash --fix
20 |
21 | .PHONY: lint lint-fix
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cosmos SDK Tutorials - Application Development
2 |
3 | 
4 |
5 | Welcome to Cosmos SDK Tutorails, your go-to resource for learning and mastering the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk). This repository contains a series of comprehensive tutorials designed to help you understand and effectively use the Cosmos SDK.
6 |
7 | ## About the Tutorials
8 |
9 | These tutorials are crafted to cater to learners at various levels, from beginners to advanced users. Each tutorial covers specific aspects of the Cosmos SDK, ensuring a thorough understanding of its principles and applications.
10 |
11 | ## Prerequisites
12 |
13 | Before you begin, ensure you have the following installed:
14 |
15 | - [Install Golang](https://go.dev/doc/install)
16 |
17 |
26 |
27 | ## How to Use This Repository
28 |
29 | Navigate to the specific tutorial you are interested in, and follow the instructions provided within each tutorial's directory.
30 |
31 | ## Contributing
32 |
33 | We welcome contributions to this repository! Please read our [Contributing Guidelines](./CONTRIBUTING.md) for more information on how you can contribute.
34 |
35 | ## Feedback
36 |
37 | If you have any feedback or suggestions, please open an issue in this repository.
38 |
39 | ## License
40 |
41 | This project is licensed under the Apache license - see the [LICENSE.md](./LICENSE) file for details.
42 |
--------------------------------------------------------------------------------
/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmos/sdk-tutorials/34fe852e441259fff969b98bf1134716f2fa3ccb/banner.png
--------------------------------------------------------------------------------
/go.work.example:
--------------------------------------------------------------------------------
1 | go 1.21
2 |
3 | toolchain go1.21
4 |
5 | use (
6 | ./tutorials/base
7 | )
8 |
--------------------------------------------------------------------------------
/scripts/go-lint-all.bash:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e -o pipefail
4 |
5 | REPO_ROOT="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd )"
6 | export REPO_ROOT
7 |
8 | LINT_TAGS="e2e,ledger,test_ledger_mock"
9 | if [[ ! -z "${NIX:-}" ]]; then
10 | LINT_TAGS+=",rocksdb"
11 | fi
12 | export LINT_TAGS
13 |
14 | lint_module() {
15 | local root="$1"
16 | shift
17 | if [ -f $root ]; then
18 | cd "$(dirname "$root")"
19 | else
20 | cd "$REPO_ROOT/$root"
21 | fi
22 | echo "linting $(grep "^module" go.mod) [$(date -Iseconds -u)]"
23 | golangci-lint run ./... -c "${REPO_ROOT}/.golangci.yml" "$@" --build-tags=${LINT_TAGS}
24 |
25 | # always lint simapp with app_v1 build tag, otherwise it never gets linted
26 | if [[ "$(grep "^module" go.mod)" == "module cosmossdk.io/simapp" ]]; then
27 | golangci-lint run ./... -c "${REPO_ROOT}/.golangci.yml" "$@" --build-tags=app_v1
28 | fi
29 | }
30 | export -f lint_module
31 |
32 | # if LINT_DIFF env is set, only lint the files in the current commit otherwise lint all files
33 | if [[ -z "${LINT_DIFF:-}" ]]; then
34 | find "${REPO_ROOT}" -type f -name go.mod -print0 | xargs -0 -I{} bash -c 'lint_module "$@"' _ {} "$@"
35 | else
36 | if [[ -z $GIT_DIFF ]]; then
37 | GIT_DIFF=$(git diff --name-only) || true
38 | fi
39 |
40 | if [[ -z "$GIT_DIFF" ]]; then
41 | echo "no files to lint"
42 | exit 0
43 | fi
44 |
45 | GIT_DIFF=$(echo $GIT_DIFF | tr -d "'" | tr ' ' '\n' | grep '\.go$' | grep -v '\.pb\.go$' | grep -Eo '^[^/]+\/[^/]+' | uniq)
46 |
47 | lint_sdk=false
48 | for dir in ${GIT_DIFF[@]}; do
49 | if [[ ! -f "$REPO_ROOT/$dir/go.mod" ]]; then
50 | lint_sdk=true
51 | else
52 | lint_module $dir "$@"
53 | fi
54 | done
55 |
56 | if [[ $lint_sdk ]]; then
57 | cd "$REPO_ROOT"
58 | echo "linting github.com/cosmos/cosmos-sdk [$(date -Iseconds -u)]"
59 | golangci-lint run ./... -c "${REPO_ROOT}/.golangci.yml" "$@" --build-tags=${LINT_TAGS}
60 | fi
61 | fi
62 |
--------------------------------------------------------------------------------
/tutorials/base/Makefile:
--------------------------------------------------------------------------------
1 | BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
2 | COMMIT := $(shell git log -1 --format='%H')
3 |
4 | # don't override user values
5 | ifeq (,$(VERSION))
6 | VERSION := $(shell git describe --exact-match 2>/dev/null)
7 | # if VERSION is empty, then populate it with branch's name and raw commit hash
8 | ifeq (,$(VERSION))
9 | VERSION := $(BRANCH)-$(COMMIT)
10 | endif
11 | endif
12 |
13 | # Update the ldflags with the app, client & server names
14 | ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=example \
15 | -X github.com/cosmos/cosmos-sdk/version.AppName=exampled \
16 | -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \
17 | -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT)
18 |
19 | BUILD_FLAGS := -ldflags '$(ldflags)'
20 |
21 | ###########
22 | # Install #
23 | ###########
24 |
25 | all: install
26 |
27 | install:
28 | @echo "--> ensure dependencies have not been modified"
29 | @go mod verify
30 | @echo "--> installing exampled"
31 | @go install $(BUILD_FLAGS) -mod=readonly ./cmd/exampled
32 |
33 | init:
34 | ./scripts/init.sh
--------------------------------------------------------------------------------
/tutorials/base/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | _ "embed"
5 | "io"
6 | "os"
7 | "path/filepath"
8 |
9 | _ "cosmossdk.io/api/cosmos/tx/config/v1" // import for side-effects
10 | "cosmossdk.io/core/appconfig"
11 | "cosmossdk.io/depinject"
12 | "cosmossdk.io/log"
13 | storetypes "cosmossdk.io/store/types"
14 | dbm "github.com/cosmos/cosmos-db"
15 | "github.com/cosmos/cosmos-sdk/baseapp"
16 | "github.com/cosmos/cosmos-sdk/client"
17 | "github.com/cosmos/cosmos-sdk/codec"
18 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
19 | "github.com/cosmos/cosmos-sdk/runtime"
20 | "github.com/cosmos/cosmos-sdk/server"
21 | "github.com/cosmos/cosmos-sdk/server/api"
22 | "github.com/cosmos/cosmos-sdk/server/config"
23 | servertypes "github.com/cosmos/cosmos-sdk/server/types"
24 | "github.com/cosmos/cosmos-sdk/types/module"
25 | _ "github.com/cosmos/cosmos-sdk/x/auth" // import for side-effects
26 | authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
27 | _ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import for side-effects
28 | _ "github.com/cosmos/cosmos-sdk/x/bank" // import for side-effects
29 | bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
30 | _ "github.com/cosmos/cosmos-sdk/x/consensus" // import for side-effects
31 | consensuskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper"
32 | _ "github.com/cosmos/cosmos-sdk/x/distribution" // import for side-effects
33 | distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
34 | "github.com/cosmos/cosmos-sdk/x/genutil"
35 | genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
36 | _ "github.com/cosmos/cosmos-sdk/x/mint" // import for side-effects
37 | _ "github.com/cosmos/cosmos-sdk/x/staking" // import for side-effects
38 | stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
39 | )
40 |
41 | // DefaultNodeHome default home directories for the application daemon
42 | var DefaultNodeHome string
43 |
44 | //go:embed app.yaml
45 | var AppConfigYAML []byte
46 |
47 | var (
48 | _ runtime.AppI = (*ExampleApp)(nil)
49 | _ servertypes.Application = (*ExampleApp)(nil)
50 | )
51 |
52 | // ExampleApp extends an ABCI application, but with most of its parameters exported.
53 | // They are exported for convenience in creating helper functions, as object
54 | // capabilities aren't needed for testing.
55 | type ExampleApp struct {
56 | *runtime.App
57 | legacyAmino *codec.LegacyAmino
58 | appCodec codec.Codec
59 | txConfig client.TxConfig
60 | interfaceRegistry codectypes.InterfaceRegistry
61 |
62 | // keepers
63 | AccountKeeper authkeeper.AccountKeeper
64 | BankKeeper bankkeeper.Keeper
65 | StakingKeeper *stakingkeeper.Keeper
66 | DistrKeeper distrkeeper.Keeper
67 | ConsensusParamsKeeper consensuskeeper.Keeper
68 |
69 | // simulation manager
70 | sm *module.SimulationManager
71 | }
72 |
73 | func init() {
74 | userHomeDir, err := os.UserHomeDir()
75 | if err != nil {
76 | panic(err)
77 | }
78 |
79 | DefaultNodeHome = filepath.Join(userHomeDir, ".exampled")
80 | }
81 |
82 | // AppConfig returns the default app config.
83 | func AppConfig() depinject.Config {
84 | return depinject.Configs(
85 | appconfig.LoadYAML(AppConfigYAML),
86 | depinject.Supply(
87 | // supply custom module basics
88 | map[string]module.AppModuleBasic{
89 | genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
90 | },
91 | ),
92 | )
93 | }
94 |
95 | // NewExampleApp returns a reference to an initialized ExampleApp.
96 | func NewExampleApp(
97 | logger log.Logger,
98 | db dbm.DB,
99 | traceStore io.Writer,
100 | loadLatest bool,
101 | appOpts servertypes.AppOptions,
102 | baseAppOptions ...func(*baseapp.BaseApp),
103 | ) (*ExampleApp, error) {
104 | var (
105 | app = &ExampleApp{}
106 | appBuilder *runtime.AppBuilder
107 | )
108 |
109 | if err := depinject.Inject(
110 | depinject.Configs(
111 | AppConfig(),
112 | depinject.Supply(
113 | logger,
114 | appOpts,
115 | ),
116 | ),
117 | &appBuilder,
118 | &app.appCodec,
119 | &app.legacyAmino,
120 | &app.txConfig,
121 | &app.interfaceRegistry,
122 | &app.AccountKeeper,
123 | &app.BankKeeper,
124 | &app.StakingKeeper,
125 | &app.DistrKeeper,
126 | &app.ConsensusParamsKeeper,
127 | ); err != nil {
128 | return nil, err
129 | }
130 |
131 | app.App = appBuilder.Build(db, traceStore, baseAppOptions...)
132 |
133 | // register streaming services
134 | if err := app.RegisterStreamingServices(appOpts, app.kvStoreKeys()); err != nil {
135 | return nil, err
136 | }
137 |
138 | /**** Module Options ****/
139 |
140 | // create the simulation manager and define the order of the modules for detertutorialstic simulations
141 | // NOTE: this is not required apps that don't use the simulator for fuzz testing transactions
142 | app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, make(map[string]module.AppModuleSimulation, 0))
143 | app.sm.RegisterStoreDecoders()
144 |
145 | if err := app.Load(loadLatest); err != nil {
146 | return nil, err
147 | }
148 |
149 | return app, nil
150 | }
151 |
152 | // LegacyAmino returns ExampleApp's amino codec.
153 | func (app *ExampleApp) LegacyAmino() *codec.LegacyAmino {
154 | return app.legacyAmino
155 | }
156 |
157 | // GetKey returns the KVStoreKey for the provided store key.
158 | func (app *ExampleApp) GetKey(storeKey string) *storetypes.KVStoreKey {
159 | sk := app.UnsafeFindStoreKey(storeKey)
160 | kvStoreKey, ok := sk.(*storetypes.KVStoreKey)
161 | if !ok {
162 | return nil
163 | }
164 | return kvStoreKey
165 | }
166 |
167 | func (app *ExampleApp) kvStoreKeys() map[string]*storetypes.KVStoreKey {
168 | keys := make(map[string]*storetypes.KVStoreKey)
169 | for _, k := range app.GetStoreKeys() {
170 | if kv, ok := k.(*storetypes.KVStoreKey); ok {
171 | keys[kv.Name()] = kv
172 | }
173 | }
174 |
175 | return keys
176 | }
177 |
178 | // SimulationManager implements the SimulationApp interface
179 | func (app *ExampleApp) SimulationManager() *module.SimulationManager {
180 | return app.sm
181 | }
182 |
183 | // RegisterAPIRoutes registers all application module routes with the provided
184 | // API server.
185 | func (app *ExampleApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) {
186 | app.App.RegisterAPIRoutes(apiSvr, apiConfig)
187 | // register swagger API in app.go so that other applications can override easily
188 | if err := server.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil {
189 | panic(err)
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/tutorials/base/app/app.yaml:
--------------------------------------------------------------------------------
1 | modules:
2 | - name: runtime
3 | config:
4 | "@type": cosmos.app.runtime.v1alpha1.Module
5 | app_name: ExampleApp
6 | # During begin block slashing happens after distr.BeginBlocker so that
7 | # there is nothing left over in the validator fee pool, so as to keep the CanWithdrawInvariant invariant.
8 | # NOTE: staking module is required if HistoricalEntries param > 0
9 | begin_blockers: [distribution, staking]
10 | end_blockers: [staking]
11 | # NOTE: The genutils module must occur after staking so that pools are properly initialized with tokens from genesis accounts.
12 | # NOTE: The genutils module must also occur after auth so that it can access the params from auth.
13 | init_genesis: [auth, bank, distribution, staking, genutil]
14 | override_store_keys:
15 | - module_name: auth
16 | kv_store_key: acc
17 | - name: auth
18 | config:
19 | "@type": cosmos.auth.module.v1.Module
20 | bech32_prefix: tutorial
21 | module_account_permissions:
22 | - account: fee_collector
23 | - account: distribution
24 | - account: bonded_tokens_pool
25 | permissions: [burner, staking]
26 | - account: not_bonded_tokens_pool
27 | permissions: [burner, staking]
28 | - name: bank
29 | config:
30 | "@type": cosmos.bank.module.v1.Module
31 | blocked_module_accounts_override:
32 | [auth, distribution, bonded_tokens_pool, not_bonded_tokens_pool]
33 | - name: staking
34 | config:
35 | "@type": cosmos.staking.module.v1.Module
36 | - name: distribution
37 | config:
38 | "@type": cosmos.distribution.module.v1.Module
39 | - name: consensus
40 | config:
41 | "@type": cosmos.consensus.module.v1.Module
42 | - name: genutil
43 | config:
44 | "@type": cosmos.genutil.module.v1.Module
45 | - name: tx
46 | config:
47 | "@type": cosmos.tx.config.v1.Config
48 |
--------------------------------------------------------------------------------
/tutorials/base/app/params/config.go:
--------------------------------------------------------------------------------
1 | package params
2 |
3 | import (
4 | "cosmossdk.io/errors"
5 | "cosmossdk.io/math"
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | "github.com/cosmos/cosmos-sdk/types/address"
8 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
9 | )
10 |
11 | const (
12 | CoinUnit = "example"
13 |
14 | DefaultBondDenom = CoinUnit
15 |
16 | // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address.
17 | Bech32PrefixAccAddr = "example"
18 | )
19 |
20 | var (
21 | // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key.
22 | Bech32PrefixAccPub = Bech32PrefixAccAddr + "pub"
23 | // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address.
24 | Bech32PrefixValAddr = Bech32PrefixAccAddr + "valoper"
25 | // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key.
26 | Bech32PrefixValPub = Bech32PrefixAccAddr + "valoperpub"
27 | // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address.
28 | Bech32PrefixConsAddr = Bech32PrefixAccAddr + "valcons"
29 | // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key.
30 | Bech32PrefixConsPub = Bech32PrefixAccAddr + "valconspub"
31 | )
32 |
33 | func init() {
34 | SetAddressPrefixes()
35 | RegisterDenoms()
36 | }
37 |
38 | func RegisterDenoms() {
39 | err := sdk.RegisterDenom(CoinUnit, math.LegacyOneDec())
40 | if err != nil {
41 | panic(err)
42 | }
43 | }
44 |
45 | func SetAddressPrefixes() {
46 | config := sdk.GetConfig()
47 | config.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub)
48 | config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub)
49 | config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub)
50 |
51 | // This is copied from the cosmos sdk v0.43.0-beta1
52 | // source: https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/types/address.go#L141
53 | config.SetAddressVerifier(func(bytes []byte) error {
54 | if len(bytes) == 0 {
55 | return errors.Wrap(sdkerrors.ErrUnknownAddress, "addresses cannot be empty")
56 | }
57 |
58 | if len(bytes) > address.MaxAddrLen {
59 | return errors.Wrapf(sdkerrors.ErrUnknownAddress, "address max length is %d, got %d", address.MaxAddrLen, len(bytes))
60 | }
61 |
62 | if len(bytes) != 20 && len(bytes) != 32 {
63 | return errors.Wrapf(sdkerrors.ErrUnknownAddress, "address length must be 20 or 32 bytes, got %d", len(bytes))
64 | }
65 |
66 | return nil
67 | })
68 | }
69 |
--------------------------------------------------------------------------------
/tutorials/base/app/params/encoding.go:
--------------------------------------------------------------------------------
1 | package params
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/client"
5 | "github.com/cosmos/cosmos-sdk/codec"
6 | "github.com/cosmos/cosmos-sdk/codec/types"
7 | )
8 |
9 | // EncodingConfig specifies the concrete encoding types to use for a given app.
10 | // This is provided for compatibility between protobuf and amino implementations.
11 | type EncodingConfig struct {
12 | InterfaceRegistry types.InterfaceRegistry
13 | Codec codec.Codec
14 | TxConfig client.TxConfig
15 | Amino *codec.LegacyAmino
16 | }
17 |
--------------------------------------------------------------------------------
/tutorials/base/cmd/exampled/cmd/commands.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "errors"
5 | "io"
6 |
7 | "cosmossdk.io/log"
8 | confixcmd "cosmossdk.io/tools/confix/cmd"
9 | dbm "github.com/cosmos/cosmos-db"
10 | "github.com/cosmos/cosmos-sdk/client"
11 | "github.com/cosmos/cosmos-sdk/client/debug"
12 | "github.com/cosmos/cosmos-sdk/client/flags"
13 | "github.com/cosmos/cosmos-sdk/client/keys"
14 | "github.com/cosmos/cosmos-sdk/client/pruning"
15 | "github.com/cosmos/cosmos-sdk/client/rpc"
16 | "github.com/cosmos/cosmos-sdk/client/snapshot"
17 | "github.com/cosmos/cosmos-sdk/server"
18 | servertypes "github.com/cosmos/cosmos-sdk/server/types"
19 | sdk "github.com/cosmos/cosmos-sdk/types"
20 | "github.com/cosmos/cosmos-sdk/types/module"
21 | authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
22 | genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
23 | "github.com/spf13/cobra"
24 | "github.com/spf13/viper"
25 |
26 | "github.com/cosmos/sdk-tutorials/tutorials/base/app"
27 | )
28 |
29 | func initRootCmd(rootCmd *cobra.Command, txConfig client.TxConfig, basicManager module.BasicManager) {
30 | cfg := sdk.GetConfig()
31 | cfg.Seal()
32 |
33 | rootCmd.AddCommand(
34 | genutilcli.InitCmd(basicManager, app.DefaultNodeHome),
35 | debug.Cmd(),
36 | confixcmd.ConfigCommand(),
37 | pruning.Cmd(newApp, app.DefaultNodeHome),
38 | snapshot.Cmd(newApp),
39 | )
40 |
41 | server.AddCommands(rootCmd, app.DefaultNodeHome, newApp, appExport, func(startCmd *cobra.Command) {})
42 |
43 | // add keybase, auxiliary RPC, query, genesis, and tx child commands
44 | rootCmd.AddCommand(
45 | server.StatusCommand(),
46 | genutilcli.Commands(txConfig, basicManager, app.DefaultNodeHome),
47 | queryCommand(),
48 | txCommand(),
49 | keys.Commands(),
50 | )
51 | }
52 |
53 | func queryCommand() *cobra.Command {
54 | cmd := &cobra.Command{
55 | Use: "query",
56 | Aliases: []string{"q"},
57 | Short: "Querying subcommands",
58 | DisableFlagParsing: false,
59 | SuggestionsMinimumDistance: 2,
60 | RunE: client.ValidateCmd,
61 | }
62 |
63 | cmd.AddCommand(
64 | rpc.ValidatorCommand(),
65 | server.QueryBlockCmd(),
66 | authcmd.QueryTxsByEventsCmd(),
67 | server.QueryBlocksCmd(),
68 | authcmd.QueryTxCmd(),
69 | server.QueryBlockResultsCmd(),
70 | )
71 |
72 | return cmd
73 | }
74 |
75 | func txCommand() *cobra.Command {
76 | cmd := &cobra.Command{
77 | Use: "tx",
78 | Short: "Transactions subcommands",
79 | DisableFlagParsing: false,
80 | SuggestionsMinimumDistance: 2,
81 | RunE: client.ValidateCmd,
82 | }
83 |
84 | cmd.AddCommand(
85 | authcmd.GetSignCommand(),
86 | authcmd.GetSignBatchCommand(),
87 | authcmd.GetMultiSignCommand(),
88 | authcmd.GetMultiSignBatchCmd(),
89 | authcmd.GetValidateSignaturesCommand(),
90 | authcmd.GetBroadcastCommand(),
91 | authcmd.GetEncodeCommand(),
92 | authcmd.GetDecodeCommand(),
93 | authcmd.GetSimulateCmd(),
94 | )
95 |
96 | return cmd
97 | }
98 |
99 | // newApp is an appCreator
100 | func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application {
101 | baseappOptions := server.DefaultBaseappOptions(appOpts)
102 | app, err := app.NewExampleApp(logger, db, traceStore, true, appOpts, baseappOptions...)
103 | if err != nil {
104 | panic(err)
105 | }
106 |
107 | return app
108 | }
109 |
110 | // appExport creates a new app (optionally at a given height) and exports state.
111 | func appExport(
112 | logger log.Logger,
113 | db dbm.DB,
114 | traceStore io.Writer,
115 | height int64,
116 | forZeroHeight bool,
117 | jailAllowedAddrs []string,
118 | appOpts servertypes.AppOptions,
119 | modulesToExport []string,
120 | ) (servertypes.ExportedApp, error) {
121 | var (
122 | ExampleApp *app.ExampleApp
123 | err error
124 | )
125 |
126 | // this check is necessary as we use the flag in x/upgrade.
127 | // we can exit more gracefully by checking the flag here.
128 | homePath, ok := appOpts.Get(flags.FlagHome).(string)
129 | if !ok || homePath == "" {
130 | return servertypes.ExportedApp{}, errors.New("application home not set")
131 | }
132 |
133 | viperAppOpts, ok := appOpts.(*viper.Viper)
134 | if !ok {
135 | return servertypes.ExportedApp{}, errors.New("appOpts is not viper.Viper")
136 | }
137 |
138 | // overwrite the FlagInvCheckPeriod
139 | viperAppOpts.Set(server.FlagInvCheckPeriod, 1)
140 | appOpts = viperAppOpts
141 |
142 | if height != -1 {
143 | ExampleApp, err = app.NewExampleApp(logger, db, traceStore, false, appOpts)
144 | if err != nil {
145 | return servertypes.ExportedApp{}, err
146 | }
147 |
148 | if err := ExampleApp.LoadHeight(height); err != nil {
149 | return servertypes.ExportedApp{}, err
150 | }
151 | } else {
152 | ExampleApp, err = app.NewExampleApp(logger, db, traceStore, true, appOpts)
153 | if err != nil {
154 | return servertypes.ExportedApp{}, err
155 | }
156 | }
157 |
158 | return ExampleApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
159 | }
160 |
--------------------------------------------------------------------------------
/tutorials/base/cmd/exampled/cmd/root.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "os"
5 | "time"
6 |
7 | "cosmossdk.io/client/v2/autocli"
8 | clientv2keyring "cosmossdk.io/client/v2/autocli/keyring"
9 | "cosmossdk.io/core/address"
10 | "cosmossdk.io/depinject"
11 | "cosmossdk.io/log"
12 | cmtcfg "github.com/cometbft/cometbft/config"
13 | "github.com/cosmos/cosmos-sdk/client"
14 | "github.com/cosmos/cosmos-sdk/client/config"
15 | "github.com/cosmos/cosmos-sdk/codec"
16 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
17 | "github.com/cosmos/cosmos-sdk/crypto/keyring"
18 | "github.com/cosmos/cosmos-sdk/server"
19 | serverconfig "github.com/cosmos/cosmos-sdk/server/config"
20 | "github.com/cosmos/cosmos-sdk/types/module"
21 | "github.com/cosmos/cosmos-sdk/types/tx/signing"
22 | "github.com/cosmos/cosmos-sdk/x/auth/tx"
23 | txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
24 | "github.com/cosmos/cosmos-sdk/x/auth/types"
25 | "github.com/spf13/cobra"
26 |
27 | "github.com/cosmos/sdk-tutorials/tutorials/base/app"
28 | )
29 |
30 | // NewRootCmd creates a new root command for exampled. It is called once in the
31 | // main function.
32 | func NewRootCmd() *cobra.Command {
33 | var (
34 | // It can be used to set options like the signer, fee granularity, and other transaction-related configurations.
35 | txConfigOpts tx.ConfigOptions
36 | // It can include options for modules, address codecs, and other CLI-related configurations.
37 | autoCliOpts autocli.AppOptions
38 | // This includes things like genesis data, default genesis data, verifying genesis data, and the module's name.
39 | moduleBasicManager module.BasicManager
40 | // This can include things like the client's home directory, the client's input/output, and the client's trust node.
41 | clientCtx client.Context
42 | )
43 |
44 | if err := depinject.Inject(
45 | depinject.Configs(app.AppConfig(),
46 | depinject.Supply(
47 | log.NewNopLogger(),
48 | ),
49 | depinject.Provide(
50 | ProvideClientContext,
51 | ProvideKeyring,
52 | ),
53 | ),
54 | &txConfigOpts,
55 | &autoCliOpts,
56 | &moduleBasicManager,
57 | &clientCtx,
58 | ); err != nil {
59 | panic(err)
60 | }
61 |
62 | rootCmd := &cobra.Command{
63 | Use: "exampled",
64 | Short: "exampled - the example chain app",
65 | PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
66 | // set the default command outputs
67 | cmd.SetOut(cmd.OutOrStdout())
68 | cmd.SetErr(cmd.ErrOrStderr())
69 |
70 | clientCtx = clientCtx.WithCmdContext(cmd.Context())
71 | clientCtx, err := client.ReadPersistentCommandFlags(clientCtx, cmd.Flags())
72 | if err != nil {
73 | return err
74 | }
75 |
76 | clientCtx, err = config.ReadFromClientConfig(clientCtx)
77 | if err != nil {
78 | return err
79 | }
80 |
81 | // sign mode textual is only available in online mode
82 | if !clientCtx.Offline {
83 | // This needs to go after ReadFromClientConfig, as that function ets the RPC client needed for SIGN_MODE_TEXTUAL.
84 | txConfigOpts.EnabledSignModes = append(txConfigOpts.EnabledSignModes, signing.SignMode_SIGN_MODE_TEXTUAL)
85 | txConfigOpts.TextualCoinMetadataQueryFn = txmodule.NewGRPCCoinMetadataQueryFn(clientCtx)
86 | txConfigWithTextual, err := tx.NewTxConfigWithOptions(codec.NewProtoCodec(clientCtx.InterfaceRegistry), txConfigOpts)
87 | if err != nil {
88 | return err
89 | }
90 |
91 | clientCtx = clientCtx.WithTxConfig(txConfigWithTextual)
92 | }
93 |
94 | if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil {
95 | return err
96 | }
97 |
98 | if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil {
99 | return err
100 | }
101 |
102 | // overwrite the minimum gas price from the app configuration
103 | srvCfg := serverconfig.DefaultConfig()
104 | srvCfg.MinGasPrices = "0example"
105 |
106 | // overwrite the block timeout
107 | cmtCfg := cmtcfg.DefaultConfig()
108 | cmtCfg.Consensus.TimeoutCommit = 3 * time.Second
109 | cmtCfg.LogLevel = "*:error,p2p:info,state:info" // better default logging
110 |
111 | return server.InterceptConfigsPreRunHandler(cmd, serverconfig.DefaultConfigTemplate, srvCfg, cmtCfg)
112 | },
113 | }
114 |
115 | initRootCmd(rootCmd, clientCtx.TxConfig, moduleBasicManager)
116 |
117 | if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
118 | panic(err)
119 | }
120 |
121 | return rootCmd
122 | }
123 |
124 | func ProvideClientContext(
125 | appCodec codec.Codec,
126 | interfaceRegistry codectypes.InterfaceRegistry,
127 | txConfig client.TxConfig,
128 | legacyAmino *codec.LegacyAmino,
129 | ) client.Context {
130 | clientCtx := client.Context{}.
131 | WithCodec(appCodec).
132 | WithInterfaceRegistry(interfaceRegistry).
133 | WithTxConfig(txConfig).
134 | WithLegacyAmino(legacyAmino).
135 | WithInput(os.Stdin).
136 | WithAccountRetriever(types.AccountRetriever{}).
137 | WithHomeDir(app.DefaultNodeHome).
138 | WithViper("TUTORIAL") // env variable prefix
139 |
140 | // Read the config again to overwrite the default values with the values from the config file
141 | clientCtx, _ = config.ReadFromClientConfig(clientCtx)
142 |
143 | return clientCtx
144 | }
145 |
146 | func ProvideKeyring(clientCtx client.Context, addressCodec address.Codec) (clientv2keyring.Keyring, error) {
147 | kb, err := client.NewKeyringFromBackend(clientCtx, clientCtx.Keyring.Backend())
148 | if err != nil {
149 | return nil, err
150 | }
151 |
152 | return keyring.NewAutoCLIKeyring(kb)
153 | }
154 |
--------------------------------------------------------------------------------
/tutorials/base/cmd/exampled/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
8 |
9 | "github.com/cosmos/sdk-tutorials/tutorials/base/app"
10 | "github.com/cosmos/sdk-tutorials/tutorials/base/app/params"
11 | "github.com/cosmos/sdk-tutorials/tutorials/base/cmd/exampled/cmd"
12 | )
13 |
14 | func main() {
15 | params.SetAddressPrefixes()
16 |
17 | rootCmd := cmd.NewRootCmd()
18 | if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil {
19 | fmt.Fprintln(rootCmd.OutOrStderr(), err)
20 | os.Exit(1)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tutorials/base/scripts/init.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | rm -r ~/.exampled || true
4 | exampled_BIN=$(which exampled)
5 | # configure exampled
6 | $exampled_BIN config set client chain-id demo
7 | $exampled_BIN config set client keyring-backend test
8 | $exampled_BIN keys add alice
9 | $exampled_BIN keys add bob
10 | $exampled_BIN init test --chain-id demo --default-denom example
11 | # update genesis
12 | $exampled_BIN genesis add-genesis-account alice 10000000example --keyring-backend test
13 | $exampled_BIN genesis add-genesis-account bob 1000example --keyring-backend test
14 | # create default validator
15 | $exampled_BIN genesis gentx alice 1000000example --chain-id demo
16 | $exampled_BIN genesis collect-gentxs
17 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/Makefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 |
3 | VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//')
4 | COMMIT := $(shell git log -1 --format='%H')
5 | DOCKER := $(shell which docker)
6 | BUILDDIR ?= $(CURDIR)/build
7 | LEDGER_ENABLED ?= true
8 |
9 | # ********** Golang configs **********
10 |
11 | export GO111MODULE = on
12 |
13 | GO_MAJOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
14 | GO_MINOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
15 |
16 | # ********** process build tags **********
17 |
18 | build_tags = netgo
19 | ifeq ($(LEDGER_ENABLED),true)
20 | ifeq ($(OS),Windows_NT)
21 | GCCEXE = $(shell where gcc.exe 2> NUL)
22 | ifeq ($(GCCEXE),)
23 | $(error gcc.exe not installed for ledger support, please install or set LEDGER_ENABLED=false)
24 | else
25 | build_tags += ledger
26 | endif
27 | else
28 | UNAME_S = $(shell uname -s)
29 | ifeq ($(UNAME_S),OpenBSD)
30 | $(warning OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988))
31 | else
32 | GCC = $(shell command -v gcc 2> /dev/null)
33 | ifeq ($(GCC),)
34 | $(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false)
35 | else
36 | build_tags += ledger
37 | endif
38 | endif
39 | endif
40 | endif
41 |
42 | ifeq (cleveldb,$(findstring cleveldb,$(TUTORA_BUILD_OPTIONS)))
43 | build_tags += gcc cleveldb
44 | else ifeq (rocksdb,$(findstring rocksdb,$(TUTORA_BUILD_OPTIONS)))
45 | build_tags += gcc rocksdb
46 | endif
47 | build_tags += $(BUILD_TAGS)
48 | build_tags := $(strip $(build_tags))
49 |
50 | whitespace :=
51 | whitespace := $(whitespace) $(whitespace)
52 | comma := ,
53 | build_tags_comma_sep := $(subst $(whitespace),$(comma),$(build_tags))
54 |
55 | # ********** process linker flags **********
56 |
57 | ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=example \
58 | -X github.com/cosmos/cosmos-sdk/version.AppName=exampled \
59 | -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \
60 | -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \
61 | -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)"
62 |
63 | ifeq (cleveldb,$(findstring cleveldb,$(TUTORA_BUILD_OPTIONS)))
64 | ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=cleveldb
65 | else ifeq (rocksdb,$(findstring rocksdb,$(TUTORA_BUILD_OPTIONS)))
66 | ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=rocksdb
67 | endif
68 | ifeq (,$(findstring nostrip,$(TUTORA_BUILD_OPTIONS)))
69 | ldflags += -w -s
70 | endif
71 | ifeq ($(LINK_STATICALLY),true)
72 | ldflags += -linkmode=external -extldflags "-Wl,-z,muldefs -static"
73 | endif
74 | ldflags += $(LDFLAGS)
75 | ldflags := $(strip $(ldflags))
76 |
77 | BUILD_FLAGS := -tags '$(build_tags)' -ldflags '$(ldflags)'
78 | # check for nostrip option
79 | ifeq (,$(findstring nostrip,$(TUTORA_BUILD_OPTIONS)))
80 | BUILD_FLAGS += -trimpath
81 | endif
82 |
83 | all: lint test install
84 |
85 |
86 | ###############################################################################
87 | ### Build ###
88 | ###############################################################################
89 |
90 | install: enforce-go-version
91 | @echo "Installing exampled..."
92 | go install -mod=readonly $(BUILD_FLAGS) ./cmd/exampled
93 |
94 | build: enforce-go-version
95 | @echo "Building exampled..."
96 | go build $(BUILD_FLAGS) -o $(BUILDDIR)/ ./cmd/exampled
97 |
98 | enforce-go-version:
99 | @echo "Go version: $(GO_MAJOR_VERSION).$(GO_MINOR_VERSION)"
100 | ifneq ($(GO_MINOR_VERSION),21)
101 | @echo "Go version 1.20 is required"
102 | @exit 1
103 | endif
104 |
105 | clean:
106 | rm -rf $(CURDIR)/artifacts/
107 |
108 | distclean: clean
109 | rm -rf vendor/
110 |
111 |
112 | ###############################################################################
113 | ### Linting ###
114 | ###############################################################################
115 | golangci_lint_cmd=golangci-lint
116 | golangci_version=v1.52.2
117 |
118 | lint:
119 | @echo "--> Running linter"
120 | @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
121 | @$(golangci_lint_cmd) run --timeout=10m
122 |
123 | lint-fix:
124 | @echo "--> Running linter"
125 | @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
126 | @$(golangci_lint_cmd) run --fix --out-format=tab --issues-exit-code=0
127 |
128 | format:
129 | @go install mvdan.cc/gofumpt@latest
130 | @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
131 | find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/docs/statik/statik.go" -not -path "./tests/mocks/*" -not -name "*.pb.go" -not -name "*.pb.gw.go" -not -name "*.pulsar.go" -not -path "./crypto/keys/secp256k1/*" | xargs gofumpt -w -l
132 | $(golangci_lint_cmd) run --fix
133 | .PHONY: format
134 |
135 | ###############################################################################
136 | ### Localnet ###
137 | ###############################################################################
138 |
139 | start-localnet: build
140 | rm -rf ~/.exampled
141 | ./build/exampled init liveness --chain-id cosmos-1 --default-denom uatom
142 | ./build/exampled config set client chain-id cosmos-1
143 | ./build/exampled config set client keyring-backend test
144 | ./build/exampled keys add val1
145 | ./build/exampled keys add alice
146 | ./build/exampled keys add bob
147 | ./build/exampled genesis add-genesis-account val1 10000000000000000000000000uatom
148 | ./build/exampled genesis add-genesis-account alice 1000000000000000000uatom
149 | ./build/exampled genesis add-genesis-account bob 1000000000000000000uatom
150 | ./build/exampled genesis gentx val1 1000000000uatom --chain-id cosmos-1
151 | ./build/exampled genesis collect-gentxs
152 | sed -i.bak'' 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025uatom"/' ~/.exampled/config/app.toml
153 | ./build/exampled start --val-key val1
154 | ###############################################################################
155 | ### Tests & Simulation ###
156 | ###############################################################################
157 | PACKAGES_UNIT=$(shell go list ./... | grep -v -e '/tests/e2e')
158 | PACKAGES_E2E=$(shell cd tests/e2e && go list ./... | grep '/e2e')
159 | TEST_PACKAGES=./...
160 | TEST_TARGETS := test-unit test-e2e
161 |
162 | test-unit: ARGS=-timeout=5m -tags='norace'
163 | test-unit: TEST_PACKAGES=$(PACKAGES_UNIT)
164 | test-e2e: ARGS=-timeout=25m -v
165 | test-e2e: TEST_PACKAGES=$(PACKAGES_E2E)
166 | $(TEST_TARGETS): run-tests
167 |
168 | run-tests:
169 | ifneq (,$(shell which tparse 2>/dev/null))
170 | @echo "--> Running tests"
171 | @go test -mod=readonly -json $(ARGS) $(TEST_PACKAGES) | tparse
172 | else
173 | @echo "--> Running tests"
174 | @go test -mod=readonly $(ARGS) $(TEST_PACKAGES)
175 | endif
--------------------------------------------------------------------------------
/tutorials/nameservice/base/app/app.yaml:
--------------------------------------------------------------------------------
1 | modules:
2 | - name: runtime
3 | config:
4 | "@type": cosmos.app.runtime.v1alpha1.Module
5 | app_name: ExampleApp
6 | # During begin block slashing happens after distr.BeginBlocker so that
7 | # there is nothing left over in the validator fee pool, so as to keep the CanWithdrawInvariant invariant.
8 | # NOTE: staking module is required if HistoricalEntries param > 0
9 | begin_blockers: [distribution, staking]
10 | end_blockers: [staking]
11 | # NOTE: The genutils module must occur after staking so that pools are properly initialized with tokens from genesis accounts.
12 | # NOTE: The genutils module must also occur after auth so that it can access the params from auth.
13 | init_genesis: [auth, bank, distribution, staking, genutil]
14 | override_store_keys:
15 | - module_name: auth
16 | kv_store_key: acc
17 | - name: auth
18 | config:
19 | "@type": cosmos.auth.module.v1.Module
20 | bech32_prefix: tutorial
21 | module_account_permissions:
22 | - account: fee_collector
23 | - account: distribution
24 | - account: bonded_tokens_pool
25 | permissions: [burner, staking]
26 | - account: not_bonded_tokens_pool
27 | permissions: [burner, staking]
28 | - name: bank
29 | config:
30 | "@type": cosmos.bank.module.v1.Module
31 | blocked_module_accounts_override:
32 | [auth, distribution, bonded_tokens_pool, not_bonded_tokens_pool]
33 | - name: staking
34 | config:
35 | "@type": cosmos.staking.module.v1.Module
36 | - name: distribution
37 | config:
38 | "@type": cosmos.distribution.module.v1.Module
39 | - name: consensus
40 | config:
41 | "@type": cosmos.consensus.module.v1.Module
42 | - name: genutil
43 | config:
44 | "@type": cosmos.genutil.module.v1.Module
45 | - name: tx
46 | config:
47 | "@type": cosmos.tx.config.v1.Config
48 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/app/genesis.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | )
6 |
7 | // The genesis state of the blockchain is represented here as a map of raw json
8 | // messages key'd by a identifier string.
9 | // The identifier is used to determine which module genesis information belongs
10 | // to so it may be appropriately routed during init chain.
11 | // Within this application default genesis information is retrieved from
12 | // the ModuleBasicManager which populates json from each BasicModule
13 | // object provided to it during init.
14 | type GenesisState map[string]json.RawMessage
15 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/app/params/config.go:
--------------------------------------------------------------------------------
1 | package params
2 |
3 | import (
4 | "cosmossdk.io/errors"
5 | "cosmossdk.io/math"
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | "github.com/cosmos/cosmos-sdk/types/address"
8 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
9 | )
10 |
11 | const (
12 | CoinUnit = "uatom"
13 |
14 | DefaultBondDenom = CoinUnit
15 |
16 | // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address.
17 | Bech32PrefixAccAddr = "cosmos"
18 | )
19 |
20 | var (
21 | // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key.
22 | Bech32PrefixAccPub = Bech32PrefixAccAddr + "pub"
23 | // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address.
24 | Bech32PrefixValAddr = Bech32PrefixAccAddr + "valoper"
25 | // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key.
26 | Bech32PrefixValPub = Bech32PrefixAccAddr + "valoperpub"
27 | // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address.
28 | Bech32PrefixConsAddr = Bech32PrefixAccAddr + "valcons"
29 | // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key.
30 | Bech32PrefixConsPub = Bech32PrefixAccAddr + "valconspub"
31 | )
32 |
33 | func init() {
34 | SetAddressPrefixes()
35 | RegisterDenoms()
36 | }
37 |
38 | func RegisterDenoms() {
39 | err := sdk.RegisterDenom(CoinUnit, math.LegacyOneDec())
40 | if err != nil {
41 | panic(err)
42 | }
43 | }
44 |
45 | func SetAddressPrefixes() {
46 | config := sdk.GetConfig()
47 | config.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub)
48 | config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub)
49 | config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub)
50 |
51 | // This is copied from the cosmos sdk v0.43.0-beta1
52 | // source: https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/types/address.go#L141
53 | config.SetAddressVerifier(func(bytes []byte) error {
54 | if len(bytes) == 0 {
55 | return errors.Wrap(sdkerrors.ErrUnknownAddress, "addresses cannot be empty")
56 | }
57 |
58 | if len(bytes) > address.MaxAddrLen {
59 | return errors.Wrapf(sdkerrors.ErrUnknownAddress, "address max length is %d, got %d", address.MaxAddrLen, len(bytes))
60 | }
61 |
62 | if len(bytes) != 20 && len(bytes) != 32 {
63 | return errors.Wrapf(sdkerrors.ErrUnknownAddress, "address length must be 20 or 32 bytes, got %d", len(bytes))
64 | }
65 |
66 | return nil
67 | })
68 | }
69 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/app/params/encoding.go:
--------------------------------------------------------------------------------
1 | package params
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/client"
5 | "github.com/cosmos/cosmos-sdk/codec"
6 | "github.com/cosmos/cosmos-sdk/codec/types"
7 | )
8 |
9 | // EncodingConfig specifies the concrete encoding types to use for a given app.
10 | // This is provided for compatibility between protobuf and amino implementations.
11 | type EncodingConfig struct {
12 | InterfaceRegistry types.InterfaceRegistry
13 | Codec codec.Codec
14 | TxConfig client.TxConfig
15 | Amino *codec.LegacyAmino
16 | }
17 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/cmd/exampled/cmd/cmd.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "os"
5 |
6 | "cosmossdk.io/log"
7 | dbm "github.com/cosmos/cosmos-db"
8 | "github.com/cosmos/cosmos-sdk/client"
9 | "github.com/cosmos/cosmos-sdk/client/config"
10 | "github.com/cosmos/cosmos-sdk/codec"
11 | "github.com/cosmos/cosmos-sdk/server"
12 | simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
13 | "github.com/cosmos/cosmos-sdk/types/tx/signing"
14 | "github.com/cosmos/cosmos-sdk/x/auth/tx"
15 | txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
16 | "github.com/cosmos/cosmos-sdk/x/auth/types"
17 | "github.com/spf13/cobra"
18 |
19 | "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/app"
20 | "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/testutils"
21 | )
22 |
23 | func NewRootCmd() (*cobra.Command, testutils.EncodingConfig) {
24 | tmpApp := app.NewExampleApp(
25 | log.NewNopLogger(),
26 | dbm.NewMemDB(),
27 | nil,
28 | true,
29 | map[int64]bool{},
30 | "val",
31 | simtestutil.NewAppOptionsWithFlagHome(tempDir()),
32 | )
33 |
34 | encodingConfig := testutils.EncodingConfig{
35 | InterfaceRegistry: tmpApp.InterfaceRegistry(),
36 | Marshaler: tmpApp.AppCodec(),
37 | TxConfig: tmpApp.GetTxConfig(),
38 | Amino: tmpApp.LegacyAmino(),
39 | }
40 |
41 | initClientCtx := client.Context{}.
42 | WithCodec(encodingConfig.Marshaler).
43 | WithInterfaceRegistry(encodingConfig.InterfaceRegistry).
44 | WithTxConfig(encodingConfig.TxConfig).
45 | WithLegacyAmino(encodingConfig.Amino).
46 | WithInput(os.Stdin).
47 | WithAccountRetriever(types.AccountRetriever{}).
48 | WithHomeDir(app.DefaultNodeHome).
49 | WithViper("")
50 |
51 | rootCmd := &cobra.Command{
52 | Use: app.AppName + "d",
53 | Short: "Cosmos App",
54 | PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
55 | cmd.SetOut(cmd.OutOrStdout())
56 | cmd.SetErr(cmd.ErrOrStderr())
57 |
58 | initClientCtx = initClientCtx.WithCmdContext(cmd.Context())
59 | initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags())
60 | if err != nil {
61 | return err
62 | }
63 |
64 | initClientCtx, err = config.ReadFromClientConfig(initClientCtx)
65 | if err != nil {
66 | return err
67 | }
68 |
69 | // This needs to go after ReadFromClientConfig, as that function
70 | // sets the RPC client needed for SIGN_MODE_TEXTUAL.
71 | enabledSignModes := append(tx.DefaultSignModes, signing.SignMode_SIGN_MODE_TEXTUAL)
72 | txConfigOpts := tx.ConfigOptions{
73 | EnabledSignModes: enabledSignModes,
74 | TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx),
75 | }
76 | txConfigWithTextual, err := tx.NewTxConfigWithOptions(
77 | codec.NewProtoCodec(encodingConfig.InterfaceRegistry),
78 | txConfigOpts,
79 | )
80 | if err != nil {
81 | return err
82 | }
83 | initClientCtx = initClientCtx.WithTxConfig(txConfigWithTextual)
84 |
85 | if err = client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil {
86 | return err
87 | }
88 |
89 | customAppTemplate, customAppConfig := initAppConfig()
90 | customTMConfig := initTendermintConfig()
91 |
92 | return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customTMConfig)
93 | },
94 | }
95 |
96 | initRootCmd(rootCmd, encodingConfig, tmpApp.BasicManager, app.DefaultNodeHome)
97 |
98 | // // // add keyring to autocli opts
99 | // autoCliOpts := tmpApp.AutoCliOpts()
100 | // initClientCtx, _ = config.ReadFromClientConfig(initClientCtx)
101 | // // autoCliOpts.Keyring = initClientCtx.Keyring
102 |
103 | // if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
104 | // panic(err)
105 | // }
106 |
107 | return rootCmd, encodingConfig
108 | }
109 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/cmd/exampled/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
8 |
9 | "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/app"
10 | "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/cmd/exampled/cmd"
11 | )
12 |
13 | func main() {
14 | rootCmd, _ := cmd.NewRootCmd()
15 | if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil {
16 | fmt.Fprintln(rootCmd.OutOrStderr(), err)
17 | os.Exit(1)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/scripts/init.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | rm -r ~/.exampled || true
4 | exampled_BIN=$(which exampled)
5 | # configure exampled
6 | $exampled_BIN config set client chain-id demo
7 | $exampled_BIN config set client keyring-backend test
8 | $exampled_BIN keys add alice
9 | $exampled_BIN keys add bob
10 | $exampled_BIN init test --chain-id demo --default-denom example
11 | # update genesis
12 | $exampled_BIN genesis add-genesis-account alice 10000000example --keyring-backend test
13 | $exampled_BIN genesis add-genesis-account bob 1000example --keyring-backend test
14 | # create default validator
15 | $exampled_BIN genesis gentx alice 1000000example --chain-id demo
16 | $exampled_BIN genesis collect-gentxs
17 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/Makefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 |
3 | DOCKER := $(shell which docker)
4 |
5 | ##################
6 | ### Build ####
7 | ##################
8 |
9 | test:
10 | @echo "--> Running tests"
11 | go test -v ./...
12 |
13 | test-integration:
14 | @echo "--> Running integration tests"
15 | cd integration; go test -v ./...
16 |
17 | .PHONY: test test-integration
18 |
19 | ###################
20 | ### Protobuf ####
21 | ###################
22 |
23 | protoVer=0.13.2
24 | protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer)
25 | protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName)
26 |
27 | proto-all: proto-format proto-lint proto-gen
28 |
29 | proto-gen:
30 | @echo "Generating protobuf files..."
31 | @$(protoImage) sh ./scripts/protocgen.sh
32 | @go mod tidy
33 |
34 | proto-format:
35 | @$(protoImage) find ./ -name "*.proto" -exec clang-format -i {} \;
36 |
37 | proto-lint:
38 | @$(protoImage) buf lint
39 |
40 | .PHONY: proto-all proto-gen proto-format proto-lintll proto-gen proto-gen-any proto-swagger-gen proto-format proto-lint proto-check-breaking proto-update-deps mocks
41 |
42 | ##################
43 | ### Linting ####
44 | ##################
45 |
46 | golangci_lint_cmd=golangci-lint
47 | golangci_version=v1.51.2
48 |
49 | lint:
50 | @echo "--> Running linter"
51 | @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
52 | @$(golangci_lint_cmd) run ./... --timeout 15m
53 |
54 | lint-fix:
55 | @echo "--> Running linter and fixing issues"
56 | @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
57 | @$(golangci_lint_cmd) run ./... --fix --timeout 15m
58 |
59 | .PHONY: lint lint-fix
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/abci/types.go:
--------------------------------------------------------------------------------
1 | package abci
2 |
3 | import (
4 | "cosmossdk.io/log"
5 | "github.com/cosmos/cosmos-sdk/client"
6 | "github.com/cosmos/cosmos-sdk/codec"
7 |
8 | "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/mempool"
9 | "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/provider"
10 | )
11 |
12 | type PrepareProposalHandler struct {
13 | logger log.Logger
14 | txConfig client.TxConfig
15 | cdc codec.Codec
16 | mempool *mempool.ThresholdMempool
17 | txProvider provider.TxProvider
18 | runProvider bool
19 | }
20 |
21 | type ProcessProposalHandler struct {
22 | TxConfig client.TxConfig
23 | Codec codec.Codec
24 | Logger log.Logger
25 | }
26 |
27 | type VoteExtHandler struct {
28 | logger log.Logger
29 | mempool *mempool.ThresholdMempool
30 | cdc codec.Codec
31 | }
32 |
33 | type InjectedVoteExt struct {
34 | VoteExtSigner []byte
35 | Bids [][]byte
36 | }
37 |
38 | type InjectedVotes struct {
39 | Votes []InjectedVoteExt
40 | }
41 |
42 | type AppVoteExtension struct {
43 | Height int64
44 | Bids [][]byte
45 | }
46 |
47 | type SpecialTransaction struct {
48 | Height int
49 | Bids [][]byte
50 | }
51 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/abci/vote_ext.go:
--------------------------------------------------------------------------------
1 | package abci
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 |
8 | "cosmossdk.io/log"
9 | abci "github.com/cometbft/cometbft/abci/types"
10 | "github.com/cosmos/cosmos-sdk/codec"
11 | sdk "github.com/cosmos/cosmos-sdk/types"
12 |
13 | auctiontypes "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
14 | "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/mempool"
15 | )
16 |
17 | func NewVoteExtensionHandler(lg log.Logger, mp *mempool.ThresholdMempool, cdc codec.Codec) *VoteExtHandler {
18 | return &VoteExtHandler{
19 | logger: lg,
20 | mempool: mp,
21 | cdc: cdc,
22 | }
23 | }
24 |
25 | func (h *VoteExtHandler) ExtendVoteHandler() sdk.ExtendVoteHandler {
26 | return func(ctx sdk.Context, req *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) {
27 | h.logger.Info(fmt.Sprintf("Extending votes at block height : %v", req.Height))
28 |
29 | voteExtBids := [][]byte{}
30 |
31 | // Get mempool txs
32 | itr := h.mempool.SelectPending(context.Background(), nil)
33 |
34 | for itr != nil {
35 | tmptx := itr.Tx()
36 | sdkMsgs := tmptx.GetMsgs()
37 |
38 | // Iterate through msgs, check for any bids
39 | for _, msg := range sdkMsgs {
40 | switch msg := msg.(type) {
41 | case *auctiontypes.MsgBid:
42 | // Marshal sdk bids to []byte
43 | bz, err := h.cdc.Marshal(msg)
44 | if err != nil {
45 | h.logger.Error(fmt.Sprintf("Error marshaling VE Bid : %v", err))
46 | break
47 | }
48 | voteExtBids = append(voteExtBids, bz)
49 | default:
50 | }
51 | }
52 |
53 | // Move tx to ready pool
54 | err := h.mempool.Update(context.Background(), tmptx)
55 | //// Remove tx from app side mempool
56 | // err = h.mempool.Remove(tmptx)
57 | if err != nil {
58 | h.logger.Info(fmt.Sprintf("Unable to update mempool tx: %v", err))
59 | }
60 |
61 | itr = itr.Next()
62 | }
63 |
64 | // Create vote extension
65 | voteExt := AppVoteExtension{
66 | Height: req.Height,
67 | Bids: voteExtBids,
68 | }
69 |
70 | // Encode Vote Extension
71 | bz, err := json.Marshal(voteExt)
72 | if err != nil {
73 | return nil, fmt.Errorf("error marshaling VE: %w", err)
74 | }
75 |
76 | return &abci.ResponseExtendVote{VoteExtension: bz}, nil
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/api/v1/query_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 | // versions:
3 | // - protoc-gen-go-grpc v1.3.0
4 | // - protoc (unknown)
5 | // source: cosmos/auction/v1/query.proto
6 |
7 | package auctionv1
8 |
9 | import (
10 | context "context"
11 | grpc "google.golang.org/grpc"
12 | codes "google.golang.org/grpc/codes"
13 | status "google.golang.org/grpc/status"
14 | )
15 |
16 | // This is a compile-time assertion to ensure that this generated file
17 | // is compatible with the grpc package it is being compiled against.
18 | // Requires gRPC-Go v1.32.0 or later.
19 | const _ = grpc.SupportPackageIsVersion7
20 |
21 | const (
22 | Query_Name_FullMethodName = "/cosmos.auction.v1.Query/Name"
23 | )
24 |
25 | // QueryClient is the client API for Query service.
26 | //
27 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
28 | type QueryClient interface {
29 | // Name is a method that takes a QueryNameRequest and returns a QueryNameResponse.
30 | Name(ctx context.Context, in *QueryNameRequest, opts ...grpc.CallOption) (*QueryNameResponse, error)
31 | }
32 |
33 | type queryClient struct {
34 | cc grpc.ClientConnInterface
35 | }
36 |
37 | func NewQueryClient(cc grpc.ClientConnInterface) QueryClient {
38 | return &queryClient{cc}
39 | }
40 |
41 | func (c *queryClient) Name(ctx context.Context, in *QueryNameRequest, opts ...grpc.CallOption) (*QueryNameResponse, error) {
42 | out := new(QueryNameResponse)
43 | err := c.cc.Invoke(ctx, Query_Name_FullMethodName, in, out, opts...)
44 | if err != nil {
45 | return nil, err
46 | }
47 | return out, nil
48 | }
49 |
50 | // QueryServer is the server API for Query service.
51 | // All implementations must embed UnimplementedQueryServer
52 | // for forward compatibility
53 | type QueryServer interface {
54 | // Name is a method that takes a QueryNameRequest and returns a QueryNameResponse.
55 | Name(context.Context, *QueryNameRequest) (*QueryNameResponse, error)
56 | mustEmbedUnimplementedQueryServer()
57 | }
58 |
59 | // UnimplementedQueryServer must be embedded to have forward compatible implementations.
60 | type UnimplementedQueryServer struct {
61 | }
62 |
63 | func (UnimplementedQueryServer) Name(context.Context, *QueryNameRequest) (*QueryNameResponse, error) {
64 | return nil, status.Errorf(codes.Unimplemented, "method Name not implemented")
65 | }
66 | func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {}
67 |
68 | // UnsafeQueryServer may be embedded to opt out of forward compatibility for this service.
69 | // Use of this interface is not recommended, as added methods to QueryServer will
70 | // result in compilation errors.
71 | type UnsafeQueryServer interface {
72 | mustEmbedUnimplementedQueryServer()
73 | }
74 |
75 | func RegisterQueryServer(s grpc.ServiceRegistrar, srv QueryServer) {
76 | s.RegisterService(&Query_ServiceDesc, srv)
77 | }
78 |
79 | func _Query_Name_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
80 | in := new(QueryNameRequest)
81 | if err := dec(in); err != nil {
82 | return nil, err
83 | }
84 | if interceptor == nil {
85 | return srv.(QueryServer).Name(ctx, in)
86 | }
87 | info := &grpc.UnaryServerInfo{
88 | Server: srv,
89 | FullMethod: Query_Name_FullMethodName,
90 | }
91 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
92 | return srv.(QueryServer).Name(ctx, req.(*QueryNameRequest))
93 | }
94 | return interceptor(ctx, in, info, handler)
95 | }
96 |
97 | // Query_ServiceDesc is the grpc.ServiceDesc for Query service.
98 | // It's only intended for direct use with grpc.RegisterService,
99 | // and not to be introspected or modified (even as a copy)
100 | var Query_ServiceDesc = grpc.ServiceDesc{
101 | ServiceName: "cosmos.auction.v1.Query",
102 | HandlerType: (*QueryServer)(nil),
103 | Methods: []grpc.MethodDesc{
104 | {
105 | MethodName: "Name",
106 | Handler: _Query_Name_Handler,
107 | },
108 | },
109 | Streams: []grpc.StreamDesc{},
110 | Metadata: "cosmos/auction/v1/query.proto",
111 | }
112 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/api/v1/tx_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 | // versions:
3 | // - protoc-gen-go-grpc v1.3.0
4 | // - protoc (unknown)
5 | // source: cosmos/auction/v1/tx.proto
6 |
7 | package auctionv1
8 |
9 | import (
10 | context "context"
11 | grpc "google.golang.org/grpc"
12 | codes "google.golang.org/grpc/codes"
13 | status "google.golang.org/grpc/status"
14 | )
15 |
16 | // This is a compile-time assertion to ensure that this generated file
17 | // is compatible with the grpc package it is being compiled against.
18 | // Requires gRPC-Go v1.32.0 or later.
19 | const _ = grpc.SupportPackageIsVersion7
20 |
21 | const (
22 | Msg_Bid_FullMethodName = "/cosmos.auction.v1.Msg/Bid"
23 | )
24 |
25 | // MsgClient is the client API for Msg service.
26 | //
27 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
28 | type MsgClient interface {
29 | // Reserve defines a method to buy a nameservice with an associated bech32
30 | // address to resolve to.
31 | Bid(ctx context.Context, in *MsgBid, opts ...grpc.CallOption) (*MsgBidResponse, error)
32 | }
33 |
34 | type msgClient struct {
35 | cc grpc.ClientConnInterface
36 | }
37 |
38 | func NewMsgClient(cc grpc.ClientConnInterface) MsgClient {
39 | return &msgClient{cc}
40 | }
41 |
42 | func (c *msgClient) Bid(ctx context.Context, in *MsgBid, opts ...grpc.CallOption) (*MsgBidResponse, error) {
43 | out := new(MsgBidResponse)
44 | err := c.cc.Invoke(ctx, Msg_Bid_FullMethodName, in, out, opts...)
45 | if err != nil {
46 | return nil, err
47 | }
48 | return out, nil
49 | }
50 |
51 | // MsgServer is the server API for Msg service.
52 | // All implementations must embed UnimplementedMsgServer
53 | // for forward compatibility
54 | type MsgServer interface {
55 | // Reserve defines a method to buy a nameservice with an associated bech32
56 | // address to resolve to.
57 | Bid(context.Context, *MsgBid) (*MsgBidResponse, error)
58 | mustEmbedUnimplementedMsgServer()
59 | }
60 |
61 | // UnimplementedMsgServer must be embedded to have forward compatible implementations.
62 | type UnimplementedMsgServer struct {
63 | }
64 |
65 | func (UnimplementedMsgServer) Bid(context.Context, *MsgBid) (*MsgBidResponse, error) {
66 | return nil, status.Errorf(codes.Unimplemented, "method Bid not implemented")
67 | }
68 | func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {}
69 |
70 | // UnsafeMsgServer may be embedded to opt out of forward compatibility for this service.
71 | // Use of this interface is not recommended, as added methods to MsgServer will
72 | // result in compilation errors.
73 | type UnsafeMsgServer interface {
74 | mustEmbedUnimplementedMsgServer()
75 | }
76 |
77 | func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) {
78 | s.RegisterService(&Msg_ServiceDesc, srv)
79 | }
80 |
81 | func _Msg_Bid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
82 | in := new(MsgBid)
83 | if err := dec(in); err != nil {
84 | return nil, err
85 | }
86 | if interceptor == nil {
87 | return srv.(MsgServer).Bid(ctx, in)
88 | }
89 | info := &grpc.UnaryServerInfo{
90 | Server: srv,
91 | FullMethod: Msg_Bid_FullMethodName,
92 | }
93 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
94 | return srv.(MsgServer).Bid(ctx, req.(*MsgBid))
95 | }
96 | return interceptor(ctx, in, info, handler)
97 | }
98 |
99 | // Msg_ServiceDesc is the grpc.ServiceDesc for Msg service.
100 | // It's only intended for direct use with grpc.RegisterService,
101 | // and not to be introspected or modified (even as a copy)
102 | var Msg_ServiceDesc = grpc.ServiceDesc{
103 | ServiceName: "cosmos.auction.v1.Msg",
104 | HandlerType: (*MsgServer)(nil),
105 | Methods: []grpc.MethodDesc{
106 | {
107 | MethodName: "Bid",
108 | Handler: _Msg_Bid_Handler,
109 | },
110 | },
111 | Streams: []grpc.StreamDesc{},
112 | Metadata: "cosmos/auction/v1/tx.proto",
113 | }
114 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/codec.go:
--------------------------------------------------------------------------------
1 | package auction
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/codec"
5 | "github.com/cosmos/cosmos-sdk/codec/legacy"
6 | types "github.com/cosmos/cosmos-sdk/codec/types"
7 | sdk "github.com/cosmos/cosmos-sdk/types"
8 | "github.com/cosmos/cosmos-sdk/types/msgservice"
9 | )
10 |
11 | // RegisterLegacyAminoCodec registers the necessary interfaces and concrete types
12 | // on the provided LegacyAmino codec. These types are used for Amino JSON serialization.
13 | func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
14 | legacy.RegisterAminoMsg(cdc, &MsgBid{}, "namespace/MsgReserve")
15 | }
16 |
17 | // RegisterInterfaces registers the interfaces types with the interface registry.
18 | func RegisterInterfaces(registry types.InterfaceRegistry) {
19 | registry.RegisterImplementations((*sdk.Msg)(nil),
20 | &MsgBid{},
21 | )
22 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
23 | }
24 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/errors.go:
--------------------------------------------------------------------------------
1 | package auction
2 |
3 | import (
4 | "cosmossdk.io/errors"
5 | )
6 |
7 | var (
8 | ErrNameReserved = errors.Register(ModuleName, 1, "name already reserved")
9 | ErrEmptyName = errors.Register(ModuleName, 2, "name cannot be empty")
10 | )
11 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/expected_keepers.go:
--------------------------------------------------------------------------------
1 | package auction
2 |
3 | import (
4 | "context"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | )
8 |
9 | // BankKeeper defines the contract needed to be fulfilled for banking and supply
10 | // dependencies.
11 | type BankKeeper interface {
12 | GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin
13 |
14 | SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins
15 |
16 | SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
17 | }
18 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/flags.go:
--------------------------------------------------------------------------------
1 | package auction
2 |
3 | var (
4 | FlagValKey = "val-key"
5 | FlagRunProvider = "run-provider"
6 | )
7 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/genesis.go:
--------------------------------------------------------------------------------
1 | package auction
2 |
3 | func NewGenesisState() *GenesisState {
4 | return &GenesisState{}
5 | }
6 |
7 | func (gs *GenesisState) Validate() error {
8 | return nil
9 | }
10 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keeper/genesis.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 |
6 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
7 | )
8 |
9 | func (k *Keeper) InitGenesis(ctx context.Context, data *auction.GenesisState) error {
10 | // TODO: Implement
11 | return nil
12 | }
13 |
14 | func (k *Keeper) ExportGenesis(ctx context.Context) (*auction.GenesisState, error) {
15 | // TODO: Implement
16 | return &auction.GenesisState{}, nil
17 | }
18 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keeper/genesis_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | "testing"
5 |
6 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
7 |
8 | )
9 |
10 | func TestInitGenesis(t *testing.T) {
11 | fixture := initFixture(t)
12 |
13 | data := &auction.GenesisState{}
14 | err := fixture.k.InitGenesis(fixture.ctx, data)
15 | if err != nil {
16 | t.Errorf("InitGenesis returned an error: %v", err)
17 | }
18 | }
19 |
20 | func TestExportGenesis(t *testing.T) {
21 | fixture := initFixture(t)
22 |
23 | _, err := fixture.k.ExportGenesis(fixture.ctx)
24 | if err != nil {
25 | t.Errorf("InitGenesis returned an error: %v", err)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keeper/keeper.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "fmt"
5 |
6 | "cosmossdk.io/collections"
7 | "cosmossdk.io/core/address"
8 | storetypes "cosmossdk.io/core/store"
9 | "cosmossdk.io/log"
10 | "github.com/cosmos/cosmos-sdk/codec"
11 | sdk "github.com/cosmos/cosmos-sdk/types"
12 |
13 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
14 | )
15 |
16 | type Keeper struct {
17 | cdc codec.BinaryCodec
18 | addressCodec address.Codec
19 |
20 | authority string
21 |
22 | bk auction.BankKeeper
23 | defaultDenom string
24 |
25 | // state management
26 | Schema collections.Schema
27 | Names collections.Map[string, auction.Name]
28 | }
29 |
30 | func NewKeeper(cdc codec.BinaryCodec, addressCodec address.Codec, storeService storetypes.KVStoreService, authority string, bk auction.BankKeeper, denom string) Keeper {
31 | if _, err := addressCodec.StringToBytes(authority); err != nil {
32 | panic(fmt.Errorf("invalid authority address: %w", err))
33 | }
34 |
35 | sb := collections.NewSchemaBuilder(storeService)
36 | k := Keeper{
37 | cdc: cdc,
38 | addressCodec: addressCodec,
39 | authority: authority,
40 | bk: bk,
41 | defaultDenom: denom,
42 | Names: collections.NewMap(sb, auction.NamesKey, "names", collections.StringKey, codec.CollValue[auction.Name](cdc)),
43 | }
44 |
45 | schema, err := sb.Build()
46 | if err != nil {
47 | panic(err)
48 | }
49 |
50 | k.Schema = schema
51 |
52 | return k
53 | }
54 |
55 | func (k Keeper) GetAuthority() string {
56 | return k.authority
57 | }
58 |
59 | // Logger returns a module-specific logger.
60 | func (keeper Keeper) Logger(ctx sdk.Context) log.Logger {
61 | return ctx.Logger().With("module", "x/"+auction.ModuleName)
62 | }
63 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keeper/migrator.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | type Migrator struct {
4 | keeper Keeper
5 | }
6 |
7 | func NewMigrator(k Keeper) Migrator {
8 | return Migrator{
9 | keeper: k,
10 | }
11 | }
12 |
13 | // Migrate1to2 migrates the module state from version 1 to version 2.
14 | // func (m Migrator) Migrate1to2(ctx context.Context) error {
15 | // return v2.Migrate(/* migrations arguments */)
16 | // }
17 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keeper/msg_server.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 |
8 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
9 | )
10 |
11 | type msgServer struct {
12 | k Keeper
13 | }
14 |
15 | var _ auction.MsgServer = msgServer{}
16 |
17 | func NewMsgServerImpl(keeper Keeper) auction.MsgServer {
18 | return &msgServer{k: keeper}
19 | }
20 |
21 | func (ms msgServer) Bid(goCtx context.Context, msg *auction.MsgBid) (*auction.MsgBidResponse, error) {
22 | ctx := sdk.UnwrapSDKContext(goCtx)
23 |
24 | if err := ms.k.checkAvailability(ctx, msg.Name); err != nil {
25 | return nil, err
26 | }
27 |
28 | nameReservation, err := ms.k.validateAndFormat(ctx, msg)
29 | if err != nil {
30 | return nil, err
31 | }
32 |
33 | if err = ms.k.reserveName(ctx, nameReservation); err != nil {
34 | return nil, err
35 | }
36 |
37 | return &auction.MsgBidResponse{}, nil
38 | }
39 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keeper/msg_server_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | "testing"
5 |
6 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
7 |
8 | "github.com/stretchr/testify/require"
9 | )
10 |
11 | func TestMsg(t *testing.T) {
12 | f := initFixture(t)
13 | require := require.New(t)
14 |
15 | testCases := []struct {
16 | name string
17 | request *auction.MsgBid
18 | expectErrMsg string
19 | }{
20 | {
21 | name: "Success",
22 | request: &auction.MsgBid{},
23 | expectErrMsg: "",
24 | },
25 | }
26 |
27 | for _, tc := range testCases {
28 | tc := tc
29 | t.Run(tc.name, func(t *testing.T) {
30 | _, err := f.msgServer.Bid(f.ctx, tc.request)
31 | if tc.expectErrMsg != "" {
32 | require.Error(err)
33 | require.ErrorContains(err, tc.expectErrMsg)
34 | } else {
35 | require.NoError(err)
36 | }
37 | })
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keeper/nameservice.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | sdk "github.com/cosmos/cosmos-sdk/types"
8 |
9 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
10 | )
11 |
12 | func (k Keeper) GetNameRecord(ctx context.Context, name string) (auction.Name, error) {
13 | nameRecord, err := k.Names.Get(ctx, name)
14 | if err != nil {
15 | return auction.Name{}, err
16 | }
17 |
18 | return nameRecord, nil
19 | }
20 |
21 | func (k Keeper) reserveName(ctx sdk.Context, nr auction.Name) error {
22 | owner, _ := sdk.AccAddressFromBech32(nr.Owner)
23 | if err := k.executeTransfer(ctx, owner, nr.Amount); err != nil {
24 | return err
25 | }
26 | if err := k.Names.Set(ctx, nr.Name, nr); err != nil {
27 | // TODO: Custom error
28 | return err
29 | }
30 |
31 | return nil
32 | }
33 |
34 | func (k Keeper) checkAvailability(ctx sdk.Context, name string) error {
35 | hasName, err := k.Names.Has(ctx, name)
36 | if hasName {
37 | return fmt.Errorf("%s is already reserved", name)
38 | }
39 | if err != nil {
40 | return fmt.Errorf("unexpected error validating reservation :: %w", err)
41 | }
42 | return nil
43 | }
44 |
45 | func (k Keeper) validateAndFormat(ctx sdk.Context, msg *auction.MsgBid) (auction.Name, error) {
46 | nr := auction.Name{}
47 |
48 | sender, err := sdk.AccAddressFromBech32(msg.Owner)
49 | if err != nil {
50 | return nr, err
51 | }
52 |
53 | resolveAddr, err := sdk.AccAddressFromBech32(msg.ResolveAddress)
54 | if err != nil {
55 | return nr, fmt.Errorf("invalid resolve address %s", msg.ResolveAddress)
56 | }
57 |
58 | if err := k.checkSufficientBalance(ctx, sender, msg.Amount); err != nil {
59 | return nr, err
60 | }
61 |
62 | return auction.Name{Name: msg.Name, Owner: sender.String(), ResolveAddress: resolveAddr.String(), Amount: msg.Amount}, nil
63 | }
64 |
65 | func (k Keeper) checkSufficientBalance(ctx sdk.Context, senderAddr sdk.AccAddress, amount sdk.Coins) error {
66 | found, payment := amount.Find(k.defaultDenom)
67 | if !found {
68 | return fmt.Errorf("invalid coin denom - %s, default denom: %s", amount, k.defaultDenom)
69 | }
70 |
71 | balance := k.bk.GetBalance(ctx, senderAddr, payment.Denom)
72 | _, insufficientBalance := sdk.Coins{balance}.SafeSub(payment)
73 | if insufficientBalance {
74 | return fmt.Errorf("price for name exceeds account balance: %s < %s", balance, payment)
75 | }
76 | return nil
77 | }
78 |
79 | func (k Keeper) executeTransfer(ctx sdk.Context, senderAddr sdk.AccAddress, amount sdk.Coins) error {
80 | err := k.bk.SendCoinsFromAccountToModule(ctx, senderAddr, auction.ModuleName, amount)
81 | if err != nil {
82 | return err
83 | }
84 | return nil
85 | }
86 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keeper/query_server.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
8 | )
9 |
10 | var _ auction.QueryServer = queryServer{}
11 |
12 | // NewQueryServerImpl returns an implementation of the module QueryServer.
13 | func NewQueryServerImpl(k Keeper) auction.QueryServer {
14 | return queryServer{k}
15 | }
16 |
17 | type queryServer struct {
18 | k Keeper
19 | }
20 |
21 | func (qs queryServer) Name(ctx context.Context, r *auction.QueryNameRequest) (*auction.QueryNameResponse, error) {
22 | if r == nil {
23 | return nil, errors.New("empty request")
24 | }
25 | if len(r.Name) == 0 {
26 | return nil, auction.ErrEmptyName
27 | }
28 |
29 | record, err := qs.k.GetNameRecord(ctx, r.Name)
30 | if err != nil {
31 | return nil, err
32 | }
33 |
34 | return &auction.QueryNameResponse{
35 | Name: &record,
36 | }, nil
37 | }
38 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keeper/query_server_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | "testing"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
8 |
9 | "github.com/stretchr/testify/require"
10 | )
11 |
12 | func TestQueryName(t *testing.T) {
13 | f := initFixture(t)
14 | require := require.New(t)
15 |
16 | nameRes := &auction.MsgBid{
17 | Name: "bob.cosmos",
18 | ResolveAddress: f.addrs[0].String(),
19 | Owner: f.addrs[1].String(),
20 | Amount: sdk.NewCoins(sdk.NewInt64Coin("stake", 150)),
21 | }
22 |
23 | nameReq := &auction.QueryNameRequest{
24 | Name: "bob.cosmos",
25 | }
26 |
27 | _, err := f.msgServer.Bid(f.ctx, nameRes)
28 | require.NoError(err)
29 | resp, err := f.queryServer.Name(f.ctx, nameReq)
30 | require.NoError(err)
31 | require.Equal(nameReq, resp)
32 | }
33 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/keys.go:
--------------------------------------------------------------------------------
1 | package auction
2 |
3 | import "cosmossdk.io/collections"
4 |
5 | const (
6 | ModuleName = "auction"
7 | StoreKey = ModuleName
8 | )
9 |
10 | var (
11 | NamesKey = collections.NewPrefix(0)
12 | OwnersKey = collections.NewPrefix(1)
13 | )
14 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/mempool/mempool.go:
--------------------------------------------------------------------------------
1 | package mempool
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "cosmossdk.io/log"
8 | sdk "github.com/cosmos/cosmos-sdk/types"
9 | "github.com/cosmos/cosmos-sdk/types/mempool"
10 | "github.com/cosmos/cosmos-sdk/x/auth/signing"
11 | )
12 |
13 | var _ mempool.Mempool = (*ThresholdMempool)(nil)
14 |
15 | type ThresholdMempool struct {
16 | logger log.Logger
17 | pendingPool thTxs
18 | pool thTxs
19 | }
20 |
21 | func NewThresholdMempool(logger log.Logger) *ThresholdMempool {
22 | return &ThresholdMempool{
23 | logger: logger.With("module", "threshold-mempool"),
24 | }
25 | }
26 |
27 | func (t *ThresholdMempool) Insert(ctx context.Context, tx sdk.Tx) error {
28 | sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2()
29 | if err != nil {
30 | t.logger.Error("Error unable to retrieve tx signatures")
31 | return err
32 | }
33 | // Guarantee there is at least 1 signer
34 | if len(sigs) == 0 {
35 | t.logger.Error("Error missing tx signatures")
36 | return fmt.Errorf("transaction must be signed")
37 | }
38 |
39 | sig := sigs[0]
40 | sender := sdk.AccAddress(sig.PubKey.Address()).String()
41 | t.logger.Info(fmt.Sprintf("This is the sender account address :: %v", sender))
42 |
43 | // Set default 0 priority
44 | priority := int64(0)
45 | appTx := thTx{
46 | sender,
47 | priority,
48 | tx,
49 | }
50 |
51 | t.logger.Info(fmt.Sprintf("Inserting transaction from %v with priority %v", sender, priority))
52 |
53 | t.pendingPool.txs = append(t.pendingPool.txs, appTx)
54 | leng := len(t.pendingPool.txs)
55 | t.logger.Info(fmt.Sprintf("Transactions length %v", leng))
56 |
57 | return nil
58 | }
59 |
60 | func (t *ThresholdMempool) Select(ctx context.Context, i [][]byte) mempool.Iterator {
61 | if len(t.pool.txs) == 0 {
62 | return nil
63 | }
64 |
65 | return &t.pool
66 | }
67 |
68 | func (t *ThresholdMempool) SelectPending(ctx context.Context, i [][]byte) mempool.Iterator {
69 | if len(t.pendingPool.txs) == 0 {
70 | return nil
71 | }
72 |
73 | return &t.pendingPool
74 | }
75 |
76 | func (t *ThresholdMempool) Update(ctx context.Context, tx sdk.Tx) error {
77 | sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2()
78 | if err != nil {
79 | return err
80 | }
81 | if len(sigs) == 0 {
82 | return fmt.Errorf("tx must have at least one signer")
83 | }
84 |
85 | sig := sigs[0]
86 | sender := sdk.AccAddress(sig.PubKey.Address()).String()
87 |
88 | txToUpdate := thTx{
89 | sender,
90 | 1,
91 | tx,
92 | }
93 |
94 | for idx, ttx := range t.pendingPool.txs {
95 | if ttx.Equal(txToUpdate) {
96 | t.pendingPool.txs = removeAtIndex(t.pendingPool.txs, idx)
97 | t.pool.txs = append(t.pool.txs, txToUpdate)
98 | return nil
99 | }
100 | }
101 | // remove from pendingPool, add to
102 |
103 | return mempool.ErrTxNotFound
104 | }
105 |
106 | func (t *ThresholdMempool) CountTx() int {
107 | return len(t.pendingPool.txs)
108 | }
109 |
110 | func (t *ThresholdMempool) Remove(tx sdk.Tx) error {
111 | sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2()
112 | if err != nil {
113 | return err
114 | }
115 | if len(sigs) == 0 {
116 | return fmt.Errorf("tx must have at least one signer")
117 | }
118 |
119 | sig := sigs[0]
120 | sender := sdk.AccAddress(sig.PubKey.Address()).String()
121 |
122 | txToRemove := thTx{
123 | sender,
124 | 1,
125 | tx,
126 | }
127 |
128 | for idx, ttx := range t.pool.txs {
129 | if ttx.Equal(txToRemove) {
130 | t.pool.txs = removeAtIndex(t.pool.txs, idx)
131 | return nil
132 | }
133 | }
134 |
135 | return mempool.ErrTxNotFound
136 | }
137 |
138 | var _ mempool.Iterator = &thTxs{}
139 |
140 | type thTxs struct {
141 | idx int
142 | txs []thTx
143 | }
144 |
145 | func (t *thTxs) Next() mempool.Iterator {
146 | if len(t.txs) == 0 {
147 | return nil
148 | }
149 |
150 | if len(t.txs) == t.idx+1 {
151 | return nil
152 | }
153 |
154 | t.idx++
155 | return t
156 | }
157 |
158 | func (t *thTxs) Tx() sdk.Tx {
159 | if t.idx >= len(t.txs) {
160 | panic(fmt.Sprintf("index out of bound: %d, Txs: %v", t.idx, t))
161 | }
162 |
163 | return t.txs[t.idx].tx
164 | }
165 |
166 | type thTx struct {
167 | address string
168 | priority int64
169 | tx sdk.Tx
170 | }
171 |
172 | func (tx thTx) Equal(other thTx) bool {
173 | if tx.address != other.address {
174 | return false
175 | }
176 |
177 | if len(tx.tx.GetMsgs()) != len(other.tx.GetMsgs()) {
178 | return false
179 | }
180 |
181 | for i, msg := range tx.tx.GetMsgs() {
182 | if msg.String() != other.tx.GetMsgs()[i].String() {
183 | return false
184 | }
185 | }
186 |
187 | return true
188 | }
189 |
190 | func removeAtIndex[T any](slice []T, index int) []T {
191 | return append(slice[:index], slice[index+1:]...)
192 | }
193 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/mempool/testutil.go:
--------------------------------------------------------------------------------
1 | package mempool
2 |
3 | import (
4 | "fmt"
5 |
6 | cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
7 | sdk "github.com/cosmos/cosmos-sdk/types"
8 | txsigning "github.com/cosmos/cosmos-sdk/types/tx/signing"
9 | "github.com/cosmos/cosmos-sdk/x/auth/signing"
10 | "google.golang.org/protobuf/proto"
11 | )
12 |
13 | // testPubKey is a dummy implementation of PubKey used for testing.
14 | type testPubKey struct {
15 | address sdk.AccAddress
16 | }
17 |
18 | func (t testPubKey) Reset() { panic("not implemented") }
19 |
20 | func (t testPubKey) String() string { panic("not implemented") }
21 |
22 | func (t testPubKey) ProtoMessage() { panic("not implemented") }
23 |
24 | func (t testPubKey) Address() cryptotypes.Address { return t.address.Bytes() }
25 |
26 | func (t testPubKey) Bytes() []byte { panic("not implemented") }
27 |
28 | func (t testPubKey) VerifySignature(msg, sig []byte) bool { panic("not implemented") }
29 |
30 | func (t testPubKey) Equals(key cryptotypes.PubKey) bool { panic("not implemented") }
31 |
32 | func (t testPubKey) Type() string { panic("not implemented") }
33 |
34 | // testTx is a dummy implementation of Tx used for testing.
35 | type testTx struct {
36 | address sdk.AccAddress
37 | nonce uint64
38 | }
39 |
40 | func (tx testTx) GetMsgsV2() ([]proto.Message, error) {
41 | panic("implement me")
42 | }
43 |
44 | func (tx testTx) GetSigners() ([][]byte, error) { panic("not implemented") }
45 |
46 | func (tx testTx) GetPubKeys() ([]cryptotypes.PubKey, error) { panic("not implemented") }
47 |
48 | func (tx testTx) GetSignaturesV2() ([]txsigning.SignatureV2, error) {
49 | return []txsigning.SignatureV2{{
50 | PubKey: testPubKey{address: tx.address},
51 | Data: nil,
52 | Sequence: tx.nonce,
53 | }}, nil
54 | }
55 |
56 | func (tx testTx) GetGas() uint64 {
57 | return 10
58 | }
59 |
60 | func (tx testTx) GetFee() sdk.Coins {
61 | return sdk.NewCoins(sdk.NewInt64Coin("stake", int64(5)))
62 | }
63 |
64 | func (tx testTx) FeePayer() sdk.AccAddress {
65 | return tx.address
66 | }
67 |
68 | func (tx testTx) FeeGranter() sdk.AccAddress {
69 | return tx.address
70 | }
71 |
72 | var (
73 | _ sdk.Tx = (*testTx)(nil)
74 | _ signing.SigVerifiableTx = (*testTx)(nil)
75 | _ cryptotypes.PubKey = (*testPubKey)(nil)
76 | )
77 |
78 | func (tx testTx) GetMsgs() []sdk.Msg { return nil }
79 |
80 | func (tx testTx) ValidateBasic() error { return nil }
81 |
82 | func (tx testTx) String() string {
83 | return fmt.Sprintf("tx a: %s, n: %d", tx.address, tx.nonce)
84 | }
85 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/module/autocli.go:
--------------------------------------------------------------------------------
1 | package module
2 |
3 | import (
4 | autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
5 |
6 | nsv1 "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/api/v1"
7 | )
8 |
9 | // AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
10 | func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
11 | return &autocliv1.ModuleOptions{
12 | Query: &autocliv1.ServiceCommandDescriptor{
13 | Service: nsv1.Query_ServiceDesc.ServiceName,
14 | RpcCommandOptions: []*autocliv1.RpcCommandOptions{
15 | {
16 | RpcMethod: "Name",
17 | Use: "whois [name]",
18 | Short: "Get the resolve address and owner for a name record",
19 | PositionalArgs: []*autocliv1.PositionalArgDescriptor{
20 | {ProtoField: "name"},
21 | },
22 | },
23 | },
24 | },
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/module/cli.go:
--------------------------------------------------------------------------------
1 | package module
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/cosmos/cosmos-sdk/client"
8 | "github.com/cosmos/cosmos-sdk/client/flags"
9 | "github.com/cosmos/cosmos-sdk/client/tx"
10 | sdk "github.com/cosmos/cosmos-sdk/types"
11 | "github.com/spf13/cobra"
12 |
13 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
14 | )
15 |
16 | func (AppModule) GetTxCmd() *cobra.Command {
17 | cmd := &cobra.Command{
18 | Use: auction.ModuleName,
19 | Args: cobra.ExactArgs(1),
20 | RunE: client.ValidateCmd,
21 | }
22 |
23 | cmd.AddCommand(
24 | ReserveCmd(),
25 | )
26 | return cmd
27 | }
28 |
29 | func ReserveCmd() *cobra.Command {
30 | cmd := &cobra.Command{
31 | Use: "reserve [name] [resolve_address] [amount]",
32 | Short: "Create a new game",
33 | Args: cobra.ExactArgs(3),
34 | RunE: func(cmd *cobra.Command, args []string) error {
35 | clientCtx, err := client.GetClientTxContext(cmd)
36 | if err != nil {
37 | return err
38 | }
39 | cmd.Println(fmt.Sprintf("This is the name: %s", args[0]))
40 | cmd.Println(fmt.Sprintf("This is the resolve: %s", args[1]))
41 | cmd.Println(fmt.Sprintf("This is the amount: %s", args[2]))
42 |
43 | sender := clientCtx.GetFromAddress()
44 |
45 | amt, err := sdk.ParseCoinsNormalized(args[2])
46 | if err != nil {
47 | return err
48 | }
49 |
50 | msg := &auction.MsgBid{
51 | Name: args[0],
52 | ResolveAddress: args[1],
53 | Owner: sender.String(),
54 | Amount: amt,
55 | }
56 |
57 | cmd.Printf("Reserve Name: %s\n", msg)
58 |
59 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
60 | },
61 | }
62 |
63 | flags.AddTxFlagsToCmd(cmd)
64 | return cmd
65 | }
66 |
67 | func QueryWhoisCmd() *cobra.Command {
68 | cmd := &cobra.Command{
69 | Use: "whois [name]",
70 | Short: "Get the resolve address and owner for a name record",
71 | Args: cobra.ExactArgs(1),
72 | RunE: func(cmd *cobra.Command, args []string) error {
73 | clientCtx, err := client.GetClientTxContext(cmd)
74 | if err != nil {
75 | return err
76 | }
77 |
78 | queryClient := auction.NewQueryClient(clientCtx)
79 | params := &auction.QueryNameRequest{Name: args[0]}
80 | res, err := queryClient.Name(context.Background(), params)
81 | if err != nil {
82 | return err
83 | }
84 |
85 | cmd.Println(fmt.Sprintf("This is the name: %s", args[0]))
86 | cmd.Println(fmt.Sprintf("This is the resolve: %s", res.Name))
87 | cmd.Println(fmt.Sprintf("This is the owner: %s", res.Name.Owner))
88 |
89 | return nil
90 | },
91 | }
92 |
93 | flags.AddQueryFlagsToCmd(cmd)
94 | return cmd
95 | }
96 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/module/depinject.go:
--------------------------------------------------------------------------------
1 | package module
2 |
3 | import (
4 | "cosmossdk.io/core/address"
5 | "cosmossdk.io/core/appmodule"
6 | "cosmossdk.io/core/store"
7 | "cosmossdk.io/depinject"
8 | "github.com/cosmos/cosmos-sdk/codec"
9 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
10 |
11 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
12 | modulev1 "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/api/module"
13 | "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/keeper"
14 | )
15 |
16 | var _ appmodule.AppModule = AppModule{}
17 |
18 | // IsOnePerModuleType implements the depinject.OnePerModuleType interface.
19 | func (am AppModule) IsOnePerModuleType() {}
20 |
21 | // IsAppModule implements the appmodule.AppModule interface.
22 | func (am AppModule) IsAppModule() {}
23 |
24 | func init() {
25 | appmodule.Register(
26 | &modulev1.Module{},
27 | appmodule.Provide(ProvideModule),
28 | )
29 | }
30 |
31 | type ModuleInputs struct {
32 | depinject.In
33 |
34 | Cdc codec.Codec
35 | StoreService store.KVStoreService
36 | AddressCodec address.Codec
37 |
38 | BankKeeper auction.BankKeeper
39 |
40 | Config *modulev1.Module
41 | }
42 |
43 | type ModuleOutputs struct {
44 | depinject.Out
45 |
46 | Module appmodule.AppModule
47 | Keeper keeper.Keeper
48 | }
49 |
50 | func ProvideModule(in ModuleInputs) ModuleOutputs {
51 | // default to governance as authority if not provided
52 | authority := authtypes.NewModuleAddress("gov")
53 | if in.Config.Authority != "" {
54 | authority = authtypes.NewModuleAddressOrBech32Address(in.Config.Authority)
55 | }
56 |
57 | k := keeper.NewKeeper(in.Cdc, in.AddressCodec, in.StoreService, authority.String(), in.BankKeeper, in.Config.DefaultDenom)
58 | m := NewAppModule(in.Cdc, k)
59 |
60 | return ModuleOutputs{Module: m, Keeper: k}
61 | }
62 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/module/module.go:
--------------------------------------------------------------------------------
1 | package module
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 |
8 | abci "github.com/cometbft/cometbft/abci/types"
9 | "github.com/cosmos/cosmos-sdk/client"
10 | "github.com/cosmos/cosmos-sdk/codec"
11 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
12 | sdk "github.com/cosmos/cosmos-sdk/types"
13 | "github.com/cosmos/cosmos-sdk/types/module"
14 | gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
15 |
16 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
17 | "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/keeper"
18 | )
19 |
20 | const ConsensusVersion = 1
21 |
22 | type AppModule struct {
23 | cdc codec.Codec
24 | keeper keeper.Keeper
25 | }
26 |
27 | func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule {
28 | return AppModule{
29 | cdc: cdc,
30 | keeper: keeper,
31 | }
32 | }
33 |
34 | func NewAppModuleBasic(m AppModule) module.AppModuleBasic {
35 | return module.CoreAppModuleBasicAdaptor(m.Name(), m)
36 | }
37 |
38 | func (AppModule) Name() string { return auction.ModuleName }
39 |
40 | func (AppModule) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
41 | auction.RegisterLegacyAminoCodec(cdc)
42 | }
43 |
44 | func (AppModule) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwruntime.ServeMux) {
45 | if err := auction.RegisterQueryHandlerClient(context.Background(), mux, auction.NewQueryClient(clientCtx)); err != nil {
46 | panic(err)
47 | }
48 | }
49 |
50 | func (AppModule) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
51 | auction.RegisterInterfaces(registry)
52 | }
53 |
54 | func (AppModule) ConsensusVersion() uint64 { return ConsensusVersion }
55 |
56 | func (am AppModule) RegisterServices(cfg module.Configurator) {
57 | auction.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
58 | auction.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper))
59 |
60 | // Register in place module state migration migrations
61 | // m := keeper.NewMigrator(am.keeper)
62 | // if err := cfg.RegisterMigration(ns.ModuleName, 1, m.Migrate1to2); err != nil {
63 | // panic(fmt.Sprintf("failed to migrate x/%s from version 1 to 2: %v", ns.ModuleName, err))
64 | // }
65 | }
66 |
67 | func (AppModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
68 | return cdc.MustMarshalJSON(auction.NewGenesisState())
69 | }
70 |
71 | func (AppModule) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
72 | var data auction.GenesisState
73 | if err := cdc.UnmarshalJSON(bz, &data); err != nil {
74 | return fmt.Errorf("failed to unmarshal %s genesis state: %w", auction.ModuleName, err)
75 | }
76 |
77 | return data.Validate()
78 | }
79 |
80 | func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate {
81 | var genesisState auction.GenesisState
82 | cdc.MustUnmarshalJSON(data, &genesisState)
83 |
84 | if err := am.keeper.InitGenesis(ctx, &genesisState); err != nil {
85 | panic(fmt.Sprintf("failed to initialize %s genesis state: %v", auction.ModuleName, err))
86 | }
87 |
88 | return nil
89 | }
90 |
91 | func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
92 | gs, err := am.keeper.ExportGenesis(ctx)
93 | if err != nil {
94 | panic(fmt.Sprintf("failed to export %s genesis state: %v", auction.ModuleName, err))
95 | }
96 |
97 | return cdc.MustMarshalJSON(gs)
98 | }
99 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/buf.gen.gogo.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | plugins:
3 | - name: gocosmos
4 | out: ..
5 | opt: plugins=grpc,Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types,Mcosmos/orm/v1/orm.proto=cosmossdk.io/orm
6 | - name: grpc-gateway
7 | out: ..
8 | opt: logtostderr=true,allow_colon_final_segments=true
9 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/buf.gen.pulsar.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | managed:
3 | enabled: true
4 | go_package_prefix:
5 | default: github.com/tutorials/nameservice/base/x/auction/api
6 | except:
7 | - buf.build/googleapis/googleapis
8 | - buf.build/cosmos/gogo-proto
9 | - buf.build/cosmos/cosmos-proto
10 | - buf.build/cosmos/cosmos-sdk
11 | plugins:
12 | - name: go-pulsar
13 | out: ..
14 | opt: paths=source_relative,Mcosmos/app/v1alpha1/module.proto=cosmossdk.io/api/cosmos/app/v1alpha1,Mcosmos/base/v1beta1/coin.proto=cosmossdk.io/api/cosmos/base/v1beta1
15 | - name: go-grpc
16 | out: ..
17 | opt: paths=source_relative,Mcosmos/app/v1alpha1/module.proto=cosmossdk.io/api/cosmos/app/v1alpha1
18 | - name: go-cosmos-orm
19 | out: ..
20 | opt: paths=source_relative,Mcosmos/app/v1alpha1/module.proto=cosmossdk.io/api/cosmos/app/v1alpha1
21 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/buf.lock:
--------------------------------------------------------------------------------
1 | # Generated by buf. DO NOT EDIT.
2 | version: v1
3 | deps:
4 | - remote: buf.build
5 | owner: cosmos
6 | repository: cosmos-proto
7 | commit: 1935555c206d4afb9e94615dfd0fad31
8 | - remote: buf.build
9 | owner: cosmos
10 | repository: cosmos-sdk
11 | commit: d5661b4f6ef64bb1b5beb6cb7bd705b7
12 | - remote: buf.build
13 | owner: cosmos
14 | repository: gogo-proto
15 | commit: 5e5b9fdd01804356895f8f79a6f1ddc1
16 | - remote: buf.build
17 | owner: googleapis
18 | repository: googleapis
19 | commit: cc916c31859748a68fd229a3c8d7a2e8
20 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/buf.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | deps:
3 | - buf.build/cosmos/cosmos-sdk # pin the Cosmos SDK version
4 | - buf.build/cosmos/cosmos-proto
5 | - buf.build/cosmos/gogo-proto
6 | - buf.build/googleapis/googleapis
7 | lint:
8 | use:
9 | - DEFAULT
10 | - COMMENTS
11 | - FILE_LOWER_SNAKE_CASE
12 | except:
13 | - UNARY_RPC
14 | - COMMENT_FIELD
15 | - SERVICE_SUFFIX
16 | - PACKAGE_VERSION_SUFFIX
17 | - RPC_REQUEST_STANDARD_NAME
18 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/cosmos/auction/module/module.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package cosmos.auction.module;
4 |
5 | import "cosmos/app/v1alpha1/module.proto";
6 |
7 | // Module represents the main structure for the auction module.
8 | message Module {
9 | option (cosmos.app.v1alpha1.module) = {
10 | go_import : "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/types"
11 | };
12 |
13 | string authority = 1;
14 |
15 | string default_denom = 2;
16 | }
17 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/cosmos/auction/v1/event.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package cosmos.auction.v1;
3 |
4 | option go_package = "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction";
5 |
6 | import "cosmos_proto/cosmos.proto";
7 | import "cosmos/msg/v1/msg.proto";
8 | import "gogoproto/gogo.proto";
9 | import "cosmos/base/v1beta1/coin.proto";
10 | import "amino/amino.proto";
11 |
12 | // EventReserve is emitted on Msg/Reserve
13 | message EventReserve {
14 | // name is the human readable reserved address
15 | string name = 1;
16 |
17 | // owner is the bech32 address of the name's owner
18 | string owner = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
19 |
20 | // resolve is the bech32 address the name resolves to
21 | string resolve = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
22 |
23 | // receiver is the receiver address of nft
24 | repeated cosmos.base.v1beta1.Coin amount = 4 [
25 | (gogoproto.nullable) = false,
26 | (amino.dont_omitempty) = true,
27 | (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
28 | ];
29 | }
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/cosmos/auction/v1/genesis.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package cosmos.auction.v1;
3 |
4 | option go_package = "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction";
5 |
6 | import "cosmos_proto/cosmos.proto";
7 | import "cosmos/auction/v1/types.proto";
8 |
9 | // GenesisState defines the nameservice module's genesis state.
10 | message GenesisState {
11 | // listing defines all listings.
12 | // repeated nameservice.v1.NameCollection name_collection = 1;
13 | }
14 |
15 | // Entry define all names owned by an account
16 | message NameCollection {
17 | // owner is the owner address of the following nft
18 | string owner = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
19 |
20 | // names is a all names of the same owner
21 | repeated Name names = 2;
22 | }
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/cosmos/auction/v1/query.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package cosmos.auction.v1;
3 |
4 | option go_package = "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction";
5 |
6 | import "cosmos/auction/v1/types.proto";
7 | import "google/api/annotations.proto";
8 | import "cosmos/base/query/v1beta1/pagination.proto";
9 | import "cosmos/query/v1/query.proto";
10 |
11 | // Query service defines the queries for the auction module
12 | service Query {
13 | // Name is a method that takes a QueryNameRequest and returns a QueryNameResponse.
14 | rpc Name(QueryNameRequest) returns (QueryNameResponse) {
15 | option (google.api.http).get = "/cosmos/ns/name/{name}";
16 | }
17 | }
18 |
19 | // QueryNameResponse is the response type for the Query/Names RPC method
20 | message QueryNameResponse { Name name = 1; }
21 |
22 | // QueryNameRequest is the request type for the Query/Names RPC method
23 | message QueryNameRequest { string name = 1; }
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/cosmos/auction/v1/tx.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package cosmos.auction.v1;
3 |
4 | option go_package = "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction";
5 |
6 | import "cosmos/msg/v1/msg.proto";
7 | import "gogoproto/gogo.proto";
8 | import "cosmos/base/v1beta1/coin.proto";
9 | import "amino/amino.proto";
10 | import "cosmos/auction/v1/types.proto";
11 | import "cosmos_proto/cosmos.proto";
12 |
13 | // Msg defines the nameservice Msg service.
14 | service Msg {
15 | option (cosmos.msg.v1.service) = true;
16 |
17 | // Reserve defines a method to buy a nameservice with an associated bech32
18 | // address to resolve to.
19 | rpc Bid(MsgBid) returns (MsgBidResponse);
20 | }
21 |
22 | // MsgReserve represents a message to purchase a nameservice.
23 | message MsgBid {
24 | option (cosmos.msg.v1.signer) = "owner";
25 |
26 | // name defines the human readable address
27 | string name = 1;
28 |
29 | // resolveAddress defines the bech32 address to resolve to
30 | string resolve_address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
31 |
32 | // owner is the address of the owner of listing
33 | string owner = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
34 |
35 | // price is the last price paid for listing
36 | repeated cosmos.base.v1beta1.Coin amount = 4 [
37 | (gogoproto.nullable) = false,
38 | (amino.dont_omitempty) = true,
39 | (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
40 | ];
41 | }
42 |
43 | // MsgSendResponse defines the Msg/Reserve response type.
44 | message MsgBidResponse {}
45 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/proto/cosmos/auction/v1/types.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package cosmos.auction.v1;
3 |
4 | option go_package = "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction";
5 |
6 | import "cosmos_proto/cosmos.proto";
7 | import "gogoproto/gogo.proto";
8 | import "amino/amino.proto";
9 | import "cosmos/base/v1beta1/coin.proto";
10 |
11 | // Name defines the properties of a name in the name service auction system.
12 | message Name {
13 | string name = 1;
14 | string owner = 2;
15 | // Bech32 Address to Resolve
16 | string resolve_address = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
17 |
18 | repeated cosmos.base.v1beta1.Coin amount = 4 [
19 | (gogoproto.nullable) = false,
20 | (amino.dont_omitempty) = true,
21 | (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
22 | ];
23 | }
24 |
25 | // Owner defines the properties of an owner in the name service auction system.
26 | message Owner {
27 | string name = 1; // Name of the owner
28 | // Bech32 Address of Owner
29 | string owner_address = 2;
30 | }
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/provider/provider.go:
--------------------------------------------------------------------------------
1 | package provider
2 |
3 | import (
4 | "fmt"
5 |
6 | "cosmossdk.io/log"
7 | "cosmossdk.io/math"
8 | "github.com/cosmos/cosmos-sdk/client"
9 | "github.com/cosmos/cosmos-sdk/client/tx"
10 | "github.com/cosmos/cosmos-sdk/codec"
11 | "github.com/cosmos/cosmos-sdk/codec/address"
12 | "github.com/cosmos/cosmos-sdk/crypto/keyring"
13 | sdk "github.com/cosmos/cosmos-sdk/types"
14 | authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
15 | authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
16 |
17 | auction "github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction"
18 | )
19 |
20 | /*
21 | This implementation is for demo purposes only and does not reflect all limitations and
22 | constraints of a live distributed network.
23 |
24 | Transaction Provider is an embedded solution to demonstrate an interface an application could
25 | leverage to extract MEV when building and proposing a block. In this example, the
26 | application is building and signing transactions locally for the sake of a simplicity.
27 | Alternatively, another implementation could instead take transactions submitted directly
28 | via RPC to its app side mempool, and could even implement a separate custom mempool for
29 | special transactions of this nature.
30 | */
31 | type TxProvider interface {
32 | BuildProposal(ctx sdk.Context, proposalTxs []sdk.Tx) ([]sdk.Tx, error)
33 | getMatchingBid(ctx sdk.Context, bid *auction.MsgBid) sdk.Tx
34 | }
35 |
36 | type LocalSigner struct {
37 | KeyName string
38 | KeyringDir string
39 | codec codec.Codec
40 | txConfig client.TxConfig
41 | kb keyring.Keyring
42 | lg log.Logger
43 | }
44 |
45 | type LocalTxProvider struct {
46 | Logger log.Logger
47 | Codec codec.Codec
48 | Signer LocalSigner
49 | TxConfig client.TxConfig
50 | AcctKeeper authkeeper.AccountKeeper
51 | }
52 |
53 | func (bp *LocalTxProvider) Init() error {
54 | return bp.Signer.Init(bp.TxConfig, bp.Codec, bp.Logger)
55 | }
56 |
57 | func (ls *LocalSigner) Init(txCfg client.TxConfig, cdc codec.Codec, logger log.Logger) error {
58 | if len(ls.KeyName) == 0 {
59 | return fmt.Errorf("keyName must be set")
60 | }
61 |
62 | if len(ls.KeyringDir) == 0 {
63 | return fmt.Errorf("keyDir must be set")
64 | }
65 |
66 | ls.txConfig = txCfg
67 | ls.codec = cdc
68 | ls.lg = logger
69 |
70 | kb, err := keyring.New("cosmos", keyring.BackendTest, ls.KeyringDir, nil, ls.codec)
71 | if err != nil {
72 | return err
73 | }
74 | ls.kb = kb
75 | return nil
76 | }
77 |
78 | func (ls *LocalSigner) RetreiveSigner(ctx sdk.Context, actKeeper authkeeper.AccountKeeper) (sdk.AccountI, error) {
79 | lg := ls.lg
80 |
81 | info, err := ls.kb.Key(ls.KeyName)
82 | if err != nil {
83 | lg.Error(fmt.Sprintf("Error retrieving key info: %v", err))
84 | return nil, err
85 | }
86 |
87 | addrBz, err := info.GetAddress()
88 | if err != nil {
89 | lg.Error(fmt.Sprintf("Error retrieving address by key name: %v", err))
90 | return nil, err
91 | }
92 |
93 | addCodec := address.Bech32Codec{
94 | Bech32Prefix: sdk.GetConfig().GetBech32AccountAddrPrefix(),
95 | }
96 |
97 | addrStr, err := addCodec.BytesToString(addrBz)
98 | if err != nil {
99 | lg.Error(fmt.Sprintf("Error converting address bytes to str: %v", err))
100 | return nil, err
101 | }
102 |
103 | sdkAddr, err := sdk.AccAddressFromBech32(addrStr)
104 | if err != nil {
105 | lg.Error(fmt.Sprintf("Error getting acct address from addr string: %v", err))
106 | return nil, err
107 | }
108 |
109 | acct := actKeeper.GetAccount(ctx, sdkAddr)
110 | return acct, nil
111 | }
112 |
113 | func (ls *LocalSigner) BuildAndSignTx(ctx sdk.Context, acct sdk.AccountI, msg auction.MsgBid) sdk.Tx {
114 | factory := tx.Factory{}.
115 | WithTxConfig(ls.txConfig).
116 | WithKeybase(ls.kb).
117 | WithChainID(ctx.ChainID()).
118 | WithAccountNumber(acct.GetAccountNumber()).
119 | WithSequence(acct.GetSequence()).
120 | WithFees("50uatom")
121 |
122 | txBuilder, err := factory.BuildUnsignedTx(&msg)
123 | if err != nil {
124 | ls.lg.Error(fmt.Sprintf("Error building unsigned tx: %v", err))
125 |
126 | return nil
127 | }
128 | clientCtx := client.Context{}
129 |
130 | err = authclient.SignTx(factory, clientCtx, ls.KeyName, txBuilder, true, true)
131 | if err != nil {
132 | ls.lg.Error(fmt.Sprintf("Error signing tx: %v", err))
133 |
134 | return nil
135 | }
136 | return txBuilder.GetTx()
137 | }
138 |
139 | func (b *LocalTxProvider) getMatchingBid(ctx sdk.Context, bid *auction.MsgBid) sdk.Tx {
140 | acct, err := b.Signer.RetreiveSigner(ctx, b.AcctKeeper)
141 | if err != nil {
142 | b.Logger.Error(fmt.Sprintf("Error retrieving signer: %v", err))
143 | return nil
144 | }
145 | b.Logger.Info("💨 :: Created new bid")
146 |
147 | msg := auction.MsgBid{
148 | Name: bid.Name,
149 | Owner: acct.GetAddress().String(),
150 | ResolveAddress: acct.GetAddress().String(),
151 | Amount: bid.Amount.MulInt(math.NewInt(2)),
152 | }
153 |
154 | newTx := b.Signer.BuildAndSignTx(ctx, acct, msg)
155 | return newTx
156 | }
157 |
158 | func (b *LocalTxProvider) BuildProposal(ctx sdk.Context, proposalTxs []sdk.Tx) ([]sdk.Tx, error) {
159 | b.Logger.Info("💨 :: Building Proposal")
160 |
161 | var newProposal []sdk.Tx
162 | for _, tx := range proposalTxs {
163 | sdkMsgs := tx.GetMsgs()
164 | for _, msg := range sdkMsgs {
165 | switch msg := msg.(type) {
166 | case *auction.MsgBid:
167 | b.Logger.Info("💨 :: Found a Bid to Snipe")
168 |
169 | // Get matching bid from matching engine
170 | newTx := b.getMatchingBid(ctx, msg)
171 |
172 | // First append sniped Bid
173 | newProposal = append(newProposal, newTx)
174 | newProposal = append(newProposal, tx)
175 | default:
176 | // Append all other transactions
177 | newProposal = append(newProposal, tx)
178 | }
179 | }
180 | }
181 |
182 | return newProposal, nil
183 | }
184 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/configure.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -eux
3 |
4 | source ./vars.sh
5 |
6 | pkill -f exampled &> /dev/null || true
7 | sleep 1
8 | rm -rf ${NODES_ROOT_DIR}
9 |
10 | # Beacon creates genesis
11 | BEACON_DIR=${NODES_ROOT_DIR}/${BEACON_MONIKER}
12 | BEACON_DIR_KEY=${BEACON_MONIKER}-key
13 | BEACON_LISTEN_ADDR=tcp://${NODE_IP}:${RPC_LADDR_BASEPORT}
14 |
15 | # Create Account Balances
16 | for index in "${!MONIKERS[@]}"
17 | do
18 | MONIKER=${MONIKERS[$index]}
19 | # validator key
20 | KEY=${MONIKER}-key
21 |
22 | # home directory of this validator
23 | NODE_DIR=${NODES_ROOT_DIR}/${MONIKER}
24 |
25 | # Build genesis file and node directory structure
26 | $BINARY init $MONIKER --chain-id $CHAIN_ID --home ${NODE_DIR} --default-denom uatom
27 | jq ".app_state.gov.voting_params.voting_period = \"10s\" | .app_state.staking.params.unbonding_time = \"86400s\"" \
28 | ${NODE_DIR}/config/genesis.json > \
29 | ${NODE_DIR}/edited_genesis.json && mv ${NODE_DIR}/edited_genesis.json ${NODE_DIR}/config/genesis.json
30 |
31 |
32 | sleep 1
33 |
34 | # Create account keypair
35 | # echo "Adding key to ${NODE_DIR}"
36 | # echo ${KEY}
37 | $BINARY keys add $KEY --home ${NODE_DIR} --keyring-backend test --output json > ${NODE_DIR}/${KEY}.json 2>&1
38 | sleep 1
39 |
40 | if [ $MONIKER == $BEACON_MONIKER ]; then
41 | for USER in "${USERS[@]}"
42 | do
43 | USERKEY=${USER}
44 | $BINARY keys add $USERKEY --home ${NODE_DIR} --keyring-backend test --output json > ${NODE_DIR}/${USERKEY}.json 2>&1
45 | sleep 1
46 |
47 | ACC_ADDR=$(jq -r '.address' ${NODE_DIR}/${USERKEY}.json)
48 | $BINARY genesis add-genesis-account $ACC_ADDR $USER_COINS --home ${NODE_DIR} --keyring-backend test
49 | done
50 | fi
51 |
52 | # copy genesis in, unless this validator is the beacon
53 | if [ $MONIKER != $BEACON_MONIKER ]; then
54 | cp ${BEACON_DIR}/config/genesis.json ${NODE_DIR}/config/genesis.json
55 | fi
56 |
57 | # Add stake to user
58 | ACCOUNT_ADDR=$(jq -r '.address' ${NODE_DIR}/${KEY}.json)
59 | $BINARY genesis add-genesis-account $ACCOUNT_ADDR $USER_COINS --home ${NODE_DIR} --keyring-backend test
60 | sleep 1
61 |
62 | # copy genesis out, unless this validator is the beacon
63 | if [ $MONIKER != $BEACON_MONIKER ]; then
64 | cp ${NODE_DIR}/config/genesis.json ${BEACON_DIR}/config/genesis.json
65 | fi
66 |
67 | PPROF_LADDR=${NODE_IP}:$(($PPROF_LADDR_BASEPORT + $index))
68 | P2P_LADDR_PORT=$(($P2P_LADDR_BASEPORT + $index))
69 |
70 | # adjust configs of this node
71 | sed -i -r 's/timeout_commit = "5s"/timeout_commit = "3s"/g' ${NODE_DIR}/config/config.toml
72 | sed -i -r 's/timeout_propose = "3s"/timeout_propose = "1s"/g' ${NODE_DIR}/config/config.toml
73 |
74 | # make address book non-strict. necessary for this setup
75 | sed -i -r 's/addr_book_strict = true/addr_book_strict = false/g' ${NODE_DIR}/config/config.toml
76 |
77 | # avoid port double binding
78 | sed -i -r "s/pprof_laddr = \"localhost:6060\"/pprof_laddr = \"${PPROF_LADDR}\"/g" ${NODE_DIR}/config/config.toml
79 |
80 | # allow duplicate IP addresses (all nodes are on the same machine)
81 | sed -i -r 's/allow_duplicate_ip = false/allow_duplicate_ip = true/g' ${NODE_DIR}/config/config.toml
82 | done
83 |
84 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
85 |
86 | # Create Validators
87 | for MONIKER in "${MONIKERS[@]}"
88 | do
89 | # validator key
90 | KEY=${MONIKER}-key
91 |
92 | # home directory of this validator
93 | NODE_DIR=${NODES_ROOT_DIR}/${MONIKER}
94 |
95 | # copy genesis in, unless this validator is the beacon
96 | if [ $MONIKER != $BEACON_MONIKER ]; then
97 | cp ${BEACON_DIR}/config/genesis.json* ${NODE_DIR}/config/genesis.json
98 | fi
99 |
100 | # Stake 1/1000 user's coins
101 | $BINARY genesis gentx $KEY $STAKE --chain-id $CHAIN_ID --home ${NODE_DIR} --keyring-backend test --moniker $MONIKER
102 | sleep 1
103 |
104 | # Copy gentxs to the beacon for possible future collection.
105 | # Obviously we don't need to copy the first validator's gentx to itself
106 | if [ $MONIKER != $BEACON_MONIKER ]; then
107 | cp ${NODE_DIR}/config/gentx/* ${BEACON_DIR}/config/gentx/
108 | fi
109 | done
110 |
111 | # Collect genesis transactions with beacon
112 | $BINARY genesis collect-gentxs --home ${BEACON_DIR} --gentx-dir ${BEACON_DIR}/config/gentx/
113 |
114 | sleep 1
115 |
116 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
117 |
118 | # Create node config
119 |
120 | for index in "${!MONIKERS[@]}"
121 | do
122 | MONIKER=${MONIKERS[$index]}
123 |
124 | PERSISTENT_PEERS=""
125 |
126 | for peer_index in "${!MONIKERS[@]}"
127 | do
128 | if [ $index == $peer_index ]; then
129 | continue
130 | fi
131 | PEER_MONIKER=${MONIKERS[$peer_index]}
132 |
133 | PEER_NODE_DIR=${NODES_ROOT_DIR}/${PEER_MONIKER}
134 |
135 | PEER_NODE_ID=$($BINARY comet show-node-id --home ${PEER_NODE_DIR})
136 |
137 | PEER_P2P_LADDR_PORT=$(($P2P_LADDR_BASEPORT + $peer_index))
138 | PERSISTENT_PEERS="$PERSISTENT_PEERS,$PEER_NODE_ID@${NODE_IP}:${PEER_P2P_LADDR_PORT}"
139 | done
140 |
141 | # remove trailing comma from persistent peers
142 | PERSISTENT_PEERS=${PERSISTENT_PEERS:1}
143 |
144 | # validator key
145 | KEY=${MONIKER}-key
146 |
147 | # home directory of this validator on provider
148 | NODE_DIR=${NODES_ROOT_DIR}/${MONIKER}
149 |
150 | # Set min gas prices in app config
151 | sed -i.bak'' 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025uatom"/' ${NODE_DIR}/config/app.toml
152 |
153 | # Enable Vote Extensions for H-1
154 | jq '.consensus.params.abci.vote_extensions_enable_height = "2"' ${NODE_DIR}/config/genesis.json > output.json && mv output.json ${NODE_DIR}/config/genesis.json
155 |
156 | # copy genesis in, unless this validator is already the lead validator and thus it already has its genesis
157 | if [ $MONIKER != $BEACON_MONIKER ]; then
158 | cp ${BEACON_DIR}/config/genesis.json ${NODE_DIR}/config/genesis.json
159 | fi
160 |
161 | RPC_LADDR_PORT=$(($RPC_LADDR_BASEPORT + $index))
162 | P2P_LADDR_PORT=$(($P2P_LADDR_BASEPORT + $index))
163 | GRPC_LADDR_PORT=$(($GRPC_LADDR_BASEPORT + $index))
164 | NODE_ADDRESS_PORT=$(($NODE_ADDRESS_BASEPORT + $index))
165 |
166 | RUN_PROV=false
167 | if [ $MONIKER == $BEACON_MONIKER ]; then
168 | RUN_PROV=true
169 | fi
170 |
171 | # Start nodes
172 | $BINARY start \
173 | --home ${NODE_DIR} \
174 | --val-key ${KEY} \
175 | --run-provider ${RUN_PROV} \
176 | --p2p.persistent_peers ${PERSISTENT_PEERS} \
177 | --rpc.laddr tcp://${NODE_IP}:${RPC_LADDR_PORT} \
178 | --grpc.address ${NODE_IP}:${GRPC_LADDR_PORT} \
179 | --address tcp://${NODE_IP}:${NODE_ADDRESS_PORT} \
180 | --p2p.laddr tcp://${NODE_IP}:${P2P_LADDR_PORT} \
181 | --grpc-web.enable=false &> ${NODE_DIR}/logs &
182 |
183 | sleep 5
184 | done
185 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/list-beacon-keys.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source ./vars.sh &> /dev/null
4 |
5 | $BINARY keys list --home $HOME/cosmos/nodes/beacon --keyring-backend test --output json | jq
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/protocgen.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | echo "Generating gogo proto code"
6 | cd proto
7 | proto_dirs=$(find . -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq)
8 | for dir in $proto_dirs; do
9 | for file in $(find "${dir}" -maxdepth 1 -name '*.proto'); do
10 | # this regex checks if a proto file has its go_package set to github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/api/...
11 | # gogo proto files SHOULD ONLY be generated if this is false
12 | # we don't want gogo proto to run for proto files which are natively built for google.golang.org/protobuf
13 | if grep -q "option go_package" "$file" && grep -H -o -c 'option go_package.*github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/api' "$file" | grep -q ':0$'; then
14 | buf generate --template buf.gen.gogo.yaml $file
15 | fi
16 | done
17 | done
18 |
19 | echo "Generating pulsar proto code"
20 | buf generate --template buf.gen.pulsar.yaml
21 |
22 | cd ..
23 |
24 | cp -r github.com/cosmos/sdk-tutorials/tutorials/nameservice/base/x/auction/* ./
25 | rm -rf api && mkdir api
26 | mv cosmos/auction/* ./api
27 | rm -rf github.com cosmos
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/query-beacon-status.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source ./vars.sh &> /dev/null
4 |
5 | $BINARY q consensus comet node-info --node "tcp://127.0.0.1:29170" -o json | jq '.default_node_info'
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/reserve.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source ./vars.sh &> /dev/null
4 |
5 | if [ $# -eq 0 ]; then
6 | echo "Must provide a name to reserve"
7 | exit 1
8 | fi
9 |
10 | find_project_root() {
11 | local dir=$PWD
12 | while [ "$dir" != "" ] && [ ! -d "$dir/.git" ]; do
13 | dir=$(dirname "$dir")
14 | done
15 | echo "$dir"
16 | }
17 |
18 | PROJECT_ROOT=$(find_project_root)
19 | BINARY=$PROJECT_ROOT/tutorials/nameservice/base/build/exampled
20 |
21 | name="$1"
22 |
23 | $BINARY keys show alice -a --home $HOME/cosmos/nodes/beacon --keyring-backend test
24 | $BINARY keys show barbara -a --home $HOME/cosmos/nodes/beacon --keyring-backend test
25 |
26 | $BINARY tx reserve "${name}" $($BINARY keys show alice -a --home $HOME/cosmos/nodes/beacon --keyring-backend test) 1000uatom --from $($BINARY keys show barbara -a --home $HOME/cosmos/nodes/beacon --keyring-backend test) --home $HOME/cosmos/nodes/beacon --keyring-backend test --node "tcp://127.0.0.1:29170" -y
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/single_node/frontrun.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | find_project_root() {
3 | local dir=$PWD
4 | while [ "$dir" != "" ] && [ ! -d "$dir/.git" ]; do
5 | dir=$(dirname "$dir")
6 | done
7 | echo "$dir"
8 | }
9 |
10 | PROJECT_ROOT=$(find_project_root)
11 | HOME=$HOME/.exampled
12 | BINARY=$PROJECT_ROOT/tutorials/auction/base/build/exampled
13 |
14 | echo $HOME
15 | $BINARY tx reserve "bob.cosmos" $($BINARY keys show alice -a --home $HOME --keyring-backend test) 1000uatom --from $($BINARY keys show bob -a --home $HOME --keyring-backend test) --home $HOME -y
16 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/single_node/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | find_project_root() {
3 | local dir=$PWD
4 | while [ "$dir" != "" ] && [ ! -d "$dir/.git" ]; do
5 | dir=$(dirname "$dir")
6 | done
7 | echo "$dir"
8 | }
9 |
10 | PROJECT_ROOT=$(find_project_root)
11 | HOME=$HOME/.exampled
12 | BINARY=$PROJECT_ROOT/tutorials/auction/base/build/exampled
13 |
14 | echo $PROJECT_ROOT
15 | echo $HOME
16 |
17 | rm -rf $HOME
18 |
19 | make build
20 |
21 | $BINARY init liveness --chain-id cosmos-1 --default-denom uatom --home $HOME
22 | $BINARY config set client chain-id cosmos-1 --home $HOME
23 | $BINARY config set client keyring-backend test --home $HOME
24 | $BINARY keys add val1 --home $HOME
25 | $BINARY keys add alice --home $HOME
26 | $BINARY keys add bob --home $HOME
27 | $BINARY genesis add-genesis-account val1 10000000000000000000000000uatom --home $HOME
28 | $BINARY genesis add-genesis-account alice 1000000000000000000uatom --home $HOME
29 | $BINARY genesis add-genesis-account bob 1000000000000000000uatom --home $HOME
30 | $BINARY genesis gentx val1 1000000000uatom --chain-id cosmos-1 --home $HOME
31 | $BINARY genesis collect-gentxs --home $HOME
32 | sed -i.bak'' 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025uatom"/' $HOME/config/app.toml
33 | $BINARY start --val-key val1 --run-provider true --home $HOME
34 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/stop.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -eux
3 |
4 | pkill -f exampled &> /dev/null || true
5 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/vars.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | find_project_root() {
4 | local dir=$PWD
5 | while [ "$dir" != "" ] && [ ! -d "$dir/.git" ]; do
6 | dir=$(dirname "$dir")
7 | done
8 | echo "$dir"
9 | }
10 |
11 | PROJECT_ROOT=$(find_project_root)
12 | export HOME=$HOME/.exampled
13 | echo $PROJECT_ROOT
14 | BINARY=$PROJECT_ROOT/tutorials/nameservice/base/build/exampled
15 | export CHAIN_ID=test
16 | export KEYRING=test
17 | # User balance of stake tokens
18 | export USER_COINS="100000000000uatom"
19 | # Amount of stake tokens staked
20 | export STAKE="100000000uatom"
21 | # Node IP address
22 | export NODE_IP="127.0.0.1"
23 |
24 | export ALICE="alice"
25 | export BARBARA="barbara"
26 | export CINDY="cindy"
27 |
28 | # Home directory
29 | export HOME_DIR=$HOME
30 |
31 | export USERS=($ALICE $BARBARA $CINDY)
32 |
33 | # Validator moniker
34 | export MONIKERS=("beacon" "val1" "val2")
35 | export BEACON_MONIKER="beacon"
36 |
37 | export NODES_ROOT_DIR=${HOME_DIR}/cosmos/nodes
38 |
39 | # Base port. Ports assigned after these ports sequentially by nodes.
40 | export RPC_LADDR_BASEPORT=29170
41 | export P2P_LADDR_BASEPORT=29180
42 | export GRPC_LADDR_BASEPORT=29190
43 | export NODE_ADDRESS_BASEPORT=29200
44 | export PPROF_LADDR_BASEPORT=29210
45 | export CLIENT_BASEPORT=29220
46 |
47 | export VALIDATOR_2=val2
48 | export ALICE=alice
49 | export BARBARA=barb
50 | export CINDY=cindy
51 | export DON=don
52 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/scripts/whois.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | source ./vars.sh &> /dev/null
4 |
5 | if [ $# -eq 0 ]; then
6 | echo "Must provide an name to query"
7 | exit 1
8 | fi
9 |
10 | name="$1"
11 |
12 | $BINARY q whois "${name}" --node "tcp://127.0.0.1:29170" -o json
13 |
--------------------------------------------------------------------------------
/tutorials/nameservice/base/x/auction/testutils/encoding.go:
--------------------------------------------------------------------------------
1 | package testutils
2 |
3 | import (
4 | "cosmossdk.io/x/tx/signing"
5 | "github.com/cosmos/cosmos-sdk/client"
6 | "github.com/cosmos/cosmos-sdk/codec"
7 | "github.com/cosmos/cosmos-sdk/codec/address"
8 | "github.com/cosmos/cosmos-sdk/codec/testutil"
9 | "github.com/cosmos/cosmos-sdk/codec/types"
10 | "github.com/cosmos/cosmos-sdk/std"
11 | sdk "github.com/cosmos/cosmos-sdk/types"
12 | "github.com/cosmos/cosmos-sdk/types/module"
13 | "github.com/cosmos/cosmos-sdk/x/auth/tx"
14 | "github.com/cosmos/gogoproto/proto"
15 | )
16 |
17 | // EncodingConfig specifies the concrete encoding types to use for a given app.
18 | // This is provided for compatibility between protobuf and amino implementations.
19 | type EncodingConfig struct {
20 | InterfaceRegistry types.InterfaceRegistry
21 | Marshaler codec.Codec
22 | TxConfig client.TxConfig
23 | Amino *codec.LegacyAmino
24 | }
25 |
26 | // MakeEncodingConfig creates an EncodingConfig for an amino based test configuration.
27 | func MakeEncodingConfig() EncodingConfig {
28 | interfaceRegistry, _ := types.NewInterfaceRegistryWithOptions(types.InterfaceRegistryOptions{
29 | ProtoFiles: proto.HybridResolver,
30 | SigningOptions: signing.Options{
31 | AddressCodec: address.Bech32Codec{
32 | Bech32Prefix: sdk.Bech32MainPrefix,
33 | },
34 | ValidatorAddressCodec: address.Bech32Codec{
35 | Bech32Prefix: sdk.Bech32MainPrefix,
36 | },
37 | },
38 | })
39 |
40 | appCodec := codec.NewProtoCodec(interfaceRegistry)
41 | legacyAmino := codec.NewLegacyAmino()
42 | txConfig := tx.NewTxConfig(appCodec, tx.DefaultSignModes)
43 |
44 | // amino := codec.NewLegacyAmino()
45 | // interfaceRegistry := codectypes.NewInterfaceRegistry()
46 | // cdc := codec.NewProtoCodec(interfaceRegistry)
47 | // txCfg := tx.NewTxConfig(cdc, tx.DefaultSignModes)
48 | return EncodingConfig{
49 | InterfaceRegistry: interfaceRegistry,
50 | Marshaler: appCodec,
51 | TxConfig: txConfig,
52 | Amino: legacyAmino,
53 | }
54 | }
55 |
56 | func MakeTestEncodingConfig(modules ...module.AppModuleBasic) EncodingConfig {
57 | aminoCodec := codec.NewLegacyAmino()
58 | interfaceRegistry := testutil.CodecOptions{}.NewInterfaceRegistry()
59 | codec := codec.NewProtoCodec(interfaceRegistry)
60 |
61 | encCfg := EncodingConfig{
62 | InterfaceRegistry: interfaceRegistry,
63 | Marshaler: codec,
64 | TxConfig: tx.NewTxConfig(codec, tx.DefaultSignModes),
65 | Amino: aminoCodec,
66 | }
67 |
68 | mb := module.NewBasicManager(modules...)
69 |
70 | std.RegisterLegacyAminoCodec(encCfg.Amino)
71 | std.RegisterInterfaces(encCfg.InterfaceRegistry)
72 | mb.RegisterLegacyAminoCodec(encCfg.Amino)
73 | mb.RegisterInterfaces(encCfg.InterfaceRegistry)
74 |
75 | return encCfg
76 | }
77 |
78 | func MakeTestTxConfig() client.TxConfig {
79 | interfaceRegistry := testutil.CodecOptions{}.NewInterfaceRegistry()
80 | cdc := codec.NewProtoCodec(interfaceRegistry)
81 | return tx.NewTxConfig(cdc, tx.DefaultSignModes)
82 | }
83 |
--------------------------------------------------------------------------------
/tutorials/nameservice/docs/00-getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Table of Contents
4 |
5 | - [Getting Started](#overview-of-the-project)
6 | - [Understanding Front-Running](./01-understanding-frontrunning.md)
7 | - [Mitigating Front-running with Vote Extensions](./02-mitigating-front-running-with-vote-extesions.md)
8 | - [Demo of Mitigating Front-Running](./03-demo-of-mitigating-front-running.md)
9 |
10 | ## Getting Started
11 |
12 | ### Overview of the Project
13 |
14 | This tutorial outlines the development of a module designed to mitigate front-running in nameservice auctions. The following functions are central to this module:
15 |
16 | * `ExtendVote`: Gathers bids from the mempool and includes them in the vote extension to ensure a fair and transparent auction process.
17 | * `PrepareProposal`: Processes the vote extensions from the previous block, creating a special transaction that encapsulates bids to be included in the current proposal.
18 | * `ProcessProposal`: Validates that the first transaction in the proposal is the special transaction containing the vote extensions and ensures the integrity of the bids.
19 |
20 | In this advanced tutorial, we will be working with an example application that facilitates the auctioning of nameservices. To see what frontrunning and nameservices are [here](./01-understanding-frontrunning.md) This application provides a practical use case to explore the prevention of auction front-running, also known as "bid sniping", where a validator takes advantage of seeing a bid in the mempool to place their own higher bid before the original bid is processed.
21 |
22 | The tutorial will guide you through using the Cosmos SDK to mitigate front-running using vote extensions. The module will be built on top of the base blockchain provided in the `tutorials/base` directory and will use the `auction` module as a foundation. By the end of this tutorial, you will have a better understanding of how to prevent front-running in blockchain auctions, specifically in the context of nameservice auctioning.
23 |
24 | ## What are Vote extensions?
25 |
26 | Vote extensions is arbitrary information which can be inserted into a block. This feature is part of ABCI 2.0, which is available for use in the SDK 0.50 release and part of the 0.38 CometBFT release.
27 |
28 | More information about vote extensions can be seen [here](https://docs.cosmos.network/main/build/abci/vote-extensions).
29 |
30 | ## Requirements and Setup
31 |
32 | Before diving into the advanced tutorial on auction front-running simulation, ensure you meet the following requirements:
33 |
34 | * [Golang >1.21.5](https://golang.org/doc/install) installed
35 | * Familiarity with the concepts of front-running and MEV, as detailed in [Understanding Front-Running](./01-understanding-frontrunning.md)
36 | * Understanding of Vote Extensions as described [here](https://docs.cosmos.network/main/build/abci/vote-extensions)
37 |
38 | You will also need a foundational blockchain to build upon coupled with your own module. The `tutorials/base` directory has the necessary blockchain code to start your custom project with the Cosmos SDK. For the module, you can use the `auction` module provided in the `tutorials/auction/x/auction` directory as a reference but please be aware that all of the code needed to implement vote extensions is already implemented in this module.
39 |
40 | This will set up a strong base for your blockchain, enabling the integration of advanced features such as auction front-running simulation.
41 |
--------------------------------------------------------------------------------
/tutorials/nameservice/docs/01-understanding-frontrunning.md:
--------------------------------------------------------------------------------
1 | # Understanding Front-Running and more
2 |
3 | ## Introduction
4 |
5 | Blockchain technology is vulnerable to practices that can affect the fairness and security of the network. Two such practices are front-running and Maximal Extractable Value (MEV), which are important for blockchain participants to understand.
6 |
7 | ## What is Front-Running?
8 |
9 | Front-running is when someone, such as a validator, uses their ability to see pending transactions to execute their own transactions first, benefiting from the knowledge of upcoming transactions. In nameservice auctions, a front-runner might place a higher bid before the original bid is confirmed, unfairly winning the auction.
10 |
11 | ## Nameservices and Nameservice Auctions
12 |
13 | Nameservices are human-readable identifiers on a blockchain, akin to internet domain names, that correspond to specific addresses or resources. They simplify interactions with typically long and complex blockchain addresses, allowing users to have a memorable and unique identifier for their blockchain address or smart contract.
14 |
15 | Nameservice auctions are the process by which these identifiers are bid on and acquired. To combat front-running—where someone might use knowledge of pending bids to place a higher bid first—mechanisms such as commit-reveal schemes, auction extensions, and fair sequencing are implemented. These strategies ensure a transparent and fair bidding process, reducing the potential for Maximal Extractable Value (MEV) exploitation.
16 |
17 | ## What is Maximal Extractable Value (MEV)?
18 |
19 | MEV is the highest value that can be extracted by manipulating the order of transactions within a block, beyond the standard block rewards and fees. This has become more prominent with the growth of decentralised finance (DeFi), where transaction order can greatly affect profits.
20 |
21 | ## Implications of MEV
22 |
23 | MEV can lead to:
24 |
25 | - **Network Security**: Potential centralisation, as those with more computational power might dominate the process, increasing the risk of attacks.
26 | - **Market Fairness**: An uneven playing field where only a few can gain at the expense of the majority.
27 | - **User Experience**: Higher fees and network congestion due to the competition for MEV.
28 |
29 | ## Mitigating MEV and Front-Running
30 |
31 | Some solutions being developed to mitigate MEV and front-running, including:
32 |
33 | - **Time-delayed Transactions**: Random delays to make transaction timing unpredictable.
34 | - **Private Transaction Pools**: Concealing transactions until they are mined.
35 | - **Fair Sequencing Services**: Processing transactions in the order they are received.
36 |
37 | For this tutorial, we will be exploring the last solution, fair sequencing services, in the context of nameservice auctions.
38 |
39 | ## Conclusion
40 |
41 | MEV and front-running are challenges to blockchain integrity and fairness. Ongoing innovation and implementation of mitigation strategies are crucial for the ecosystem's health and success.
42 |
--------------------------------------------------------------------------------
/tutorials/nameservice/docs/03-demo-of-mitigating-front-running.md:
--------------------------------------------------------------------------------
1 | # Demo of Mitigating Front-Running with Vote Extensions
2 |
3 | The purpose of this demo is to test the implementation of the `VoteExtensionHandler` and `PrepareProposalHandler` that we have just added to the codebase. These handlers are designed to mitigate front-running by ensuring that all validators have a consistent view of the mempool when preparing proposals.
4 |
5 | In this demo, we are using a 3 validator network. The Beacon validator is special because it has a custom transaction provider enabled. This means that it can potentially manipulate the order of transactions in a proposal to its advantage (i.e., front-running).
6 |
7 | 1. Bootstrap the validator network: This sets up a network with 3 validators. The script `./scripts/configure.sh is used to configure the network and the validators.
8 |
9 | ```shell
10 | cd scripts
11 | configure.sh
12 | ```
13 |
14 | If this doesn't work please ensure you have run `make build` in the `tutorials/nameservice/base` directory.
15 |
16 |
17 | 2. Have alice attempt to reserve `bob.cosmos`: This is a normal transaction that alice wants to execute. The script ``./scripts/reserve.sh "bob.cosmos"` is used to send this transaction.
18 |
19 | ```shell
20 | reserve.sh "bob.cosmos"
21 | ```
22 |
23 | 3. Query to verify the name has been reserved: This is to check the result of the transaction. The script `./scripts/whois.sh "bob.cosmos"` is used to query the state of the blockchain.
24 |
25 | ```shell
26 | whois.sh "bob.cosmos"
27 | ```
28 |
29 | It should return:
30 |
31 | ```{
32 | "name": {
33 | "name": "bob.cosmos",
34 | "owner": "cosmos1nq9wuvuju4jdmpmzvxmg8zhhu2ma2y2l2pnu6w",
35 | "resolve_address": "cosmos1h6zy2kn9efxtw5z22rc5k9qu7twl70z24kr3ht",
36 | "amount": [
37 | {
38 | "denom": "uatom",
39 | "amount": "1000"
40 | }
41 | ]
42 | }
43 | }
44 | ```
45 |
46 | To detect front-running attempts by the beacon, scrutinise the logs during the `ProcessProposal` stage. Open the logs for each validator, including the beacon, `val1`, and `val2`, to observe the following behavior. Open the log file of the validator node. The location of this file can vary depending on your setup, but it's typically located in a directory like `$HOME/cosmos/nodes/#{validator}/logs`. The directory in this case will be under the validator so, `beacon` `val1` or `val2`. Run the following to tail the logs of the validator or beacon:
47 |
48 | ```shell
49 | tail -f $HOME/cosmos/nodes/#{validator}/logs
50 | ```
51 |
52 | ```shell
53 | 2:47PM ERR ❌️:: Detected invalid proposal bid :: name:"bob.cosmos" resolveAddress:"cosmos1wmuwv38pdur63zw04t0c78r2a8dyt08hf9tpvd" owner:"cosmos1wmuwv38pdur63zw04t0c78r2a8dyt08hf9tpvd" amount: module=server
54 | 2:47PM ERR ❌️:: Unable to validate bids in Process Proposal :: module=server
55 | 2:47PM ERR prevote step: state machine rejected a proposed block; this should not happen:the proposer may be misbehaving; prevoting nil err=null height=142 module=consensus round=0
56 | ```
57 |
58 |
59 | 4. List the Beacon's keys: This is to verify the addresses of the validators. The script `./scripts/list-beacon-keys.sh` is used to list the keys.
60 |
61 | ```shell
62 | list-beacon-keys.sh
63 | ```
64 |
65 | We should receive something similar to the following:
66 |
67 | ```shell
68 | [
69 | {
70 | "name": "alice",
71 | "type": "local",
72 | "address": "cosmos1h6zy2kn9efxtw5z22rc5k9qu7twl70z24kr3ht",
73 | "pubkey": "{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A32cvBUkNJz+h2vld4A5BxvU5Rd+HyqpR3aGtvEhlm4C\"}"
74 | },
75 | {
76 | "name": "barbara",
77 | "type": "local",
78 | "address": "cosmos1nq9wuvuju4jdmpmzvxmg8zhhu2ma2y2l2pnu6w",
79 | "pubkey": "{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"Ag9PFsNyTQPoJdbyCWia5rZH9CrvSrjMsk7Oz4L3rXQ5\"}"
80 | },
81 | {
82 | "name": "beacon-key",
83 | "type": "local",
84 | "address": "cosmos1ez9a6x7lz4gvn27zr368muw8jeyas7sv84lfup",
85 | "pubkey": "{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"AlzJZMWyN7lass710TnAhyuFKAFIaANJyw5ad5P2kpcH\"}"
86 | },
87 | {
88 | "name": "cindy",
89 | "type": "local",
90 | "address": "cosmos1m5j6za9w4qc2c5ljzxmm2v7a63mhjeag34pa3g",
91 | "pubkey": "{\"@type\":\"/cosmos.crypto.secp256k1.PubKey\",\"key\":\"A6F1/3yot5OpyXoSkBbkyl+3rqBkxzRVSJfvSpm/AvW5\"}"
92 | }
93 | ]
94 | ```
95 |
96 | This allows us to match up the addresses and see that the bid was not front run by the beacon due as the resolve address is Alice's address and not the beacons address.
97 |
98 | By running this demo, we can verify that the `VoteExtensionHandler` and `PrepareProposalHandler` are working as expected and that they are able to prevent front-running.
99 |
100 | ## Conclusion
101 |
102 | In this tutorial, we've tackled front-running and MEV, focusing on nameservice auctions' vulnerability to these issues. We've explored vote extensions, a key feature of ABCI 2.0, and integrated them into a Cosmos SDK application.
103 |
104 | Through practical exercises, you've implemented vote extensions, and tested their effectiveness in creating a fair auction system. You've gained practical insights by configuring a validator network and analysing blockchain logs.
105 |
106 | Keep experimenting with these concepts, engage with the community, and stay updated on new advancements. The knowledge you've acquired here is crucial for developing secure and fair blockchain applications.
107 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/Makefile:
--------------------------------------------------------------------------------
1 | BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
2 | COMMIT := $(shell git log -1 --format='%H')
3 |
4 | # don't override user values
5 | ifeq (,$(VERSION))
6 | VERSION := $(shell git describe --exact-match 2>/dev/null)
7 | # if VERSION is empty, then populate it with branch's name and raw commit hash
8 | ifeq (,$(VERSION))
9 | VERSION := $(BRANCH)-$(COMMIT)
10 | endif
11 | endif
12 |
13 | # Update the ldflags with the app, client & server names
14 | ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=example \
15 | -X github.com/cosmos/cosmos-sdk/version.AppName=exampled \
16 | -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \
17 | -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT)
18 |
19 | BUILD_FLAGS := -ldflags '$(ldflags)'
20 |
21 | ###########
22 | # Install #
23 | ###########
24 |
25 | all: install
26 |
27 | install:
28 | @echo "--> ensure dependencies have not been modified"
29 | @go mod verify
30 | @echo "--> installing exampled"
31 | @go install $(BUILD_FLAGS) -mod=readonly ./cmd/exampled
32 |
33 | init:
34 | ./scripts/init.sh
--------------------------------------------------------------------------------
/tutorials/oracle/base/app/app.yaml:
--------------------------------------------------------------------------------
1 | modules:
2 | - name: runtime
3 | config:
4 | "@type": cosmos.app.runtime.v1alpha1.Module
5 | app_name: ExampleApp
6 | # During begin block slashing happens after distr.BeginBlocker so that
7 | # there is nothing left over in the validator fee pool, so as to keep the CanWithdrawInvariant invariant.
8 | # NOTE: staking module is required if HistoricalEntries param > 0
9 | begin_blockers: [distribution, staking]
10 | end_blockers: [staking]
11 | # NOTE: The genutils module must occur after staking so that pools are properly initialized with tokens from genesis accounts.
12 | # NOTE: The genutils module must also occur after auth so that it can access the params from auth.
13 | init_genesis: [auth, bank, distribution, staking, genutil, oracle]
14 | override_store_keys:
15 | - module_name: auth
16 | kv_store_key: acc
17 | - name: auth
18 | config:
19 | "@type": cosmos.auth.module.v1.Module
20 | bech32_prefix: tutorial
21 | module_account_permissions:
22 | - account: fee_collector
23 | - account: distribution
24 | - account: bonded_tokens_pool
25 | permissions: [burner, staking]
26 | - account: not_bonded_tokens_pool
27 | permissions: [burner, staking]
28 | - name: bank
29 | config:
30 | "@type": cosmos.bank.module.v1.Module
31 | blocked_module_accounts_override:
32 | [auth, distribution, bonded_tokens_pool, not_bonded_tokens_pool]
33 | - name: staking
34 | config:
35 | "@type": cosmos.staking.module.v1.Module
36 | - name: distribution
37 | config:
38 | "@type": cosmos.distribution.module.v1.Module
39 | - name: consensus
40 | config:
41 | "@type": cosmos.consensus.module.v1.Module
42 | - name: genutil
43 | config:
44 | "@type": cosmos.genutil.module.v1.Module
45 | - name: tx
46 | config:
47 | "@type": cosmos.tx.config.v1.Config
48 | - name: oracle
49 | config:
50 | "@type": cosmos.oracle.module.v1.Module
--------------------------------------------------------------------------------
/tutorials/oracle/base/app/params/config.go:
--------------------------------------------------------------------------------
1 | package params
2 |
3 | import (
4 | "cosmossdk.io/errors"
5 | "cosmossdk.io/math"
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | "github.com/cosmos/cosmos-sdk/types/address"
8 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
9 | )
10 |
11 | const (
12 | CoinUnit = "tutorial"
13 |
14 | DefaultBondDenom = CoinUnit
15 |
16 | // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address.
17 | Bech32PrefixAccAddr = "tutorial"
18 | )
19 |
20 | var (
21 | // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key.
22 | Bech32PrefixAccPub = Bech32PrefixAccAddr + "pub"
23 | // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address.
24 | Bech32PrefixValAddr = Bech32PrefixAccAddr + "valoper"
25 | // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key.
26 | Bech32PrefixValPub = Bech32PrefixAccAddr + "valoperpub"
27 | // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address.
28 | Bech32PrefixConsAddr = Bech32PrefixAccAddr + "valcons"
29 | // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key.
30 | Bech32PrefixConsPub = Bech32PrefixAccAddr + "valconspub"
31 | )
32 |
33 | func init() {
34 | SetAddressPrefixes()
35 | RegisterDenoms()
36 | }
37 |
38 | func RegisterDenoms() {
39 | err := sdk.RegisterDenom(CoinUnit, math.LegacyOneDec())
40 | if err != nil {
41 | panic(err)
42 | }
43 | }
44 |
45 | func SetAddressPrefixes() {
46 | config := sdk.GetConfig()
47 | config.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub)
48 | config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub)
49 | config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub)
50 |
51 | // This is copied from the cosmos sdk v0.43.0-beta1
52 | // source: https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-beta1/types/address.go#L141
53 | config.SetAddressVerifier(func(bytes []byte) error {
54 | if len(bytes) == 0 {
55 | return errors.Wrap(sdkerrors.ErrUnknownAddress, "addresses cannot be empty")
56 | }
57 |
58 | if len(bytes) > address.MaxAddrLen {
59 | return errors.Wrapf(sdkerrors.ErrUnknownAddress, "address max length is %d, got %d", address.MaxAddrLen, len(bytes))
60 | }
61 |
62 | if len(bytes) != 20 && len(bytes) != 32 {
63 | return errors.Wrapf(sdkerrors.ErrUnknownAddress, "address length must be 20 or 32 bytes, got %d", len(bytes))
64 | }
65 |
66 | return nil
67 | })
68 | }
69 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/app/params/encoding.go:
--------------------------------------------------------------------------------
1 | package params
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/client"
5 | "github.com/cosmos/cosmos-sdk/codec"
6 | "github.com/cosmos/cosmos-sdk/codec/types"
7 | )
8 |
9 | // EncodingConfig specifies the concrete encoding types to use for a given app.
10 | // This is provided for compatibility between protobuf and amino implementations.
11 | type EncodingConfig struct {
12 | InterfaceRegistry types.InterfaceRegistry
13 | Codec codec.Codec
14 | TxConfig client.TxConfig
15 | Amino *codec.LegacyAmino
16 | }
17 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/cmd/exampled/cmd/commands.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "errors"
5 | "io"
6 |
7 | "cosmossdk.io/log"
8 | confixcmd "cosmossdk.io/tools/confix/cmd"
9 | dbm "github.com/cosmos/cosmos-db"
10 | "github.com/cosmos/cosmos-sdk/client"
11 | "github.com/cosmos/cosmos-sdk/client/debug"
12 | "github.com/cosmos/cosmos-sdk/client/flags"
13 | "github.com/cosmos/cosmos-sdk/client/keys"
14 | "github.com/cosmos/cosmos-sdk/client/pruning"
15 | "github.com/cosmos/cosmos-sdk/client/rpc"
16 | "github.com/cosmos/cosmos-sdk/client/snapshot"
17 | "github.com/cosmos/cosmos-sdk/server"
18 | servertypes "github.com/cosmos/cosmos-sdk/server/types"
19 | sdk "github.com/cosmos/cosmos-sdk/types"
20 | "github.com/cosmos/cosmos-sdk/types/module"
21 | authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
22 | genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
23 | "github.com/spf13/cobra"
24 | "github.com/spf13/viper"
25 |
26 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/app"
27 | )
28 |
29 | func initRootCmd(rootCmd *cobra.Command, txConfig client.TxConfig, basicManager module.BasicManager) {
30 | cfg := sdk.GetConfig()
31 | cfg.Seal()
32 |
33 | rootCmd.AddCommand(
34 | genutilcli.InitCmd(basicManager, app.DefaultNodeHome),
35 | debug.Cmd(),
36 | confixcmd.ConfigCommand(),
37 | pruning.Cmd(newApp, app.DefaultNodeHome),
38 | snapshot.Cmd(newApp),
39 | )
40 |
41 | server.AddCommands(rootCmd, app.DefaultNodeHome, newApp, appExport, func(startCmd *cobra.Command) {})
42 |
43 | // add keybase, auxiliary RPC, query, genesis, and tx child commands
44 | rootCmd.AddCommand(
45 | server.StatusCommand(),
46 | genutilcli.Commands(txConfig, basicManager, app.DefaultNodeHome),
47 | queryCommand(),
48 | txCommand(),
49 | keys.Commands(),
50 | )
51 | }
52 |
53 | func queryCommand() *cobra.Command {
54 | cmd := &cobra.Command{
55 | Use: "query",
56 | Aliases: []string{"q"},
57 | Short: "Querying subcommands",
58 | DisableFlagParsing: false,
59 | SuggestionsMinimumDistance: 2,
60 | RunE: client.ValidateCmd,
61 | }
62 |
63 | cmd.AddCommand(
64 | rpc.ValidatorCommand(),
65 | server.QueryBlockCmd(),
66 | authcmd.QueryTxsByEventsCmd(),
67 | server.QueryBlocksCmd(),
68 | authcmd.QueryTxCmd(),
69 | server.QueryBlockResultsCmd(),
70 | )
71 |
72 | return cmd
73 | }
74 |
75 | func txCommand() *cobra.Command {
76 | cmd := &cobra.Command{
77 | Use: "tx",
78 | Short: "Transactions subcommands",
79 | DisableFlagParsing: false,
80 | SuggestionsMinimumDistance: 2,
81 | RunE: client.ValidateCmd,
82 | }
83 |
84 | cmd.AddCommand(
85 | authcmd.GetSignCommand(),
86 | authcmd.GetSignBatchCommand(),
87 | authcmd.GetMultiSignCommand(),
88 | authcmd.GetMultiSignBatchCmd(),
89 | authcmd.GetValidateSignaturesCommand(),
90 | authcmd.GetBroadcastCommand(),
91 | authcmd.GetEncodeCommand(),
92 | authcmd.GetDecodeCommand(),
93 | authcmd.GetSimulateCmd(),
94 | )
95 |
96 | return cmd
97 | }
98 |
99 | // newApp is an appCreator
100 | func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application {
101 | baseappOptions := server.DefaultBaseappOptions(appOpts)
102 | app, err := app.NewExampleApp(logger, db, traceStore, true, appOpts, baseappOptions...)
103 | if err != nil {
104 | panic(err)
105 | }
106 |
107 | return app
108 | }
109 |
110 | // appExport creates a new app (optionally at a given height) and exports state.
111 | func appExport(
112 | logger log.Logger,
113 | db dbm.DB,
114 | traceStore io.Writer,
115 | height int64,
116 | forZeroHeight bool,
117 | jailAllowedAddrs []string,
118 | appOpts servertypes.AppOptions,
119 | modulesToExport []string,
120 | ) (servertypes.ExportedApp, error) {
121 | var (
122 | ExampleApp *app.ExampleApp
123 | err error
124 | )
125 |
126 | // this check is necessary as we use the flag in x/upgrade.
127 | // we can exit more gracefully by checking the flag here.
128 | homePath, ok := appOpts.Get(flags.FlagHome).(string)
129 | if !ok || homePath == "" {
130 | return servertypes.ExportedApp{}, errors.New("application home not set")
131 | }
132 |
133 | viperAppOpts, ok := appOpts.(*viper.Viper)
134 | if !ok {
135 | return servertypes.ExportedApp{}, errors.New("appOpts is not viper.Viper")
136 | }
137 |
138 | // overwrite the FlagInvCheckPeriod
139 | viperAppOpts.Set(server.FlagInvCheckPeriod, 1)
140 | appOpts = viperAppOpts
141 |
142 | if height != -1 {
143 | ExampleApp, err = app.NewExampleApp(logger, db, traceStore, false, appOpts)
144 | if err != nil {
145 | return servertypes.ExportedApp{}, err
146 | }
147 |
148 | if err := ExampleApp.LoadHeight(height); err != nil {
149 | return servertypes.ExportedApp{}, err
150 | }
151 | } else {
152 | ExampleApp, err = app.NewExampleApp(logger, db, traceStore, true, appOpts)
153 | if err != nil {
154 | return servertypes.ExportedApp{}, err
155 | }
156 | }
157 |
158 | return ExampleApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
159 | }
160 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/cmd/exampled/cmd/root.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "os"
5 | "time"
6 |
7 | "cosmossdk.io/client/v2/autocli"
8 | clientv2keyring "cosmossdk.io/client/v2/autocli/keyring"
9 | "cosmossdk.io/core/address"
10 | "cosmossdk.io/depinject"
11 | "cosmossdk.io/log"
12 | cmtcfg "github.com/cometbft/cometbft/config"
13 | "github.com/cosmos/cosmos-sdk/client"
14 | "github.com/cosmos/cosmos-sdk/client/config"
15 | "github.com/cosmos/cosmos-sdk/codec"
16 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
17 | "github.com/cosmos/cosmos-sdk/crypto/keyring"
18 | "github.com/cosmos/cosmos-sdk/server"
19 | serverconfig "github.com/cosmos/cosmos-sdk/server/config"
20 | "github.com/cosmos/cosmos-sdk/types/module"
21 | "github.com/cosmos/cosmos-sdk/types/tx/signing"
22 | "github.com/cosmos/cosmos-sdk/x/auth/tx"
23 | txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
24 | "github.com/cosmos/cosmos-sdk/x/auth/types"
25 | "github.com/spf13/cobra"
26 |
27 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/app"
28 | )
29 |
30 | // NewRootCmd creates a new root command for exampled. It is called once in the
31 | // main function.
32 | func NewRootCmd() *cobra.Command {
33 | var (
34 | // It can be used to set options like the signer, fee granularity, and other transaction-related configurations.
35 | txConfigOpts tx.ConfigOptions
36 | // It can include options for modules, address codecs, and other CLI-related configurations.
37 | autoCliOpts autocli.AppOptions
38 | // This includes things like genesis data, default genesis data, verifying genesis data, and the module's name.
39 | moduleBasicManager module.BasicManager
40 | // This can include things like the client's home directory, the client's input/output, and the client's trust node.
41 | clientCtx client.Context
42 | )
43 |
44 | if err := depinject.Inject(
45 | depinject.Configs(app.AppConfig(),
46 | depinject.Supply(
47 | log.NewNopLogger(),
48 | ),
49 | depinject.Provide(
50 | ProvideClientContext,
51 | ProvideKeyring,
52 | ),
53 | ),
54 | &txConfigOpts,
55 | &autoCliOpts,
56 | &moduleBasicManager,
57 | &clientCtx,
58 | ); err != nil {
59 | panic(err)
60 | }
61 |
62 | rootCmd := &cobra.Command{
63 | Use: "exampled",
64 | Short: "exampled - the example chain app",
65 | PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
66 | // set the default command outputs
67 | cmd.SetOut(cmd.OutOrStdout())
68 | cmd.SetErr(cmd.ErrOrStderr())
69 |
70 | clientCtx = clientCtx.WithCmdContext(cmd.Context())
71 | clientCtx, err := client.ReadPersistentCommandFlags(clientCtx, cmd.Flags())
72 | if err != nil {
73 | return err
74 | }
75 |
76 | clientCtx, err = config.ReadFromClientConfig(clientCtx)
77 | if err != nil {
78 | return err
79 | }
80 |
81 | // sign mode textual is only available in online mode
82 | if !clientCtx.Offline {
83 | // This needs to go after ReadFromClientConfig, as that function ets the RPC client needed for SIGN_MODE_TEXTUAL.
84 | txConfigOpts.EnabledSignModes = append(txConfigOpts.EnabledSignModes, signing.SignMode_SIGN_MODE_TEXTUAL)
85 | txConfigOpts.TextualCoinMetadataQueryFn = txmodule.NewGRPCCoinMetadataQueryFn(clientCtx)
86 | txConfigWithTextual, err := tx.NewTxConfigWithOptions(codec.NewProtoCodec(clientCtx.InterfaceRegistry), txConfigOpts)
87 | if err != nil {
88 | return err
89 | }
90 |
91 | clientCtx = clientCtx.WithTxConfig(txConfigWithTextual)
92 | }
93 |
94 | if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil {
95 | return err
96 | }
97 |
98 | if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil {
99 | return err
100 | }
101 |
102 | // overwrite the minimum gas price from the app configuration
103 | srvCfg := serverconfig.DefaultConfig()
104 | srvCfg.MinGasPrices = "0example"
105 |
106 | // overwrite the block timeout
107 | cmtCfg := cmtcfg.DefaultConfig()
108 | cmtCfg.Consensus.TimeoutCommit = 3 * time.Second
109 | cmtCfg.LogLevel = "*:error,p2p:info,state:info" // better default logging
110 |
111 | // TODO: copy from nameservice/simapp
112 | return server.InterceptConfigsPreRunHandler(cmd, "", srvCfg, cmtCfg)
113 | },
114 | }
115 |
116 | initRootCmd(rootCmd, clientCtx.TxConfig, moduleBasicManager)
117 |
118 | if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
119 | panic(err)
120 | }
121 |
122 | return rootCmd
123 | }
124 |
125 | func ProvideClientContext(
126 | appCodec codec.Codec,
127 | interfaceRegistry codectypes.InterfaceRegistry,
128 | txConfig client.TxConfig,
129 | legacyAmino *codec.LegacyAmino,
130 | ) client.Context {
131 | clientCtx := client.Context{}.
132 | WithCodec(appCodec).
133 | WithInterfaceRegistry(interfaceRegistry).
134 | WithTxConfig(txConfig).
135 | WithLegacyAmino(legacyAmino).
136 | WithInput(os.Stdin).
137 | WithAccountRetriever(types.AccountRetriever{}).
138 | WithHomeDir(app.DefaultNodeHome).
139 | WithViper("TUTORIAL") // env variable prefix
140 |
141 | // Read the config again to overwrite the default values with the values from the config file
142 | clientCtx, _ = config.ReadFromClientConfig(clientCtx)
143 |
144 | return clientCtx
145 | }
146 |
147 | func ProvideKeyring(clientCtx client.Context, addressCodec address.Codec) (clientv2keyring.Keyring, error) {
148 | kb, err := client.NewKeyringFromBackend(clientCtx, clientCtx.Keyring.Backend())
149 | if err != nil {
150 | return nil, err
151 | }
152 |
153 | return keyring.NewAutoCLIKeyring(kb)
154 | }
155 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/cmd/exampled/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
8 |
9 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/app"
10 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/app/params"
11 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/cmd/exampled/cmd"
12 | )
13 |
14 | func main() {
15 | params.SetAddressPrefixes()
16 |
17 | rootCmd := cmd.NewRootCmd()
18 | if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil {
19 | fmt.Fprintln(rootCmd.OutOrStderr(), err)
20 | os.Exit(1)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/scripts/init.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | rm -r ~/.exampled || true
4 | exampled_BIN=$(which exampled)
5 | # configure exampled
6 | $exampled_BIN config set client chain-id demo
7 | $exampled_BIN config set client keyring-backend test
8 | $exampled_BIN keys add alice
9 | $exampled_BIN keys add bob
10 | $exampled_BIN init test --chain-id demo --default-denom example
11 | # update genesis
12 | $exampled_BIN genesis add-genesis-account alice 10000000example --keyring-backend test
13 | $exampled_BIN genesis add-genesis-account bob 1000example --keyring-backend test
14 | # create default validator
15 | $exampled_BIN genesis gentx alice 1000000example --chain-id demo
16 | sed -i.bak'' 's/minimum-gas-prices = ""/minimum-gas-prices = "0.025uatom"/' $HOME/.exampled/config/app.toml
17 | jq '.consensus.params.abci.vote_extensions_enable_height = "1"' ~/.exampled/config/genesis.json > output.json && mv output.json ~/.exampled/config/genesis.json
18 | $exampled_BIN genesis collect-gentxs
19 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/Makefile:
--------------------------------------------------------------------------------
1 | DOCKER := $(shell which docker)
2 |
3 | #################
4 | ### Build ###
5 | #################
6 |
7 | test:
8 | @echo "--> Running tests"
9 | go test -v ./...
10 |
11 | test-integration:
12 | @echo "--> Running integration tests"
13 | cd integration; go test -v ./...
14 |
15 | .PHONY: test test-integration
16 |
17 | ##################
18 | ### Protobuf ###
19 | ##################
20 |
21 | protoVer=0.14.0
22 | protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer)
23 | protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName)
24 |
25 | proto-all: proto-format proto-lint proto-gen
26 |
27 | proto-gen:
28 | @echo "Generating protobuf files..."
29 | @$(protoImage) sh ./scripts/protocgen.sh
30 | @go mod tidy
31 |
32 | proto-format:
33 | @$(protoImage) find ./ -name "*.proto" -exec clang-format -i {} \;
34 |
35 | proto-lint:
36 | @$(protoImage) buf lint proto/ --error-format=json
37 |
38 | .PHONY: proto-all proto-gen proto-format proto-lint
39 |
40 | #################
41 | ### Linting ###
42 | #################
43 |
44 | golangci_lint_cmd=golangci-lint
45 | golangci_version=v1.51.2
46 |
47 | lint:
48 | @echo "--> Running linter"
49 | @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
50 | @$(golangci_lint_cmd) run ./... --timeout 15m
51 |
52 | lint-fix:
53 | @echo "--> Running linter and fixing issues"
54 | @go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(golangci_version)
55 | @$(golangci_lint_cmd) run ./... --fix --timeout 15m
56 |
57 | .PHONY: lint lint-fix
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/abci/provider_aggregator.go:
--------------------------------------------------------------------------------
1 | package abci
2 |
3 | import (
4 | "sync"
5 |
6 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle/keeper"
7 | )
8 |
9 | // Provider defines an interface for fetching prices and candles for a given set
10 | // of currency pairs. The provider is presumed to be a trusted source of prices.
11 | type Provider interface {
12 | GetTickerPrices(...keeper.CurrencyPair) (map[string]keeper.TickerPrice, error)
13 | }
14 |
15 | // ProviderAggregator is a simple aggregator for provider prices and candles.
16 | // It is thread-safe since it is assumed to be called concurrently in price
17 | // fetching goroutines, i.e. ExtendVote.
18 | type ProviderAggregator struct {
19 | mtx sync.Mutex
20 |
21 | providerPrices keeper.AggregatedProviderPrices
22 | }
23 |
24 | func NewProviderAggregator() *ProviderAggregator {
25 | return &ProviderAggregator{
26 | providerPrices: make(keeper.AggregatedProviderPrices),
27 | }
28 | }
29 |
30 | func (p *ProviderAggregator) SetProviderTickerPricesAndCandles(
31 | providerName string,
32 | prices map[string]keeper.TickerPrice,
33 | pair keeper.CurrencyPair,
34 | ) bool {
35 | p.mtx.Lock()
36 | defer p.mtx.Unlock()
37 |
38 | // set prices and candles for this provider if we haven't seen it before
39 | if _, ok := p.providerPrices[providerName]; !ok {
40 | p.providerPrices[providerName] = make(map[string]keeper.TickerPrice)
41 | }
42 |
43 | // set price for provider/base (e.g. Binance -> ATOM -> 11.98)
44 | tp, pricesOk := prices[pair.String()]
45 | if pricesOk {
46 | p.providerPrices[providerName][pair.Base] = tp
47 | }
48 |
49 | // return true if we set at least one price
50 | return pricesOk
51 | }
52 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/api/v1/query_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 | // versions:
3 | // - protoc-gen-go-grpc v1.3.0
4 | // - protoc (unknown)
5 | // source: cosmos/oracle/v1/query.proto
6 |
7 | package oraclev1
8 |
9 | import (
10 | context "context"
11 | grpc "google.golang.org/grpc"
12 | codes "google.golang.org/grpc/codes"
13 | status "google.golang.org/grpc/status"
14 | )
15 |
16 | // This is a compile-time assertion to ensure that this generated file
17 | // is compatible with the grpc package it is being compiled against.
18 | // Requires gRPC-Go v1.32.0 or later.
19 | const _ = grpc.SupportPackageIsVersion7
20 |
21 | const (
22 | Query_Counter_FullMethodName = "/cosmos.oracle.v1.Query/Counter"
23 | Query_Prices_FullMethodName = "/cosmos.oracle.v1.Query/Prices"
24 | )
25 |
26 | // QueryClient is the client API for Query service.
27 | //
28 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
29 | type QueryClient interface {
30 | // Counter returns the current counter value.
31 | Counter(ctx context.Context, in *QueryCounterRequest, opts ...grpc.CallOption) (*QueryCounterResponse, error)
32 | // Prices returns the current prices.
33 | Prices(ctx context.Context, in *QueryPricesRequest, opts ...grpc.CallOption) (*QueryPricesResponse, error)
34 | }
35 |
36 | type queryClient struct {
37 | cc grpc.ClientConnInterface
38 | }
39 |
40 | func NewQueryClient(cc grpc.ClientConnInterface) QueryClient {
41 | return &queryClient{cc}
42 | }
43 |
44 | func (c *queryClient) Counter(ctx context.Context, in *QueryCounterRequest, opts ...grpc.CallOption) (*QueryCounterResponse, error) {
45 | out := new(QueryCounterResponse)
46 | err := c.cc.Invoke(ctx, Query_Counter_FullMethodName, in, out, opts...)
47 | if err != nil {
48 | return nil, err
49 | }
50 | return out, nil
51 | }
52 |
53 | func (c *queryClient) Prices(ctx context.Context, in *QueryPricesRequest, opts ...grpc.CallOption) (*QueryPricesResponse, error) {
54 | out := new(QueryPricesResponse)
55 | err := c.cc.Invoke(ctx, Query_Prices_FullMethodName, in, out, opts...)
56 | if err != nil {
57 | return nil, err
58 | }
59 | return out, nil
60 | }
61 |
62 | // QueryServer is the server API for Query service.
63 | // All implementations must embed UnimplementedQueryServer
64 | // for forward compatibility
65 | type QueryServer interface {
66 | // Counter returns the current counter value.
67 | Counter(context.Context, *QueryCounterRequest) (*QueryCounterResponse, error)
68 | // Prices returns the current prices.
69 | Prices(context.Context, *QueryPricesRequest) (*QueryPricesResponse, error)
70 | mustEmbedUnimplementedQueryServer()
71 | }
72 |
73 | // UnimplementedQueryServer must be embedded to have forward compatible implementations.
74 | type UnimplementedQueryServer struct {
75 | }
76 |
77 | func (UnimplementedQueryServer) Counter(context.Context, *QueryCounterRequest) (*QueryCounterResponse, error) {
78 | return nil, status.Errorf(codes.Unimplemented, "method Counter not implemented")
79 | }
80 | func (UnimplementedQueryServer) Prices(context.Context, *QueryPricesRequest) (*QueryPricesResponse, error) {
81 | return nil, status.Errorf(codes.Unimplemented, "method Prices not implemented")
82 | }
83 | func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {}
84 |
85 | // UnsafeQueryServer may be embedded to opt out of forward compatibility for this service.
86 | // Use of this interface is not recommended, as added methods to QueryServer will
87 | // result in compilation errors.
88 | type UnsafeQueryServer interface {
89 | mustEmbedUnimplementedQueryServer()
90 | }
91 |
92 | func RegisterQueryServer(s grpc.ServiceRegistrar, srv QueryServer) {
93 | s.RegisterService(&Query_ServiceDesc, srv)
94 | }
95 |
96 | func _Query_Counter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
97 | in := new(QueryCounterRequest)
98 | if err := dec(in); err != nil {
99 | return nil, err
100 | }
101 | if interceptor == nil {
102 | return srv.(QueryServer).Counter(ctx, in)
103 | }
104 | info := &grpc.UnaryServerInfo{
105 | Server: srv,
106 | FullMethod: Query_Counter_FullMethodName,
107 | }
108 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
109 | return srv.(QueryServer).Counter(ctx, req.(*QueryCounterRequest))
110 | }
111 | return interceptor(ctx, in, info, handler)
112 | }
113 |
114 | func _Query_Prices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
115 | in := new(QueryPricesRequest)
116 | if err := dec(in); err != nil {
117 | return nil, err
118 | }
119 | if interceptor == nil {
120 | return srv.(QueryServer).Prices(ctx, in)
121 | }
122 | info := &grpc.UnaryServerInfo{
123 | Server: srv,
124 | FullMethod: Query_Prices_FullMethodName,
125 | }
126 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
127 | return srv.(QueryServer).Prices(ctx, req.(*QueryPricesRequest))
128 | }
129 | return interceptor(ctx, in, info, handler)
130 | }
131 |
132 | // Query_ServiceDesc is the grpc.ServiceDesc for Query service.
133 | // It's only intended for direct use with grpc.RegisterService,
134 | // and not to be introspected or modified (even as a copy)
135 | var Query_ServiceDesc = grpc.ServiceDesc{
136 | ServiceName: "cosmos.oracle.v1.Query",
137 | HandlerType: (*QueryServer)(nil),
138 | Methods: []grpc.MethodDesc{
139 | {
140 | MethodName: "Counter",
141 | Handler: _Query_Counter_Handler,
142 | },
143 | {
144 | MethodName: "Prices",
145 | Handler: _Query_Prices_Handler,
146 | },
147 | },
148 | Streams: []grpc.StreamDesc{},
149 | Metadata: "cosmos/oracle/v1/query.proto",
150 | }
151 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/api/v1/tx_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 | // versions:
3 | // - protoc-gen-go-grpc v1.3.0
4 | // - protoc (unknown)
5 | // source: cosmos/oracle/v1/tx.proto
6 |
7 | package oraclev1
8 |
9 | import (
10 | context "context"
11 | grpc "google.golang.org/grpc"
12 | codes "google.golang.org/grpc/codes"
13 | status "google.golang.org/grpc/status"
14 | )
15 |
16 | // This is a compile-time assertion to ensure that this generated file
17 | // is compatible with the grpc package it is being compiled against.
18 | // Requires gRPC-Go v1.32.0 or later.
19 | const _ = grpc.SupportPackageIsVersion7
20 |
21 | const (
22 | Msg_IncrementCounter_FullMethodName = "/cosmos.oracle.v1.Msg/IncrementCounter"
23 | )
24 |
25 | // MsgClient is the client API for Msg service.
26 | //
27 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
28 | type MsgClient interface {
29 | // IncrementCounter increments the counter.
30 | IncrementCounter(ctx context.Context, in *MsgIncrementCounter, opts ...grpc.CallOption) (*MsgIncrementCounterResponse, error)
31 | }
32 |
33 | type msgClient struct {
34 | cc grpc.ClientConnInterface
35 | }
36 |
37 | func NewMsgClient(cc grpc.ClientConnInterface) MsgClient {
38 | return &msgClient{cc}
39 | }
40 |
41 | func (c *msgClient) IncrementCounter(ctx context.Context, in *MsgIncrementCounter, opts ...grpc.CallOption) (*MsgIncrementCounterResponse, error) {
42 | out := new(MsgIncrementCounterResponse)
43 | err := c.cc.Invoke(ctx, Msg_IncrementCounter_FullMethodName, in, out, opts...)
44 | if err != nil {
45 | return nil, err
46 | }
47 | return out, nil
48 | }
49 |
50 | // MsgServer is the server API for Msg service.
51 | // All implementations must embed UnimplementedMsgServer
52 | // for forward compatibility
53 | type MsgServer interface {
54 | // IncrementCounter increments the counter.
55 | IncrementCounter(context.Context, *MsgIncrementCounter) (*MsgIncrementCounterResponse, error)
56 | mustEmbedUnimplementedMsgServer()
57 | }
58 |
59 | // UnimplementedMsgServer must be embedded to have forward compatible implementations.
60 | type UnimplementedMsgServer struct {
61 | }
62 |
63 | func (UnimplementedMsgServer) IncrementCounter(context.Context, *MsgIncrementCounter) (*MsgIncrementCounterResponse, error) {
64 | return nil, status.Errorf(codes.Unimplemented, "method IncrementCounter not implemented")
65 | }
66 | func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {}
67 |
68 | // UnsafeMsgServer may be embedded to opt out of forward compatibility for this service.
69 | // Use of this interface is not recommended, as added methods to MsgServer will
70 | // result in compilation errors.
71 | type UnsafeMsgServer interface {
72 | mustEmbedUnimplementedMsgServer()
73 | }
74 |
75 | func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) {
76 | s.RegisterService(&Msg_ServiceDesc, srv)
77 | }
78 |
79 | func _Msg_IncrementCounter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
80 | in := new(MsgIncrementCounter)
81 | if err := dec(in); err != nil {
82 | return nil, err
83 | }
84 | if interceptor == nil {
85 | return srv.(MsgServer).IncrementCounter(ctx, in)
86 | }
87 | info := &grpc.UnaryServerInfo{
88 | Server: srv,
89 | FullMethod: Msg_IncrementCounter_FullMethodName,
90 | }
91 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
92 | return srv.(MsgServer).IncrementCounter(ctx, req.(*MsgIncrementCounter))
93 | }
94 | return interceptor(ctx, in, info, handler)
95 | }
96 |
97 | // Msg_ServiceDesc is the grpc.ServiceDesc for Msg service.
98 | // It's only intended for direct use with grpc.RegisterService,
99 | // and not to be introspected or modified (even as a copy)
100 | var Msg_ServiceDesc = grpc.ServiceDesc{
101 | ServiceName: "cosmos.oracle.v1.Msg",
102 | HandlerType: (*MsgServer)(nil),
103 | Methods: []grpc.MethodDesc{
104 | {
105 | MethodName: "IncrementCounter",
106 | Handler: _Msg_IncrementCounter_Handler,
107 | },
108 | },
109 | Streams: []grpc.StreamDesc{},
110 | Metadata: "cosmos/oracle/v1/tx.proto",
111 | }
112 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/codec.go:
--------------------------------------------------------------------------------
1 | package oracle
2 |
3 | import (
4 | types "github.com/cosmos/cosmos-sdk/codec/types"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | "github.com/cosmos/cosmos-sdk/types/msgservice"
7 | )
8 |
9 | // RegisterInterfaces registers the interfaces types with the interface registry.
10 | func RegisterInterfaces(registry types.InterfaceRegistry) {
11 | registry.RegisterImplementations((*sdk.Msg)(nil),
12 | &MsgIncrementCounter{},
13 | )
14 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
15 | }
16 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/errors.go:
--------------------------------------------------------------------------------
1 | package oracle
2 |
3 | import "cosmossdk.io/errors"
4 |
5 | // ErrDuplicateAddress error if there is a duplicate address
6 | var ErrDuplicateAddress = errors.Register(ModuleName, 2, "duplicate address")
7 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/genesis.go:
--------------------------------------------------------------------------------
1 | package oracle
2 |
3 | // NewGenesisState creates a new genesis state with default values.
4 | func NewGenesisState() *GenesisState {
5 | return &GenesisState{
6 | }
7 | }
8 |
9 | // Validate performs basic genesis state validation returning an error upon any
10 | func (gs *GenesisState) Validate() error {
11 | uniq := make(map[string]bool)
12 | for _, counter := range gs.Counters {
13 | if _, ok := uniq[counter.Address]; ok {
14 | return ErrDuplicateAddress
15 | }
16 |
17 | uniq[counter.Address] = true
18 | }
19 |
20 | return nil
21 | }
22 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/integration/integration_test.go:
--------------------------------------------------------------------------------
1 | package integration_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 |
8 | // blank import for app wiring registration
9 | _ "github.com/cosmos/cosmos-sdk/x/auth"
10 | _ "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
11 | _ "github.com/cosmos/cosmos-sdk/x/bank"
12 | _ "github.com/cosmos/cosmos-sdk/x/consensus"
13 | _ "github.com/cosmos/cosmos-sdk/x/genutil"
14 | _ "github.com/cosmos/cosmos-sdk/x/mint"
15 | _ "github.com/cosmos/sdk-tutorials/x/staking"
16 | _ "github.com/cosmos/sdk-tutorials/x/oracle"
17 |
18 | appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
19 | "cosmossdk.io/core/appconfig"
20 | "cosmossdk.io/depinject"
21 | "cosmossdk.io/log"
22 | "github.com/cosmos/cosmos-sdk/testutil/configurator"
23 | simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
24 |
25 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle"
26 | oraclemodulev1 "github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle/api/module/v1"
27 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle/keeper"
28 | )
29 |
30 | // ExampleModule is a configurator.ModuleOption that add the oracle module to the app config.
31 | var ExampleModule = func() configurator.ModuleOption {
32 | return func(config *configurator.Config) {
33 | config.ModuleConfigs[oracle.ModuleName] = &appv1alpha1.ModuleConfig{
34 | Name: oracle.ModuleName,
35 | Config: appconfig.WrapAny(&oraclemodulev1.Module{}),
36 | }
37 | }
38 | }
39 |
40 | func TestIntegration(t *testing.T) {
41 | t.Parallel()
42 |
43 | logger := log.NewTestLogger(t)
44 | appConfig := depinject.Configs(
45 | configurator.NewAppConfig(
46 | configurator.AuthModule(),
47 | configurator.BankModule(),
48 | configurator.StakingModule(),
49 | configurator.TxModule(),
50 | configurator.ConsensusModule(),
51 | configurator.GenutilModule(),
52 | configurator.MintModule(),
53 | ExampleModule(),
54 | configurator.WithCustomInitGenesisOrder(
55 | "auth",
56 | "bank",
57 | "staking",
58 | "mint",
59 | "genutil",
60 | "consensus",
61 | oracle.ModuleName,
62 | ),
63 | ),
64 | depinject.Supply(logger))
65 |
66 | var keeper keeper.Keeper
67 | app, err := simtestutil.Setup(appConfig, &keeper)
68 | require.NoError(t, err)
69 | require.NotNil(t, app) // use the app or the keeper for running integration tests
70 | }
71 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keeper/genesis.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle"
7 | )
8 |
9 | // InitGenesis initializes the module state from a genesis state.
10 | func (k *Keeper) InitGenesis(ctx context.Context, data *oracle.GenesisState) error {
11 | for _, counter := range data.Counters {
12 | if err := k.Counter.Set(ctx, counter.Address, counter.Count); err != nil {
13 | return err
14 | }
15 | }
16 |
17 | return nil
18 | }
19 |
20 | // ExportGenesis exports the module state to a genesis state.
21 | func (k *Keeper) ExportGenesis(ctx context.Context) (*oracle.GenesisState, error) {
22 | var counters []oracle.Counter
23 | if err := k.Counter.Walk(ctx, nil, func(address string, count uint64) (bool, error) {
24 | counters = append(counters, oracle.Counter{
25 | Address: address,
26 | Count: count,
27 | })
28 |
29 | return false, nil
30 | }); err != nil {
31 | return nil, err
32 | }
33 |
34 | return &oracle.GenesisState{
35 | Counters: counters,
36 | }, nil
37 | }
38 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keeper/genesis_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 |
8 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle"
9 | )
10 |
11 | func TestInitGenesis(t *testing.T) {
12 | fixture := initFixture(t)
13 |
14 | data := &oracle.GenesisState{
15 | Counters: []oracle.Counter{
16 | {
17 | Address: fixture.addrs[0].String(),
18 | Count: 5,
19 | },
20 | },
21 | }
22 | err := fixture.k.InitGenesis(fixture.ctx, data)
23 | require.NoError(t, err)
24 |
25 | count, err := fixture.k.Counter.Get(fixture.ctx, fixture.addrs[0].String())
26 | require.NoError(t, err)
27 | require.Equal(t, uint64(5), count)
28 | }
29 |
30 | func TestExportGenesis(t *testing.T) {
31 | fixture := initFixture(t)
32 |
33 | _, err := fixture.msgServer.IncrementCounter(fixture.ctx, &oracle.MsgIncrementCounter{
34 | Sender: fixture.addrs[0].String(),
35 | })
36 | require.NoError(t, err)
37 |
38 | out, err := fixture.k.ExportGenesis(fixture.ctx)
39 | require.NoError(t, err)
40 |
41 | require.Equal(t, uint64(1), out.Counters[0].Count)
42 | }
43 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keeper/keeper.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "cosmossdk.io/collections"
8 | "cosmossdk.io/core/address"
9 | storetypes "cosmossdk.io/core/store"
10 | "cosmossdk.io/math"
11 | "github.com/cosmos/cosmos-sdk/codec"
12 |
13 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle"
14 | )
15 |
16 | type Keeper struct {
17 | cdc codec.BinaryCodec
18 | addressCodec address.Codec
19 |
20 | // authority is the address capable of executing a MsgUpdateParams and other authority-gated message.
21 | // typically, this should be the x/gov module account.
22 | authority string
23 |
24 | // state management
25 | Schema collections.Schema
26 | Counter collections.Map[string, uint64]
27 | Prices collections.Map[string, []byte]
28 | }
29 |
30 | // NewKeeper creates a new Keeper instance
31 | func NewKeeper(cdc codec.BinaryCodec, addressCodec address.Codec, storeService storetypes.KVStoreService, authority string) Keeper {
32 | if _, err := addressCodec.StringToBytes(authority); err != nil {
33 | panic(fmt.Errorf("invalid authority address: %w", err))
34 | }
35 |
36 | sb := collections.NewSchemaBuilder(storeService)
37 | k := Keeper{
38 | cdc: cdc,
39 | addressCodec: addressCodec,
40 | authority: authority,
41 | Counter: collections.NewMap(sb, oracle.CounterKey, "counter", collections.StringKey, collections.Uint64Value),
42 | Prices: collections.NewMap(sb, oracle.PricesKey, "prices", collections.StringKey, collections.BytesValue),
43 | }
44 |
45 | schema, err := sb.Build()
46 | if err != nil {
47 | panic(err)
48 | }
49 |
50 | k.Schema = schema
51 |
52 | return k
53 | }
54 |
55 | // GetAuthority returns the module's authority.
56 | func (k Keeper) GetAuthority() string {
57 | return k.authority
58 | }
59 |
60 | func (k Keeper) GetSupportedPairs(_ context.Context) []CurrencyPair {
61 | return []CurrencyPair{
62 | {Base: "ATOM", Quote: "USD"},
63 | {Base: "OSMO", Quote: "USD"},
64 | }
65 | }
66 |
67 | func (k Keeper) SetOraclePrices(ctx context.Context, prices map[string]math.LegacyDec) error {
68 | for b, q := range prices {
69 | bz, err := q.Marshal()
70 | if err != nil {
71 | return err
72 | }
73 |
74 | err = k.Prices.Set(ctx, b, bz)
75 | if err != nil {
76 | return err
77 | }
78 | }
79 | return nil
80 | }
81 |
82 | func (k Keeper) GetOraclePrices(ctx context.Context) (map[string]math.LegacyDec, error) {
83 | prices := make(map[string]math.LegacyDec)
84 | err := k.Prices.Walk(ctx, nil, func(key string, value []byte) (bool, error) {
85 | var q math.LegacyDec
86 | if err := q.Unmarshal(value); err != nil {
87 | return true, err
88 | }
89 | prices[key] = q
90 | return false, nil
91 | })
92 | if err != nil {
93 | return nil, err
94 | }
95 | return prices, nil
96 | }
97 |
98 | type (
99 | CurrencyPair struct {
100 | Base string
101 | Quote string
102 | }
103 |
104 | TickerPrice struct {
105 | Price math.LegacyDec // last trade price
106 | Volume math.LegacyDec // 24h volume
107 | }
108 |
109 | // AggregatedProviderPrices defines a type alias for a map of
110 | // provider -> asset -> TickerPrice (e.g. Binance -> ATOM/USD -> 11.98)
111 | AggregatedProviderPrices map[string]map[string]TickerPrice
112 | )
113 |
114 | func (cp CurrencyPair) String() string {
115 | return cp.Base + cp.Quote
116 | }
117 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keeper/keeper_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | "testing"
5 |
6 | storetypes "cosmossdk.io/store/types"
7 | addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
8 | "github.com/cosmos/cosmos-sdk/runtime"
9 | "github.com/cosmos/cosmos-sdk/testutil"
10 | simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
11 | sdk "github.com/cosmos/cosmos-sdk/types"
12 | moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
13 |
14 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle"
15 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle/keeper"
16 | )
17 |
18 | type testFixture struct {
19 | ctx sdk.Context
20 | k keeper.Keeper
21 | msgServer oracle.MsgServer
22 | queryServer oracle.QueryServer
23 |
24 | addrs []sdk.AccAddress
25 | }
26 |
27 | func initFixture(t *testing.T) *testFixture {
28 | t.Helper()
29 | encCfg := moduletestutil.MakeTestEncodingConfig()
30 | key := storetypes.NewKVStoreKey(oracle.ModuleName)
31 | testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
32 | storeService := runtime.NewKVStoreService(key)
33 | addrs := simtestutil.CreateIncrementalAccounts(3)
34 |
35 | k := keeper.NewKeeper(encCfg.Codec, addresscodec.NewBech32Codec("cosmos"), storeService, addrs[0].String())
36 | err := k.InitGenesis(testCtx.Ctx, oracle.NewGenesisState())
37 | if err != nil {
38 | panic(err)
39 | }
40 |
41 | return &testFixture{
42 | ctx: testCtx.Ctx,
43 | k: k,
44 | msgServer: keeper.NewMsgServerImpl(k),
45 | queryServer: keeper.NewQueryServerImpl(k),
46 | addrs: addrs,
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keeper/migrator.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | // Migrator is a struct for handling in-place state migrations.
4 | type Migrator struct {
5 | keeper Keeper
6 | }
7 |
8 | // NewMigrator returns Migrator instance for the state migration.
9 | func NewMigrator(k Keeper) Migrator {
10 | return Migrator{
11 | keeper: k,
12 | }
13 | }
14 |
15 | // Migrate1to2 migrates the module state from version 1 to version 2.
16 | // func (m Migrator) Migrate1to2(ctx context.Context) error {
17 | // return v2.Migrate(/* migrations arguments */)
18 | // }
19 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keeper/msg_server.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 |
8 | "cosmossdk.io/collections"
9 |
10 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle"
11 | )
12 |
13 | type msgServer struct {
14 | k Keeper
15 | }
16 |
17 | var _ oracle.MsgServer = msgServer{}
18 |
19 | // NewMsgServerImpl returns an implementation of the module MsgServer interface.
20 | func NewMsgServerImpl(keeper Keeper) oracle.MsgServer {
21 | return &msgServer{k: keeper}
22 | }
23 |
24 | // IncrementCounter defines the handler for the MsgIncrementCounter message.
25 | func (ms msgServer) IncrementCounter(ctx context.Context, msg *oracle.MsgIncrementCounter) (*oracle.MsgIncrementCounterResponse, error) {
26 | if _, err := ms.k.addressCodec.StringToBytes(msg.Sender); err != nil {
27 | return nil, fmt.Errorf("invalid sender address: %w", err)
28 | }
29 |
30 | counter, err := ms.k.Counter.Get(ctx, msg.Sender)
31 | if err != nil && !errors.Is(err, collections.ErrNotFound) {
32 | return nil, err
33 | }
34 |
35 | counter++
36 |
37 | if err := ms.k.Counter.Set(ctx, msg.Sender, counter); err != nil {
38 | return nil, err
39 | }
40 |
41 | return &oracle.MsgIncrementCounterResponse{}, nil
42 | }
43 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keeper/msg_server_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 |
8 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle"
9 | )
10 |
11 | func TestIncrementCounter(t *testing.T) {
12 | f := initFixture(t)
13 | require := require.New(t)
14 |
15 | testCases := []struct {
16 | name string
17 | request *oracle.MsgIncrementCounter
18 | expectErrMsg string
19 | expectedCounter uint64
20 | }{
21 | {
22 | name: "set invalid sender (not an address)",
23 | request: &oracle.MsgIncrementCounter{
24 | Sender: "foo",
25 | },
26 | expectErrMsg: "invalid sender address",
27 | },
28 | {
29 | name: "set valid sender",
30 | request: &oracle.MsgIncrementCounter{
31 | Sender: "cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5",
32 | },
33 | expectErrMsg: "",
34 | expectedCounter: 1,
35 | },
36 | }
37 |
38 | for _, tc := range testCases {
39 | tc := tc
40 | t.Run(tc.name, func(t *testing.T) {
41 | _, err := f.msgServer.IncrementCounter(f.ctx, tc.request)
42 | if tc.expectErrMsg != "" {
43 | require.Error(err)
44 | require.ErrorContains(err, tc.expectErrMsg)
45 | } else {
46 | require.NoError(err)
47 |
48 | counter, err := f.k.Counter.Get(f.ctx, tc.request.Sender)
49 | require.NoError(err)
50 | require.Equal(tc.expectedCounter, counter)
51 | }
52 | })
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keeper/query_server.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 |
8 | "cosmossdk.io/collections"
9 | "google.golang.org/grpc/codes"
10 | "google.golang.org/grpc/status"
11 |
12 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle"
13 | )
14 |
15 | var _ oracle.QueryServer = queryServer{}
16 |
17 | // NewQueryServerImpl returns an implementation of the module QueryServer.
18 | func NewQueryServerImpl(k Keeper) oracle.QueryServer {
19 | return queryServer{k}
20 | }
21 |
22 | type queryServer struct {
23 | k Keeper
24 | }
25 |
26 | // Counter defines the handler for the Query/Counter RPC method.
27 | func (qs queryServer) Counter(ctx context.Context, req *oracle.QueryCounterRequest) (*oracle.QueryCounterResponse, error) {
28 | if _, err := qs.k.addressCodec.StringToBytes(req.Address); err != nil {
29 | return nil, fmt.Errorf("invalid sender address: %w", err)
30 | }
31 |
32 | counter, err := qs.k.Counter.Get(ctx, req.Address)
33 | if err != nil {
34 | if errors.Is(err, collections.ErrNotFound) {
35 | return &oracle.QueryCounterResponse{Counter: 0}, nil
36 | }
37 |
38 | return nil, status.Error(codes.Internal, err.Error())
39 | }
40 |
41 | return &oracle.QueryCounterResponse{Counter: counter}, nil
42 | }
43 |
44 | func (qs queryServer) Prices(ctx context.Context, req *oracle.QueryPricesRequest) (*oracle.QueryPricesResponse, error) {
45 | var prices []*oracle.Price
46 |
47 | p, err := qs.k.GetOraclePrices(ctx)
48 | if err != nil {
49 | return nil, err
50 | }
51 |
52 | for symbol, price := range p {
53 | prices = append(prices, &oracle.Price{
54 | Symbol: symbol,
55 | Price: price.String(),
56 | })
57 | }
58 |
59 | return &oracle.QueryPricesResponse{Prices: prices}, nil
60 | }
61 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keeper/query_server_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 |
8 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle"
9 | )
10 |
11 | func TestQueryCounter(t *testing.T) {
12 | f := initFixture(t)
13 | require := require.New(t)
14 |
15 | resp, err := f.queryServer.Counter(f.ctx, &oracle.QueryCounterRequest{Address: f.addrs[0].String()})
16 | require.NoError(err)
17 | require.Equal(uint64(0), resp.Counter)
18 |
19 | _, err = f.msgServer.IncrementCounter(f.ctx, &oracle.MsgIncrementCounter{Sender: f.addrs[0].String()})
20 | require.NoError(err)
21 |
22 | resp, err = f.queryServer.Counter(f.ctx, &oracle.QueryCounterRequest{Address: f.addrs[0].String()})
23 | require.NoError(err)
24 | require.Equal(uint64(1), resp.Counter)
25 | }
26 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/keys.go:
--------------------------------------------------------------------------------
1 | package oracle
2 |
3 | import "cosmossdk.io/collections"
4 |
5 | const ModuleName = "oracle"
6 |
7 | var (
8 | ParamsKey = collections.NewPrefix(0)
9 | CounterKey = collections.NewPrefix(1)
10 | PricesKey = collections.NewPrefix(2)
11 | )
12 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/mockprovider/provider.go:
--------------------------------------------------------------------------------
1 | package mockprovider
2 |
3 | import (
4 | "cosmossdk.io/math"
5 |
6 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle/keeper"
7 | )
8 |
9 | type Provider interface {
10 | GetTickerPrices(...keeper.CurrencyPair) (map[string]keeper.TickerPrice, error)
11 | }
12 |
13 | var _ Provider = MockProvider{}
14 |
15 | type MockProvider struct{}
16 |
17 | func NewMockProvider() MockProvider {
18 | return MockProvider{}
19 | }
20 |
21 | func (p MockProvider) GetTickerPrices(pairs ...keeper.CurrencyPair) (map[string]keeper.TickerPrice, error) {
22 | tickers := map[string]keeper.TickerPrice{}
23 | for _, p := range pairs {
24 |
25 | pair := p.String()
26 | price := math.LegacyNewDec(0)
27 | switch pair {
28 | case "ATOMUSD":
29 | price = math.LegacyNewDec(10)
30 | case "OSMOUSD":
31 | price = math.LegacyNewDec(2)
32 | }
33 |
34 | tickers[pair] = keeper.TickerPrice{
35 | Price: price,
36 | Volume: math.LegacyNewDec(10000000),
37 | }
38 | }
39 |
40 | return tickers, nil
41 | }
42 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/module/autocli.go:
--------------------------------------------------------------------------------
1 | package module
2 |
3 | import (
4 | autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
5 |
6 | oraclev1 "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle/api/v1"
7 | )
8 |
9 | // AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
10 | func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
11 | return &autocliv1.ModuleOptions{
12 | Query: &autocliv1.ServiceCommandDescriptor{
13 | Service: oraclev1.Query_ServiceDesc.ServiceName,
14 | RpcCommandOptions: []*autocliv1.RpcCommandOptions{
15 | {
16 | RpcMethod: "Counter",
17 | Use: "counter [address]",
18 | Short: "Get the current value of the counter for an address",
19 | PositionalArgs: []*autocliv1.PositionalArgDescriptor{
20 | {ProtoField: "address"},
21 | },
22 | },
23 | {
24 | RpcMethod: "Prices",
25 | Use: "prices",
26 | Short: "Get the current prices",
27 | },
28 | },
29 | },
30 | Tx: &autocliv1.ServiceCommandDescriptor{
31 | Service: oraclev1.Msg_ServiceDesc.ServiceName,
32 | RpcCommandOptions: []*autocliv1.RpcCommandOptions{
33 | {
34 | RpcMethod: "IncrementCounter",
35 | Use: "counter [sender]",
36 | Short: "Increments the counter by 1 for the sender",
37 | PositionalArgs: []*autocliv1.PositionalArgDescriptor{
38 | {ProtoField: "sender"},
39 | },
40 | },
41 | // The UpdateParams tx is purposely left empty, the MsgUpdateParams is gov gated.
42 | },
43 | },
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/module/depinject.go:
--------------------------------------------------------------------------------
1 | package module
2 |
3 | import (
4 | "cosmossdk.io/core/address"
5 | "cosmossdk.io/core/appmodule"
6 | "cosmossdk.io/core/store"
7 | "cosmossdk.io/depinject"
8 | "github.com/cosmos/cosmos-sdk/codec"
9 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
10 |
11 | modulev1 "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle/api/module/v1"
12 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle/keeper"
13 | )
14 |
15 | var _ appmodule.AppModule = AppModule{}
16 |
17 | // IsOnePerModuleType implements the depinject.OnePerModuleType interface.
18 | func (am AppModule) IsOnePerModuleType() {}
19 |
20 | // IsAppModule implements the appmodule.AppModule interface.
21 | func (am AppModule) IsAppModule() {}
22 |
23 | func init() {
24 | appmodule.Register(
25 | &modulev1.Module{},
26 | appmodule.Provide(ProvideModule),
27 | )
28 | }
29 |
30 | type ModuleInputs struct {
31 | depinject.In
32 |
33 | Cdc codec.Codec
34 | StoreService store.KVStoreService
35 | AddressCodec address.Codec
36 |
37 | Config *modulev1.Module
38 | }
39 |
40 | type ModuleOutputs struct {
41 | depinject.Out
42 |
43 | Module appmodule.AppModule
44 | Keeper keeper.Keeper
45 | }
46 |
47 | func ProvideModule(in ModuleInputs) ModuleOutputs {
48 | // default to governance as authority if not provided
49 | authority := authtypes.NewModuleAddress("gov")
50 | if in.Config.Authority != "" {
51 | authority = authtypes.NewModuleAddressOrBech32Address(in.Config.Authority)
52 | }
53 |
54 | k := keeper.NewKeeper(in.Cdc, in.AddressCodec, in.StoreService, authority.String())
55 | m := NewAppModule(in.Cdc, k)
56 |
57 | return ModuleOutputs{Module: m, Keeper: k}
58 | }
59 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/module/module.go:
--------------------------------------------------------------------------------
1 | package module
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 |
8 | "cosmossdk.io/core/appmodule"
9 | "github.com/cosmos/cosmos-sdk/client"
10 | "github.com/cosmos/cosmos-sdk/codec"
11 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
12 | sdk "github.com/cosmos/cosmos-sdk/types"
13 | "github.com/cosmos/cosmos-sdk/types/module"
14 | gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
15 |
16 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle"
17 | "github.com/cosmos/sdk-tutorials/tutorials/oracle/base/x/oracle/keeper"
18 | )
19 |
20 | var (
21 | _ module.AppModuleBasic = AppModule{}
22 | _ module.HasGenesis = AppModule{}
23 | _ appmodule.AppModule = AppModule{}
24 | )
25 |
26 | // ConsensusVersion defines the current module consensus version.
27 | const ConsensusVersion = 1
28 |
29 | type AppModule struct {
30 | cdc codec.Codec
31 | keeper keeper.Keeper
32 | }
33 |
34 | // NewAppModule creates a new AppModule object
35 | func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule {
36 | return AppModule{
37 | cdc: cdc,
38 | keeper: keeper,
39 | }
40 | }
41 |
42 | func NewAppModuleBasic(m AppModule) module.AppModuleBasic {
43 | return module.CoreAppModuleBasicAdaptor(m.Name(), m)
44 | }
45 |
46 | // Name returns the oracle module's name.
47 | func (AppModule) Name() string { return oracle.ModuleName }
48 |
49 | // RegisterLegacyAminoCodec registers the oracle module's types on the LegacyAmino codec.
50 | // New modules do not need to support Amino.
51 | func (AppModule) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {}
52 |
53 | // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the oracle module.
54 | func (AppModule) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwruntime.ServeMux) {
55 | if err := oracle.RegisterQueryHandlerClient(context.Background(), mux, oracle.NewQueryClient(clientCtx)); err != nil {
56 | panic(err)
57 | }
58 | }
59 |
60 | // RegisterInterfaces registers interfaces and implementations of the oracle module.
61 | func (AppModule) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
62 | oracle.RegisterInterfaces(registry)
63 | }
64 |
65 | // ConsensusVersion implements AppModule/ConsensusVersion.
66 | func (AppModule) ConsensusVersion() uint64 { return ConsensusVersion }
67 |
68 | // RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries.
69 | func (am AppModule) RegisterServices(cfg module.Configurator) {
70 | oracle.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
71 | oracle.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper))
72 |
73 | // Register in place module state migration migrations
74 | // m := keeper.NewMigrator(am.keeper)
75 | // if err := cfg.RegisterMigration(oracle.ModuleName, 1, m.Migrate1to2); err != nil {
76 | // panic(fmt.Sprintf("failed to migrate x/%s from version 1 to 2: %v", oracle.ModuleName, err))
77 | // }
78 | }
79 |
80 | // DefaultGenesis returns default genesis state as raw bytes for the module.
81 | func (AppModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
82 | return cdc.MustMarshalJSON(oracle.NewGenesisState())
83 | }
84 |
85 | // ValidateGenesis performs genesis state validation for the circuit module.
86 | func (AppModule) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
87 | var data oracle.GenesisState
88 | if err := cdc.UnmarshalJSON(bz, &data); err != nil {
89 | return fmt.Errorf("failed to unmarshal %s genesis state: %w", oracle.ModuleName, err)
90 | }
91 |
92 | return data.Validate()
93 | }
94 |
95 | // InitGenesis performs genesis initialization for the oracle module.
96 | // It returns no validator updates.
97 | func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) {
98 | var genesisState oracle.GenesisState
99 | cdc.MustUnmarshalJSON(data, &genesisState)
100 |
101 | if err := am.keeper.InitGenesis(ctx, &genesisState); err != nil {
102 | panic(fmt.Sprintf("failed to initialize %s genesis state: %v", oracle.ModuleName, err))
103 | }
104 | }
105 |
106 | // ExportGenesis returns the exported genesis state as raw bytes for the circuit
107 | // module.
108 | func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
109 | gs, err := am.keeper.ExportGenesis(ctx)
110 | if err != nil {
111 | panic(fmt.Sprintf("failed to export %s genesis state: %v", oracle.ModuleName, err))
112 | }
113 |
114 | return cdc.MustMarshalJSON(gs)
115 | }
116 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/proto/buf.gen.gogo.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | plugins:
3 | - name: gocosmos
4 | out: ..
5 | opt: plugins=grpc,Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types,Mcosmos/orm/v1/orm.proto=cosmossdk.io/orm
6 | - name: grpc-gateway
7 | out: ..
8 | opt: logtostderr=true,allow_colon_final_segments=true
9 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/proto/buf.gen.pulsar.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | managed:
3 | enabled: true
4 | go_package_prefix:
5 | default: github.com/cosmos/sdk-tutorial/x/oracle/api
6 | except:
7 | - buf.build/googleapis/googleapis
8 | - buf.build/cosmos/gogo-proto
9 | - buf.build/cosmos/cosmos-proto
10 | - buf.build/cosmos/cosmos-sdk
11 | plugins:
12 | - name: go-pulsar
13 | out: ..
14 | opt: paths=source_relative,Mcosmos/app/v1alpha1/module.proto=cosmossdk.io/api/cosmos/app/v1alpha1,Mcosmos/base/v1beta1/coin.proto=cosmossdk.io/api/cosmos/base/v1beta1
15 | - name: go-grpc
16 | out: ..
17 | opt: paths=source_relative,Mcosmos/app/v1alpha1/module.proto=cosmossdk.io/api/cosmos/app/v1alpha1
18 | - name: go-cosmos-orm
19 | out: ..
20 | opt: paths=source_relative,Mcosmos/app/v1alpha1/module.proto=cosmossdk.io/api/cosmos/app/v1alpha1
21 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/proto/buf.lock:
--------------------------------------------------------------------------------
1 | # Generated by buf. DO NOT EDIT.
2 | version: v1
3 | deps:
4 | - remote: buf.build
5 | owner: cosmos
6 | repository: cosmos-proto
7 | commit: 1935555c206d4afb9e94615dfd0fad31
8 | digest: shake256:c74d91a3ac7ae07d579e90eee33abf9b29664047ac8816500cf22c081fec0d72d62c89ce0bebafc1f6fec7aa5315be72606717740ca95007248425102c365377
9 | - remote: buf.build
10 | owner: cosmos
11 | repository: cosmos-sdk
12 | commit: cf13c7d232dd405180c2af616fa8a075
13 | digest: shake256:769a38e306a98339b549bc96991c97fae8bd3ceb1a7646c7bfe9a74e406ab068372970fbc5abda1891e2f3c36527cf2d3a25f631739d36900787226e564bb612
14 | - remote: buf.build
15 | owner: cosmos
16 | repository: gogo-proto
17 | commit: 5e5b9fdd01804356895f8f79a6f1ddc1
18 | digest: shake256:0b85da49e2e5f9ebc4806eae058e2f56096ff3b1c59d1fb7c190413dd15f45dd456f0b69ced9059341c80795d2b6c943de15b120a9e0308b499e43e4b5fc2952
19 | - remote: buf.build
20 | owner: googleapis
21 | repository: googleapis
22 | commit: 28151c0d0a1641bf938a7672c500e01d
23 | digest: shake256:49215edf8ef57f7863004539deff8834cfb2195113f0b890dd1f67815d9353e28e668019165b9d872395871eeafcbab3ccfdb2b5f11734d3cca95be9e8d139de
24 | - remote: buf.build
25 | owner: protocolbuffers
26 | repository: wellknowntypes
27 | commit: 657250e6a39648cbb169d079a60bd9ba
28 | digest: shake256:00de25001b8dd2e29d85fc4bcc3ede7aed886d76d67f5e0f7a9b320b90f871d3eb73507d50818d823a0512f3f8db77a11c043685528403e31ff3fef18323a9fb
29 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/proto/buf.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | deps:
3 | - buf.build/cosmos/cosmos-sdk # pin the Cosmos SDK version
4 | - buf.build/cosmos/cosmos-proto
5 | - buf.build/cosmos/gogo-proto
6 | - buf.build/googleapis/googleapis
7 | lint:
8 | use:
9 | - DEFAULT
10 | - COMMENTS
11 | - FILE_LOWER_SNAKE_CASE
12 | except:
13 | - UNARY_RPC
14 | - COMMENT_FIELD
15 | - SERVICE_SUFFIX
16 | - PACKAGE_VERSION_SUFFIX
17 | - RPC_REQUEST_STANDARD_NAME
18 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/proto/cosmos/oracle/module/v1/module.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package cosmos.oracle.module.v1;
4 |
5 | import "cosmos/app/v1alpha1/module.proto";
6 |
7 | // Module is the app config object of the module.
8 | // Learn more: https://docs.cosmos.network/main/building-modules/depinject
9 | message Module {
10 | option (cosmos.app.v1alpha1.module) = {
11 | go_import : "github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle"
12 | };
13 |
14 | // authority defines the custom module authority.
15 | // if not set, defaults to the governance module.
16 | string authority = 1;
17 | }
18 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/proto/cosmos/oracle/v1/query.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package cosmos.oracle.v1;
3 |
4 | option go_package = "github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle";
5 |
6 | import "cosmos/oracle/v1/types.proto";
7 | import "google/api/annotations.proto";
8 | import "cosmos/query/v1/query.proto";
9 | import "amino/amino.proto";
10 | import "gogoproto/gogo.proto";
11 |
12 | // Msg defines the module Msg service.
13 | service Query {
14 | // Counter returns the current counter value.
15 | rpc Counter(QueryCounterRequest) returns (QueryCounterResponse) {
16 | option (cosmos.query.v1.module_query_safe) = true;
17 | option (google.api.http).get =
18 | "/cosmos/sdk-tutorials/x/oracle/v1/counter/{address}";
19 | }
20 |
21 | // Prices returns the current prices.
22 | rpc Prices(QueryPricesRequest) returns (QueryPricesResponse) {
23 | option (google.api.http).get = "/cosmos/oracle/v1/prices";
24 | }
25 | }
26 |
27 | // QueryPricesRequest is the request type for the Query/Prices RPC method.
28 | message QueryPricesRequest {}
29 |
30 | // QueryPricesResponse is the response type for the Query/Prices RPC method.
31 | message QueryPricesResponse {
32 | repeated Price prices = 1;
33 | }
34 |
35 | // Price represents the price of a specific symbol.
36 | message Price {
37 | string symbol = 1;
38 | string price = 2;
39 | }
40 |
41 | // QueryCounterRequest is the request type for the Query/Counter RPC
42 | // method.
43 | message QueryCounterRequest {
44 | // address defines the address to query for the counter.
45 | string address = 1;
46 | }
47 |
48 | // QueryCounterResponse is the response type for the Query/Counter RPC
49 | // method.
50 | message QueryCounterResponse {
51 | // counter defines the current counter for the sender.
52 | uint64 counter = 1;
53 | }
54 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/proto/cosmos/oracle/v1/tx.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package cosmos.oracle.v1;
3 |
4 | option go_package = "github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle";
5 |
6 | import "cosmos/msg/v1/msg.proto";
7 | import "gogoproto/gogo.proto";
8 | import "amino/amino.proto";
9 | import "cosmos/oracle/v1/types.proto";
10 | import "cosmos_proto/cosmos.proto";
11 |
12 | // Msg defines the module Msg service.
13 | service Msg {
14 | option (cosmos.msg.v1.service) = true;
15 |
16 | // IncrementCounter increments the counter.
17 | rpc IncrementCounter(MsgIncrementCounter)
18 | returns (MsgIncrementCounterResponse);
19 | }
20 |
21 | // MsgIncrementCounter defines the Msg/IncrementCounter request type.
22 | message MsgIncrementCounter {
23 | option (cosmos.msg.v1.signer) = "sender";
24 | option (amino.name) = "cosmos/sdk-tutorials/x/oracle/MsgIncrementCounter";
25 |
26 | // sender is the message sender.
27 | string sender = 1;
28 | }
29 |
30 | // MsgIncrementCounterResponse defines the Msg/IncrementCounter response type.
31 | message MsgIncrementCounterResponse {}
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/proto/cosmos/oracle/v1/types.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package cosmos.oracle.v1;
3 |
4 | option go_package = "github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle";
5 |
6 | import "cosmos_proto/cosmos.proto";
7 | import "gogoproto/gogo.proto";
8 | import "amino/amino.proto";
9 |
10 |
11 | // Counter defines a counter object.
12 | // It is used only for genesis purposes. Collections does not need to use it.
13 | message Counter {
14 | option (amino.name) = "cosmos/oracle/Counter";
15 |
16 | // count defines the count of the counter.
17 | uint64 count = 1;
18 |
19 | // address defines the address that is associated with the count.
20 | string address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
21 | ;
22 | }
23 |
24 | // GenesisState is the state that must be provided at genesis.
25 | message GenesisState {
26 | // counter defines the counter object.
27 | repeated Counter counters = 1
28 | [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
29 | }
30 |
--------------------------------------------------------------------------------
/tutorials/oracle/base/x/oracle/scripts/protocgen.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | echo "Generating gogo proto code"
6 | cd proto
7 | proto_dirs=$(find . -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq)
8 | for dir in $proto_dirs; do
9 | for file in $(find "${dir}" -maxdepth 1 -name '*.proto'); do
10 | # this regex checks if a proto file has its go_package set to github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle/api/...
11 | # gogo proto files SHOULD ONLY be generated if this is false
12 | # we don't want gogo proto to run for proto files which are natively built for google.golang.org/protobuf
13 | if grep -q "option go_package" "$file" && grep -H -o -c 'option go_package.*github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle/api' "$file" | grep -q ':0$'; then
14 | buf generate --template buf.gen.gogo.yaml $file
15 | fi
16 | done
17 | done
18 |
19 | echo "Generating pulsar proto code"
20 | buf generate --template buf.gen.pulsar.yaml
21 |
22 | cd ..
23 |
24 | cp -r github.com/cosmos/sdk-tutorials/tutorials/oracle/x/oracle/* ./
25 | rm -rf api && mkdir api
26 | mv cosmos/oracle/* ./api
27 | rm -rf github.com cosmos
--------------------------------------------------------------------------------
/tutorials/oracle/docs/00-getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Table of Contents
4 |
5 | * [What is an Oracle?](./01-what-is-an-oracle.md)
6 | * [Implementing Vote Extensions](./02-implementing-vote-extensions.md)
7 | * [Testing the Oracle Module](./03-testing-oracle.md)
8 |
9 | ## Prerequisites
10 |
11 | Before you start with this tutorial, make sure you have:
12 |
13 | * A working chain project. This tutorial won't cover the steps of creating a new chain/module.
14 | * Familiarity with the Cosmos SDK. If you're not, we suggest you start with [Cosmos SDK Tutorials](https://tutorials.cosmos.network), as ABCI++ is considered an advanced topic.
15 | * Read and understood [What is an Oracle?](01-what-is-an-oracle.md). This provides necessary background information for understanding the Oracle module.
16 | * Basic understanding of Go programming language.
17 |
18 | ## What are Vote extensions?
19 |
20 | Vote extensions is arbitrary information which can be inserted into a block. This feature is part of ABCI 2.0, which is available for use in the SDK 0.50 release and part of the 0.38 CometBFT release.
21 |
22 | More information about vote extensions can be seen [here](https://docs.cosmos.network/main/build/abci/vote-extensions).
23 |
24 | ## Overview of the project
25 |
26 | We’ll go through the creation of a simple price oracle module focusing on the vote extensions implementation, ignoring the details inside the price oracle itself.
27 |
28 | We’ll go through the implementation of:
29 |
30 | * `ExtendVote` to get information from external price APIs.
31 | * `VerifyVoteExtension` to check that the format of the provided votes is correct.
32 | * `PrepareProposal` to process the vote extensions from the previous block and include them into the proposal as a transaction.
33 | * `ProcessProposal` to check that the first transaction in the proposal is actually a “special tx” that contains the price information.
34 | * `PreBlocker` to make price information available during FinalizeBlock.
35 |
36 | If you would like to see the complete working oracle module please see [here](https://github.com/cosmos/sdk-tutorials/blob/master/tutorials/oracle/base/x/oracle)
37 |
--------------------------------------------------------------------------------
/tutorials/oracle/docs/01-what-is-an-oracle.md:
--------------------------------------------------------------------------------
1 | # What is an Oracle?
2 |
3 | An oracle in blockchain technology is a system that provides external data to a blockchain network. It acts as a source of information that is not natively accessible within the blockchain's closed environment. This can range from financial market prices to real-world event, making it crucial for decentralised applications.
4 |
5 | ## Oracle in the Cosmos SDK
6 |
7 | In the Cosmos SDK, an oracle module can be implemented to provide external data to the blockchain. This module can use features like vote extensions to submit additional data during the consensus process, which can then be used by the blockchain to update its state with information from the outside world.
8 |
9 | For instance, a price oracle module in the Cosmos SDK could supply timely and accurate asset price information, which is vital for various financial operations within the blockchain ecosystem.
10 |
11 | ## Conclusion
12 |
13 | Oracles are essential for blockchains to interact with external data, enabling them to respond to real-world information and events. Their implementation is key to the reliability and robustness of blockchain networks.
14 |
--------------------------------------------------------------------------------
/tutorials/oracle/docs/03-testing-oracle.md:
--------------------------------------------------------------------------------
1 | # Testing the Oracle Module
2 |
3 | We will guide you through the process of testing the Oracle module in your application. The Oracle module uses vote extensions to provide current price data. If you would like to see the complete working oracle module please see [here](https://github.com/cosmos/sdk-tutorials/blob/master/tutorials/oracle/base/x/oracle).
4 |
5 | ## Step 1: Compile and Install the Application
6 |
7 | First, we need to compile and install the application. Please ensure you are in the `tutorials/oracle/base` directory. Run the following command in your terminal:
8 |
9 | ```shell
10 | make install
11 | ```
12 |
13 | This command compiles the application and moves the resulting binary to a location in your system's PATH.
14 |
15 | ## Step 2: Initialise the Application
16 |
17 | Next, we need to initialise the application. Run the following command in your terminal:
18 |
19 | ```shell
20 | make init
21 | ```
22 |
23 | This command runs the script `tutorials/oracle/base/scripts/init.sh`, which sets up the necessary configuration for your application to run. This includes creating the `app.toml` configuration file and initialising the blockchain with a genesis block.
24 |
25 | ## Step 3: Start the Application
26 |
27 | Now, we can start the application. Run the following command in your terminal:
28 |
29 | ```shell
30 | exampled start
31 | ```
32 |
33 | This command starts your application, begins the blockchain node, and starts processing transactions.
34 |
35 | ## Step 4: Query the Oracle Prices
36 |
37 | Finally, we can query the current prices from the Oracle module. Run the following command in your terminal:
38 |
39 | ```shell
40 | exampled q oracle prices
41 | ```
42 |
43 | This command queries the current prices from the Oracle module. The expected output shows that the vote extensions were successfully included in the block and the Oracle module was able to retrieve the price data.
44 |
45 | ## Understanding Vote Extensions in Oracle
46 |
47 | In the Oracle module, the `ExtendVoteHandler` function is responsible for creating the vote extensions. This function fetches the current prices from the provider, creates a `OracleVoteExtension` struct with these prices, and then marshals this struct into bytes. These bytes are then set as the vote extension.
48 |
49 | In the context of testing, the Oracle module uses a mock provider to simulate the behavior of a real price provider. This mock provider is defined in the mockprovider package and is used to return predefined prices for specific currency pairs.
50 |
51 | ## Conclusion
52 |
53 | In this tutorial, we've delved into the concept of Oracle's in blockchain technology, focusing on their role in providing external data to a blockchain network. We've explored vote extensions, a powerful feature of ABCI++, and integrated them into a Cosmos SDK application to create a price oracle module.
54 |
55 | Through hands-on exercises, you've implemented vote extensions, and tested their effectiveness in providing timely and accurate asset price information. You've gained practical insights by setting up a mock provider for testing and analysing the process of extending votes, verifying vote extensions, and preparing and processing proposals.
56 |
57 | Keep experimenting with these concepts, engage with the community, and stay updated on new advancements. The knowledge you've acquired here is crucial for developing robust and reliable blockchain applications that can interact with real-world data.
58 |
--------------------------------------------------------------------------------