├── .github
└── workflows
│ ├── gh-pages.yml
│ └── lint.yml
├── .gitignore
├── .gitpod.yml
├── .golangci.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── cmd
└── starport-ica
│ ├── cmd
│ ├── cmd.go
│ └── module.go
│ └── main.go
├── docs
├── .vuepress
│ ├── config.js
│ ├── public
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-256x256.png
│ │ ├── apple-touch-icon-precomposed.png
│ │ ├── apple-touch-icon.png
│ │ ├── browserconfig.xml
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon-svg.svg
│ │ ├── logo-bw.svg
│ │ ├── logo.svg
│ │ ├── mstile-150x150.png
│ │ ├── safari-pinned-tab.svg
│ │ └── site.webmanifest
│ └── styles
│ │ └── index.styl
├── README.md
├── package-lock.json
├── package.json
├── post.sh
├── pre.sh
└── starport
│ └── README.md
├── go.mod
├── go.sum
├── proto
└── ibc
│ └── account
│ ├── account.proto
│ ├── genesis.proto
│ ├── query.proto
│ ├── testing
│ └── msgs.proto
│ └── types.proto
├── scripts
└── protocgen.sh
├── simapp
├── app.go
├── config.go
├── encoding.go
├── export.go
├── genesis.go
├── genesis_account.go
├── sim_test.go
├── simd
│ ├── cmd
│ │ ├── cmd_test.go
│ │ ├── genaccounts.go
│ │ ├── root.go
│ │ └── testnet.go
│ └── main.go
├── state.go
├── test_helpers.go
└── utils.go
└── x
├── README.md
└── ibc-account
├── client
└── cli
│ └── query.go
├── genesis.go
├── keeper
├── account.go
├── grpc_query.go
├── grpc_query_test.go
├── handshake.go
├── handshake_test.go
├── keeper.go
├── keeper_test.go
├── querier.go
├── relay.go
└── relay_test.go
├── module.go
├── spec
├── 03_types.md
├── 04_keeper.md
├── 05_packets.md
└── README.md
├── testing
├── chain.go
├── coordinator.go
├── mock
│ ├── client
│ │ └── cli
│ │ │ └── tx.go
│ ├── handler.go
│ ├── keeper
│ │ └── keeper.go
│ ├── module.go
│ ├── privval.go
│ ├── privval_test.go
│ └── types
│ │ ├── codec.go
│ │ ├── keys.go
│ │ ├── msgs.go
│ │ └── msgs.pb.go
├── solomachine.go
└── types.go
└── types
├── account.go
├── account.pb.go
├── account_test.go
├── codec.go
├── common_test.go
├── encoder.go
├── errors.go
├── expected_keepers.go
├── genesis.go
├── genesis.pb.go
├── hook.go
├── keys.go
├── packet.go
├── querier.go
├── query.pb.go
├── query.pb.gw.go
└── types.pb.go
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - master # Set a branch name to trigger deployment
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-18.04
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: actions/setup-node@v2
14 | with:
15 | node-version: '12'
16 |
17 | - name: Setup Vuepress
18 | run: npm install && npm run build
19 | working-directory: ./docs
20 | env:
21 | VUEPRESS_BASE: /cosmos-sdk-interchain-account/
22 |
23 | - name: Deploy
24 | uses: peaceiris/actions-gh-pages@v3
25 | with:
26 | github_token: ${{ secrets.GITHUB_TOKEN }}
27 | publish_dir: ./docs/.vuepress/dist
28 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - master
8 |
9 | jobs:
10 | golangci:
11 | name: golangci-lint
12 | runs-on: ubuntu-latest
13 | timeout-minutes: 6
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: golangci/golangci-lint-action@master
17 | with:
18 | # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
19 | version: v1.28
20 | args: --timeout 10m
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .vscode
3 | .DS_Store
4 |
5 | # Build
6 | vendor
7 | build
8 | docs/_build
9 | docs/tutorial
10 | docs/node_modules
11 | docs/modules
12 | dist
13 | tools-stamp
14 | buf-stamp
15 | artifacts
16 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | image: gitpod/workspace-full
2 | checkoutLocation: "ibc-account"
3 | workspaceLocation: "./ibc-account/docs"
4 | tasks:
5 | - name: Install Starport
6 | init: |
7 | # Install Starport
8 | export BIN_PATH=$GOPATH/bin
9 | export VUE_APP_CUSTOM_URL=$(gp url)
10 | export CHISEL_ADDR=$(gp url 7575)
11 | mkdir -p $BIN_PATH
12 | (cd /tmp && git clone https://github.com/tendermint/starport && cd starport && git checkout v0.13.1 && ./scripts/install)
13 | # Install Github CLI
14 | brew install gh
15 |
16 | # install rly
17 | (cd /tmp && git clone https://github.com/ovrclk/relayer && cd relayer && git checkout 3080dab10e37e6db9e691c343b1c0cdb6d845753 && make -C /tmp/relayer install)
18 |
19 | # install starport-ica
20 | (cd /tmp && git clone https://github.com/chainapsis/cosmos-sdk-interchain-account && cd ./cosmos-sdk-interchain-account && go install ./cmd/starport-ica)
21 |
22 | command: |
23 | export VUE_APP_CUSTOM_URL=$(gp url)
24 | export CHISEL_ADDR=$(gp url 7575)
25 | export RPC_ADDRESS=$(gp url 26657):443
26 |
27 | clear && printf '\e[3J'
28 | echo -e "
29 |
30 | Welcome to Starport! 💫 The friendly CLI that makes building blockchains easy.
31 |
32 | In the sidebar you can see a list of guides that will help you get started.
33 |
34 | This terminal has the \033[1mstarport\033[0m binary already installed and ready to go.
35 |
36 | "
37 |
38 | ports:
39 | - port: 1317
40 | - port: 26657
41 | - port: 8080
42 | - port: 7575
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | run:
2 | tests: false
3 | # # timeout for analysis, e.g. 30s, 5m, default is 1m
4 | # timeout: 5m
5 |
6 | linters:
7 | disable-all: true
8 | enable:
9 | - bodyclose
10 | - deadcode
11 | - depguard
12 | - dogsled
13 | # - errcheck
14 | - goconst
15 | - gocritic
16 | - gofmt
17 | - goimports
18 | - golint
19 | - gosec
20 | - gosimple
21 | - govet
22 | - ineffassign
23 | - interfacer
24 | - maligned
25 | - misspell
26 | - nakedret
27 | - prealloc
28 | - scopelint
29 | - staticcheck
30 | - structcheck
31 | - stylecheck
32 | - typecheck
33 | - unconvert
34 | - unused
35 | - unparam
36 | - misspell
37 | # - wsl
38 | - nolintlint
39 |
40 | issues:
41 | exclude-rules:
42 | - text: "Use of weak random number generator"
43 | linters:
44 | - gosec
45 | - text: "comment on exported var"
46 | linters:
47 | - golint
48 | - text: "don't use an underscore in package name"
49 | linters:
50 | - golint
51 | - text: "ST1003:"
52 | linters:
53 | - stylecheck
54 | # FIXME: Disabled until golangci-lint updates stylecheck with this fix:
55 | # https://github.com/dominikh/go-tools/issues/389
56 | - text: "ST1016:"
57 | linters:
58 | - stylecheck
59 | max-issues-per-linter: 10000
60 | max-same-issues: 10000
61 |
62 | linters-settings:
63 | dogsled:
64 | max-blank-identifiers: 3
65 | maligned:
66 | # print struct with more effective memory layout or not, false by default
67 | suggest-new: true
68 | nolintlint:
69 | allow-unused: false
70 | allow-leading-space: true
71 | require-explanation: false
72 | require-specific: false
73 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## PreHistory
4 |
5 | ##### July, 2020
6 |
7 | * Implement MVP version of interchain account
8 | * Update cosmos-sdk version to cosmos-sdk#43837b16e768
9 | * Change the name of interchain account keeper from `InterchainAccountKeeper` to `IBCAccountKeeper`
10 | * Rename some field from `InterchainAccount` to `IBCAccount`
11 | * Make `IBCAccountKeeper` handle `onRecvPacket` and `onTimeoutPacket`
12 | * Make some `try` method like `TryRegisterIBCAccount` and `TryRunTx` able to be used in other keepers
13 | * Add `hook` concept similar to `staking hook` to help other keepers to communicate with other chains based on `sourcePort` and `sourceChannel`
14 |
15 | ##### June, 2020
16 |
17 | * Implement PoC version of interchain account
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Note: ⚠️ Interchain Accounts is now being developed on [cosmos/interchain-account](https://github.com/cosmos/interchain-account)
2 | This repository has now been archived. Please check the latest development progress at [cosmos/interchain-account](https://github.com/cosmos/interchain-account).
3 |
4 | # Interchain Accounts
5 |
6 | > Interchain accounts allows a IBC-connected foreign blockchain to
7 | > send a transaction via the local account of the a local blockchain.
8 |
9 | ## Introduction
10 |
11 | This repository contains the code to operate an IBC interchain account as specified in [Interchain Standards 27](https://github.com/cosmos/ics/tree/master/spec/ics-027-interchain-accounts). Please refer to the ICS27 specification for technical details on how interchain accounts work. Current implementation supports interchain accounts on the Cosmos-SDK implementation of ICS27. For information on use-cases of interchain accounts, please refer to this [introductory blogpost](https://medium.com/chainapsis/why-interchain-accounts-change-everything-for-cosmos-interoperability-59c19032bf11).
12 |
13 | ## Demo Build
14 |
15 | Use the [demo branch](https://github.com/chainapsis/cosmos-sdk-interchain-account/tree/demo) to build the interchain accounts demo.
16 |
17 | Instructions on how to build can be found [here](https://github.com/chainapsis/cosmos-sdk-interchain-account/blob/demo/demo.md).
18 |
19 | ## License
20 |
21 | [Apache2.0](https://github.com/chainapsis/cosmos-sdk-interchain-account/blob/master/LICENSE)
22 |
--------------------------------------------------------------------------------
/cmd/starport-ica/cmd/cmd.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import "github.com/spf13/cobra"
4 |
5 | func New() *cobra.Command {
6 | c := &cobra.Command{
7 | Use: "starport-ica",
8 | }
9 | c.AddCommand(NewModule())
10 | return c
11 | }
12 |
--------------------------------------------------------------------------------
/cmd/starport-ica/cmd/module.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "go/parser"
7 | "go/token"
8 | "os"
9 | "path/filepath"
10 | "strings"
11 |
12 | "github.com/gobuffalo/genny"
13 |
14 | "github.com/tendermint/starport/starport/pkg/cmdrunner"
15 | "github.com/tendermint/starport/starport/pkg/cmdrunner/step"
16 |
17 | "github.com/spf13/cobra"
18 |
19 | "github.com/tendermint/starport/starport/pkg/cosmosver"
20 | )
21 |
22 | const (
23 | icaImport = "github.com/chainapsis/cosmos-sdk-interchain-account"
24 | apppkg = "app"
25 | // moduleDir = "x"
26 | // icaVersionCommit = "daba1321259a442f929f82738b9a9d632eeb4351"
27 |
28 | // Placeholders in Stargate app.go
29 | placeholderSgAppModuleImport = "// this line is used by starport scaffolding # stargate/app/moduleImport"
30 | placeholderSgAppModuleBasic = "// this line is used by starport scaffolding # stargate/app/moduleBasic"
31 | placeholderSgAppKeeperDeclaration = "// this line is used by starport scaffolding # stargate/app/keeperDeclaration"
32 | placeholderSgAppStoreKey = "// this line is used by starport scaffolding # stargate/app/storeKey"
33 | placeholderSgAppKeeperDefinition = "// this line is used by starport scaffolding # stargate/app/keeperDefinition"
34 | placeholderSgAppAppModule = "// this line is used by starport scaffolding # stargate/app/appModule"
35 | placeholderSgAppInitGenesis = "// this line is used by starport scaffolding # stargate/app/initGenesis"
36 | placeholderSgAppParamSubspace = "// this line is used by starport scaffolding # stargate/app/paramSubspace"
37 | )
38 |
39 | func NewModule() *cobra.Command {
40 | c := &cobra.Command{
41 | Use: "module",
42 | Short: "Manage ICA module for cosmos app",
43 | }
44 | c.AddCommand(NewModuleImport())
45 | return c
46 | }
47 |
48 | func NewModuleImport() *cobra.Command {
49 | c := &cobra.Command{
50 | Use: "import",
51 | Short: "Import a ICA modulr to cosmos app",
52 | RunE: importModuleHandler,
53 | }
54 | return c
55 | }
56 |
57 | func importModuleHandler(cmd *cobra.Command, args []string) error {
58 | version, err := cosmosver.Detect("")
59 | if err != nil {
60 | return err
61 | }
62 |
63 | if version != cosmosver.Stargate {
64 | return fmt.Errorf("ICA only support the stargate")
65 | }
66 |
67 | installed, err := isICAImported("")
68 | if err != nil {
69 | return err
70 | }
71 | if installed && len(args) == 0 {
72 | return nil
73 | }
74 |
75 | if !installed {
76 | err = installICA()
77 | if err != nil {
78 | return err
79 | }
80 | }
81 |
82 | if len(args) > 0 {
83 | if args[0] == "mock" {
84 | return importMockModule()
85 | }
86 | return fmt.Errorf("unknown module")
87 | }
88 |
89 | g := genny.New()
90 | g.RunFn(func(r *genny.Runner) error {
91 | path := "app/app.go"
92 | f, err := r.Disk.Find(path)
93 | if err != nil {
94 | return err
95 | }
96 | template := `%[1]v
97 | ibcaccount "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account"
98 | ibcaccountkeeper "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/keeper"
99 | ibcaccounttypes "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"`
100 | replacement := fmt.Sprintf(template, placeholderSgAppModuleImport)
101 | content := strings.Replace(f.String(), placeholderSgAppModuleImport, replacement, 1)
102 |
103 | template2 := `%[1]v
104 | ibcaccount.AppModuleBasic{},`
105 | replacement2 := fmt.Sprintf(template2, placeholderSgAppModuleBasic)
106 | content = strings.Replace(content, placeholderSgAppModuleBasic, replacement2, 1)
107 |
108 | template3 := `%[1]v
109 | IBCAccountKeeper ibcaccountkeeper.Keeper
110 | ScopedIBCAccountKeeper capabilitykeeper.ScopedKeeper`
111 | replacement3 := fmt.Sprintf(template3, placeholderSgAppKeeperDeclaration)
112 | content = strings.Replace(content, placeholderSgAppKeeperDeclaration, replacement3, 1)
113 |
114 | template4 := `%[1]v
115 | ibcaccounttypes.StoreKey,`
116 | replacement5 := fmt.Sprintf(template4, placeholderSgAppStoreKey)
117 | content = strings.Replace(content, placeholderSgAppStoreKey, replacement5, 1)
118 |
119 | template5 := `scopedIBCAccountKeeper := app.CapabilityKeeper.ScopeToModule(ibcaccounttypes.ModuleName)
120 | app.IBCAccountKeeper = ibcaccountkeeper.NewKeeper(appCodec, keys[ibcaccounttypes.StoreKey],
121 | map[string]ibcaccounttypes.TxEncoder{
122 | // register the tx encoder for cosmos-sdk
123 | "cosmos-sdk": ibcaccountkeeper.SerializeCosmosTx(appCodec, interfaceRegistry),
124 | }, app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper,
125 | app.AccountKeeper, scopedIBCAccountKeeper, app.Router(),
126 | )
127 | ibcAccountModule := ibcaccount.NewAppModule(app.IBCAccountKeeper)
128 | ibcRouter.AddRoute(ibcaccounttypes.ModuleName, ibcAccountModule)
129 | app.IBCKeeper.SetRouter(ibcRouter)`
130 | content = strings.Replace(content, "app.IBCKeeper.SetRouter(ibcRouter)", template5, 1)
131 |
132 | template6 := `%[1]v
133 | ibcAccountModule,`
134 | replacement6 := fmt.Sprintf(template6, placeholderSgAppAppModule)
135 | content = strings.Replace(content, placeholderSgAppAppModule, replacement6, 1)
136 |
137 | template7 := `%[1]v
138 | ibcaccounttypes.ModuleName,`
139 | replacement7 := fmt.Sprintf(template7, placeholderSgAppInitGenesis)
140 | content = strings.Replace(content, placeholderSgAppInitGenesis, replacement7, 1)
141 |
142 | template8 := `%[1]v
143 | paramsKeeper.Subspace(ibcaccounttypes.ModuleName)`
144 | replacement8 := fmt.Sprintf(template8, placeholderSgAppParamSubspace)
145 | content = strings.Replace(content, placeholderSgAppParamSubspace, replacement8, 1)
146 |
147 | newFile := genny.NewFileS(path, content)
148 | return r.File(newFile)
149 | })
150 |
151 | run := genny.WetRunner(context.Background())
152 | run.With(g)
153 |
154 | return run.Run()
155 | }
156 |
157 | func importMockModule() error {
158 | g := genny.New()
159 | g.RunFn(func(r *genny.Runner) error {
160 | path := "app/app.go"
161 | f, err := r.Disk.Find(path)
162 | if err != nil {
163 | return err
164 | }
165 | template := `%[1]v
166 | ibcaccountmock "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock"
167 | ibcaccountmockkeeper "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock/keeper"`
168 | replacement := fmt.Sprintf(template, placeholderSgAppModuleImport)
169 | content := strings.Replace(f.String(), placeholderSgAppModuleImport, replacement, 1)
170 |
171 | template2 := `%[1]v
172 | ibcaccountmock.AppModuleBasic{},`
173 | replacement2 := fmt.Sprintf(template2, placeholderSgAppModuleBasic)
174 | content = strings.Replace(content, placeholderSgAppModuleBasic, replacement2, 1)
175 |
176 | template3 := `%[1]v
177 | IBCAccountMockKeeper ibcaccountmockkeeper.Keeper`
178 | replacement3 := fmt.Sprintf(template3, placeholderSgAppKeeperDeclaration)
179 | content = strings.Replace(content, placeholderSgAppKeeperDeclaration, replacement3, 1)
180 |
181 | template5 := `%[1]v
182 | app.IBCAccountMockKeeper = ibcaccountmockkeeper.NewKeeper(app.IBCAccountKeeper)
183 | ibcAccountMockModule := ibcaccountmock.NewAppModule(app.IBCAccountMockKeeper)`
184 | replacement5 := fmt.Sprintf(template5, placeholderSgAppKeeperDefinition)
185 | content = strings.Replace(content, placeholderSgAppKeeperDefinition, replacement5, 1)
186 |
187 | template6 := `%[1]v
188 | ibcAccountMockModule,`
189 | replacement6 := fmt.Sprintf(template6, placeholderSgAppAppModule)
190 | content = strings.Replace(content, placeholderSgAppAppModule, replacement6, 1)
191 |
192 | newFile := genny.NewFileS(path, content)
193 | return r.File(newFile)
194 | })
195 |
196 | run := genny.WetRunner(context.Background())
197 | run.With(g)
198 |
199 | return run.Run()
200 | }
201 |
202 | func isICAImported(appPath string) (bool, error) {
203 | abspath, err := filepath.Abs(filepath.Join(appPath, apppkg))
204 | if err != nil {
205 | return false, err
206 | }
207 | fset := token.NewFileSet()
208 | all, err := parser.ParseDir(fset, abspath, func(os.FileInfo) bool { return true }, parser.ImportsOnly)
209 | if err != nil {
210 | return false, err
211 | }
212 | for _, pkg := range all {
213 | for _, f := range pkg.Files {
214 | for _, imp := range f.Imports {
215 | if strings.Contains(imp.Path.Value, icaImport) {
216 | return true, nil
217 | }
218 | }
219 | }
220 | }
221 | return false, nil
222 | }
223 |
224 | func installICA() error {
225 | return cmdrunner.
226 | New(
227 | cmdrunner.DefaultStderr(os.Stderr),
228 | ).
229 | Run(context.Background(),
230 | step.New(
231 | step.Exec(
232 | "go",
233 | "get",
234 | icaImport,
235 | ),
236 | ),
237 | )
238 | }
239 |
--------------------------------------------------------------------------------
/cmd/starport-ica/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/chainapsis/cosmos-sdk-interchain-account/cmd/starport-ica/cmd"
7 | )
8 |
9 | func main() {
10 | rootCmd := cmd.New()
11 | if err := rootCmd.Execute(); err != nil {
12 | os.Exit(1)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/docs/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | theme: "cosmos",
3 | title: "Cosmos IBC Account",
4 | locales: {
5 | "/": {
6 | lang: "en-US"
7 | },
8 | kr: {
9 | lang: "kr"
10 | },
11 | cn: {
12 | lang: "cn"
13 | },
14 | ru: {
15 | lang: "ru"
16 | }
17 | },
18 | base: process.env.VUEPRESS_BASE || "/",
19 | head: [
20 | ['link', { rel: "apple-touch-icon", sizes: "180x180", href: "/apple-touch-icon.png" }],
21 | ['link', { rel: "icon", type: "image/png", sizes: "32x32", href: "/favicon-32x32.png" }],
22 | ['link', { rel: "icon", type: "image/png", sizes: "16x16", href: "/favicon-16x16.png" }],
23 | ['link', { rel: "manifest", href: "/site.webmanifest" }],
24 | ['meta', { name: "msapplication-TileColor", content: "#2e3148" }],
25 | ['meta', { name: "theme-color", content: "#ffffff" }],
26 | ['link', { rel: "icon", type: "image/svg+xml", href: "/favicon-svg.svg" }],
27 | ['link', { rel: "apple-touch-icon-precomposed", href: "/apple-touch-icon-precomposed.png" }],
28 | ],
29 | themeConfig: {
30 | repo: "chainapsis/cosmos-sdk-interchain-account",
31 | docsRepo: "chainapsis/cosmos-sdk-interchain-account",
32 | docsDir: "docs",
33 | label: "sdk",
34 | topbar: {
35 | banner: false
36 | },
37 | sidebar: {
38 | auto: false,
39 | nav: [
40 | {
41 | title: "Using the SDK",
42 | children: [
43 | {
44 | title: "Modules",
45 | directory: true,
46 | path: "/modules"
47 | }
48 | ]
49 | },
50 | {
51 | title: "Test with Starport",
52 | children: [
53 | {
54 | title: "Demo app guide",
55 | directory: true,
56 | path: "/starport"
57 | }
58 | ]
59 | }
60 | ]
61 | }
62 | }
63 | };
64 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chainapsis/cosmos-sdk-interchain-account/aeacf625cf339618d0da073b5766bb32848b8f43/docs/.vuepress/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/android-chrome-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chainapsis/cosmos-sdk-interchain-account/aeacf625cf339618d0da073b5766bb32848b8f43/docs/.vuepress/public/android-chrome-256x256.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chainapsis/cosmos-sdk-interchain-account/aeacf625cf339618d0da073b5766bb32848b8f43/docs/.vuepress/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chainapsis/cosmos-sdk-interchain-account/aeacf625cf339618d0da073b5766bb32848b8f43/docs/.vuepress/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #da532c
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chainapsis/cosmos-sdk-interchain-account/aeacf625cf339618d0da073b5766bb32848b8f43/docs/.vuepress/public/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chainapsis/cosmos-sdk-interchain-account/aeacf625cf339618d0da073b5766bb32848b8f43/docs/.vuepress/public/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/favicon-svg.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/logo-bw.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chainapsis/cosmos-sdk-interchain-account/aeacf625cf339618d0da073b5766bb32848b8f43/docs/.vuepress/public/mstile-150x150.png
--------------------------------------------------------------------------------
/docs/.vuepress/public/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
26 |
--------------------------------------------------------------------------------
/docs/.vuepress/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Cosmos SDK Documentation",
3 | "short_name": "Cosmos SDK",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-256x256.png",
12 | "sizes": "256x256",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/docs/.vuepress/styles/index.styl:
--------------------------------------------------------------------------------
1 | :root
2 | --color-primary #5064fb
3 | --color-link #5064fb
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 |
25 |
26 | # Cosmos SDK Documentation
27 |
28 | ## Get Started
29 |
30 | - **[SDK Intro](./intro/overview.md)**: High-level overview of the Cosmos SDK.
31 | - **[Quick Start Guide](./using-the-sdk/quick-start.md)**: Scaffold a standard Cosmos SDK app and run a node.
32 | - **[SDK Application Tutorial](https://github.com/cosmos/sdk-application-tutorial)**: A tutorial that showcases how to build an SDK-based blockchain from scratch and explains the basic principles of the SDK in the process.
33 |
34 | ## Reference
35 |
36 | - **[Basics](./basics/)**: Documentation on the basic concepts of the Cosmos SDK, like the standard anatomy of an application, the transaction lifecycle and accounts management.
37 | - **[Core](./core/)**: Documentation on the core concepts of the Cosmos SDK, like `baseapp`, the `store` or the `server`.
38 | - **[Building Modules](./building-modules/)**: Important concepts for module developers like `message`s, `keeper`s, `handler`s and `querier`s.
39 | - **[IBC](./ibc/)**: Documentation for the IBC protocol integration and concepts.
40 | - **[Running a Node, API, CLI](./run-node/)**: Documentation on how to run a node, and how to interact with it using the CLI and the API.
41 | - **[Migrations](./migrations/)**: Migration guides for updating to Stargate.
42 |
43 | ## Other Resources
44 |
45 | - **[Module Directory](../x/)**: Module implementations and their respective documentation.
46 | - **[Specifications](./spec/)**: Specifications of modules and other parts of the Cosmos SDK.
47 | - **[SDK API Reference](https://godoc.org/github.com/cosmos/cosmos-sdk)**: Godocs of the Cosmos SDK.
48 | - **[REST API spec](https://cosmos.network/rpc/)**: List of endpoints to interact with a `gaia` full-node through REST.
49 |
50 | ## Cosmos Hub
51 |
52 | The Cosmos Hub (`gaia`) docs have moved [here](https://github.com/cosmos/gaia/tree/master/docs).
53 |
54 | ## Languages
55 |
56 | The Cosmos-SDK is currently written in [Golang](https://golang.org/), though the
57 | framework could be implemented similarly in other languages.
58 | Contact us for information about funding an implementation in another language.
59 |
60 | ## Contribute
61 |
62 | See [this file](https://github.com/cosmos/cosmos-sdk/blob/master/docs/DOCS_README.md) for details of the build process and
63 | considerations when making changes.
64 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "1.0.0",
4 | "description": "Cosmos IBC Account Documentation",
5 | "main": "index.js",
6 | "scripts": {
7 | "preserve": "./pre.sh",
8 | "serve": "trap 'exit 0' SIGINT; vuepress dev --no-cache",
9 | "postserve": "./post.sh",
10 | "prebuild": "./pre.sh",
11 | "build": "trap 'exit 0' SIGINT; vuepress build --no-cache",
12 | "postbuild": "./post.sh"
13 | },
14 | "author": "",
15 | "license": "ISC",
16 | "dependencies": {
17 | "vuepress-theme-cosmos": "^1.0.179"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/docs/post.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | rm -rf modules
4 |
--------------------------------------------------------------------------------
/docs/pre.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | mkdir -p modules
4 |
5 | for D in ../x/*; do
6 | echo $D
7 | echo $(echo $D | awk -F/ '{print $NF}')
8 | if [ -d "${D}" ]; then
9 | rm -rf "modules/$(echo $D | awk -F/ '{print $NF}')"
10 | mkdir -p "modules/$(echo $D | awk -F/ '{print $NF}')" && cp -r $D/spec/* "$_"
11 | fi
12 | done
13 |
14 | cat ../x/README.md | sed 's/\.\/x/\/modules/g' | sed 's/spec\/README.md//g' | sed 's/\.\.\/docs\/building-modules\/README\.md/\/building-modules\/intro\.html/g' > ./modules/README.md
15 |
--------------------------------------------------------------------------------
/docs/starport/README.md:
--------------------------------------------------------------------------------
1 | # Bootstrap an Interchain Accounts Testnet using Starport
2 |
3 | Interchain Accounts currently experimentally supports [Starport v0.13.1](https://github.com/tendermint/starport/releases/tag/v0.13.1). However, it should be noted that stability is not guaranteed as the module import functionality is an experimental feature.
4 |
5 | ## Testing ICA with Starport
6 |
7 | ### 1. Start two chains with Starport
8 |
9 | Open two [Gitpod](https://gitpod.io/#https://github.com/chainapsis/cosmos-sdk-interchain-account) workspaces (Create fresh workspace → create) to start two instances of Starport chains: `foo` and `bar`.
10 |
11 | **Workspace 1**
12 |
13 | ```bash
14 | starport app github.com/foo/foo --sdk-version stargate
15 |
16 | cd foo
17 | ```
18 |
19 | **Workspace 2**
20 |
21 | ```bash
22 | starport app github.com/bar/bar --sdk-version stargate
23 |
24 | cd bar
25 | ```
26 |
27 | ### 2. Import the IBC Account module to the two chains
28 |
29 | **Workspace 1/2**
30 |
31 | ```bash
32 | starport-ica module import
33 | starport-ica module import mock
34 | ```
35 |
36 | ### 3. Start the two chains
37 |
38 | **Workspace 1/2**
39 |
40 | ```bash
41 | starport serve
42 | ```
43 |
44 | ### 4. Check the relayer information
45 |
46 | After bootstrapping each chain, each workspace terminal will display the relayer information as follows. Note that the value displayed will be different from the example.
47 |
48 | **Workspace 1/2 Console**
49 |
50 | ```bash
51 | ✨ Relayer info: eyJDaGFpbklEIjoiYmFyIiwiTW5lbW9uaWMiOiJmcm9zdCByYXpvciBoYWxmIGxhdW5kcnkgcHJvZml0IHdpc2UgdG9uZSBibHVzaCBzdXJnZSBrZWVwIHRvZ2V0aGVyIHNsaWNlIHlvdXRoIHRydXRoIGVubGlzdCBjdXBib2FyZCBhYnNvcmIgc2VlZCBzZXJpZXMgZG91YmxlIHZpbGxhZ2UgdG9uZ3VlIGZsYXNoIGdvcmlsbGEiLCJSUENBZGRyZXNzIjoiaHR0cHM6Ly8yNjY1Ny1jNzllNDk2ZC1kZDk4LTQ4MWQtOTlmZi1jZGQ4OTA2NWQ4MWIud3MtZXUwMS5naXRwb2QuaW86NDQzIn0
52 | ```
53 |
54 | ### 5. Add Chain Foo
55 |
56 | Head over to Workspace 2 and open a new terminal window. Input the following code to add the IBC connected chain. Make sure to use the relayer information shown in step 4 instead of the value provided in the example.
57 |
58 | **Workspace 2**
59 |
60 | ```bash
61 | cd ../bar
62 | starport chain add {workspace1-relayer-info}
63 | ```
64 |
65 | ### 6. Modify the configuration
66 |
67 | At this stage, IBC transfers connections are open. However, as Starport currently only natively supports IBC transfers we need to change the relayer configuration.
68 |
69 | Refer to the bar-foo IBC path by checking the `config.yaml` file as follows:
70 |
71 | **Workspace 2**
72 |
73 | ```bash
74 | vi ~/.relayer/config/config.yaml
75 | ```
76 |
77 | Manually add a new path by appending the following example path information underneath the existing `bar-foo` path (Note that the `client-id` and `connection-id` doesn't have to be changed from the example below, but you must change the `channel-id` name):
78 |
79 | ```yaml
80 | global:
81 | timeout: 10s
82 | lite-cache-size: 20
83 | chains:
84 | - key: testkey
85 | chain-id: bar
86 | rpc-addr: https://26657-b444783c-780f-4b19-ad73-aa02cf4309df.ws-us03.gitpod.io:443
87 | account-prefix: cosmos
88 | gas-adjustment: 1.5
89 | trusting-period: 336h
90 | - key: testkey
91 | chain-id: foo
92 | rpc-addr: https://26657-e8783cc5-17eb-44b5-990c-584a9705271e.ws-us03.gitpod.io:443
93 | account-prefix: cosmos
94 | gas-adjustment: 1.5
95 | trusting-period: 336h
96 | paths:
97 | bar-foo:
98 | src:
99 | chain-id: bar
100 | client-id: b4f6056a-1344-4e5d-9d20-93ea896dfb6c
101 | connection-id: f3cab589-db3e-4e19-8108-4ea4c39a32f7
102 | channel-id: test
103 | port-id: transfer
104 | order: unordered
105 | version: ics20-1
106 | dst:
107 | chain-id: foo
108 | client-id: 75284422-ec2a-447d-9764-c59fdc744e1a
109 | connection-id: e683f7bb-a49b-4c97-83a5-d342ad17d28e
110 | channel-id: test
111 | port-id: transfer
112 | order: unordered
113 | version: ics20-1
114 | strategy:
115 | type: naive
116 | //append the bar-foo-ica path information as shown here
117 | //but change the channel-id
118 | bar-foo-ica:
119 | src:
120 | chain-id: bar
121 | client-id: 1599fbea-43a0-4e8b-9c04-3f7e5cd11f94
122 | connection-id: 1f397527-e94e-4362-90cc-bc58e5aabffd
123 | channel-id: {your-channel-id-name}
124 | port-id: ibcaccount
125 | order: ordered
126 | version: ics27-1
127 | dst:
128 | chain-id: foo
129 | client-id: 15da2512-e2b3-4607-8355-74c994925cc9
130 | connection-id: 1f4c0fc3-0479-43a7-84c4-21d602c7ed98
131 | channel-id: {your-channel-id-name}
132 | port-id: ibcaccount
133 | order: ordered
134 | version: ics27-1
135 | strategy:
136 | type: naive
137 | ```
138 |
139 | Link the paths:
140 |
141 | ```bash
142 | rly tx link bar-foo-ica
143 | ```
144 |
145 | Once the linking is successful, you can use the `mock` module to run some test transactions that use interchain accounts.
146 |
147 | ### 7. Running test transactions
148 |
149 | First, you need to register an IBCAccount on chain `foo` that chain `bar` manages. Use the destination chain's `channel-id` as shown in `config.yaml`.
150 |
151 | **Workspace 2**
152 |
153 | ```bash
154 | bard tx ibcaccount register ibcaccount {dst.channel-id} test --from bar --absolute-timeouts --packet-timeout-height "0-1000000" --packet-timeout-timestamp 0
155 | ```
156 |
157 | Now you can use the relayer to send over the packet information to the chain that the IBC account will be created on.
158 |
159 | **Workspace 2**
160 |
161 | ```bash
162 | rly tx relay-packets bar-foo-ica
163 | rly tx relay-acknowledgements bar-foo-ica
164 | ```
165 |
166 | Now let's check if the IBC account has been registered on chain `foo`.
167 |
168 | Open a new terminal window in workspace 1
169 |
170 | Use the `src.channel-id` in the relayer's `config.yaml` for the next command.
171 |
172 | **Workspace 1**
173 |
174 | ```bash
175 | food q ibcaccount ibcaccount test ibcaccount {src.channel-id}
176 | ```
177 |
178 | Send a small amount of tokens to the IBC account address.
179 |
180 | **Workspace 1**
181 |
182 | ```bash
183 | food tx bank send user2 {ibc_account_address} 100token
184 | ```
185 |
186 | Check the balance of the IBC account.
187 |
188 | **Workspace 1**
189 |
190 | ```bash
191 | food q bank balances {ibc_account_address}
192 | ```
193 |
194 | Now move to workspace 2 to use the `bar` chain to send a token from the interchain account on `foo` chain.
195 |
196 | **Workspace 2**
197 |
198 | ```bash
199 | bard tx ibcaccount send ibcaccount {src.channel-id} {ibc-account-address} {receiving-account-address} 50token --from bar --absolute-timeouts --packet-timeout-height "0-1000000" --packet-timeout-timestamp 0
200 | ```
201 |
202 | Relay the packet through the relayer.
203 |
204 | **Workspace 2**
205 |
206 | ```bash
207 | rly tx relay-packets bar-foo-ica
208 | rly tx relay-acknowledgements bar-foo-ica
209 | ```
210 |
211 | Check the balance of the interchain account on chain `foo`.
212 |
213 | **Workspace 1**
214 |
215 | ```bash
216 | food q bank balances {ibc_account_address}
217 | ```
218 |
219 | Congratulations! You have now successfully sent a local transaction from an interchain account on chain `bar` through the interchain accounts IBC message.
220 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/chainapsis/cosmos-sdk-interchain-account
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/coreos/etcd v3.3.13+incompatible
7 | github.com/cosmos/cosmos-sdk v0.40.0-rc3
8 | github.com/ghodss/yaml v1.0.0
9 | github.com/gibson042/canonicaljson-go v1.0.3 // indirect
10 | github.com/gobuffalo/genny v0.6.0
11 | github.com/gogo/protobuf v1.3.1
12 | github.com/golang/protobuf v1.4.3
13 | github.com/gorilla/mux v1.8.0
14 | github.com/grpc-ecosystem/grpc-gateway v1.15.2
15 | github.com/rakyll/statik v0.1.7
16 | github.com/regen-network/cosmos-proto v0.3.0
17 | github.com/spf13/cast v1.3.1
18 | github.com/spf13/cobra v1.1.1
19 | github.com/spf13/viper v1.7.1
20 | github.com/stretchr/testify v1.6.1
21 | github.com/tendermint/starport v0.12.1-0.20201213075021-0e5d2c0217eb
22 | github.com/tendermint/tendermint v0.34.0-rc6
23 | github.com/tendermint/tm-db v0.6.2
24 | google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154
25 | google.golang.org/grpc v1.33.0
26 | gopkg.in/yaml.v2 v2.3.0
27 | rsc.io/quote/v3 v3.1.0 // indirect
28 | )
29 |
30 | replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.2-alpha.regen.4
31 |
--------------------------------------------------------------------------------
/proto/ibc/account/account.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package ibc.account;
3 |
4 | import "cosmos_proto/cosmos.proto";
5 | import "gogoproto/gogo.proto";
6 |
7 | import "cosmos/auth/v1beta1/auth.proto";
8 |
9 | option go_package = "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types";
10 |
11 | // IBCAccount defines an account to which other chains have privileges
12 | message IBCAccount {
13 | option (gogoproto.goproto_getters) = false;
14 | option (gogoproto.goproto_stringer) = false;
15 | option (cosmos_proto.implements_interface) = "IBCAccountI";
16 |
17 | cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""];
18 | string sourcePort = 2;
19 | string sourceChannel = 3;
20 | string destinationPort = 4;
21 | string destinationChannel = 5;
22 | }
23 |
--------------------------------------------------------------------------------
/proto/ibc/account/genesis.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package ibc.account;
3 |
4 | option go_package = "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types";
5 |
6 | import "gogoproto/gogo.proto";
7 |
8 | // GenesisState defines the ibc-account genesis state
9 | message GenesisState {
10 | string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""];
11 | }
12 |
--------------------------------------------------------------------------------
/proto/ibc/account/query.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package ibc.account;
3 |
4 | import "gogoproto/gogo.proto";
5 | import "google/api/annotations.proto";
6 |
7 | import "ibc/account/account.proto";
8 |
9 | option go_package = "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types";
10 |
11 | // Query defines the gRPC querier service.
12 | service Query {
13 | rpc IBCAccount(QueryIBCAccountRequest) returns (QueryIBCAccountResponse) {
14 | option (google.api.http).get = "/cosmos/ibc-account/v1beta1/ibc-account/{address}";
15 | }
16 |
17 | rpc IBCAccountFromData(QueryIBCAccountFromDataRequest) returns (QueryIBCAccountResponse) {
18 | option (google.api.http).get = "/cosmos/ibc-account/v1beta1/ibc-account-from-data/{port}/{channel}/{data}";
19 | }
20 | }
21 |
22 | message QueryIBCAccountRequest {
23 | option (gogoproto.equal) = false;
24 | option (gogoproto.goproto_getters) = false;
25 |
26 | // address is the address to query.
27 | string address = 1;
28 | }
29 |
30 | message QueryIBCAccountFromDataRequest {
31 | option (gogoproto.equal) = false;
32 | option (gogoproto.goproto_getters) = false;
33 |
34 | string port = 1;
35 | string channel = 2;
36 | string data = 3;
37 | }
38 |
39 | message QueryIBCAccountResponse {
40 | // account defines the account of the corresponding address.
41 | ibc.account.IBCAccount account = 1;
42 | }
43 |
--------------------------------------------------------------------------------
/proto/ibc/account/testing/msgs.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package ibc.account.testing;
3 |
4 | option go_package = "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock/types";
5 |
6 | import "gogoproto/gogo.proto";
7 | import "ibc/core/client/v1/client.proto";
8 | import "cosmos/base/v1beta1/coin.proto";
9 |
10 | message MsgTryRegisterIBCAccount {
11 | // the port on which the packet will be sent
12 | string source_port = 1 [(gogoproto.moretags) = "yaml:\"source_port\""];
13 | // the channel by which the packet will be sent
14 | string source_channel = 2 [(gogoproto.moretags) = "yaml:\"source_channel\""];
15 | // the salt that will be transfered to counterparty chain.
16 | bytes salt = 3 [(gogoproto.moretags) = "yaml:\"source_channel\""];
17 | // Timeout height relative to the current block height.
18 | // The timeout is disabled when set to 0.
19 | ibc.core.client.v1.Height timeout_height = 4 [(gogoproto.moretags) = "yaml:\"timeout_height\"", (gogoproto.nullable) = false];
20 | // Timeout timestamp (in nanoseconds) relative to the current block timestamp.
21 | // The timeout is disabled when set to 0.
22 | uint64 timeout_timestamp = 5 [(gogoproto.moretags) = "yaml:\"timeout_timestamp\""];
23 | bytes sender = 6 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"];
24 | }
25 |
26 | message MsgTryRunTxMsgSend {
27 | // the port on which the packet will be sent
28 | string source_port = 1 [(gogoproto.moretags) = "yaml:\"source_port\""];
29 | // the channel by which the packet will be sent
30 | string source_channel = 2 [(gogoproto.moretags) = "yaml:\"source_channel\""];
31 | // Timeout height relative to the current block height.
32 | // The timeout is disabled when set to 0.
33 | ibc.core.client.v1.Height timeout_height = 4 [(gogoproto.moretags) = "yaml:\"timeout_height\"", (gogoproto.nullable) = false];
34 | // Timeout timestamp (in nanoseconds) relative to the current block timestamp.
35 | // The timeout is disabled when set to 0.
36 | uint64 timeout_timestamp = 5 [(gogoproto.moretags) = "yaml:\"timeout_timestamp\""];
37 | bytes from_address = 6 [
38 | (gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress",
39 | (gogoproto.moretags) = "yaml:\"from_address\""
40 | ];
41 | bytes to_address = 7 [
42 | (gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress",
43 | (gogoproto.moretags) = "yaml:\"to_address\""
44 | ];
45 | repeated cosmos.base.v1beta1.Coin amount = 8
46 | [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
47 |
48 | bytes sender = 9 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"];
49 | }
50 |
--------------------------------------------------------------------------------
/proto/ibc/account/types.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package ibc.account;
3 |
4 | import "google/protobuf/any.proto";
5 |
6 | option go_package = "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types";
7 |
8 | message IBCTxRaw {
9 | bytes body_bytes = 1;
10 | }
11 |
12 | message IBCTxBody {
13 | repeated google.protobuf.Any messages = 1;
14 | }
15 |
16 | enum Type {
17 | REGISTER = 0;
18 | RUNTX = 1;
19 | }
20 |
21 | message IBCAccountPacketData {
22 | Type type = 1;
23 | bytes data = 2;
24 | }
25 |
26 | message IBCAccountPacketAcknowledgement {
27 | Type type = 1;
28 | string chainID = 2;
29 | uint32 code = 3;
30 | bytes data = 4;
31 | string error = 5;
32 | }
33 |
--------------------------------------------------------------------------------
/scripts/protocgen.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eo pipefail
4 |
5 | # Get the path of the cosmos-sdk repo from go/pkg/mod
6 | cosmos_sdk_dir=$(go list -f '{{ .Dir }}' -m github.com/cosmos/cosmos-sdk)
7 | proto_dirs=$(find . -path ./third_party -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq)
8 | for dir in $proto_dirs; do
9 | protoc \
10 | -I "proto" \
11 | -I "$cosmos_sdk_dir/third_party/proto" \
12 | -I "$cosmos_sdk_dir/proto" \
13 | --gocosmos_out=plugins=interfacetype+grpc,\
14 | Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types:. \
15 | $(find "${dir}" -maxdepth 1 -name '*.proto')
16 |
17 | # command to generate gRPC gateway (*.pb.gw.go in respective modules) files
18 | protoc \
19 | -I "proto" \
20 | -I "$cosmos_sdk_dir/third_party/proto" \
21 | -I "$cosmos_sdk_dir/proto" \
22 | --grpc-gateway_out=logtostderr=true:. \
23 | $(find "${dir}" -maxdepth 1 -name '*.proto')
24 | done
25 |
26 | cp -r github.com/chainapsis/cosmos-sdk-interchain-account/* ./
27 | rm -rf github.com
28 |
--------------------------------------------------------------------------------
/simapp/config.go:
--------------------------------------------------------------------------------
1 | package simapp
2 |
3 | import (
4 | "flag"
5 |
6 | "github.com/cosmos/cosmos-sdk/types/simulation"
7 | )
8 |
9 | // List of available flags for the simulator
10 | var (
11 | FlagGenesisFileValue string
12 | FlagParamsFileValue string
13 | FlagExportParamsPathValue string
14 | FlagExportParamsHeightValue int
15 | FlagExportStatePathValue string
16 | FlagExportStatsPathValue string
17 | FlagSeedValue int64
18 | FlagInitialBlockHeightValue int
19 | FlagNumBlocksValue int
20 | FlagBlockSizeValue int
21 | FlagLeanValue bool
22 | FlagCommitValue bool
23 | FlagOnOperationValue bool // TODO: Remove in favor of binary search for invariant violation
24 | FlagAllInvariantsValue bool
25 |
26 | FlagEnabledValue bool
27 | FlagVerboseValue bool
28 | FlagPeriodValue uint
29 | FlagGenesisTimeValue int64
30 | )
31 |
32 | // GetSimulatorFlags gets the values of all the available simulation flags
33 | func GetSimulatorFlags() {
34 | // config fields
35 | flag.StringVar(&FlagGenesisFileValue, "Genesis", "", "custom simulation genesis file; cannot be used with params file")
36 | flag.StringVar(&FlagParamsFileValue, "Params", "", "custom simulation params file which overrides any random params; cannot be used with genesis")
37 | flag.StringVar(&FlagExportParamsPathValue, "ExportParamsPath", "", "custom file path to save the exported params JSON")
38 | flag.IntVar(&FlagExportParamsHeightValue, "ExportParamsHeight", 0, "height to which export the randomly generated params")
39 | flag.StringVar(&FlagExportStatePathValue, "ExportStatePath", "", "custom file path to save the exported app state JSON")
40 | flag.StringVar(&FlagExportStatsPathValue, "ExportStatsPath", "", "custom file path to save the exported simulation statistics JSON")
41 | flag.Int64Var(&FlagSeedValue, "Seed", 42, "simulation random seed")
42 | flag.IntVar(&FlagInitialBlockHeightValue, "InitialBlockHeight", 1, "initial block to start the simulation")
43 | flag.IntVar(&FlagNumBlocksValue, "NumBlocks", 500, "number of new blocks to simulate from the initial block height")
44 | flag.IntVar(&FlagBlockSizeValue, "BlockSize", 200, "operations per block")
45 | flag.BoolVar(&FlagLeanValue, "Lean", false, "lean simulation log output")
46 | flag.BoolVar(&FlagCommitValue, "Commit", false, "have the simulation commit")
47 | flag.BoolVar(&FlagOnOperationValue, "SimulateEveryOperation", false, "run slow invariants every operation")
48 | flag.BoolVar(&FlagAllInvariantsValue, "PrintAllInvariants", false, "print all invariants if a broken invariant is found")
49 |
50 | // simulation flags
51 | flag.BoolVar(&FlagEnabledValue, "Enabled", false, "enable the simulation")
52 | flag.BoolVar(&FlagVerboseValue, "Verbose", false, "verbose log output")
53 | flag.UintVar(&FlagPeriodValue, "Period", 0, "run slow invariants only once every period assertions")
54 | flag.Int64Var(&FlagGenesisTimeValue, "GenesisTime", 0, "override genesis UNIX time instead of using a random UNIX time")
55 | }
56 |
57 | // NewConfigFromFlags creates a simulation from the retrieved values of the flags.
58 | func NewConfigFromFlags() simulation.Config {
59 | return simulation.Config{
60 | GenesisFile: FlagGenesisFileValue,
61 | ParamsFile: FlagParamsFileValue,
62 | ExportParamsPath: FlagExportParamsPathValue,
63 | ExportParamsHeight: FlagExportParamsHeightValue,
64 | ExportStatePath: FlagExportStatePathValue,
65 | ExportStatsPath: FlagExportStatsPathValue,
66 | Seed: FlagSeedValue,
67 | InitialBlockHeight: FlagInitialBlockHeightValue,
68 | NumBlocks: FlagNumBlocksValue,
69 | BlockSize: FlagBlockSizeValue,
70 | Lean: FlagLeanValue,
71 | Commit: FlagCommitValue,
72 | OnOperation: FlagOnOperationValue,
73 | AllInvariants: FlagAllInvariantsValue,
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/simapp/encoding.go:
--------------------------------------------------------------------------------
1 | package simapp
2 |
3 | import (
4 | simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
5 | "github.com/cosmos/cosmos-sdk/std"
6 | )
7 |
8 | // MakeTestEncodingConfig creates an EncodingConfig for testing.
9 | // This function should be used only internally (in the SDK).
10 | // App user should'nt create new codecs - use the app.AppCodec instead.
11 | // [DEPRECATED]
12 | func MakeTestEncodingConfig() simappparams.EncodingConfig {
13 | encodingConfig := simappparams.MakeTestEncodingConfig()
14 | std.RegisterLegacyAminoCodec(encodingConfig.Amino)
15 | std.RegisterInterfaces(encodingConfig.InterfaceRegistry)
16 | ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino)
17 | ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry)
18 | return encodingConfig
19 | }
20 |
--------------------------------------------------------------------------------
/simapp/export.go:
--------------------------------------------------------------------------------
1 | package simapp
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 |
7 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
8 |
9 | servertypes "github.com/cosmos/cosmos-sdk/server/types"
10 | sdk "github.com/cosmos/cosmos-sdk/types"
11 | slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
12 | "github.com/cosmos/cosmos-sdk/x/staking"
13 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
14 | )
15 |
16 | // ExportAppStateAndValidators exports the state of the application for a genesis
17 | // file.
18 | func (app *SimApp) ExportAppStateAndValidators(
19 | forZeroHeight bool, jailAllowedAddrs []string,
20 | ) (servertypes.ExportedApp, error) {
21 | // as if they could withdraw from the start of the next block
22 | ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()})
23 |
24 | // We export at last height + 1, because that's the height at which
25 | // Tendermint will start InitChain.
26 | height := app.LastBlockHeight() + 1
27 | if forZeroHeight {
28 | height = 0
29 | app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs)
30 | }
31 |
32 | genState := app.mm.ExportGenesis(ctx, app.appCodec)
33 | appState, err := json.MarshalIndent(genState, "", " ")
34 | if err != nil {
35 | return servertypes.ExportedApp{}, err
36 | }
37 |
38 | validators, err := staking.WriteValidators(ctx, app.StakingKeeper)
39 | return servertypes.ExportedApp{
40 | AppState: appState,
41 | Validators: validators,
42 | Height: height,
43 | ConsensusParams: app.BaseApp.GetConsensusParams(ctx),
44 | }, err
45 | }
46 |
47 | // prepare for fresh start at zero height
48 | // NOTE zero height genesis is a temporary feature which will be deprecated
49 | // in favour of export at a block height
50 | func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) {
51 | applyAllowedAddrs := false
52 |
53 | // check if there is a allowed address list
54 | if len(jailAllowedAddrs) > 0 {
55 | applyAllowedAddrs = true
56 | }
57 |
58 | allowedAddrsMap := make(map[string]bool)
59 |
60 | for _, addr := range jailAllowedAddrs {
61 | _, err := sdk.ValAddressFromBech32(addr)
62 | if err != nil {
63 | log.Fatal(err)
64 | }
65 | allowedAddrsMap[addr] = true
66 | }
67 |
68 | /* Just to be safe, assert the invariants on current state. */
69 | app.CrisisKeeper.AssertInvariants(ctx)
70 |
71 | /* Handle fee distribution state. */
72 |
73 | // withdraw all validator commission
74 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
75 | _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
76 | return false
77 | })
78 |
79 | // withdraw all delegator rewards
80 | dels := app.StakingKeeper.GetAllDelegations(ctx)
81 | for _, delegation := range dels {
82 | valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress)
83 | if err != nil {
84 | panic(err)
85 | }
86 |
87 | delAddr, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress)
88 | if err != nil {
89 | panic(err)
90 | }
91 | _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr)
92 | }
93 |
94 | // clear validator slash events
95 | app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx)
96 |
97 | // clear validator historical rewards
98 | app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
99 |
100 | // set context height to zero
101 | height := ctx.BlockHeight()
102 | ctx = ctx.WithBlockHeight(0)
103 |
104 | // reinitialize all validators
105 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
106 | // donate any unwithdrawn outstanding reward fraction tokens to the community pool
107 | scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator())
108 | feePool := app.DistrKeeper.GetFeePool(ctx)
109 | feePool.CommunityPool = feePool.CommunityPool.Add(scraps...)
110 | app.DistrKeeper.SetFeePool(ctx, feePool)
111 |
112 | app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
113 | return false
114 | })
115 |
116 | // reinitialize all delegations
117 | for _, del := range dels {
118 | valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress)
119 | if err != nil {
120 | panic(err)
121 | }
122 | delAddr, err := sdk.AccAddressFromBech32(del.DelegatorAddress)
123 | if err != nil {
124 | panic(err)
125 | }
126 | app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr)
127 | app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr)
128 | }
129 |
130 | // reset context height
131 | ctx = ctx.WithBlockHeight(height)
132 |
133 | /* Handle staking state. */
134 |
135 | // iterate through redelegations, reset creation height
136 | app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) {
137 | for i := range red.Entries {
138 | red.Entries[i].CreationHeight = 0
139 | }
140 | app.StakingKeeper.SetRedelegation(ctx, red)
141 | return false
142 | })
143 |
144 | // iterate through unbonding delegations, reset creation height
145 | app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) {
146 | for i := range ubd.Entries {
147 | ubd.Entries[i].CreationHeight = 0
148 | }
149 | app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
150 | return false
151 | })
152 |
153 | // Iterate through validators by power descending, reset bond heights, and
154 | // update bond intra-tx counters.
155 | store := ctx.KVStore(app.keys[stakingtypes.StoreKey])
156 | iter := sdk.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey)
157 | counter := int16(0)
158 |
159 | for ; iter.Valid(); iter.Next() {
160 | addr := sdk.ValAddress(iter.Key()[1:])
161 | validator, found := app.StakingKeeper.GetValidator(ctx, addr)
162 | if !found {
163 | panic("expected validator, not found")
164 | }
165 |
166 | validator.UnbondingHeight = 0
167 | if applyAllowedAddrs && !allowedAddrsMap[addr.String()] {
168 | validator.Jailed = true
169 | }
170 |
171 | app.StakingKeeper.SetValidator(ctx, validator)
172 | counter++
173 | }
174 |
175 | iter.Close()
176 |
177 | _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
178 | if err != nil {
179 | log.Fatal(err)
180 | }
181 |
182 | /* Handle slashing state. */
183 |
184 | // reset start height on signing infos
185 | app.SlashingKeeper.IterateValidatorSigningInfos(
186 | ctx,
187 | func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) {
188 | info.StartHeight = 0
189 | app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
190 | return false
191 | },
192 | )
193 | }
194 |
--------------------------------------------------------------------------------
/simapp/genesis.go:
--------------------------------------------------------------------------------
1 | package simapp
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 |
16 | // NewDefaultGenesisState generates the default state for the application.
17 | func NewDefaultGenesisState() GenesisState {
18 | config := MakeTestEncodingConfig()
19 | return ModuleBasics.DefaultGenesis(config.Marshaler)
20 | }
21 |
--------------------------------------------------------------------------------
/simapp/genesis_account.go:
--------------------------------------------------------------------------------
1 | package simapp
2 |
3 | import (
4 | "errors"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
8 | )
9 |
10 | var _ authtypes.GenesisAccount = (*SimGenesisAccount)(nil)
11 |
12 | // SimGenesisAccount defines a type that implements the GenesisAccount interface
13 | // to be used for simulation accounts in the genesis state.
14 | type SimGenesisAccount struct {
15 | *authtypes.BaseAccount
16 |
17 | // vesting account fields
18 | OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` // total vesting coins upon initialization
19 | DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` // delegated vested coins at time of delegation
20 | DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` // delegated vesting coins at time of delegation
21 | StartTime int64 `json:"start_time" yaml:"start_time"` // vesting start time (UNIX Epoch time)
22 | EndTime int64 `json:"end_time" yaml:"end_time"` // vesting end time (UNIX Epoch time)
23 |
24 | // module account fields
25 | ModuleName string `json:"module_name" yaml:"module_name"` // name of the module account
26 | ModulePermissions []string `json:"module_permissions" yaml:"module_permissions"` // permissions of module account
27 | }
28 |
29 | // Validate checks for errors on the vesting and module account parameters
30 | func (sga SimGenesisAccount) Validate() error {
31 | if !sga.OriginalVesting.IsZero() {
32 | if sga.StartTime >= sga.EndTime {
33 | return errors.New("vesting start-time cannot be before end-time")
34 | }
35 | }
36 |
37 | if sga.ModuleName != "" {
38 | ma := authtypes.ModuleAccount{
39 | BaseAccount: sga.BaseAccount, Name: sga.ModuleName, Permissions: sga.ModulePermissions,
40 | }
41 | if err := ma.Validate(); err != nil {
42 | return err
43 | }
44 | }
45 |
46 | return sga.BaseAccount.Validate()
47 | }
48 |
--------------------------------------------------------------------------------
/simapp/simd/cmd/cmd_test.go:
--------------------------------------------------------------------------------
1 | package cmd_test
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/require"
8 |
9 | "github.com/cosmos/cosmos-sdk/simapp/simd/cmd"
10 | "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
11 | )
12 |
13 | func TestInitCmd(t *testing.T) {
14 | rootCmd, _ := cmd.NewRootCmd()
15 | rootCmd.SetArgs([]string{
16 | "init", // Test the init cmd
17 | "simapp-test", // Moniker
18 | fmt.Sprintf("--%s=%s", cli.FlagOverwrite, "true"), // Overwrite genesis.json, in case it already exists
19 | })
20 |
21 | err := cmd.Execute(rootCmd)
22 | require.NoError(t, err)
23 | }
24 |
--------------------------------------------------------------------------------
/simapp/simd/cmd/genaccounts.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "bufio"
5 | "encoding/json"
6 | "errors"
7 | "fmt"
8 |
9 | "github.com/spf13/cobra"
10 |
11 | "github.com/cosmos/cosmos-sdk/client"
12 | "github.com/cosmos/cosmos-sdk/client/flags"
13 | "github.com/cosmos/cosmos-sdk/codec"
14 | "github.com/cosmos/cosmos-sdk/crypto/keyring"
15 | "github.com/cosmos/cosmos-sdk/server"
16 | sdk "github.com/cosmos/cosmos-sdk/types"
17 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
18 | authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
19 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
20 | "github.com/cosmos/cosmos-sdk/x/genutil"
21 | genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
22 | )
23 |
24 | const (
25 | flagVestingStart = "vesting-start-time"
26 | flagVestingEnd = "vesting-end-time"
27 | flagVestingAmt = "vesting-amount"
28 | )
29 |
30 | // AddGenesisAccountCmd returns add-genesis-account cobra Command.
31 | func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command {
32 | cmd := &cobra.Command{
33 | Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]",
34 | Short: "Add a genesis account to genesis.json",
35 | Long: `Add a genesis account to genesis.json. The provided account must specify
36 | the account address or key name and a list of initial coins. If a key name is given,
37 | the address will be looked up in the local Keybase. The list of initial tokens must
38 | contain valid denominations. Accounts may optionally be supplied with vesting parameters.
39 | `,
40 | Args: cobra.ExactArgs(2),
41 | RunE: func(cmd *cobra.Command, args []string) error {
42 | clientCtx := client.GetClientContextFromCmd(cmd)
43 | depCdc := clientCtx.JSONMarshaler
44 | cdc := depCdc.(codec.Marshaler)
45 |
46 | serverCtx := server.GetServerContextFromCmd(cmd)
47 | config := serverCtx.Config
48 |
49 | config.SetRoot(clientCtx.HomeDir)
50 |
51 | addr, err := sdk.AccAddressFromBech32(args[0])
52 | if err != nil {
53 | inBuf := bufio.NewReader(cmd.InOrStdin())
54 | keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend)
55 |
56 | // attempt to lookup address from Keybase if no address was provided
57 | kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf)
58 | if err != nil {
59 | return err
60 | }
61 |
62 | info, err := kb.Key(args[0])
63 | if err != nil {
64 | return fmt.Errorf("failed to get address from Keybase: %w", err)
65 | }
66 |
67 | addr = info.GetAddress()
68 | }
69 |
70 | coins, err := sdk.ParseCoins(args[1])
71 | if err != nil {
72 | return fmt.Errorf("failed to parse coins: %w", err)
73 | }
74 |
75 | vestingStart, _ := cmd.Flags().GetInt64(flagVestingStart)
76 | vestingEnd, _ := cmd.Flags().GetInt64(flagVestingEnd)
77 | vestingAmtStr, _ := cmd.Flags().GetString(flagVestingAmt)
78 |
79 | vestingAmt, err := sdk.ParseCoins(vestingAmtStr)
80 | if err != nil {
81 | return fmt.Errorf("failed to parse vesting amount: %w", err)
82 | }
83 |
84 | // create concrete account type based on input parameters
85 | var genAccount authtypes.GenesisAccount
86 |
87 | balances := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()}
88 | baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0)
89 |
90 | if !vestingAmt.IsZero() {
91 | baseVestingAccount := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd)
92 |
93 | if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) ||
94 | baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) {
95 | return errors.New("vesting amount cannot be greater than total amount")
96 | }
97 |
98 | switch {
99 | case vestingStart != 0 && vestingEnd != 0:
100 | genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart)
101 |
102 | case vestingEnd != 0:
103 | genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount)
104 |
105 | default:
106 | return errors.New("invalid vesting parameters; must supply start and end time or end time")
107 | }
108 | } else {
109 | genAccount = baseAccount
110 | }
111 |
112 | if err := genAccount.Validate(); err != nil {
113 | return fmt.Errorf("failed to validate new genesis account: %w", err)
114 | }
115 |
116 | genFile := config.GenesisFile()
117 | appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile)
118 | if err != nil {
119 | return fmt.Errorf("failed to unmarshal genesis state: %w", err)
120 | }
121 |
122 | authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState)
123 |
124 | accs, err := authtypes.UnpackAccounts(authGenState.Accounts)
125 | if err != nil {
126 | return fmt.Errorf("failed to get accounts from any: %w", err)
127 | }
128 |
129 | if accs.Contains(addr) {
130 | return fmt.Errorf("cannot add account at existing address %s", addr)
131 | }
132 |
133 | // Add the new account to the set of genesis accounts and sanitize the
134 | // accounts afterwards.
135 | accs = append(accs, genAccount)
136 | accs = authtypes.SanitizeGenesisAccounts(accs)
137 |
138 | genAccs, err := authtypes.PackAccounts(accs)
139 | if err != nil {
140 | return fmt.Errorf("failed to convert accounts into any's: %w", err)
141 | }
142 | authGenState.Accounts = genAccs
143 |
144 | authGenStateBz, err := cdc.MarshalJSON(&authGenState)
145 | if err != nil {
146 | return fmt.Errorf("failed to marshal auth genesis state: %w", err)
147 | }
148 |
149 | appState[authtypes.ModuleName] = authGenStateBz
150 |
151 | bankGenState := banktypes.GetGenesisStateFromAppState(depCdc, appState)
152 | bankGenState.Balances = append(bankGenState.Balances, balances)
153 | bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances)
154 |
155 | bankGenStateBz, err := cdc.MarshalJSON(bankGenState)
156 | if err != nil {
157 | return fmt.Errorf("failed to marshal bank genesis state: %w", err)
158 | }
159 |
160 | appState[banktypes.ModuleName] = bankGenStateBz
161 |
162 | appStateJSON, err := json.Marshal(appState)
163 | if err != nil {
164 | return fmt.Errorf("failed to marshal application genesis state: %w", err)
165 | }
166 |
167 | genDoc.AppState = appStateJSON
168 | return genutil.ExportGenesisFile(genDoc, genFile)
169 | },
170 | }
171 |
172 | cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
173 | cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)")
174 | cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts")
175 | cmd.Flags().Int64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts")
176 | cmd.Flags().Int64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts")
177 | flags.AddQueryFlagsToCmd(cmd)
178 |
179 | return cmd
180 | }
181 |
--------------------------------------------------------------------------------
/simapp/simd/cmd/root.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "context"
5 | "io"
6 | "os"
7 | "path/filepath"
8 |
9 | "github.com/spf13/cast"
10 | "github.com/spf13/cobra"
11 | tmcli "github.com/tendermint/tendermint/libs/cli"
12 | "github.com/tendermint/tendermint/libs/log"
13 | dbm "github.com/tendermint/tm-db"
14 |
15 | "github.com/chainapsis/cosmos-sdk-interchain-account/simapp"
16 | "github.com/cosmos/cosmos-sdk/baseapp"
17 | "github.com/cosmos/cosmos-sdk/client"
18 | "github.com/cosmos/cosmos-sdk/client/debug"
19 | "github.com/cosmos/cosmos-sdk/client/flags"
20 | "github.com/cosmos/cosmos-sdk/client/keys"
21 | "github.com/cosmos/cosmos-sdk/client/rpc"
22 | "github.com/cosmos/cosmos-sdk/codec"
23 | "github.com/cosmos/cosmos-sdk/server"
24 | servertypes "github.com/cosmos/cosmos-sdk/server/types"
25 | "github.com/cosmos/cosmos-sdk/simapp/params"
26 | "github.com/cosmos/cosmos-sdk/snapshots"
27 | "github.com/cosmos/cosmos-sdk/store"
28 | sdk "github.com/cosmos/cosmos-sdk/types"
29 | authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
30 | authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
31 | "github.com/cosmos/cosmos-sdk/x/auth/types"
32 | vestingcli "github.com/cosmos/cosmos-sdk/x/auth/vesting/client/cli"
33 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
34 | "github.com/cosmos/cosmos-sdk/x/crisis"
35 | genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
36 | )
37 |
38 | // NewRootCmd creates a new root command for simd. It is called once in the
39 | // main function.
40 | func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
41 | encodingConfig := simapp.MakeTestEncodingConfig()
42 | initClientCtx := client.Context{}.
43 | WithJSONMarshaler(encodingConfig.Marshaler).
44 | WithInterfaceRegistry(encodingConfig.InterfaceRegistry).
45 | WithTxConfig(encodingConfig.TxConfig).
46 | WithLegacyAmino(encodingConfig.Amino).
47 | WithInput(os.Stdin).
48 | WithAccountRetriever(types.AccountRetriever{}).
49 | WithBroadcastMode(flags.BroadcastBlock).
50 | WithHomeDir(simapp.DefaultNodeHome)
51 |
52 | rootCmd := &cobra.Command{
53 | Use: "simd",
54 | Short: "simulation app",
55 | PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
56 | if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil {
57 | return err
58 | }
59 |
60 | return server.InterceptConfigsPreRunHandler(cmd)
61 | },
62 | }
63 |
64 | initRootCmd(rootCmd, encodingConfig)
65 |
66 | return rootCmd, encodingConfig
67 | }
68 |
69 | // Execute executes the root command.
70 | func Execute(rootCmd *cobra.Command) error {
71 | // Create and set a client.Context on the command's Context. During the pre-run
72 | // of the root command, a default initialized client.Context is provided to
73 | // seed child command execution with values such as AccountRetriver, Keyring,
74 | // and a Tendermint RPC. This requires the use of a pointer reference when
75 | // getting and setting the client.Context. Ideally, we utilize
76 | // https://github.com/spf13/cobra/pull/1118.
77 | srvCtx := server.NewDefaultContext()
78 | ctx := context.Background()
79 | ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{})
80 | ctx = context.WithValue(ctx, server.ServerContextKey, srvCtx)
81 |
82 | rootCmd.PersistentFlags().String("log_level", srvCtx.Config.LogLevel, "The logging level in the format of :,...")
83 |
84 | executor := tmcli.PrepareBaseCmd(rootCmd, "", simapp.DefaultNodeHome)
85 | return executor.ExecuteContext(ctx)
86 | }
87 |
88 | func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
89 | authclient.Codec = encodingConfig.Marshaler
90 |
91 | rootCmd.AddCommand(
92 | genutilcli.InitCmd(simapp.ModuleBasics, simapp.DefaultNodeHome),
93 | genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome),
94 | genutilcli.MigrateGenesisCmd(),
95 | genutilcli.GenTxCmd(simapp.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome),
96 | genutilcli.ValidateGenesisCmd(simapp.ModuleBasics, encodingConfig.TxConfig),
97 | AddGenesisAccountCmd(simapp.DefaultNodeHome),
98 | tmcli.NewCompletionCmd(rootCmd, true),
99 | testnetCmd(simapp.ModuleBasics, banktypes.GenesisBalancesIterator{}),
100 | debug.Cmd(),
101 | )
102 |
103 | server.AddCommands(rootCmd, simapp.DefaultNodeHome, newApp, createSimappAndExport, addModuleInitFlags)
104 |
105 | // add keybase, auxiliary RPC, query, and tx child commands
106 | rootCmd.AddCommand(
107 | rpc.StatusCommand(),
108 | queryCommand(),
109 | txCommand(),
110 | keys.Commands(simapp.DefaultNodeHome),
111 | )
112 | }
113 |
114 | func addModuleInitFlags(startCmd *cobra.Command) {
115 | crisis.AddModuleInitFlags(startCmd)
116 | }
117 |
118 | func queryCommand() *cobra.Command {
119 | cmd := &cobra.Command{
120 | Use: "query",
121 | Aliases: []string{"q"},
122 | Short: "Querying subcommands",
123 | DisableFlagParsing: true,
124 | SuggestionsMinimumDistance: 2,
125 | RunE: client.ValidateCmd,
126 | }
127 |
128 | cmd.AddCommand(
129 | authcmd.GetAccountCmd(),
130 | rpc.ValidatorCommand(),
131 | rpc.BlockCommand(),
132 | authcmd.QueryTxsByEventsCmd(),
133 | authcmd.QueryTxCmd(),
134 | )
135 |
136 | simapp.ModuleBasics.AddQueryCommands(cmd)
137 | cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID")
138 |
139 | return cmd
140 | }
141 |
142 | func txCommand() *cobra.Command {
143 | cmd := &cobra.Command{
144 | Use: "tx",
145 | Short: "Transactions subcommands",
146 | DisableFlagParsing: true,
147 | SuggestionsMinimumDistance: 2,
148 | RunE: client.ValidateCmd,
149 | }
150 |
151 | cmd.AddCommand(
152 | authcmd.GetSignCommand(),
153 | authcmd.GetSignBatchCommand(),
154 | authcmd.GetMultiSignCommand(),
155 | authcmd.GetValidateSignaturesCommand(),
156 | flags.LineBreak,
157 | authcmd.GetBroadcastCommand(),
158 | authcmd.GetEncodeCommand(),
159 | authcmd.GetDecodeCommand(),
160 | flags.LineBreak,
161 | vestingcli.GetTxCmd(),
162 | )
163 |
164 | simapp.ModuleBasics.AddTxCommands(cmd)
165 | cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID")
166 |
167 | return cmd
168 | }
169 |
170 | func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application {
171 | var cache sdk.MultiStorePersistentCache
172 |
173 | if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) {
174 | cache = store.NewCommitKVStoreCacheManager()
175 | }
176 |
177 | skipUpgradeHeights := make(map[int64]bool)
178 | for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) {
179 | skipUpgradeHeights[int64(h)] = true
180 | }
181 |
182 | pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts)
183 | if err != nil {
184 | panic(err)
185 | }
186 |
187 | snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots")
188 | snapshotDB, err := sdk.NewLevelDB("metadata", snapshotDir)
189 | if err != nil {
190 | panic(err)
191 | }
192 | snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir)
193 | if err != nil {
194 | panic(err)
195 | }
196 |
197 | return simapp.NewSimApp(
198 | logger, db, traceStore, true, skipUpgradeHeights,
199 | cast.ToString(appOpts.Get(flags.FlagHome)),
200 | cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)),
201 | simapp.MakeTestEncodingConfig(), // Ideally, we would reuse the one created by NewRootCmd.
202 | appOpts,
203 | baseapp.SetPruning(pruningOpts),
204 | baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))),
205 | baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))),
206 | baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))),
207 | baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))),
208 | baseapp.SetInterBlockCache(cache),
209 | baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))),
210 | baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))),
211 | baseapp.SetSnapshotStore(snapshotStore),
212 | baseapp.SetSnapshotInterval(cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval))),
213 | baseapp.SetSnapshotKeepRecent(cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent))),
214 | )
215 | }
216 |
217 | // createSimappAndExport creates a new simapp (optionally at a given height)
218 | // and exports state.
219 | func createSimappAndExport(
220 | logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string,
221 | appOpts servertypes.AppOptions) (servertypes.ExportedApp, error) {
222 |
223 | encCfg := simapp.MakeTestEncodingConfig() // Ideally, we would reuse the one created by NewRootCmd.
224 | encCfg.Marshaler = codec.NewProtoCodec(encCfg.InterfaceRegistry)
225 | var simApp *simapp.SimApp
226 | if height != -1 {
227 | simApp = simapp.NewSimApp(logger, db, traceStore, false, map[int64]bool{}, "", uint(1), encCfg, appOpts)
228 |
229 | if err := simApp.LoadHeight(height); err != nil {
230 | return servertypes.ExportedApp{}, err
231 | }
232 | } else {
233 | simApp = simapp.NewSimApp(logger, db, traceStore, true, map[int64]bool{}, "", uint(1), encCfg, appOpts)
234 | }
235 |
236 | return simApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs)
237 | }
238 |
--------------------------------------------------------------------------------
/simapp/simd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/chainapsis/cosmos-sdk-interchain-account/simapp/simd/cmd"
7 | "github.com/cosmos/cosmos-sdk/server"
8 | )
9 |
10 | func main() {
11 | rootCmd, _ := cmd.NewRootCmd()
12 | if err := cmd.Execute(rootCmd); err != nil {
13 | switch e := err.(type) {
14 | case server.ErrorCode:
15 | os.Exit(e.Code)
16 | default:
17 | os.Exit(1)
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/simapp/state.go:
--------------------------------------------------------------------------------
1 | package simapp
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io"
7 | "io/ioutil"
8 | "math/rand"
9 | "time"
10 |
11 | tmjson "github.com/tendermint/tendermint/libs/json"
12 | tmtypes "github.com/tendermint/tendermint/types"
13 |
14 | "github.com/cosmos/cosmos-sdk/codec"
15 | "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
16 | simapparams "github.com/cosmos/cosmos-sdk/simapp/params"
17 | "github.com/cosmos/cosmos-sdk/types/module"
18 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
19 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
20 | )
21 |
22 | // AppStateFn returns the initial application state using a genesis or the simulation parameters.
23 | // It panics if the user provides files for both of them.
24 | // If a file is not given for the genesis or the sim params, it creates a randomized one.
25 | func AppStateFn(cdc codec.JSONMarshaler, simManager *module.SimulationManager) simtypes.AppStateFn {
26 | return func(r *rand.Rand, accs []simtypes.Account, config simtypes.Config,
27 | ) (appState json.RawMessage, simAccs []simtypes.Account, chainID string, genesisTimestamp time.Time) {
28 |
29 | if FlagGenesisTimeValue == 0 {
30 | genesisTimestamp = simtypes.RandTimestamp(r)
31 | } else {
32 | genesisTimestamp = time.Unix(FlagGenesisTimeValue, 0)
33 | }
34 |
35 | chainID = config.ChainID
36 | switch {
37 | case config.ParamsFile != "" && config.GenesisFile != "":
38 | panic("cannot provide both a genesis file and a params file")
39 |
40 | case config.GenesisFile != "":
41 | // override the default chain-id from simapp to set it later to the config
42 | genesisDoc, accounts := AppStateFromGenesisFileFn(r, cdc, config.GenesisFile)
43 |
44 | if FlagGenesisTimeValue == 0 {
45 | // use genesis timestamp if no custom timestamp is provided (i.e no random timestamp)
46 | genesisTimestamp = genesisDoc.GenesisTime
47 | }
48 |
49 | appState = genesisDoc.AppState
50 | chainID = genesisDoc.ChainID
51 | simAccs = accounts
52 |
53 | case config.ParamsFile != "":
54 | appParams := make(simtypes.AppParams)
55 | bz, err := ioutil.ReadFile(config.ParamsFile)
56 | if err != nil {
57 | panic(err)
58 | }
59 |
60 | err = json.Unmarshal(bz, &appParams)
61 | if err != nil {
62 | panic(err)
63 | }
64 | appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams)
65 |
66 | default:
67 | appParams := make(simtypes.AppParams)
68 | appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams)
69 | }
70 |
71 | return appState, simAccs, chainID, genesisTimestamp
72 | }
73 | }
74 |
75 | // AppStateRandomizedFn creates calls each module's GenesisState generator function
76 | // and creates the simulation params
77 | func AppStateRandomizedFn(
78 | simManager *module.SimulationManager, r *rand.Rand, cdc codec.JSONMarshaler,
79 | accs []simtypes.Account, genesisTimestamp time.Time, appParams simtypes.AppParams,
80 | ) (json.RawMessage, []simtypes.Account) {
81 | numAccs := int64(len(accs))
82 | genesisState := NewDefaultGenesisState()
83 |
84 | // generate a random amount of initial stake coins and a random initial
85 | // number of bonded accounts
86 | var initialStake, numInitiallyBonded int64
87 | appParams.GetOrGenerate(
88 | cdc, simapparams.StakePerAccount, &initialStake, r,
89 | func(r *rand.Rand) { initialStake = r.Int63n(1e12) },
90 | )
91 | appParams.GetOrGenerate(
92 | cdc, simapparams.InitiallyBondedValidators, &numInitiallyBonded, r,
93 | func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(300)) },
94 | )
95 |
96 | if numInitiallyBonded > numAccs {
97 | numInitiallyBonded = numAccs
98 | }
99 |
100 | fmt.Printf(
101 | `Selected randomly generated parameters for simulated genesis:
102 | {
103 | stake_per_account: "%d",
104 | initially_bonded_validators: "%d"
105 | }
106 | `, initialStake, numInitiallyBonded,
107 | )
108 |
109 | simState := &module.SimulationState{
110 | AppParams: appParams,
111 | Cdc: cdc,
112 | Rand: r,
113 | GenState: genesisState,
114 | Accounts: accs,
115 | InitialStake: initialStake,
116 | NumBonded: numInitiallyBonded,
117 | GenTimestamp: genesisTimestamp,
118 | }
119 |
120 | simManager.GenerateGenesisStates(simState)
121 |
122 | appState, err := json.Marshal(genesisState)
123 | if err != nil {
124 | panic(err)
125 | }
126 |
127 | return appState, accs
128 | }
129 |
130 | // AppStateFromGenesisFileFn util function to generate the genesis AppState
131 | // from a genesis.json file.
132 | func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONMarshaler, genesisFile string) (tmtypes.GenesisDoc, []simtypes.Account) {
133 | bytes, err := ioutil.ReadFile(genesisFile)
134 | if err != nil {
135 | panic(err)
136 | }
137 |
138 | var genesis tmtypes.GenesisDoc
139 | // NOTE: Tendermint uses a custom JSON decoder for GenesisDoc
140 | err = tmjson.Unmarshal(bytes, &genesis)
141 | if err != nil {
142 | panic(err)
143 | }
144 |
145 | var appState GenesisState
146 | err = json.Unmarshal(genesis.AppState, &appState)
147 | if err != nil {
148 | panic(err)
149 | }
150 |
151 | var authGenesis authtypes.GenesisState
152 | if appState[authtypes.ModuleName] != nil {
153 | cdc.MustUnmarshalJSON(appState[authtypes.ModuleName], &authGenesis)
154 | }
155 |
156 | newAccs := make([]simtypes.Account, len(authGenesis.Accounts))
157 | for i, acc := range authGenesis.Accounts {
158 | // Pick a random private key, since we don't know the actual key
159 | // This should be fine as it's only used for mock Tendermint validators
160 | // and these keys are never actually used to sign by mock Tendermint.
161 | privkeySeed := make([]byte, 15)
162 | if _, err := r.Read(privkeySeed); err != nil {
163 | panic(err)
164 | }
165 |
166 | privKey := secp256k1.GenPrivKeyFromSecret(privkeySeed)
167 |
168 | a, ok := acc.GetCachedValue().(authtypes.AccountI)
169 | if !ok {
170 | panic("expected account")
171 | }
172 |
173 | // create simulator accounts
174 | simAcc := simtypes.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: a.GetAddress()}
175 | newAccs[i] = simAcc
176 | }
177 |
178 | return genesis, newAccs
179 | }
180 |
--------------------------------------------------------------------------------
/simapp/utils.go:
--------------------------------------------------------------------------------
1 | package simapp
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 |
8 | "github.com/tendermint/tendermint/libs/log"
9 | dbm "github.com/tendermint/tm-db"
10 |
11 | "github.com/cosmos/cosmos-sdk/codec"
12 | simapp "github.com/cosmos/cosmos-sdk/simapp"
13 | "github.com/cosmos/cosmos-sdk/simapp/helpers"
14 | sdk "github.com/cosmos/cosmos-sdk/types"
15 | "github.com/cosmos/cosmos-sdk/types/kv"
16 | "github.com/cosmos/cosmos-sdk/types/module"
17 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
18 | )
19 |
20 | // SetupSimulation creates the config, db (levelDB), temporary directory and logger for
21 | // the simulation tests. If `FlagEnabledValue` is false it skips the current test.
22 | // Returns error on an invalid db intantiation or temp dir creation.
23 | func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DB, string, log.Logger, bool, error) {
24 | if !FlagEnabledValue {
25 | return simtypes.Config{}, nil, "", nil, true, nil
26 | }
27 |
28 | config := NewConfigFromFlags()
29 | config.ChainID = helpers.SimAppChainID
30 |
31 | var logger log.Logger
32 | if FlagVerboseValue {
33 | logger = log.TestingLogger()
34 | } else {
35 | logger = log.NewNopLogger()
36 | }
37 |
38 | dir, err := ioutil.TempDir("", dirPrefix)
39 | if err != nil {
40 | return simtypes.Config{}, nil, "", nil, false, err
41 | }
42 |
43 | db, err := sdk.NewLevelDB(dbName, dir)
44 | if err != nil {
45 | return simtypes.Config{}, nil, "", nil, false, err
46 | }
47 |
48 | return config, db, dir, logger, false, nil
49 | }
50 |
51 | // SimulationOperations retrieves the simulation params from the provided file path
52 | // and returns all the modules weighted operations
53 | func SimulationOperations(app simapp.App, cdc codec.JSONMarshaler, config simtypes.Config) []simtypes.WeightedOperation {
54 | simState := module.SimulationState{
55 | AppParams: make(simtypes.AppParams),
56 | Cdc: cdc,
57 | }
58 |
59 | if config.ParamsFile != "" {
60 | bz, err := ioutil.ReadFile(config.ParamsFile)
61 | if err != nil {
62 | panic(err)
63 | }
64 |
65 | err = json.Unmarshal(bz, &simState.AppParams)
66 | if err != nil {
67 | panic(err)
68 | }
69 | }
70 |
71 | simState.ParamChanges = app.SimulationManager().GenerateParamChanges(config.Seed)
72 | simState.Contents = app.SimulationManager().GetProposalContents(simState)
73 | return app.SimulationManager().WeightedOperations(simState)
74 | }
75 |
76 | // CheckExportSimulation exports the app state and simulation parameters to JSON
77 | // if the export paths are defined.
78 | func CheckExportSimulation(
79 | app simapp.App, config simtypes.Config, params simtypes.Params,
80 | ) error {
81 | if config.ExportStatePath != "" {
82 | fmt.Println("exporting app state...")
83 | exported, err := app.ExportAppStateAndValidators(false, nil)
84 | if err != nil {
85 | return err
86 | }
87 |
88 | if err := ioutil.WriteFile(config.ExportStatePath, []byte(exported.AppState), 0600); err != nil {
89 | return err
90 | }
91 | }
92 |
93 | if config.ExportParamsPath != "" {
94 | fmt.Println("exporting simulation params...")
95 | paramsBz, err := json.MarshalIndent(params, "", " ")
96 | if err != nil {
97 | return err
98 | }
99 |
100 | if err := ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0600); err != nil {
101 | return err
102 | }
103 | }
104 | return nil
105 | }
106 |
107 | // PrintStats prints the corresponding statistics from the app DB.
108 | func PrintStats(db dbm.DB) {
109 | fmt.Println("\nLevelDB Stats")
110 | fmt.Println(db.Stats()["leveldb.stats"])
111 | fmt.Println("LevelDB cached block size", db.Stats()["leveldb.cachedblock"])
112 | }
113 |
114 | // GetSimulationLog unmarshals the KVPair's Value to the corresponding type based on the
115 | // each's module store key and the prefix bytes of the KVPair's key.
116 | func GetSimulationLog(storeName string, sdr sdk.StoreDecoderRegistry, kvAs, kvBs []kv.Pair) (log string) {
117 | for i := 0; i < len(kvAs); i++ {
118 | if len(kvAs[i].Value) == 0 && len(kvBs[i].Value) == 0 {
119 | // skip if the value doesn't have any bytes
120 | continue
121 | }
122 |
123 | decoder, ok := sdr[storeName]
124 | if ok {
125 | log += decoder(kvAs[i], kvBs[i])
126 | } else {
127 | log += fmt.Sprintf("store A %X => %X\nstore B %X => %X\n", kvAs[i].Key, kvAs[i].Value, kvBs[i].Key, kvBs[i].Value)
128 | }
129 | }
130 |
131 | return log
132 | }
133 |
--------------------------------------------------------------------------------
/x/README.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # List of Modules
7 |
8 | - [IBCAccount](ibc-account/spec/README.md)
9 |
--------------------------------------------------------------------------------
/x/ibc-account/client/cli/query.go:
--------------------------------------------------------------------------------
1 | package cli
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cosmos/cosmos-sdk/client"
7 | "github.com/cosmos/cosmos-sdk/client/flags"
8 | sdk "github.com/cosmos/cosmos-sdk/types"
9 | "github.com/spf13/cobra"
10 |
11 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
12 | )
13 |
14 | func GetQueryCmd() *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: types.ModuleName,
17 | Short: "Querying commands for the ibc account module",
18 | DisableFlagParsing: true,
19 | SuggestionsMinimumDistance: 2,
20 | RunE: client.ValidateCmd,
21 | }
22 |
23 | cmd.AddCommand(GetIBCAccountCmd())
24 |
25 | return cmd
26 | }
27 |
28 | func GetIBCAccountCmd() *cobra.Command {
29 | cmd := &cobra.Command{
30 | Use: "ibcaccount [address_or_data] [port] [channel]",
31 | RunE: func(cmd *cobra.Command, args []string) error {
32 | clientCtx := client.GetClientContextFromCmd(cmd)
33 | clientCtx, err := client.ReadQueryCommandFlags(clientCtx, cmd.Flags())
34 | if err != nil {
35 | return err
36 | }
37 |
38 | address, addrErr := sdk.AccAddressFromBech32(args[0])
39 |
40 | queryClient := types.NewQueryClient(clientCtx)
41 | if addrErr == nil {
42 | res, err := queryClient.IBCAccount(context.Background(), &types.QueryIBCAccountRequest{Address: address.String()})
43 | if err != nil {
44 | return err
45 | }
46 |
47 | return clientCtx.PrintOutput(res.Account)
48 | }
49 |
50 | res, err := queryClient.IBCAccountFromData(context.Background(), &types.QueryIBCAccountFromDataRequest{Data: args[0], Port: args[1], Channel: args[2]})
51 | if err != nil {
52 | return err
53 | }
54 |
55 | return clientCtx.PrintOutput(res.Account)
56 | },
57 | }
58 |
59 | flags.AddQueryFlagsToCmd(cmd)
60 |
61 | return cmd
62 | }
63 |
--------------------------------------------------------------------------------
/x/ibc-account/genesis.go:
--------------------------------------------------------------------------------
1 | package ibc_account
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/keeper"
7 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
8 | sdk "github.com/cosmos/cosmos-sdk/types"
9 | )
10 |
11 | func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, state types.GenesisState) {
12 | if !keeper.IsBound(ctx, state.PortId) {
13 | err := keeper.BindPort(ctx, state.PortId)
14 | if err != nil {
15 | panic(fmt.Sprintf("could not claim port capability: %v", err))
16 | }
17 | }
18 | }
19 |
20 | // ExportGenesis exports transfer module's portID into its geneis state
21 | func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState {
22 | portID := keeper.GetPort(ctx)
23 |
24 | return &types.GenesisState{
25 | PortId: portID,
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/account.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
7 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
8 | "github.com/tendermint/tendermint/crypto/tmhash"
9 | )
10 |
11 | // RegisterIBCAccount performs registering IBC account.
12 | // It will generate the deterministic address by hashing {sourcePort}/{sourceChannel}{salt}.
13 | func (k Keeper) registerIBCAccount(ctx sdk.Context, sourcePort, sourceChannel, destPort, destChannel string, salt []byte) (types.IBCAccountI, error) {
14 | identifier := types.GetIdentifier(destPort, destChannel)
15 | address := k.GenerateAddress(identifier, salt)
16 |
17 | account := k.accountKeeper.GetAccount(ctx, address)
18 | // TODO: Discuss the vulnerabilities when creating a new account only if the old account does not exist
19 | // Attackers can interrupt creating accounts by sending some assets before the packet is delivered.
20 | // So it is needed to check that the account is not created from users.
21 | // Returns an error only if the account was created by other chain.
22 | // We need to discuss how we can judge this case.
23 | if account != nil {
24 | return nil, sdkerrors.Wrap(types.ErrAccountAlreadyExist, account.String())
25 | }
26 |
27 | ibcAccount := types.NewIBCAccount(
28 | authtypes.NewBaseAccountWithAddress(address),
29 | sourcePort, sourceChannel, destPort, destChannel,
30 | )
31 | k.accountKeeper.NewAccount(ctx, ibcAccount)
32 | k.accountKeeper.SetAccount(ctx, ibcAccount)
33 |
34 | return ibcAccount, nil
35 | }
36 |
37 | // Determine account's address that will be created.
38 | func (k Keeper) GenerateAddress(identifier string, salt []byte) []byte {
39 | return tmhash.SumTruncated(append([]byte(identifier), salt...))
40 | }
41 |
42 | func (k Keeper) GetIBCAccount(ctx sdk.Context, addr sdk.AccAddress) (types.IBCAccount, error) {
43 | acc := k.accountKeeper.GetAccount(ctx, addr)
44 | if acc == nil {
45 | return types.IBCAccount{}, sdkerrors.Wrap(types.ErrIBCAccountNotFound, "their is no account")
46 | }
47 |
48 | ibcAcc, ok := acc.(*types.IBCAccount)
49 | if !ok {
50 | return types.IBCAccount{}, sdkerrors.Wrap(types.ErrIBCAccountNotFound, "account is not IBC account")
51 | }
52 | return *ibcAcc, nil
53 | }
54 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/grpc_query.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 |
8 | "google.golang.org/grpc/codes"
9 | "google.golang.org/grpc/status"
10 |
11 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
12 | )
13 |
14 | var _ types.QueryServer = Keeper{}
15 |
16 | // IBCAccount implements the Query/IBCAccount gRPC method
17 | func (k Keeper) IBCAccount(ctx context.Context, req *types.QueryIBCAccountRequest) (*types.QueryIBCAccountResponse, error) {
18 | if req == nil {
19 | return nil, status.Error(codes.InvalidArgument, "empty request")
20 | }
21 |
22 | if req.Address == "" {
23 | return nil, status.Error(codes.InvalidArgument, "address cannot be empty")
24 | }
25 |
26 | sdkCtx := sdk.UnwrapSDKContext(ctx)
27 | address, err := sdk.AccAddressFromBech32(req.Address)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | ibcAccount, err := k.GetIBCAccount(sdkCtx, address)
33 | if err != nil {
34 | return nil, err
35 | }
36 |
37 | return &types.QueryIBCAccountResponse{Account: &ibcAccount}, nil
38 | }
39 |
40 | // IBCAccountFromData implements the Query/IBCAccount gRPC method
41 | func (k Keeper) IBCAccountFromData(ctx context.Context, req *types.QueryIBCAccountFromDataRequest) (*types.QueryIBCAccountResponse, error) {
42 | if req == nil {
43 | return nil, status.Error(codes.InvalidArgument, "empty request")
44 | }
45 |
46 | if req.Port == "" {
47 | return nil, status.Error(codes.InvalidArgument, "port cannot be empty")
48 | }
49 |
50 | if req.Channel == "" {
51 | return nil, status.Error(codes.InvalidArgument, "channel cannot be empty")
52 | }
53 |
54 | sdkCtx := sdk.UnwrapSDKContext(ctx)
55 | identifier := types.GetIdentifier(req.Port, req.Channel)
56 | address := k.GenerateAddress(identifier, []byte(req.Data))
57 |
58 | ibcAccount, err := k.GetIBCAccount(sdkCtx, address)
59 | if err != nil {
60 | return nil, err
61 | }
62 |
63 | return &types.QueryIBCAccountResponse{Account: &ibcAccount}, nil
64 | }
65 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/grpc_query_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | "fmt"
5 |
6 | clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"
7 |
8 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
9 | sdk "github.com/cosmos/cosmos-sdk/types"
10 | channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"
11 | )
12 |
13 | func (suite *KeeperTestSuite) TestQueryIBCAccount() {
14 | var (
15 | req *types.QueryIBCAccountRequest
16 | )
17 |
18 | testCases := []struct {
19 | msg string
20 | malleate func()
21 | expPass bool
22 | }{
23 | {
24 | "empty request",
25 | func() {
26 | req = &types.QueryIBCAccountRequest{
27 | Address: "",
28 | }
29 | },
30 | false,
31 | },
32 | {
33 | "invalid bech32 address",
34 | func() {
35 | req = &types.QueryIBCAccountRequest{
36 | Address: "cosmos1ntck6f6534u630q87jpamettes6shwgddag761",
37 | }
38 | },
39 | false,
40 | },
41 | {
42 | "unexist address",
43 | func() {
44 | req = &types.QueryIBCAccountRequest{
45 | Address: "cosmos1ntck6f6534u630q87jpamettes6shwgddag769",
46 | }
47 | },
48 | false,
49 | },
50 | {
51 | "success",
52 | func() {
53 | packetData := types.IBCAccountPacketData{
54 | Type: types.Type_REGISTER,
55 | Data: []byte{},
56 | }
57 |
58 | packet := channeltypes.Packet{
59 | Sequence: 0,
60 | SourcePort: "sp",
61 | SourceChannel: "sc",
62 | DestinationPort: "dp",
63 | DestinationChannel: "dc",
64 | Data: packetData.GetBytes(),
65 | TimeoutHeight: clienttypes.Height{},
66 | TimeoutTimestamp: 0,
67 | }
68 |
69 | err := suite.chainA.App.IBCAccountKeeper.OnRecvPacket(suite.chainA.GetContext(), packet)
70 | if err != nil {
71 | panic(err)
72 | }
73 |
74 | address := suite.chainA.App.IBCAccountKeeper.GenerateAddress(types.GetIdentifier("dp", "dc"), []byte{})
75 |
76 | req = &types.QueryIBCAccountRequest{
77 | Address: sdk.AccAddress(address).String(),
78 | }
79 | },
80 | true,
81 | },
82 | }
83 |
84 | for _, tc := range testCases {
85 | suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
86 | suite.SetupTest() // reset
87 |
88 | tc.malleate()
89 | ctx := sdk.WrapSDKContext(suite.chainA.GetContext())
90 |
91 | res, err := suite.queryClientA.IBCAccount(ctx, req)
92 |
93 | if tc.expPass {
94 | suite.Require().NoError(err)
95 | suite.Require().NotNil(res)
96 | } else {
97 | suite.Require().Error(err)
98 | }
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/handshake.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
6 | capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
7 | channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"
8 | porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types"
9 | host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host"
10 |
11 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
12 | )
13 |
14 | func (k Keeper) OnChanOpenInit(
15 | ctx sdk.Context,
16 | order channeltypes.Order,
17 | connectionHops []string,
18 | portID string,
19 | channelID string,
20 | chanCap *capabilitytypes.Capability,
21 | counterparty channeltypes.Counterparty,
22 | version string,
23 | ) error {
24 | // Require portID is the portID transfer module is bound to
25 | boundPort := k.GetPort(ctx)
26 | if boundPort != portID {
27 | return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort)
28 | }
29 |
30 | if version != types.Version {
31 | return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid version: %s, expected %s", version, "ics20-1")
32 | }
33 |
34 | if order != channeltypes.ORDERED {
35 | return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelOrdering, "invalid channel ordering: %s, expected %s", order.String(), channeltypes.ORDERED.String())
36 | }
37 |
38 | // Claim channel capability passed back by IBC module
39 | if err := k.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
40 | return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, err.Error())
41 | }
42 |
43 | return nil
44 | }
45 |
46 | func (k Keeper) OnChanOpenTry(
47 | ctx sdk.Context,
48 | order channeltypes.Order,
49 | connectionHops []string,
50 | portID,
51 | channelID string,
52 | chanCap *capabilitytypes.Capability,
53 | counterparty channeltypes.Counterparty,
54 | version,
55 | counterpartyVersion string,
56 | ) error {
57 | boundPort := k.GetPort(ctx)
58 | if boundPort != portID {
59 | return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort)
60 | }
61 |
62 | if version != types.Version {
63 | return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid version: %s, expected %s", version, "ics20-1")
64 | }
65 |
66 | if order != channeltypes.ORDERED {
67 | return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelOrdering, "invalid channel ordering: %s, expected %s", order.String(), channeltypes.ORDERED.String())
68 | }
69 |
70 | // TODO: Check counterparty version
71 | // if counterpartyVersion != types.Version {
72 | // return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid counterparty version: %s, expected %s", counterpartyVersion, "ics20-1")
73 | // }
74 |
75 | // Claim channel capability passed back by IBC module
76 | if err := k.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
77 | return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, err.Error())
78 | }
79 |
80 | return nil
81 | }
82 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/handshake_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/keeper.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/cosmos/cosmos-sdk/codec"
7 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
8 | sdk "github.com/cosmos/cosmos-sdk/types"
9 | capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper"
10 | capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
11 | host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host"
12 |
13 | "github.com/tendermint/tendermint/libs/log"
14 |
15 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
16 | )
17 |
18 | func SerializeCosmosTx(cdc codec.BinaryMarshaler, registry codectypes.InterfaceRegistry) func(data interface{}) ([]byte, error) {
19 | return func(data interface{}) ([]byte, error) {
20 | msgs := make([]sdk.Msg, 0)
21 | switch data := data.(type) {
22 | case sdk.Msg:
23 | msgs = append(msgs, data)
24 | case []sdk.Msg:
25 | msgs = append(msgs, data...)
26 | default:
27 | return nil, types.ErrInvalidOutgoingData
28 | }
29 |
30 | msgAnys := make([]*codectypes.Any, len(msgs))
31 |
32 | for i, msg := range msgs {
33 | var err error
34 | msgAnys[i], err = codectypes.NewAnyWithValue(msg)
35 | if err != nil {
36 | return nil, err
37 | }
38 | }
39 |
40 | txBody := &types.IBCTxBody{
41 | Messages: msgAnys,
42 | }
43 |
44 | txRaw := &types.IBCTxRaw{
45 | BodyBytes: cdc.MustMarshalBinaryBare(txBody),
46 | }
47 |
48 | bz, err := cdc.MarshalBinaryBare(txRaw)
49 | if err != nil {
50 | return nil, err
51 | }
52 |
53 | return bz, nil
54 | }
55 | }
56 |
57 | // Keeper defines the IBC transfer keeper
58 | type Keeper struct {
59 | storeKey sdk.StoreKey
60 | cdc codec.BinaryMarshaler
61 |
62 | // Key can be chain type which means what blockchain framework the host chain was built on or just direct chain id.
63 | txEncoders map[string]types.TxEncoder
64 |
65 | hook types.IBCAccountHooks
66 |
67 | channelKeeper types.ChannelKeeper
68 | portKeeper types.PortKeeper
69 | accountKeeper types.AccountKeeper
70 |
71 | scopedKeeper capabilitykeeper.ScopedKeeper
72 |
73 | router types.Router
74 | }
75 |
76 | // NewKeeper creates a new IBC account Keeper instance
77 | func NewKeeper(
78 | cdc codec.BinaryMarshaler, key sdk.StoreKey,
79 | txEncoders map[string]types.TxEncoder, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper,
80 | accountKeeper types.AccountKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, router types.Router,
81 | ) Keeper {
82 | return Keeper{
83 | storeKey: key,
84 | cdc: cdc,
85 | txEncoders: txEncoders,
86 | channelKeeper: channelKeeper,
87 | portKeeper: portKeeper,
88 | accountKeeper: accountKeeper,
89 | scopedKeeper: scopedKeeper,
90 | router: router,
91 | }
92 | }
93 |
94 | func (k Keeper) AddTxEncoder(typ string, txEncoder types.TxEncoder) error {
95 | _, ok := k.txEncoders[typ]
96 | if ok {
97 | return types.ErrTxEncoderAlreadyRegistered
98 | }
99 | k.txEncoders[typ] = txEncoder
100 | return nil
101 | }
102 |
103 | func (k Keeper) GetTxEncoder(typ string) (types.TxEncoder, bool) {
104 | info, ok := k.txEncoders[typ]
105 | return info, ok
106 | }
107 |
108 | func (k Keeper) Logger(ctx sdk.Context) log.Logger {
109 | return ctx.Logger().With("module", fmt.Sprintf("x/%s-%s", host.ModuleName, types.ModuleName))
110 | }
111 |
112 | // IsBound checks if the interchain account module is already bound to the desired port
113 | func (k Keeper) IsBound(ctx sdk.Context, portID string) bool {
114 | _, ok := k.scopedKeeper.GetCapability(ctx, host.PortPath(portID))
115 | return ok
116 | }
117 |
118 | // BindPort defines a wrapper function for the ort Keeper's function in
119 | // order to expose it to module's InitGenesis function
120 | func (k Keeper) BindPort(ctx sdk.Context, portID string) error {
121 | // Set the portID into our store so we can retrieve it later
122 | store := ctx.KVStore(k.storeKey)
123 | store.Set([]byte(types.PortKey), []byte(portID))
124 |
125 | cap := k.portKeeper.BindPort(ctx, portID)
126 | return k.ClaimCapability(ctx, cap, host.PortPath(portID))
127 | }
128 |
129 | // GetPort returns the portID for the ibc account module. Used in ExportGenesis
130 | func (k Keeper) GetPort(ctx sdk.Context) string {
131 | store := ctx.KVStore(k.storeKey)
132 | return string(store.Get([]byte(types.PortKey)))
133 | }
134 |
135 | // ClaimCapability allows the transfer module that can claim a capability that IBC module
136 | // passes to it
137 | func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error {
138 | return k.scopedKeeper.ClaimCapability(ctx, cap, name)
139 | }
140 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/keeper_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/suite"
7 |
8 | ibcacctesting "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing"
9 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
10 | "github.com/cosmos/cosmos-sdk/baseapp"
11 | )
12 |
13 | type KeeperTestSuite struct {
14 | suite.Suite
15 |
16 | coordinator *ibcacctesting.Coordinator
17 |
18 | chainA *ibcacctesting.TestChain
19 | chainB *ibcacctesting.TestChain
20 |
21 | queryClientA types.QueryClient
22 | }
23 |
24 | func (suite *KeeperTestSuite) SetupTest() {
25 | suite.coordinator = ibcacctesting.NewCoordinator(suite.T(), 2)
26 | suite.chainA = suite.coordinator.GetChain(ibcacctesting.GetChainID(0))
27 | suite.chainB = suite.coordinator.GetChain(ibcacctesting.GetChainID(1))
28 |
29 | queryHelperA := baseapp.NewQueryServerTestHelper(suite.chainA.GetContext(), suite.chainA.App.InterfaceRegistry())
30 | types.RegisterQueryServer(queryHelperA, suite.chainA.App.IBCAccountKeeper)
31 | suite.queryClientA = types.NewQueryClient(queryHelperA)
32 | }
33 |
34 | func TestKeeperTestSuite(t *testing.T) {
35 | suite.Run(t, new(KeeperTestSuite))
36 | }
37 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/querier.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
5 | "github.com/cosmos/cosmos-sdk/codec"
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
8 | abci "github.com/tendermint/tendermint/abci/types"
9 | "google.golang.org/grpc/codes"
10 | "google.golang.org/grpc/status"
11 | )
12 |
13 | func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
14 | return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) {
15 | switch path[0] {
16 | case types.QueryIBCAccount:
17 | return queryIBCAccount(ctx, req, k, legacyQuerierCdc)
18 |
19 | case types.QueryIBCAccountFromData:
20 | return queryIBCAccountFromData(ctx, req, k, legacyQuerierCdc)
21 |
22 | default:
23 | return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query path: %s", path[0])
24 | }
25 | }
26 | }
27 |
28 | func queryIBCAccount(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
29 | var params types.QueryIBCAccountRequest
30 | if err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms); err != nil {
31 | return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
32 | }
33 |
34 | addr, err := sdk.AccAddressFromBech32(params.Address)
35 | if err != nil {
36 | return nil, err
37 | }
38 |
39 | account, err := k.GetIBCAccount(ctx, addr)
40 | if err != nil {
41 | return nil, err
42 | }
43 |
44 | bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, account)
45 | if err != nil {
46 | return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
47 | }
48 |
49 | return bz, nil
50 | }
51 |
52 | func queryIBCAccountFromData(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
53 | var params types.QueryIBCAccountFromDataRequest
54 | if err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms); err != nil {
55 | return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
56 | }
57 |
58 | if params.Port == "" {
59 | return nil, status.Error(codes.InvalidArgument, "port cannot be empty")
60 | }
61 |
62 | if params.Channel == "" {
63 | return nil, status.Error(codes.InvalidArgument, "channel cannot be empty")
64 | }
65 |
66 | identifier := types.GetIdentifier(params.Port, params.Channel)
67 | address := k.GenerateAddress(identifier, []byte(params.Data))
68 |
69 | account, err := k.GetIBCAccount(ctx, address)
70 | if err != nil {
71 | return nil, err
72 | }
73 |
74 | bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, account)
75 | if err != nil {
76 | return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
77 | }
78 |
79 | return bz, nil
80 | }
81 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/relay.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "encoding/binary"
5 |
6 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
7 | sdk "github.com/cosmos/cosmos-sdk/types"
8 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
9 | clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"
10 | channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"
11 | host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host"
12 | "github.com/tendermint/tendermint/crypto/tmhash"
13 | )
14 |
15 | // TryRegisterIBCAccount try to register IBC account to source channel.
16 | // If no source channel exists or doesn't have capability, it will return error.
17 | // Salt is used to generate deterministic address.
18 | func (k Keeper) TryRegisterIBCAccount(ctx sdk.Context, sourcePort, sourceChannel string, salt []byte, timeoutHeight clienttypes.Height, timeoutTimestamp uint64) error {
19 | sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel)
20 | if !found {
21 | return sdkerrors.Wrap(channeltypes.ErrChannelNotFound, sourceChannel)
22 | }
23 |
24 | destinationPort := sourceChannelEnd.GetCounterparty().GetPortID()
25 | destinationChannel := sourceChannelEnd.GetCounterparty().GetChannelID()
26 |
27 | channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(sourcePort, sourceChannel))
28 | if !ok {
29 | return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
30 | }
31 |
32 | // get the next sequence
33 | sequence, found := k.channelKeeper.GetNextSequenceSend(ctx, sourcePort, sourceChannel)
34 | if !found {
35 | return channeltypes.ErrSequenceSendNotFound
36 | }
37 |
38 | packetData := types.IBCAccountPacketData{
39 | Type: types.Type_REGISTER,
40 | Data: salt,
41 | }
42 |
43 | // TODO: Add timeout height and timestamp
44 | packet := channeltypes.NewPacket(
45 | packetData.GetBytes(),
46 | sequence,
47 | sourcePort,
48 | sourceChannel,
49 | destinationPort,
50 | destinationChannel,
51 | timeoutHeight,
52 | timeoutTimestamp,
53 | )
54 |
55 | return k.channelKeeper.SendPacket(ctx, channelCap, packet)
56 | }
57 |
58 | // TryRunTx try to send messages to source channel.
59 | func (k Keeper) TryRunTx(ctx sdk.Context, sourcePort, sourceChannel, typ string, data interface{}, timeoutHeight clienttypes.Height, timeoutTimestamp uint64) ([]byte, error) {
60 | sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel)
61 | if !found {
62 | return []byte{}, sdkerrors.Wrap(channeltypes.ErrChannelNotFound, sourceChannel)
63 | }
64 |
65 | destinationPort := sourceChannelEnd.GetCounterparty().GetPortID()
66 | destinationChannel := sourceChannelEnd.GetCounterparty().GetChannelID()
67 |
68 | return k.createOutgoingPacket(ctx, sourcePort, sourceChannel, destinationPort, destinationChannel, typ, data, timeoutHeight, timeoutTimestamp)
69 | }
70 |
71 | func (k Keeper) createOutgoingPacket(
72 | ctx sdk.Context,
73 | sourcePort,
74 | sourceChannel,
75 | destinationPort,
76 | destinationChannel,
77 | typ string,
78 | data interface{},
79 | timeoutHeight clienttypes.Height,
80 | timeoutTimestamp uint64,
81 | ) ([]byte, error) {
82 | if data == nil {
83 | return []byte{}, types.ErrInvalidOutgoingData
84 | }
85 |
86 | txEncoder, ok := k.GetTxEncoder(typ)
87 | if !ok {
88 | return []byte{}, types.ErrUnsupportedChain
89 | }
90 |
91 | var msgs []sdk.Msg
92 |
93 | switch data := data.(type) {
94 | case []sdk.Msg:
95 | msgs = data
96 | case sdk.Msg:
97 | msgs = []sdk.Msg{data}
98 | default:
99 | return []byte{}, types.ErrInvalidOutgoingData
100 | }
101 |
102 | txBytes, err := txEncoder(msgs)
103 | if err != nil {
104 | return []byte{}, sdkerrors.Wrap(err, "invalid packet data or codec")
105 | }
106 |
107 | channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(sourcePort, sourceChannel))
108 | if !ok {
109 | return []byte{}, sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
110 | }
111 |
112 | // get the next sequence
113 | sequence, found := k.channelKeeper.GetNextSequenceSend(ctx, sourcePort, sourceChannel)
114 | if !found {
115 | return []byte{}, channeltypes.ErrSequenceSendNotFound
116 | }
117 |
118 | packetData := types.IBCAccountPacketData{
119 | Type: types.Type_RUNTX,
120 | Data: txBytes,
121 | }
122 |
123 | // TODO: Add timeout height and timestamp
124 | packet := channeltypes.NewPacket(
125 | packetData.GetBytes(),
126 | sequence,
127 | sourcePort,
128 | sourceChannel,
129 | destinationPort,
130 | destinationChannel,
131 | timeoutHeight,
132 | timeoutTimestamp,
133 | )
134 |
135 | return k.ComputeVirtualTxHash(packetData.Data, packet.Sequence), k.channelKeeper.SendPacket(ctx, channelCap, packet)
136 | }
137 |
138 | func (k Keeper) DeserializeTx(_ sdk.Context, txBytes []byte) ([]sdk.Msg, error) {
139 | var txRaw types.IBCTxRaw
140 |
141 | err := k.cdc.UnmarshalBinaryBare(txBytes, &txRaw)
142 | if err != nil {
143 | return nil, err
144 | }
145 |
146 | var txBody types.IBCTxBody
147 |
148 | err = k.cdc.UnmarshalBinaryBare(txRaw.BodyBytes, &txBody)
149 | if err != nil {
150 | return nil, err
151 | }
152 |
153 | anys := txBody.Messages
154 | res := make([]sdk.Msg, len(anys))
155 | for i, any := range anys {
156 | var msg sdk.Msg
157 | err := k.cdc.UnpackAny(any, &msg)
158 | if err != nil {
159 | return nil, err
160 | }
161 | res[i] = msg
162 | }
163 |
164 | return res, nil
165 | }
166 |
167 | func (k Keeper) runTx(ctx sdk.Context, destPort, destChannel string, msgs []sdk.Msg) error {
168 | identifier := types.GetIdentifier(destPort, destChannel)
169 | err := k.AuthenticateTx(ctx, msgs, identifier)
170 | if err != nil {
171 | return err
172 | }
173 |
174 | for _, msg := range msgs {
175 | err := msg.ValidateBasic()
176 | if err != nil {
177 | return err
178 | }
179 | }
180 |
181 | // Use cache context.
182 | // Receive packet msg should succeed regardless of the result of logic.
183 | // But, if we just return the success even though handler is failed,
184 | // the leftovers of state transition in handler will remain.
185 | // However, this can make the unexpected error.
186 | // To solve this problem, use cache context instead of context,
187 | // and write the state transition if handler succeeds.
188 | cacheContext, writeFn := ctx.CacheContext()
189 | err = nil
190 | for _, msg := range msgs {
191 | _, msgErr := k.runMsg(cacheContext, msg)
192 | if msgErr != nil {
193 | err = msgErr
194 | break
195 | }
196 | }
197 |
198 | if err != nil {
199 | return err
200 | }
201 |
202 | // Write the state transitions if all handlers succeed.
203 | writeFn()
204 |
205 | return nil
206 | }
207 |
208 | // AuthenticateTx verifies that the messages have the right permission.
209 | // It will check that the message's signers are the IBC account created by the right chain.
210 | func (k Keeper) AuthenticateTx(ctx sdk.Context, msgs []sdk.Msg, identifier string) error {
211 | seen := map[string]bool{}
212 | var signers []sdk.AccAddress
213 | for _, msg := range msgs {
214 | for _, addr := range msg.GetSigners() {
215 | if !seen[addr.String()] {
216 | signers = append(signers, addr)
217 | seen[addr.String()] = true
218 | }
219 | }
220 | }
221 |
222 | for _, signer := range signers {
223 | // Check where the interchain account is made from.
224 | account := k.accountKeeper.GetAccount(ctx, signer)
225 | if account == nil {
226 | return sdkerrors.ErrUnauthorized
227 | }
228 |
229 | ibcAccount, ok := account.(types.IBCAccountI)
230 | if !ok {
231 | return sdkerrors.ErrUnauthorized
232 | }
233 |
234 | if types.GetIdentifier(ibcAccount.GetDestinationPort(), ibcAccount.GetDestinationChannel()) != identifier {
235 | return sdkerrors.ErrUnauthorized
236 | }
237 | }
238 |
239 | return nil
240 | }
241 |
242 | // RunMsg executes the message.
243 | // It tries to get the handler from router. And, if router exites, it will perform message.
244 | func (k Keeper) runMsg(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
245 | hander := k.router.Route(ctx, msg.Route())
246 | if hander == nil {
247 | return nil, types.ErrInvalidRoute
248 | }
249 |
250 | return hander(ctx, msg)
251 | }
252 |
253 | // Compute the virtual tx hash that is used only internally.
254 | func (k Keeper) ComputeVirtualTxHash(txBytes []byte, seq uint64) []byte {
255 | bz := make([]byte, 8)
256 | binary.LittleEndian.PutUint64(bz, seq)
257 | return tmhash.SumTruncated(append(txBytes, bz...))
258 | }
259 |
260 | func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) error {
261 | var data types.IBCAccountPacketData
262 | // TODO: Remove the usage of global variable "ModuleCdc"
263 | if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
264 | return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal interchain account packet data: %s", err.Error())
265 | }
266 |
267 | switch data.Type {
268 | case types.Type_REGISTER:
269 | _, err := k.registerIBCAccount(ctx, packet.SourcePort, packet.SourceChannel, packet.DestinationPort, packet.DestinationChannel, data.Data)
270 | if err != nil {
271 | return err
272 | }
273 |
274 | return nil
275 | case types.Type_RUNTX:
276 | msgs, err := k.DeserializeTx(ctx, data.Data)
277 | if err != nil {
278 | return err
279 | }
280 |
281 | err = k.runTx(ctx, packet.DestinationPort, packet.DestinationChannel, msgs)
282 | if err != nil {
283 | return err
284 | }
285 |
286 | return nil
287 | default:
288 | return types.ErrUnknownPacketData
289 | }
290 | }
291 |
292 | func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IBCAccountPacketData, ack types.IBCAccountPacketAcknowledgement) error {
293 | switch ack.Type {
294 | case types.Type_REGISTER:
295 | if ack.Code == 0 {
296 | if k.hook != nil {
297 | k.hook.OnAccountCreated(ctx, packet.SourcePort, packet.SourceChannel, k.GenerateAddress(types.GetIdentifier(packet.DestinationPort, packet.DestinationChannel), data.Data))
298 | }
299 | }
300 | return nil
301 | case types.Type_RUNTX:
302 | if ack.Code == 0 {
303 | if k.hook != nil {
304 | k.hook.OnTxSucceeded(ctx, packet.SourcePort, packet.SourceChannel, k.ComputeVirtualTxHash(data.Data, packet.Sequence), data.Data)
305 | }
306 | } else {
307 | if k.hook != nil {
308 | k.hook.OnTxFailed(ctx, packet.SourcePort, packet.SourceChannel, k.ComputeVirtualTxHash(data.Data, packet.Sequence), data.Data)
309 | }
310 | }
311 | return nil
312 | default:
313 | panic("unknown type of acknowledgement")
314 | }
315 | }
316 |
317 | func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IBCAccountPacketData) error {
318 | if k.hook != nil {
319 | k.hook.OnTxFailed(ctx, packet.SourcePort, packet.SourceChannel, k.ComputeVirtualTxHash(data.Data, packet.Sequence), data.Data)
320 | }
321 |
322 | return nil
323 | }
324 |
--------------------------------------------------------------------------------
/x/ibc-account/keeper/relay_test.go:
--------------------------------------------------------------------------------
1 | package keeper_test
2 |
3 | import (
4 | ibcaccountkeeper "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/keeper"
5 | ibcaccountmocktypes "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock/types"
6 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
7 | sdk "github.com/cosmos/cosmos-sdk/types"
8 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
9 | clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"
10 | channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"
11 | host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host"
12 | ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types"
13 | )
14 |
15 | func (suite *KeeperTestSuite) TestTryRegisterIBCAccount() {
16 | // init connection and channel between chain A and chain B
17 | _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, ibctmtypes.Tendermint)
18 | channelA, channelB := suite.coordinator.CreateIBCAccountChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.ORDERED)
19 |
20 | // assume that chain A try to register IBC Account to chain B
21 | msg := &ibcaccountmocktypes.MsgTryRegisterIBCAccount{
22 | SourcePort: channelA.PortID,
23 | SourceChannel: channelA.ID,
24 | Salt: []byte("test"),
25 | TimeoutHeight: clienttypes.Height{
26 | VersionNumber: 0,
27 | VersionHeight: 100,
28 | },
29 | TimeoutTimestamp: 0,
30 | Sender: suite.chainA.SenderAccount.GetAddress(),
31 | }
32 | err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, channelB.ClientID, msg)
33 | suite.Require().NoError(err) // message committed
34 |
35 | packetData := types.IBCAccountPacketData{
36 | Type: types.Type_REGISTER,
37 | Data: []byte("test"),
38 | }
39 | packet := channeltypes.NewPacket(packetData.GetBytes(), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.NewHeight(0, 100), 0)
40 |
41 | // get proof of packet commitment from chainA
42 | packetKey := host.KeyPacketCommitment(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
43 | proof, proofHeight := suite.chainA.QueryProof(packetKey)
44 |
45 | nextAccNum := len(suite.chainB.App.AccountKeeper.GetAllAccounts(suite.chainB.GetContext()))
46 |
47 | recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainB.SenderAccount.GetAddress())
48 | err = suite.coordinator.SendMsg(suite.chainB, suite.chainA, channelA.ClientID, recvMsg)
49 | suite.Require().NoError(err) // message committed
50 |
51 | acc := suite.chainB.App.AccountKeeper.GetAccount(suite.chainB.GetContext(), suite.chainB.App.IBCAccountKeeper.GenerateAddress(types.GetIdentifier(channelB.PortID, channelB.ID), []byte("test")))
52 | suite.Require().NotNil(acc)
53 |
54 | suite.Require().Equal(nextAccNum, int(acc.GetAccountNumber()))
55 | }
56 |
57 | func (suite *KeeperTestSuite) TestRunTx() {
58 | // init connection and channel between chain A and chain B
59 | _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, ibctmtypes.Tendermint)
60 | channelA, channelB := suite.coordinator.CreateIBCAccountChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.ORDERED)
61 |
62 | func() {
63 | // assume that chain A try to register IBC Account to chain B
64 | msg := &ibcaccountmocktypes.MsgTryRegisterIBCAccount{
65 | SourcePort: channelA.PortID,
66 | SourceChannel: channelA.ID,
67 | Salt: []byte("test"),
68 | TimeoutHeight: clienttypes.Height{
69 | VersionNumber: 0,
70 | VersionHeight: 100,
71 | },
72 | TimeoutTimestamp: 0,
73 | Sender: suite.chainA.SenderAccount.GetAddress(),
74 | }
75 | err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, channelB.ClientID, msg)
76 | suite.Require().NoError(err) // message committed
77 |
78 | packetData := types.IBCAccountPacketData{
79 | Type: types.Type_REGISTER,
80 | Data: []byte("test"),
81 | }
82 | packet := channeltypes.NewPacket(packetData.GetBytes(), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.NewHeight(0, 100), 0)
83 |
84 | // get proof of packet commitment from chainA
85 | packetKey := host.KeyPacketCommitment(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
86 | proof, proofHeight := suite.chainA.QueryProof(packetKey)
87 |
88 | recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainB.SenderAccount.GetAddress())
89 | err = suite.coordinator.SendMsg(suite.chainB, suite.chainA, channelA.ClientID, recvMsg)
90 | suite.Require().NoError(err) // message committed
91 |
92 | acc := suite.chainB.App.AccountKeeper.GetAccount(suite.chainB.GetContext(), suite.chainB.App.IBCAccountKeeper.GenerateAddress(types.GetIdentifier(channelB.PortID, channelB.ID), []byte("test")))
93 | suite.Require().NotNil(acc)
94 | }()
95 |
96 | acc := suite.chainB.App.AccountKeeper.GetAccount(suite.chainB.GetContext(), suite.chainB.App.IBCAccountKeeper.GenerateAddress(types.GetIdentifier(channelB.PortID, channelB.ID), []byte("test")))
97 |
98 | // mint the token to IBC account on Chain B
99 | err := suite.chainB.App.BankKeeper.AddCoins(suite.chainB.GetContext(), acc.GetAddress(), sdk.Coins{sdk.Coin{
100 | Denom: "test",
101 | Amount: sdk.NewInt(10000),
102 | }})
103 | suite.Require().NoError(err)
104 |
105 | toAddress, err := sdk.AccAddressFromHex("0000000000000000000000000000000000000000")
106 | suite.Require().NoError(err)
107 | // try to run tx that sends the token from IBC account to other account
108 | msg := &ibcaccountmocktypes.MsgTryRunTxMsgSend{
109 | SourcePort: channelA.PortID,
110 | SourceChannel: channelA.ID,
111 | TimeoutHeight: clienttypes.Height{
112 | VersionNumber: 0,
113 | VersionHeight: 100,
114 | },
115 | TimeoutTimestamp: 0,
116 | FromAddress: acc.GetAddress(),
117 | ToAddress: toAddress,
118 | Amount: sdk.Coins{sdk.Coin{
119 | Denom: "test",
120 | Amount: sdk.NewInt(5000),
121 | }},
122 | Sender: suite.chainA.SenderAccount.GetAddress(),
123 | }
124 | err = suite.coordinator.SendMsg(suite.chainA, suite.chainB, channelB.ClientID, msg)
125 | suite.Require().NoError(err) // message committed
126 |
127 | txBytes, err := ibcaccountkeeper.SerializeCosmosTx(suite.chainB.App.AppCodec(), suite.chainB.App.InterfaceRegistry())([]sdk.Msg{
128 | banktypes.NewMsgSend(acc.GetAddress(), toAddress, sdk.Coins{sdk.Coin{
129 | Denom: "test",
130 | Amount: sdk.NewInt(5000),
131 | }}),
132 | })
133 | packetData := types.IBCAccountPacketData{
134 | Type: types.Type_RUNTX,
135 | Data: txBytes,
136 | }
137 | packet := channeltypes.NewPacket(packetData.GetBytes(), 2, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.NewHeight(0, 100), 0)
138 |
139 | // get proof of packet commitment from chainA
140 | packetKey := host.KeyPacketCommitment(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
141 | proof, proofHeight := suite.chainA.QueryProof(packetKey)
142 |
143 | recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainB.SenderAccount.GetAddress())
144 | err = suite.coordinator.SendMsg(suite.chainB, suite.chainA, channelA.ClientID, recvMsg)
145 | suite.Require().NoError(err) // message committed
146 |
147 | // check that the balance has been transfered
148 | bal := suite.chainB.App.BankKeeper.GetBalance(suite.chainB.GetContext(), acc.GetAddress(), "test")
149 | suite.Equal("5000", bal.Amount.String())
150 | bal = suite.chainB.App.BankKeeper.GetBalance(suite.chainB.GetContext(), toAddress, "test")
151 | suite.Equal("5000", bal.Amount.String())
152 | }
153 |
--------------------------------------------------------------------------------
/x/ibc-account/module.go:
--------------------------------------------------------------------------------
1 | package ibc_account
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 |
7 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/client/cli"
8 |
9 | "github.com/grpc-ecosystem/grpc-gateway/runtime"
10 |
11 | capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
12 |
13 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
14 | channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"
15 |
16 | abci "github.com/tendermint/tendermint/abci/types"
17 |
18 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/keeper"
19 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
20 | "github.com/cosmos/cosmos-sdk/client"
21 | "github.com/cosmos/cosmos-sdk/codec"
22 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
23 | sdk "github.com/cosmos/cosmos-sdk/types"
24 | "github.com/cosmos/cosmos-sdk/types/module"
25 | porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types"
26 | "github.com/gorilla/mux"
27 | "github.com/spf13/cobra"
28 | )
29 |
30 | var (
31 | _ module.AppModule = AppModule{}
32 | _ porttypes.IBCModule = AppModule{}
33 | _ module.AppModuleBasic = AppModuleBasic{}
34 | )
35 |
36 | type AppModuleBasic struct{}
37 |
38 | func (AppModuleBasic) Name() string {
39 | return types.ModuleName
40 | }
41 |
42 | // RegisterLegacyAminoCodec implements AppModuleBasic interface
43 | func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
44 | types.RegisterLegacyAminoCodec(cdc)
45 | }
46 |
47 | func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage {
48 | return cdc.MustMarshalJSON(types.DefaultGenesis())
49 | }
50 |
51 | func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error {
52 | return nil
53 | }
54 |
55 | func (AppModuleBasic) RegisterRESTRoutes(ctx client.Context, rtr *mux.Router) {
56 | // noop
57 | }
58 |
59 | func (AppModuleBasic) GetTxCmd() *cobra.Command {
60 | // noop
61 | return nil
62 | }
63 |
64 | func (AppModuleBasic) GetQueryCmd() *cobra.Command {
65 | return cli.GetQueryCmd()
66 | }
67 |
68 | // RegisterInterfaces registers module concrete types into protobuf Any.
69 | func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
70 | types.RegisterInterfaces(registry)
71 | }
72 |
73 | // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the ibc-account module.
74 | func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
75 | types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx))
76 | }
77 |
78 | type AppModule struct {
79 | AppModuleBasic
80 | keeper keeper.Keeper
81 | }
82 |
83 | func NewAppModule(k keeper.Keeper) AppModule {
84 | return AppModule{
85 | keeper: k,
86 | }
87 | }
88 |
89 | func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
90 | // TODO
91 | }
92 |
93 | func (AppModule) Route() sdk.Route {
94 | return sdk.NewRoute(types.RouterKey, nil)
95 | }
96 |
97 | func (AppModule) NewHandler() sdk.Handler {
98 | return nil
99 | }
100 |
101 | func (AppModule) QuerierRoute() string {
102 | return types.QuerierRoute
103 | }
104 |
105 | // LegacyQuerierHandler implements the AppModule interface
106 | func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
107 | return keeper.NewQuerier(am.keeper, legacyQuerierCdc)
108 | }
109 |
110 | // RegisterServices registers a GRPC query service to respond to the
111 | // module-specific GRPC queries.
112 | func (am AppModule) RegisterServices(cfg module.Configurator) {
113 | types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
114 | }
115 |
116 | func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate {
117 | var genesisState types.GenesisState
118 | cdc.MustUnmarshalJSON(data, &genesisState)
119 |
120 | InitGenesis(ctx, am.keeper, genesisState)
121 | return []abci.ValidatorUpdate{}
122 | }
123 |
124 | func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage {
125 | gs := ExportGenesis(ctx, am.keeper)
126 | return cdc.MustMarshalJSON(gs)
127 | }
128 |
129 | // BeginBlock implements the AppModule interface
130 | func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
131 |
132 | }
133 |
134 | // EndBlock implements the AppModule interface
135 | func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate {
136 | return []abci.ValidatorUpdate{}
137 | }
138 |
139 | // Implement IBCModule callbacks
140 | func (am AppModule) OnChanOpenInit(
141 | ctx sdk.Context,
142 | order channeltypes.Order,
143 | connectionHops []string,
144 | portID string,
145 | channelID string,
146 | chanCap *capabilitytypes.Capability,
147 | counterparty channeltypes.Counterparty,
148 | version string,
149 | ) error {
150 | return am.keeper.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version)
151 | }
152 |
153 | func (am AppModule) OnChanOpenTry(
154 | ctx sdk.Context,
155 | order channeltypes.Order,
156 | connectionHops []string,
157 | portID,
158 | channelID string,
159 | chanCap *capabilitytypes.Capability,
160 | counterparty channeltypes.Counterparty,
161 | version,
162 | counterpartyVersion string,
163 | ) error {
164 | return am.keeper.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version, counterpartyVersion)
165 | }
166 |
167 | func (am AppModule) OnChanOpenAck(
168 | ctx sdk.Context,
169 | portID,
170 | channelID string,
171 | counterpartyVersion string,
172 | ) error {
173 | // TODO
174 | // if counterpartyVersion != types.Version {
175 | // return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid counterparty version: %s, expected %s", counterpartyVersion, "ics20-1")
176 | // }
177 | return nil
178 | }
179 |
180 | func (am AppModule) OnChanOpenConfirm(
181 | ctx sdk.Context,
182 | portID,
183 | channelID string,
184 | ) error {
185 | return nil
186 | }
187 |
188 | func (am AppModule) OnChanCloseInit(
189 | ctx sdk.Context,
190 | portID,
191 | channelID string,
192 | ) error {
193 | // Disallow user-initiated channel closing for interchain account channels
194 | return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel")
195 | }
196 |
197 | func (am AppModule) OnChanCloseConfirm(
198 | ctx sdk.Context,
199 | portID,
200 | channelID string,
201 | ) error {
202 | return nil
203 | }
204 |
205 | func (am AppModule) OnRecvPacket(
206 | ctx sdk.Context,
207 | packet channeltypes.Packet,
208 | ) (*sdk.Result, []byte, error) {
209 | var data types.IBCAccountPacketData
210 | // TODO: Remove the usage of global variable "ModuleCdc"
211 | if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
212 | return nil, nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal interchain account packet data: %s", err.Error())
213 | }
214 |
215 | err := am.keeper.OnRecvPacket(ctx, packet)
216 |
217 | switch data.Type {
218 | case types.Type_REGISTER:
219 | acknowledgement := types.IBCAccountPacketAcknowledgement{
220 | ChainID: ctx.ChainID(),
221 | }
222 | if err == nil {
223 | acknowledgement.Code = 0
224 | } else {
225 | acknowledgement.Code = 1
226 | acknowledgement.Error = err.Error()
227 | }
228 |
229 | return &sdk.Result{
230 | Events: ctx.EventManager().Events().ToABCIEvents(),
231 | }, acknowledgement.GetBytes(), nil
232 | case types.Type_RUNTX:
233 | acknowledgement := types.IBCAccountPacketAcknowledgement{
234 | ChainID: ctx.ChainID(),
235 | }
236 | if err == nil {
237 | acknowledgement.Code = 0
238 | } else {
239 | acknowledgement.Code = 1
240 | acknowledgement.Error = err.Error()
241 | }
242 |
243 | return &sdk.Result{
244 | Events: ctx.EventManager().Events().ToABCIEvents(),
245 | }, acknowledgement.GetBytes(), nil
246 | default:
247 | return nil, nil, types.ErrUnknownPacketData
248 | }
249 | }
250 |
251 | func (am AppModule) OnAcknowledgementPacket(
252 | ctx sdk.Context,
253 | packet channeltypes.Packet,
254 | acknowledgement []byte,
255 | ) (*sdk.Result, error) {
256 | var ack types.IBCAccountPacketAcknowledgement
257 | if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil {
258 | return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-27 interchain account packet acknowledgment: %v", err)
259 | }
260 | var data types.IBCAccountPacketData
261 | if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
262 | return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-27 interchain account packet data: %s", err.Error())
263 | }
264 |
265 | if err := am.keeper.OnAcknowledgementPacket(ctx, packet, data, ack); err != nil {
266 | return nil, err
267 | }
268 |
269 | // TODO: Add events.
270 |
271 | return &sdk.Result{
272 | Events: ctx.EventManager().Events().ToABCIEvents(),
273 | }, nil
274 | }
275 |
276 | func (am AppModule) OnTimeoutPacket(
277 | ctx sdk.Context,
278 | packet channeltypes.Packet,
279 | ) (*sdk.Result, error) {
280 | // TODO
281 | return nil, nil
282 | }
283 |
--------------------------------------------------------------------------------
/x/ibc-account/spec/03_types.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Types
6 |
7 | ## IBCAccount
8 |
9 | IBCAccount implements the standard AccountI interface similar to Cosmos SDK x/auth module's BaseAccount or Module Account
10 |
11 | ```proto
12 | // IBCAccount defines an account to which other chains have privileges
13 | message IBCAccount {
14 | option (gogoproto.goproto_getters) = false;
15 | option (gogoproto.goproto_stringer) = false;
16 | option (cosmos_proto.implements_interface) = "IBCAccountI";
17 |
18 | cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""];
19 | string sourcePort = 2;
20 | string sourceChannel = 3;
21 | string destinationPort = 4;
22 | string destinationChannel = 5;
23 | }
24 | ```
25 |
26 | As shown above, IBCAccount embeds the BaseAccount, and is assigned an Address and AccountNumber similar to the BaseAccount. However, because IBCAccount was designed to be used through the module, and not the user, there is no need to designate a PubKey or Sequence (which is implemented in the ModuleAccount).
27 |
28 | Also, IBCAccount stores the information on the IBC Port and Channel that requested the creation of the account.
29 |
30 | One can check if a specific address is an IBCAccount as well as the actual IBCAccount type through the IBCAccountKeeper's GetIBCAccount method. If the address queried doesn't exist or is not an IBC account, an error is returned.
31 |
--------------------------------------------------------------------------------
/x/ibc-account/spec/04_keeper.md:
--------------------------------------------------------------------------------
1 |
4 | # Keeper
5 |
6 | ## Structure
7 |
8 | ```go
9 |
10 | type TxEncoder func(data interface{}) ([]byte, error)
11 |
12 | type Keeper struct {
13 | ...
14 | txEncoders map[string]types.TxEncoder
15 | ...
16 | router types.Router
17 | }
18 | ```
19 |
20 | The most important part of the IBC account keeper, as shown above, is the **map of txEncoders** and the **router**. Because ICS-027 specification defines that the chain can send arbitrary tx bytes to the counterparty chain, both chains must define the way that they process the caller's requests or make the tx bytes that the callee can process.
21 |
22 | The `TxEncoder` serializes the source chain's tx bytes from any data. And the map of `TxEncoder` has the key, such as `chain-id` and `keeper`, which the keeper uses to send packets. Therefore, it is necessary to know which source chain's transaction is being executed.
23 |
24 | `SerializeCosmosTx(cdc codec.BinaryMarshaler, registry codectypes.InterfaceRegistry)` provides a way to serialize the tx bytes from messages if the destination chain is based on the Cosmos-SDK.
25 |
26 | The router is used to delegate the process of handling the message to a module. When a packet which requests a set of transaction bytes to be run is passed, the router deserializes the tx bytes and passes the message to the handler. The keeper checks the result of each message, and if any message returns an error, the entire transaction is aborted, and state change rolled back.
27 |
28 | `TryRunTx(ctx sdk.Context, sourcePort, sourceChannel, typ string, data interface{}, timeoutHeight clienttypes.Height, timeoutTimestamp uint64)` method is used to request transactions to be run on the destination chain. This method uses the `typ` parameter from the `txEncoders map`'s key to find the right `txEncoder`. If the `txEncoder` exists, the transaction is serialized and a `RUNTX` packet is sent to the destination chain. The `TryRunTx` also returns the virtual txHash which is used in the 'Hook' section shown below. This virtual txHash is not related to the actual on-chain transaction, but only 'virtually' created so transactions requested by the Hook can be identified.
29 |
30 | ### IBC Packets
31 |
32 | ```go
33 |
34 | enum Type {
35 | REGISTER = 0;
36 | RUNTX = 1;
37 | }
38 |
39 | message IBCAccountPacketData {
40 | Type type = 1;
41 | bytes data = 2;
42 | }
43 |
44 | message IBCAccountPacketAcknowledgement {
45 | Type type = 1;
46 | string chainID = 2;
47 | uint32 code = 3;
48 | string error = 4;
49 | }
50 | ```
51 |
52 | The example above shows the IBC packets that are used in ICS-027. `Type` indicates what action the packet is performing. When a `REGISTER` packet type is delivered, the counterparty chain will create an account with the address using the hash of {destPort}/{destChannel}/{packet.data}, assuming a duplicate prior account doesn't exist.
53 |
54 | If the account is created successfully, it returns an acknowledgement packet to the origin chain with type `REGISTER` and code `0`. If there's an error, it returns the acknowledgement packet with type `REGISTER` and the code of the resulting error.
55 |
56 | When a `RUNTX` type packet is delivered, the counterparty chain will deserialize the tx bytes (packet's data field) in a predefined way.
57 |
58 | In this implementation of ICS27 for the Cosmos-SDK, it deserializes the tx bytes into slices of messages and gets the handler from the router and executes and checks the result like described above.
59 |
60 | If the all messages are successful, it returns the acknowledgment packet to the chain with type `RUNTX` and code `0`. If there's an error, it returns the acknowledgement packet with type `RUNTX` and the code and error of the first failed message.
61 |
62 | ### Hook
63 |
64 | ```go
65 |
66 | type IBCAccountHooks interface {
67 | OnAccountCreated(ctx sdk.Context, sourcePort, sourceChannel string, address sdk.AccAddress)
68 | OnTxSucceeded(ctx sdk.Context, sourcePort, sourceChannel string, txHash []byte, txBytes []byte)
69 | OnTxFailed(ctx sdk.Context, sourcePort, sourceChannel string, txHash []byte, txBytes []byte)
70 | }
71 | ```
72 |
73 | The example above shows the hook for helping developer using the IBC account keeper.
74 |
75 | The hook lets the developer know whether the IBC account has been successfully created on the counterparty chain.
76 |
77 | After sending the packet with an `IBCAccountPacketData` with the type `REGISTER`, if the acknowledgement packet with the type `REGISTER` and code `0` is delivered, `OnAccountCreated` is executed with the counterparty chain's chain-id and address.
78 |
79 | After sending the packet with an `IBCAccountPacketData` with the type `RUNTX`, if the acknowledgement packet with the type `RUNTX` and code `0` is delivered, `OnTxSucceeded` is executed with the counterparty chain's chain-id, virtual tx hash and requested data that is not serialized. Virtual tx hash is used only for internal logic to distinguish the requested tx and it is computed by hashing the tx bytes and sequence of packet. Otherwise, `OnTxFailed` will be executed.
80 |
--------------------------------------------------------------------------------
/x/ibc-account/spec/05_packets.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Packets
6 |
7 | ```proto
8 | message IBCTxRaw {
9 | bytes body_bytes = 1;
10 | }
11 |
12 | message IBCTxBody {
13 | repeated google.protobuf.Any messages = 1;
14 | }
15 |
16 | enum Type {
17 | REGISTER = 0;
18 | RUNTX = 1;
19 | }
20 |
21 | message IBCAccountPacketData {
22 | Type type = 1;
23 | bytes data = 2;
24 | }
25 |
26 | message IBCAccountPacketAcknowledgement {
27 | Type type = 1;
28 | string chainID = 2;
29 | uint32 code = 3;
30 | bytes data = 4;
31 | string error = 5;
32 | }
33 | ```
34 |
35 | - `IBCAccountPacketAcknowledgement` returns the result of the packet request back to the chain that sent the packet.
36 | - `IBCAccountPacketData` is sent when the counterparty chain registers a IBCAccount or wants to execute a specific tx through the IBC Account.
37 | - `IBCAccountPacketData` type field displays the behavior requested by the packet. If the type is `REGISTER`, this means request to register a new IBCAccount. In this case, the destination chain can set the IBCAccount's address, but typically it is recommended to refer to the data field to create the address in a deterministic way. If the IBCAccount has been successfully registered, an `IBCAccountPacketAcknowledgment` is returned to the requesting chain with the `Code` field set to `0`. If there was an error, an `IBCAccountPacketAcknowledgment` is returned to the requesting chain with the `Code` field including the error message.
38 |
--------------------------------------------------------------------------------
/x/ibc-account/spec/README.md:
--------------------------------------------------------------------------------
1 |
7 |
8 | # IBC Account
9 |
10 | ## Abstract
11 |
12 | This document specifies the IBC account module for the Cosmos SDK.
13 |
14 | The IBCAccount module manages the creation of IBC Accounts and ICS20 packet handling for the IBC accounts. This module is built based on the [ICS27 specification](https://github.com/cosmos/ics/tree/master/spec/ics-027-interchain-accounts). IBC Accounts allow a remote, IBC-connected **source blockchain** to request an arbitrary transaction to be executed on the **destination blockchain**(the chain which hosts the IBC account) via the IBC account. It should be noted that an IBC account has similar properties to a user account, and are bound to the same restrictions (unbonding periods, redelegation rules, etc).
15 |
16 | The current implementation allows the same IBCAccount module on the destination chain to run any of the domiciling blockchain's native transactions that a user account is able to request(i.e. same module can handle 'send', 'stake', 'vote', etc), but the controlling chain/source chain must implement its own logic for controlling the IBC account from its own IBCAccount logic.
17 |
18 | ## Contents
19 | 1. **[Types](03_types.md)**
20 | 2. **[Keeper](04_keeper.md)**
21 | 3. **[Packets](05_packets.md)**
22 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/mock/client/cli/tx.go:
--------------------------------------------------------------------------------
1 | package cli
2 |
3 | import (
4 | "time"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 |
8 | "github.com/spf13/cobra"
9 |
10 | "github.com/cosmos/cosmos-sdk/client"
11 | "github.com/cosmos/cosmos-sdk/client/flags"
12 | "github.com/cosmos/cosmos-sdk/client/tx"
13 | clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"
14 | channelutils "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/client/utils"
15 |
16 | mocktypes "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock/types"
17 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
18 | )
19 |
20 | const (
21 | flagPacketTimeoutHeight = "packet-timeout-height"
22 | flagPacketTimeoutTimestamp = "packet-timeout-timestamp"
23 | flagAbsoluteTimeouts = "absolute-timeouts"
24 | )
25 |
26 | // NewTxCmd returns a root CLI command handler for all x/bank transaction commands.
27 | func NewTxCmd() *cobra.Command {
28 | txCmd := &cobra.Command{
29 | Use: types.ModuleName,
30 | Short: "IBC account mock transaction subcommands",
31 | DisableFlagParsing: true,
32 | SuggestionsMinimumDistance: 2,
33 | RunE: client.ValidateCmd,
34 | }
35 |
36 | txCmd.AddCommand(NewRegisterIBCAccountTxCmd())
37 | txCmd.AddCommand(NewSendTxCmd())
38 |
39 | return txCmd
40 | }
41 |
42 | func NewRegisterIBCAccountTxCmd() *cobra.Command {
43 | cmd := &cobra.Command{
44 | Use: "register [source_port] [source_channel] [salt]",
45 | Args: cobra.ExactArgs(3),
46 | RunE: func(cmd *cobra.Command, args []string) error {
47 | clientCtx := client.GetClientContextFromCmd(cmd)
48 | clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
49 | if err != nil {
50 | return err
51 | }
52 |
53 | timeoutHeightStr, err := cmd.Flags().GetString(flagPacketTimeoutHeight)
54 | if err != nil {
55 | return err
56 | }
57 | timeoutHeight, err := clienttypes.ParseHeight(timeoutHeightStr)
58 | if err != nil {
59 | return err
60 | }
61 |
62 | timeoutTimestamp, err := cmd.Flags().GetUint64(flagPacketTimeoutTimestamp)
63 | if err != nil {
64 | return err
65 | }
66 |
67 | absoluteTimeouts, err := cmd.Flags().GetBool(flagAbsoluteTimeouts)
68 | if err != nil {
69 | return err
70 | }
71 |
72 | // if the timeouts are not absolute, retrieve latest block height and block timestamp
73 | // for the consensus state connected to the destination port/channel
74 | if !absoluteTimeouts {
75 | consensusState, height, _, err := channelutils.QueryLatestConsensusState(clientCtx, args[0], args[1])
76 | if err != nil {
77 | return err
78 | }
79 |
80 | if !timeoutHeight.IsZero() {
81 | absoluteHeight := height
82 | absoluteHeight.VersionNumber += timeoutHeight.VersionNumber
83 | absoluteHeight.VersionHeight += timeoutHeight.VersionHeight
84 | timeoutHeight = absoluteHeight
85 | }
86 |
87 | if timeoutTimestamp != 0 {
88 | timeoutTimestamp = consensusState.GetTimestamp() + timeoutTimestamp
89 | }
90 | }
91 |
92 | msg := &mocktypes.MsgTryRegisterIBCAccount{
93 | SourcePort: args[0],
94 | SourceChannel: args[1],
95 | Salt: []byte(args[2]),
96 | TimeoutHeight: timeoutHeight,
97 | TimeoutTimestamp: timeoutTimestamp,
98 | Sender: clientCtx.GetFromAddress(),
99 | }
100 |
101 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
102 | },
103 | }
104 |
105 | cmd.Flags().String(flagPacketTimeoutHeight, "0-1000", "Packet timeout block height. The timeout is disabled when set to 0-0.")
106 | cmd.Flags().Uint64(flagPacketTimeoutTimestamp, uint64((time.Duration(10) * time.Minute).Nanoseconds()), "Packet timeout timestamp in nanoseconds. Default is 10 minutes. The timeout is disabled when set to 0.")
107 | cmd.Flags().Bool(flagAbsoluteTimeouts, false, "Timeout flags are used as absolute timeouts.")
108 | flags.AddTxFlagsToCmd(cmd)
109 |
110 | return cmd
111 | }
112 |
113 | // NewSendTxCmd returns a CLI command handler for creating a MsgSend transaction.
114 | func NewSendTxCmd() *cobra.Command {
115 | cmd := &cobra.Command{
116 | Use: "send [source_port] [source_channel] [ibc_account_address] [to_address] [amount]",
117 | Args: cobra.ExactArgs(5),
118 | RunE: func(cmd *cobra.Command, args []string) error {
119 | clientCtx := client.GetClientContextFromCmd(cmd)
120 | clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
121 | if err != nil {
122 | return err
123 | }
124 |
125 | fromAddr, err := sdk.AccAddressFromBech32(args[2])
126 | if err != nil {
127 | return err
128 | }
129 | toAddr, err := sdk.AccAddressFromBech32(args[3])
130 | if err != nil {
131 | return err
132 | }
133 |
134 | coins, err := sdk.ParseCoins(args[4])
135 | if err != nil {
136 | return err
137 | }
138 |
139 | timeoutHeightStr, err := cmd.Flags().GetString(flagPacketTimeoutHeight)
140 | if err != nil {
141 | return err
142 | }
143 | timeoutHeight, err := clienttypes.ParseHeight(timeoutHeightStr)
144 | if err != nil {
145 | return err
146 | }
147 |
148 | timeoutTimestamp, err := cmd.Flags().GetUint64(flagPacketTimeoutTimestamp)
149 | if err != nil {
150 | return err
151 | }
152 |
153 | absoluteTimeouts, err := cmd.Flags().GetBool(flagAbsoluteTimeouts)
154 | if err != nil {
155 | return err
156 | }
157 |
158 | // if the timeouts are not absolute, retrieve latest block height and block timestamp
159 | // for the consensus state connected to the destination port/channel
160 | if !absoluteTimeouts {
161 | consensusState, height, _, err := channelutils.QueryLatestConsensusState(clientCtx, args[0], args[1])
162 | if err != nil {
163 | return err
164 | }
165 |
166 | if !timeoutHeight.IsZero() {
167 | absoluteHeight := height
168 | absoluteHeight.VersionNumber += timeoutHeight.VersionNumber
169 | absoluteHeight.VersionHeight += timeoutHeight.VersionHeight
170 | timeoutHeight = absoluteHeight
171 | }
172 |
173 | if timeoutTimestamp != 0 {
174 | timeoutTimestamp = consensusState.GetTimestamp() + timeoutTimestamp
175 | }
176 | }
177 |
178 | msg := &mocktypes.MsgTryRunTxMsgSend{
179 | SourcePort: args[0],
180 | SourceChannel: args[1],
181 | TimeoutHeight: timeoutHeight,
182 | TimeoutTimestamp: timeoutTimestamp,
183 | FromAddress: fromAddr,
184 | ToAddress: toAddr,
185 | Amount: coins,
186 | Sender: clientCtx.GetFromAddress(),
187 | }
188 |
189 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
190 | },
191 | }
192 |
193 | cmd.Flags().String(flagPacketTimeoutHeight, "0-1000", "Packet timeout block height. The timeout is disabled when set to 0-0.")
194 | cmd.Flags().Uint64(flagPacketTimeoutTimestamp, uint64((time.Duration(10) * time.Minute).Nanoseconds()), "Packet timeout timestamp in nanoseconds. Default is 10 minutes. The timeout is disabled when set to 0.")
195 | cmd.Flags().Bool(flagAbsoluteTimeouts, false, "Timeout flags are used as absolute timeouts.")
196 | flags.AddTxFlagsToCmd(cmd)
197 |
198 | return cmd
199 | }
200 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/mock/handler.go:
--------------------------------------------------------------------------------
1 | package mock
2 |
3 | import (
4 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock/keeper"
5 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock/types"
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
8 | )
9 |
10 | func NewHandler(k keeper.Keeper) sdk.Handler {
11 | return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
12 | ctx = ctx.WithEventManager(sdk.NewEventManager())
13 |
14 | switch msg := msg.(type) {
15 | case *types.MsgTryRegisterIBCAccount:
16 | return handleMsgTryRegisterIBCAccount(ctx, k, msg)
17 | case *types.MsgTryRunTxMsgSend:
18 | return handleMsgTryRunTxMsgSend(ctx, k, msg)
19 | default:
20 | return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized mock ibc account message type: %T", msg)
21 | }
22 | }
23 | }
24 |
25 | func handleMsgTryRegisterIBCAccount(ctx sdk.Context, k keeper.Keeper, msg *types.MsgTryRegisterIBCAccount) (*sdk.Result, error) {
26 | if err := k.TryRegisterIBCAccount(ctx, msg.SourcePort, msg.SourceChannel, msg.Salt, msg.TimeoutHeight, msg.TimeoutTimestamp); err != nil {
27 | return nil, err
28 | }
29 |
30 | return &sdk.Result{
31 | Events: ctx.EventManager().Events().ToABCIEvents(),
32 | }, nil
33 | }
34 |
35 | func handleMsgTryRunTxMsgSend(ctx sdk.Context, k keeper.Keeper, msg *types.MsgTryRunTxMsgSend) (*sdk.Result, error) {
36 | if err := k.TryRunTxMsgSend(ctx, msg.SourcePort, msg.SourceChannel, msg.TimeoutHeight, msg.TimeoutTimestamp, msg.FromAddress, msg.ToAddress, msg.Amount); err != nil {
37 | return nil, err
38 | }
39 |
40 | return &sdk.Result{
41 | Events: ctx.EventManager().Events().ToABCIEvents(),
42 | }, nil
43 | }
44 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/mock/keeper/keeper.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/keeper"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
7 | clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"
8 | )
9 |
10 | type Keeper struct {
11 | ibcAccountKeeper keeper.Keeper
12 | }
13 |
14 | func NewKeeper(ibcAccountKeeper keeper.Keeper) Keeper {
15 | return Keeper{
16 | ibcAccountKeeper: ibcAccountKeeper,
17 | }
18 | }
19 |
20 | func (keeper Keeper) TryRegisterIBCAccount(ctx sdk.Context, sourcePort, sourceChannel string, salt []byte, timeoutHeight clienttypes.Height, timeoutTimestamp uint64) error {
21 | return keeper.ibcAccountKeeper.TryRegisterIBCAccount(ctx, sourcePort, sourceChannel, salt, timeoutHeight, timeoutTimestamp)
22 | }
23 |
24 | func (keeper Keeper) TryRunTxMsgSend(ctx sdk.Context, sourcePort, sourceChannel string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, fromAddr, toAddr sdk.AccAddress, amount sdk.Coins) error {
25 | msg := banktypes.NewMsgSend(fromAddr, toAddr, amount)
26 | _, err := keeper.ibcAccountKeeper.TryRunTx(ctx, sourcePort, sourceChannel, "cosmos-sdk", msg, timeoutHeight, timeoutTimestamp)
27 | return err
28 | }
29 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/mock/module.go:
--------------------------------------------------------------------------------
1 | package mock
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/cosmos/cosmos-sdk/types/module"
7 |
8 | "github.com/grpc-ecosystem/grpc-gateway/runtime"
9 |
10 | "github.com/gorilla/mux"
11 | "github.com/spf13/cobra"
12 |
13 | abci "github.com/tendermint/tendermint/abci/types"
14 |
15 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock/client/cli"
16 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock/keeper"
17 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/testing/mock/types"
18 | "github.com/cosmos/cosmos-sdk/client"
19 | "github.com/cosmos/cosmos-sdk/codec"
20 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
21 | sdk "github.com/cosmos/cosmos-sdk/types"
22 | )
23 |
24 | var (
25 | _ module.AppModule = AppModule{}
26 | _ module.AppModuleBasic = AppModuleBasic{}
27 | )
28 |
29 | // AppModuleBasic is the mock AppModuleBasic.
30 | type AppModuleBasic struct{}
31 |
32 | // Name implements AppModuleBasic interface.
33 | func (AppModuleBasic) Name() string {
34 | return types.ModuleName
35 | }
36 |
37 | // RegisterLegacyAminoCodec implements AppModuleBasic interface.
38 | func (AppModuleBasic) RegisterLegacyAminoCodec(*codec.LegacyAmino) {}
39 |
40 | // RegisterInterfaces implements AppModuleBasic interface.
41 | func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
42 | types.RegisterInterfaces(registry)
43 | }
44 |
45 | // DefaultGenesis implements AppModuleBasic interface.
46 | func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage {
47 | return nil
48 | }
49 |
50 | // ValidateGenesis implements the AppModuleBasic interface.
51 | func (AppModuleBasic) ValidateGenesis(codec.JSONMarshaler, client.TxEncodingConfig, json.RawMessage) error {
52 | return nil
53 | }
54 |
55 | // RegisterRESTRoutes implements AppModuleBasic interface.
56 | func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) {}
57 |
58 | // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the bank module.
59 | func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
60 | // noop
61 | }
62 |
63 | // GetTxCmd implements AppModuleBasic interface.
64 | func (AppModuleBasic) GetTxCmd() *cobra.Command {
65 | return cli.NewTxCmd()
66 | }
67 |
68 | // GetQueryCmd implements AppModuleBasic interface.
69 | func (AppModuleBasic) GetQueryCmd() *cobra.Command {
70 | return nil
71 | }
72 |
73 | // AppModule represents the AppModule for the mock module.
74 | type AppModule struct {
75 | AppModuleBasic
76 | mockKeeper keeper.Keeper
77 | }
78 |
79 | // NewAppModule returns a mock AppModule instance.
80 | func NewAppModule(k keeper.Keeper) AppModule {
81 | return AppModule{
82 | mockKeeper: k,
83 | }
84 | }
85 |
86 | // RegisterInvariants implements the AppModule interface.
87 | func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {}
88 |
89 | // Route implements the AppModule interface.
90 | func (am AppModule) Route() sdk.Route {
91 | return sdk.NewRoute(types.ModuleName, NewHandler(am.mockKeeper))
92 | }
93 |
94 | // QuerierRoute implements the AppModule interface.
95 | func (AppModule) QuerierRoute() string {
96 | return ""
97 | }
98 |
99 | // LegacyQuerierHandler implements the AppModule interface.
100 | func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier {
101 | return nil
102 | }
103 |
104 | // RegisterServices registers module services.
105 | func (am AppModule) RegisterServices(cfg module.Configurator) {
106 | // noop
107 | }
108 |
109 | // InitGenesis implements the AppModule interface.
110 | func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate {
111 | return []abci.ValidatorUpdate{}
112 | }
113 |
114 | // ExportGenesis implements the AppModule interface.
115 | func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage {
116 | return nil
117 | }
118 |
119 | // BeginBlock implements the AppModule interface
120 | func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
121 | }
122 |
123 | // EndBlock implements the AppModule interface
124 | func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate {
125 | return []abci.ValidatorUpdate{}
126 | }
127 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/mock/privval.go:
--------------------------------------------------------------------------------
1 | package mock
2 |
3 | import (
4 | "github.com/tendermint/tendermint/crypto"
5 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
6 | tmtypes "github.com/tendermint/tendermint/types"
7 |
8 | "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
9 | )
10 |
11 | var _ tmtypes.PrivValidator = PV{}
12 |
13 | // MockPV implements PrivValidator without any safety or persistence.
14 | // Only use it for testing.
15 | type PV struct {
16 | PrivKey crypto.PrivKey
17 | }
18 |
19 | func NewPV() PV {
20 | return PV{ed25519.GenPrivKey()}
21 | }
22 |
23 | // GetPubKey implements PrivValidator interface
24 | func (pv PV) GetPubKey() (crypto.PubKey, error) {
25 | return pv.PrivKey.PubKey(), nil
26 | }
27 |
28 | // SignVote implements PrivValidator interface
29 | func (pv PV) SignVote(chainID string, vote *tmproto.Vote) error {
30 | signBytes := tmtypes.VoteSignBytes(chainID, vote)
31 | sig, err := pv.PrivKey.Sign(signBytes)
32 | if err != nil {
33 | return err
34 | }
35 | vote.Signature = sig
36 | return nil
37 | }
38 |
39 | // SignProposal implements PrivValidator interface
40 | func (pv PV) SignProposal(chainID string, proposal *tmproto.Proposal) error {
41 | signBytes := tmtypes.ProposalSignBytes(chainID, proposal)
42 | sig, err := pv.PrivKey.Sign(signBytes)
43 | if err != nil {
44 | return err
45 | }
46 | proposal.Signature = sig
47 | return nil
48 | }
49 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/mock/privval_test.go:
--------------------------------------------------------------------------------
1 | package mock_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
8 | tmtypes "github.com/tendermint/tendermint/types"
9 |
10 | "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock"
11 | )
12 |
13 | const chainID = "testChain"
14 |
15 | func TestGetPubKey(t *testing.T) {
16 | pv := mock.NewPV()
17 | pk, err := pv.GetPubKey()
18 | require.NoError(t, err)
19 | require.Equal(t, "ed25519", pk.Type())
20 | }
21 |
22 | func TestSignVote(t *testing.T) {
23 | pv := mock.NewPV()
24 | pk, _ := pv.GetPubKey()
25 |
26 | vote := &tmproto.Vote{Height: 2}
27 | pv.SignVote(chainID, vote)
28 |
29 | msg := tmtypes.VoteSignBytes(chainID, vote)
30 | ok := pk.VerifySignature(msg, vote.Signature)
31 | require.True(t, ok)
32 | }
33 |
34 | func TestSignProposal(t *testing.T) {
35 | pv := mock.NewPV()
36 | pk, _ := pv.GetPubKey()
37 |
38 | proposal := &tmproto.Proposal{Round: 2}
39 | pv.SignProposal(chainID, proposal)
40 |
41 | msg := tmtypes.ProposalSignBytes(chainID, proposal)
42 | ok := pk.VerifySignature(msg, proposal.Signature)
43 | require.True(t, ok)
44 | }
45 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/mock/types/codec.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/codec"
5 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | )
8 |
9 | // RegisterInterfaces register the ibc transfer module interfaces to protobuf
10 | // Any.
11 | func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
12 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgTryRegisterIBCAccount{})
13 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgTryRunTxMsgSend{})
14 | }
15 |
16 | var (
17 | // ModuleCdc references the global x/ibc-transfer module codec. Note, the codec
18 | // should ONLY be used in certain instances of tests and for JSON encoding.
19 | //
20 | // The actual codec used for serialization should be provided to x/ibc-transfer and
21 | // defined at the application level.
22 | ModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
23 | )
24 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/mock/types/keys.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | const (
4 | ModuleName = "ibcaccountmock"
5 | )
6 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/mock/types/msgs.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import sdk "github.com/cosmos/cosmos-sdk/types"
4 |
5 | func (MsgTryRegisterIBCAccount) Route() string {
6 | return ModuleName
7 | }
8 |
9 | func (MsgTryRegisterIBCAccount) Type() string {
10 | return "try-register-ibc-account"
11 | }
12 |
13 | func (MsgTryRegisterIBCAccount) ValidateBasic() error {
14 | return nil
15 | }
16 |
17 | func (msg MsgTryRegisterIBCAccount) GetSignBytes() []byte {
18 | return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg))
19 | }
20 |
21 | func (msg MsgTryRegisterIBCAccount) GetSigners() []sdk.AccAddress {
22 | // no need to have signer
23 | return []sdk.AccAddress{msg.Sender}
24 | }
25 |
26 | func (MsgTryRunTxMsgSend) Route() string {
27 | return ModuleName
28 | }
29 |
30 | func (MsgTryRunTxMsgSend) Type() string {
31 | return "try-run-tx-msg-send"
32 | }
33 |
34 | func (MsgTryRunTxMsgSend) ValidateBasic() error {
35 | return nil
36 | }
37 |
38 | func (msg MsgTryRunTxMsgSend) GetSignBytes() []byte {
39 | return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg))
40 | }
41 |
42 | func (msg MsgTryRunTxMsgSend) GetSigners() []sdk.AccAddress {
43 | // no need to have signer
44 | return []sdk.AccAddress{msg.Sender}
45 | }
46 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/solomachine.go:
--------------------------------------------------------------------------------
1 | package ibctesting
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 | "github.com/tendermint/tendermint/crypto"
8 |
9 | "github.com/cosmos/cosmos-sdk/codec"
10 | kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
11 | "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
12 | "github.com/cosmos/cosmos-sdk/crypto/types/multisig"
13 | "github.com/cosmos/cosmos-sdk/types/tx/signing"
14 | "github.com/cosmos/cosmos-sdk/x/auth/tx"
15 | clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"
16 | commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types"
17 | host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host"
18 | "github.com/cosmos/cosmos-sdk/x/ibc/core/exported"
19 | solomachinetypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types"
20 | )
21 |
22 | var prefix = commitmenttypes.NewMerklePrefix([]byte("ibc"))
23 |
24 | // Solomachine is a testing helper used to simulate a counterparty
25 | // solo machine client.
26 | type Solomachine struct {
27 | t *testing.T
28 |
29 | cdc codec.BinaryMarshaler
30 | ClientID string
31 | PrivateKeys []crypto.PrivKey // keys used for signing
32 | PublicKeys []crypto.PubKey // keys used for generating solo machine pub key
33 | PublicKey crypto.PubKey // key used for verification
34 | Sequence uint64
35 | Time uint64
36 | Diversifier string
37 | }
38 |
39 | // NewSolomachine returns a new solomachine instance with an `nKeys` amount of
40 | // generated private/public key pairs and a sequence starting at 1. If nKeys
41 | // is greater than 1 then a multisig public key is used.
42 | func NewSolomachine(t *testing.T, cdc codec.BinaryMarshaler, clientID, diversifier string, nKeys uint64) *Solomachine {
43 | privKeys, pubKeys, pk := GenerateKeys(t, nKeys)
44 |
45 | return &Solomachine{
46 | t: t,
47 | cdc: cdc,
48 | ClientID: clientID,
49 | PrivateKeys: privKeys,
50 | PublicKeys: pubKeys,
51 | PublicKey: pk,
52 | Sequence: 1,
53 | Time: 10,
54 | Diversifier: diversifier,
55 | }
56 | }
57 |
58 | // GenerateKeys generates a new set of secp256k1 private keys and public keys.
59 | // If the number of keys is greater than one then the public key returned represents
60 | // a multisig public key. The private keys are used for signing, the public
61 | // keys are used for generating the public key and the public key is used for
62 | // solo machine verification. The usage of secp256k1 is entirely arbitrary.
63 | // The key type can be swapped for any key type supported by the PublicKey
64 | // interface, if needed. The same is true for the amino based Multisignature
65 | // public key.
66 | func GenerateKeys(t *testing.T, n uint64) ([]crypto.PrivKey, []crypto.PubKey, crypto.PubKey) {
67 | require.NotEqual(t, uint64(0), n, "generation of zero keys is not allowed")
68 |
69 | privKeys := make([]crypto.PrivKey, n)
70 | pubKeys := make([]crypto.PubKey, n)
71 | for i := uint64(0); i < n; i++ {
72 | privKeys[i] = secp256k1.GenPrivKey()
73 | pubKeys[i] = privKeys[i].PubKey()
74 | }
75 |
76 | var pk crypto.PubKey
77 | if len(privKeys) > 1 {
78 | // generate multi sig pk
79 | pk = kmultisig.NewLegacyAminoPubKey(int(n), pubKeys)
80 | } else {
81 | pk = privKeys[0].PubKey()
82 | }
83 |
84 | return privKeys, pubKeys, pk
85 | }
86 |
87 | // ClientState returns a new solo machine ClientState instance. Default usage does not allow update
88 | // after governance proposal
89 | func (solo *Solomachine) ClientState() *solomachinetypes.ClientState {
90 | return solomachinetypes.NewClientState(solo.Sequence, solo.ConsensusState(), false)
91 | }
92 |
93 | // ConsensusState returns a new solo machine ConsensusState instance
94 | func (solo *Solomachine) ConsensusState() *solomachinetypes.ConsensusState {
95 | publicKey, err := tx.PubKeyToAny(solo.PublicKey)
96 | require.NoError(solo.t, err)
97 |
98 | return &solomachinetypes.ConsensusState{
99 | PublicKey: publicKey,
100 | Diversifier: solo.Diversifier,
101 | Timestamp: solo.Time,
102 | }
103 | }
104 |
105 | // GetHeight returns an exported.Height with Sequence as VersionHeight
106 | func (solo *Solomachine) GetHeight() exported.Height {
107 | return clienttypes.NewHeight(0, solo.Sequence)
108 | }
109 |
110 | // CreateHeader generates a new private/public key pair and creates the
111 | // necessary signature to construct a valid solo machine header.
112 | func (solo *Solomachine) CreateHeader() *solomachinetypes.Header {
113 | // generate new private keys and signature for header
114 | newPrivKeys, newPubKeys, newPubKey := GenerateKeys(solo.t, uint64(len(solo.PrivateKeys)))
115 |
116 | publicKey, err := tx.PubKeyToAny(newPubKey)
117 | require.NoError(solo.t, err)
118 |
119 | data := &solomachinetypes.HeaderData{
120 | NewPubKey: publicKey,
121 | NewDiversifier: solo.Diversifier,
122 | }
123 |
124 | dataBz, err := solo.cdc.MarshalBinaryBare(data)
125 | require.NoError(solo.t, err)
126 |
127 | signBytes := &solomachinetypes.SignBytes{
128 | Sequence: solo.Sequence,
129 | Timestamp: solo.Time,
130 | Diversifier: solo.Diversifier,
131 | DataType: solomachinetypes.HEADER,
132 | Data: dataBz,
133 | }
134 |
135 | bz, err := solo.cdc.MarshalBinaryBare(signBytes)
136 | require.NoError(solo.t, err)
137 |
138 | sig := solo.GenerateSignature(bz)
139 |
140 | header := &solomachinetypes.Header{
141 | Sequence: solo.Sequence,
142 | Timestamp: solo.Time,
143 | Signature: sig,
144 | NewPublicKey: publicKey,
145 | NewDiversifier: solo.Diversifier,
146 | }
147 |
148 | // assumes successful header update
149 | solo.Sequence++
150 | solo.PrivateKeys = newPrivKeys
151 | solo.PublicKeys = newPubKeys
152 | solo.PublicKey = newPubKey
153 |
154 | return header
155 | }
156 |
157 | // CreateMisbehaviour constructs testing misbehaviour for the solo machine client
158 | // by signing over two different data bytes at the same sequence.
159 | func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour {
160 | path := solo.GetClientStatePath("counterparty")
161 | dataOne, err := solomachinetypes.ClientStateDataBytes(solo.cdc, path, solo.ClientState())
162 | require.NoError(solo.t, err)
163 |
164 | path = solo.GetConsensusStatePath("counterparty", clienttypes.NewHeight(0, 1))
165 | dataTwo, err := solomachinetypes.ConsensusStateDataBytes(solo.cdc, path, solo.ConsensusState())
166 | require.NoError(solo.t, err)
167 |
168 | signBytes := &solomachinetypes.SignBytes{
169 | Sequence: solo.Sequence,
170 | Timestamp: solo.Time,
171 | Diversifier: solo.Diversifier,
172 | DataType: solomachinetypes.CLIENT,
173 | Data: dataOne,
174 | }
175 |
176 | bz, err := solo.cdc.MarshalBinaryBare(signBytes)
177 | require.NoError(solo.t, err)
178 |
179 | sig := solo.GenerateSignature(bz)
180 | signatureOne := solomachinetypes.SignatureAndData{
181 | Signature: sig,
182 | DataType: solomachinetypes.CLIENT,
183 | Data: dataOne,
184 | }
185 |
186 | signBytes = &solomachinetypes.SignBytes{
187 | Sequence: solo.Sequence,
188 | Timestamp: solo.Time,
189 | Diversifier: solo.Diversifier,
190 | DataType: solomachinetypes.CONSENSUS,
191 | Data: dataTwo,
192 | }
193 |
194 | bz, err = solo.cdc.MarshalBinaryBare(signBytes)
195 | require.NoError(solo.t, err)
196 |
197 | sig = solo.GenerateSignature(bz)
198 | signatureTwo := solomachinetypes.SignatureAndData{
199 | Signature: sig,
200 | DataType: solomachinetypes.CONSENSUS,
201 | Data: dataTwo,
202 | }
203 |
204 | return &solomachinetypes.Misbehaviour{
205 | ClientId: solo.ClientID,
206 | Sequence: solo.Sequence,
207 | SignatureOne: &signatureOne,
208 | SignatureTwo: &signatureTwo,
209 | }
210 | }
211 |
212 | // GenerateSignature uses the stored private keys to generate a signature
213 | // over the sign bytes with each key. If the amount of keys is greater than
214 | // 1 then a multisig data type is returned.
215 | func (solo *Solomachine) GenerateSignature(signBytes []byte) []byte {
216 | sigs := make([]signing.SignatureData, len(solo.PrivateKeys))
217 | for i, key := range solo.PrivateKeys {
218 | sig, err := key.Sign(signBytes)
219 | require.NoError(solo.t, err)
220 |
221 | sigs[i] = &signing.SingleSignatureData{
222 | Signature: sig,
223 | }
224 | }
225 |
226 | var sigData signing.SignatureData
227 | if len(sigs) == 1 {
228 | // single public key
229 | sigData = sigs[0]
230 | } else {
231 | // generate multi signature data
232 | multiSigData := multisig.NewMultisig(len(sigs))
233 | for i, sig := range sigs {
234 | multisig.AddSignature(multiSigData, sig, i)
235 | }
236 |
237 | sigData = multiSigData
238 | }
239 |
240 | protoSigData := signing.SignatureDataToProto(sigData)
241 | bz, err := solo.cdc.MarshalBinaryBare(protoSigData)
242 | require.NoError(solo.t, err)
243 |
244 | return bz
245 | }
246 |
247 | // GetClientStatePath returns the commitment path for the client state.
248 | func (solo *Solomachine) GetClientStatePath(counterpartyClientIdentifier string) commitmenttypes.MerklePath {
249 | clientPrefixedPath := "clients/" + counterpartyClientIdentifier + "/" + host.ClientStatePath()
250 | path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath)
251 | require.NoError(solo.t, err)
252 |
253 | return path
254 | }
255 |
256 | // GetConsensusStatePath returns the commitment path for the consensus state.
257 | func (solo *Solomachine) GetConsensusStatePath(counterpartyClientIdentifier string, consensusHeight exported.Height) commitmenttypes.MerklePath {
258 | clientPrefixedPath := "clients/" + counterpartyClientIdentifier + "/" + host.ConsensusStatePath(consensusHeight)
259 | path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath)
260 | require.NoError(solo.t, err)
261 |
262 | return path
263 | }
264 |
265 | // GetConnectionStatePath returns the commitment path for the connection state.
266 | func (solo *Solomachine) GetConnectionStatePath(connID string) commitmenttypes.MerklePath {
267 | path, err := commitmenttypes.ApplyPrefix(prefix, host.ConnectionPath(connID))
268 | require.NoError(solo.t, err)
269 |
270 | return path
271 | }
272 |
273 | // GetChannelStatePath returns the commitment path for that channel state.
274 | func (solo *Solomachine) GetChannelStatePath(portID, channelID string) commitmenttypes.MerklePath {
275 | path, err := commitmenttypes.ApplyPrefix(prefix, host.ChannelPath(portID, channelID))
276 | require.NoError(solo.t, err)
277 |
278 | return path
279 | }
280 |
281 | // GetPacketCommitmentPath returns the commitment path for a packet commitment.
282 | func (solo *Solomachine) GetPacketCommitmentPath(portID, channelID string) commitmenttypes.MerklePath {
283 | path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketCommitmentPath(portID, channelID, solo.Sequence))
284 | require.NoError(solo.t, err)
285 |
286 | return path
287 | }
288 |
289 | // GetPacketAcknowledgementPath returns the commitment path for a packet acknowledgement.
290 | func (solo *Solomachine) GetPacketAcknowledgementPath(portID, channelID string) commitmenttypes.MerklePath {
291 | path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, solo.Sequence))
292 | require.NoError(solo.t, err)
293 |
294 | return path
295 | }
296 |
297 | // GetPacketReceiptPath returns the commitment path for a packet receipt
298 | // and an absent receipts.
299 | func (solo *Solomachine) GetPacketReceiptPath(portID, channelID string) commitmenttypes.MerklePath {
300 | path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketReceiptPath(portID, channelID, solo.Sequence))
301 | require.NoError(solo.t, err)
302 |
303 | return path
304 | }
305 |
306 | // GetNextSequenceRecvPath returns the commitment path for the next sequence recv counter.
307 | func (solo *Solomachine) GetNextSequenceRecvPath(portID, channelID string) commitmenttypes.MerklePath {
308 | path, err := commitmenttypes.ApplyPrefix(prefix, host.NextSequenceRecvPath(portID, channelID))
309 | require.NoError(solo.t, err)
310 |
311 | return path
312 | }
313 |
--------------------------------------------------------------------------------
/x/ibc-account/testing/types.go:
--------------------------------------------------------------------------------
1 | package ibctesting
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // TestConnection is a testing helper struct to keep track of the connectionID, source clientID,
8 | // counterparty clientID, and the next channel version used in creating and interacting with a
9 | // connection.
10 | type TestConnection struct {
11 | ID string
12 | ClientID string
13 | CounterpartyClientID string
14 | NextChannelVersion string
15 | Channels []TestChannel
16 | }
17 |
18 | // AddTestChannel appends a new TestChannel which contains references to the port and channel ID
19 | // used for channel creation and interaction. See 'NextTestChannel' for channel ID naming format.
20 | func (conn *TestConnection) AddTestChannel(portID string) TestChannel {
21 | channel := conn.NextTestChannel(portID)
22 | conn.Channels = append(conn.Channels, channel)
23 | return channel
24 | }
25 |
26 | // NextTestChannel returns the next test channel to be created on this connection, but does not
27 | // add it to the list of created channels. This function is expected to be used when the caller
28 | // has not created the associated channel in app state, but would still like to refer to the
29 | // non-existent channel usually to test for its non-existence.
30 | //
31 | // channel ID format: -chan
32 | //
33 | // The port is passed in by the caller.
34 | func (conn *TestConnection) NextTestChannel(portID string) TestChannel {
35 | channelID := fmt.Sprintf("%s-%s%d", conn.ID, ChannelIDPrefix, len(conn.Channels))
36 | return TestChannel{
37 | PortID: portID,
38 | ID: channelID,
39 | ClientID: conn.ClientID,
40 | CounterpartyClientID: conn.CounterpartyClientID,
41 | Version: conn.NextChannelVersion,
42 | }
43 | }
44 |
45 | // FirstOrNextTestChannel returns the first test channel if it exists, otherwise it
46 | // returns the next test channel to be created. This function is expected to be used
47 | // when the caller does not know if the channel has or has not been created in app
48 | // state, but would still like to refer to it to test existence or non-existence.
49 | func (conn *TestConnection) FirstOrNextTestChannel(portID string) TestChannel {
50 | if len(conn.Channels) > 0 {
51 | return conn.Channels[0]
52 | }
53 | return conn.NextTestChannel(portID)
54 | }
55 |
56 | // TestChannel is a testing helper struct to keep track of the portID and channelID
57 | // used in creating and interacting with a channel. The clientID and counterparty
58 | // client ID are also tracked to cut down on querying and argument passing.
59 | type TestChannel struct {
60 | PortID string
61 | ID string
62 | ClientID string
63 | CounterpartyClientID string
64 | Version string
65 | }
66 |
--------------------------------------------------------------------------------
/x/ibc-account/types/account.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "strings"
8 |
9 | yaml "gopkg.in/yaml.v2"
10 |
11 | sdk "github.com/cosmos/cosmos-sdk/types"
12 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
13 | "github.com/tendermint/tendermint/crypto"
14 | )
15 |
16 | type IBCAccountI interface {
17 | authtypes.AccountI
18 |
19 | GetSourcePort() string
20 | GetSourceChannel() string
21 | GetDestinationPort() string
22 | GetDestinationChannel() string
23 | }
24 |
25 | var (
26 | _ authtypes.GenesisAccount = (*IBCAccount)(nil)
27 | _ IBCAccountI = (*IBCAccount)(nil)
28 | )
29 |
30 | func NewIBCAccount(ba *authtypes.BaseAccount, sourcePort, sourceChannel, destinationPort, destinationChannel string) *IBCAccount {
31 | return &IBCAccount{
32 | BaseAccount: ba,
33 | SourcePort: sourcePort,
34 | SourceChannel: sourceChannel,
35 | DestinationPort: destinationPort,
36 | DestinationChannel: destinationChannel,
37 | }
38 | }
39 |
40 | // SetPubKey - Implements AccountI
41 | func (IBCAccount) SetPubKey(pubKey crypto.PubKey) error {
42 | return fmt.Errorf("not supported for ibc accounts")
43 | }
44 |
45 | // SetSequence - Implements AccountI
46 | func (IBCAccount) SetSequence(seq uint64) error {
47 | return fmt.Errorf("not supported for ibc accounts")
48 | }
49 |
50 | func (ia IBCAccount) GetSourcePort() string {
51 | return ia.SourcePort
52 | }
53 |
54 | func (ia IBCAccount) GetSourceChannel() string {
55 | return ia.SourceChannel
56 | }
57 |
58 | func (ia IBCAccount) GetDestinationPort() string {
59 | return ia.DestinationPort
60 | }
61 |
62 | func (ia IBCAccount) GetDestinationChannel() string {
63 | return ia.DestinationChannel
64 | }
65 |
66 | func (ia IBCAccount) Validate() error {
67 | if strings.TrimSpace(ia.SourcePort) == "" {
68 | return errors.New("ibc account's source port cannot be blank")
69 | }
70 |
71 | if strings.TrimSpace(ia.SourceChannel) == "" {
72 | return errors.New("ibc account's source channel cannot be blank")
73 | }
74 |
75 | if strings.TrimSpace(ia.DestinationPort) == "" {
76 | return errors.New("ibc account's destination port cannot be blank")
77 | }
78 |
79 | if strings.TrimSpace(ia.DestinationChannel) == "" {
80 | return errors.New("ibc account's destination channel cannot be blank")
81 | }
82 |
83 | return ia.BaseAccount.Validate()
84 | }
85 |
86 | type ibcAccountPretty struct {
87 | Address sdk.AccAddress `json:"address" yaml:"address"`
88 | PubKey string `json:"public_key" yaml:"public_key"`
89 | AccountNumber uint64 `json:"account_number" yaml:"account_number"`
90 | Sequence uint64 `json:"sequence" yaml:"sequence"`
91 | SourcePort string `json:"source_port" yaml:"source_port"`
92 | SourceChannel string `json:"source_channel" yaml:"source_channel"`
93 | DestinationPort string `json:"destination_port" yaml:"destination_port"`
94 | DestinationChannel string `json:"destination_channel" yaml:"destination_channel"`
95 | }
96 |
97 | func (ia IBCAccount) String() string {
98 | out, _ := ia.MarshalYAML()
99 | return out.(string)
100 | }
101 |
102 | // MarshalYAML returns the YAML representation of a IBCAccount.
103 | func (ia IBCAccount) MarshalYAML() (interface{}, error) {
104 | accAddr, err := sdk.AccAddressFromBech32(ia.Address)
105 | if err != nil {
106 | return nil, err
107 | }
108 |
109 | bs, err := yaml.Marshal(ibcAccountPretty{
110 | Address: accAddr,
111 | PubKey: "",
112 | AccountNumber: ia.AccountNumber,
113 | Sequence: ia.Sequence,
114 | SourcePort: ia.SourcePort,
115 | SourceChannel: ia.SourceChannel,
116 | DestinationPort: ia.DestinationPort,
117 | DestinationChannel: ia.DestinationChannel,
118 | })
119 |
120 | if err != nil {
121 | return nil, err
122 | }
123 |
124 | return string(bs), nil
125 | }
126 |
127 | // MarshalJSON returns the JSON representation of a IBCAccount.
128 | func (ia IBCAccount) MarshalJSON() ([]byte, error) {
129 | accAddr, err := sdk.AccAddressFromBech32(ia.Address)
130 | if err != nil {
131 | return nil, err
132 | }
133 |
134 | return json.Marshal(ibcAccountPretty{
135 | Address: accAddr,
136 | PubKey: "",
137 | AccountNumber: ia.AccountNumber,
138 | Sequence: ia.Sequence,
139 | SourcePort: ia.SourcePort,
140 | SourceChannel: ia.SourceChannel,
141 | DestinationPort: ia.DestinationPort,
142 | DestinationChannel: ia.DestinationChannel,
143 | })
144 | }
145 |
146 | // UnmarshalJSON unmarshals raw JSON bytes into a ModuleAccount.
147 | func (ia *IBCAccount) UnmarshalJSON(bz []byte) error {
148 | var alias ibcAccountPretty
149 | if err := json.Unmarshal(bz, &alias); err != nil {
150 | return err
151 | }
152 |
153 | ia.BaseAccount = authtypes.NewBaseAccount(alias.Address, nil, alias.AccountNumber, alias.Sequence)
154 | ia.SourcePort = alias.SourcePort
155 | ia.SourceChannel = alias.SourceChannel
156 | ia.DestinationPort = alias.DestinationPort
157 | ia.DestinationChannel = alias.DestinationChannel
158 |
159 | return nil
160 | }
161 |
--------------------------------------------------------------------------------
/x/ibc-account/types/account_test.go:
--------------------------------------------------------------------------------
1 | package types_test
2 |
3 | import (
4 | "encoding/json"
5 | "testing"
6 |
7 | yaml "gopkg.in/yaml.v2"
8 |
9 | "github.com/chainapsis/cosmos-sdk-interchain-account/x/ibc-account/types"
10 | "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
11 | sdk "github.com/cosmos/cosmos-sdk/types"
12 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
13 | "github.com/stretchr/testify/require"
14 | )
15 |
16 | func TestIBCAccountMarshal(t *testing.T) {
17 | pubkey := secp256k1.GenPrivKey().PubKey()
18 | addr := sdk.AccAddress(pubkey.Address())
19 | baseAcc := authtypes.NewBaseAccountWithAddress(addr)
20 | ibcAcc := types.NewIBCAccount(baseAcc, "sp", "sc", "dp", "dc")
21 |
22 | // Can't set the seq or pub key on the IBC account
23 | err := ibcAcc.SetPubKey(pubkey)
24 | require.Error(t, err)
25 | err = ibcAcc.SetSequence(1)
26 | require.Error(t, err)
27 |
28 | bz, err := app.AccountKeeper.MarshalAccount(ibcAcc)
29 | require.NoError(t, err)
30 |
31 | ibcAcc2, err := app.AccountKeeper.UnmarshalAccount(bz)
32 | require.NoError(t, err)
33 | require.Equal(t, ibcAcc.String(), ibcAcc2.String())
34 |
35 | // error on bad bytes
36 | _, err = app.AccountKeeper.UnmarshalAccount(bz[:len(bz)/2])
37 | require.Error(t, err)
38 | }
39 |
40 | func TestGenesisAccountValidate(t *testing.T) {
41 | pubkey := secp256k1.GenPrivKey().PubKey()
42 | addr := sdk.AccAddress(pubkey.Address())
43 | baseAcc := authtypes.NewBaseAccountWithAddress(addr)
44 |
45 | tests := []struct {
46 | name string
47 | acc authtypes.GenesisAccount
48 | expErr bool
49 | }{
50 | {
51 | "valid ibc account",
52 | types.NewIBCAccount(baseAcc, "sp", "sc", "dp", "dc"),
53 | false,
54 | },
55 | {
56 | "invalid ibc account that has empty field",
57 | types.NewIBCAccount(baseAcc, "", "sc", "dp", "dc"),
58 | true,
59 | },
60 | {
61 | "invalid ibc account that has empty field",
62 | types.NewIBCAccount(baseAcc, "sp", "", "dp", "dc"),
63 | true,
64 | },
65 | {
66 | "invalid ibc account that has empty field",
67 | types.NewIBCAccount(baseAcc, "sp", "sc", "", "dc"),
68 | true,
69 | },
70 | {
71 | "invalid ibc account that has empty field",
72 | types.NewIBCAccount(baseAcc, "sp", "sc", "dp", ""),
73 | true,
74 | },
75 | }
76 |
77 | for _, tt := range tests {
78 | tt := tt
79 |
80 | t.Run(tt.name, func(t *testing.T) {
81 | require.Equal(t, tt.expErr, tt.acc.Validate() != nil)
82 | })
83 | }
84 | }
85 |
86 | func TestIBCAccountMarshalYAML(t *testing.T) {
87 | addr, err := sdk.AccAddressFromHex("0000000000000000000000000000000000000000")
88 | require.NoError(t, err)
89 | ba := authtypes.NewBaseAccountWithAddress(addr)
90 |
91 | ibcAcc := types.NewIBCAccount(ba, "sp", "sc", "dp", "dc")
92 |
93 | bs, err := yaml.Marshal(ibcAcc)
94 | require.NoError(t, err)
95 |
96 | want := "|\n address: cosmos1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnrql8a\n public_key: \"\"\n account_number: 0\n sequence: 0\n source_port: sp\n source_channel: sc\n destination_port: dp\n destination_channel: dc\n"
97 | require.Equal(t, want, string(bs))
98 | }
99 |
100 | func TestIBCAccountJSON(t *testing.T) {
101 | pubkey := secp256k1.GenPrivKey().PubKey()
102 | addr := sdk.AccAddress(pubkey.Address())
103 | baseAcc := authtypes.NewBaseAccountWithAddress(addr)
104 |
105 | ibcAcc := types.NewIBCAccount(baseAcc, "sp", "sc", "dp", "dc")
106 |
107 | bz, err := json.Marshal(ibcAcc)
108 | require.NoError(t, err)
109 |
110 | bz1, err := ibcAcc.MarshalJSON()
111 | require.NoError(t, err)
112 | require.Equal(t, string(bz), string(bz1))
113 |
114 | var a types.IBCAccount
115 | require.NoError(t, json.Unmarshal(bz, &a))
116 | require.Equal(t, ibcAcc.String(), a.String())
117 | }
118 |
--------------------------------------------------------------------------------
/x/ibc-account/types/codec.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/codec"
5 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
6 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
7 | )
8 |
9 | // RegisterLegacyAminoCodec registers the account interfaces and concrete types on the
10 | // provided LegacyAmino codec. These types are used for Amino JSON serialization
11 | func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
12 | cdc.RegisterInterface((*IBCAccountI)(nil), nil)
13 | cdc.RegisterConcrete(&IBCAccount{}, "ibc-account/IBCAccount", nil)
14 | }
15 |
16 | // RegisterInterface associates protoName with AccountI interface
17 | // and creates a registry of it's concrete implementations
18 | func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
19 | registry.RegisterImplementations((*authtypes.AccountI)(nil), &IBCAccount{})
20 | }
21 |
22 | var (
23 | ModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
24 | )
25 |
--------------------------------------------------------------------------------
/x/ibc-account/types/common_test.go:
--------------------------------------------------------------------------------
1 | package types_test
2 |
3 | import (
4 | "github.com/chainapsis/cosmos-sdk-interchain-account/simapp"
5 | )
6 |
7 | var (
8 | app = simapp.Setup(false)
9 | appCodec, legacyAmino = simapp.MakeCodecs()
10 | )
11 |
--------------------------------------------------------------------------------
/x/ibc-account/types/encoder.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | type TxEncoder func(data interface{}) ([]byte, error)
4 |
--------------------------------------------------------------------------------
/x/ibc-account/types/errors.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
5 | )
6 |
7 | var (
8 | ErrUnknownPacketData = sdkerrors.Register(ModuleName, 1, "unknown packet data")
9 | ErrAccountAlreadyExist = sdkerrors.Register(ModuleName, 2, "account already exist")
10 | ErrUnsupportedChain = sdkerrors.Register(ModuleName, 3, "unsupported chain")
11 | ErrInvalidOutgoingData = sdkerrors.Register(ModuleName, 4, "invalid outgoing data")
12 | ErrInvalidRoute = sdkerrors.Register(ModuleName, 5, "invalid route")
13 | ErrTxEncoderAlreadyRegistered = sdkerrors.Register(ModuleName, 6, "tx encoder already registered")
14 | ErrIBCAccountNotFound = sdkerrors.Register(ModuleName, 7, "ibc account not found")
15 | )
16 |
--------------------------------------------------------------------------------
/x/ibc-account/types/expected_keepers.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
6 | capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
7 | connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types"
8 | channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"
9 | ibcexported "github.com/cosmos/cosmos-sdk/x/ibc/core/exported"
10 | )
11 |
12 | type Router interface {
13 | Route(ctx sdk.Context, path string) sdk.Handler
14 | }
15 |
16 | // AccountKeeper defines the contract required for account APIs.
17 | type AccountKeeper interface {
18 | SetAccount(ctx sdk.Context, acc authtypes.AccountI)
19 | GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
20 | NewAccount(ctx sdk.Context, acc authtypes.AccountI) authtypes.AccountI
21 | NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
22 | }
23 |
24 | // ChannelKeeper defines the expected IBC channel keeper
25 | type ChannelKeeper interface {
26 | GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool)
27 | GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool)
28 | SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error
29 | ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error
30 | }
31 |
32 | // ClientKeeper defines the expected IBC client keeper
33 | type ClientKeeper interface {
34 | GetClientState(ctx sdk.Context, clientID string) (ibcexported.ClientState, bool)
35 | }
36 |
37 | // ConnectionKeeper defines the expected IBC connection keeper
38 | type ConnectionKeeper interface {
39 | GetConnection(ctx sdk.Context, connectionID string) (connection connectiontypes.ConnectionEnd, found bool)
40 | }
41 |
42 | // PortKeeper defines the expected IBC port keeper
43 | type PortKeeper interface {
44 | BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability
45 | }
46 |
--------------------------------------------------------------------------------
/x/ibc-account/types/genesis.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | func DefaultGenesis() *GenesisState {
4 | return &GenesisState{
5 | PortId: PortID,
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/x/ibc-account/types/genesis.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-gogo. DO NOT EDIT.
2 | // source: ibc/account/genesis.proto
3 |
4 | package types
5 |
6 | import (
7 | fmt "fmt"
8 | _ "github.com/gogo/protobuf/gogoproto"
9 | proto "github.com/gogo/protobuf/proto"
10 | io "io"
11 | math "math"
12 | math_bits "math/bits"
13 | )
14 |
15 | // Reference imports to suppress errors if they are not otherwise used.
16 | var _ = proto.Marshal
17 | var _ = fmt.Errorf
18 | var _ = math.Inf
19 |
20 | // This is a compile-time assertion to ensure that this generated file
21 | // is compatible with the proto package it is being compiled against.
22 | // A compilation error at this line likely means your copy of the
23 | // proto package needs to be updated.
24 | const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
25 |
26 | // GenesisState defines the ibc-account genesis state
27 | type GenesisState struct {
28 | PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"`
29 | }
30 |
31 | func (m *GenesisState) Reset() { *m = GenesisState{} }
32 | func (m *GenesisState) String() string { return proto.CompactTextString(m) }
33 | func (*GenesisState) ProtoMessage() {}
34 | func (*GenesisState) Descriptor() ([]byte, []int) {
35 | return fileDescriptor_c5549b69e3eeb3e4, []int{0}
36 | }
37 | func (m *GenesisState) XXX_Unmarshal(b []byte) error {
38 | return m.Unmarshal(b)
39 | }
40 | func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
41 | if deterministic {
42 | return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic)
43 | } else {
44 | b = b[:cap(b)]
45 | n, err := m.MarshalToSizedBuffer(b)
46 | if err != nil {
47 | return nil, err
48 | }
49 | return b[:n], nil
50 | }
51 | }
52 | func (m *GenesisState) XXX_Merge(src proto.Message) {
53 | xxx_messageInfo_GenesisState.Merge(m, src)
54 | }
55 | func (m *GenesisState) XXX_Size() int {
56 | return m.Size()
57 | }
58 | func (m *GenesisState) XXX_DiscardUnknown() {
59 | xxx_messageInfo_GenesisState.DiscardUnknown(m)
60 | }
61 |
62 | var xxx_messageInfo_GenesisState proto.InternalMessageInfo
63 |
64 | func (m *GenesisState) GetPortId() string {
65 | if m != nil {
66 | return m.PortId
67 | }
68 | return ""
69 | }
70 |
71 | func init() {
72 | proto.RegisterType((*GenesisState)(nil), "ibc.account.GenesisState")
73 | }
74 |
75 | func init() { proto.RegisterFile("ibc/account/genesis.proto", fileDescriptor_c5549b69e3eeb3e4) }
76 |
77 | var fileDescriptor_c5549b69e3eeb3e4 = []byte{
78 | // 211 bytes of a gzipped FileDescriptorProto
79 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcc, 0x4c, 0x4a, 0xd6,
80 | 0x4f, 0x4c, 0x4e, 0xce, 0x2f, 0xcd, 0x2b, 0xd1, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6,
81 | 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xce, 0x4c, 0x4a, 0xd6, 0x83, 0x4a, 0x49, 0x89, 0xa4,
82 | 0xe7, 0xa7, 0xe7, 0x83, 0xc5, 0xf5, 0x41, 0x2c, 0x88, 0x12, 0x25, 0x6b, 0x2e, 0x1e, 0x77, 0x88,
83 | 0x9e, 0xe0, 0x92, 0xc4, 0x92, 0x54, 0x21, 0x6d, 0x2e, 0xf6, 0x82, 0xfc, 0xa2, 0x92, 0xf8, 0xcc,
84 | 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0xa1, 0x4f, 0xf7, 0xe4, 0xf9, 0x2a, 0x13, 0x73,
85 | 0x73, 0xac, 0x94, 0xa0, 0x12, 0x4a, 0x41, 0x6c, 0x20, 0x96, 0x67, 0x8a, 0x53, 0xe2, 0x89, 0x47,
86 | 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85,
87 | 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0xb9, 0xa7, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9,
88 | 0x25, 0xe7, 0xe7, 0xea, 0x27, 0x67, 0x24, 0x66, 0xe6, 0x25, 0x16, 0x14, 0x67, 0x16, 0xeb, 0x27,
89 | 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0xeb, 0x16, 0xa7, 0x64, 0xeb, 0x66, 0xe6, 0x95, 0xa4, 0x16, 0x81,
90 | 0xa5, 0x74, 0x61, 0x8e, 0xaf, 0xd0, 0xcf, 0x4c, 0x4a, 0x86, 0xf3, 0x4a, 0x2a, 0x0b, 0x52, 0x8b,
91 | 0x93, 0xd8, 0xc0, 0xce, 0x34, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x84, 0x8e, 0x9c, 0xab, 0xe6,
92 | 0x00, 0x00, 0x00,
93 | }
94 |
95 | func (m *GenesisState) Marshal() (dAtA []byte, err error) {
96 | size := m.Size()
97 | dAtA = make([]byte, size)
98 | n, err := m.MarshalToSizedBuffer(dAtA[:size])
99 | if err != nil {
100 | return nil, err
101 | }
102 | return dAtA[:n], nil
103 | }
104 |
105 | func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) {
106 | size := m.Size()
107 | return m.MarshalToSizedBuffer(dAtA[:size])
108 | }
109 |
110 | func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
111 | i := len(dAtA)
112 | _ = i
113 | var l int
114 | _ = l
115 | if len(m.PortId) > 0 {
116 | i -= len(m.PortId)
117 | copy(dAtA[i:], m.PortId)
118 | i = encodeVarintGenesis(dAtA, i, uint64(len(m.PortId)))
119 | i--
120 | dAtA[i] = 0xa
121 | }
122 | return len(dAtA) - i, nil
123 | }
124 |
125 | func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int {
126 | offset -= sovGenesis(v)
127 | base := offset
128 | for v >= 1<<7 {
129 | dAtA[offset] = uint8(v&0x7f | 0x80)
130 | v >>= 7
131 | offset++
132 | }
133 | dAtA[offset] = uint8(v)
134 | return base
135 | }
136 | func (m *GenesisState) Size() (n int) {
137 | if m == nil {
138 | return 0
139 | }
140 | var l int
141 | _ = l
142 | l = len(m.PortId)
143 | if l > 0 {
144 | n += 1 + l + sovGenesis(uint64(l))
145 | }
146 | return n
147 | }
148 |
149 | func sovGenesis(x uint64) (n int) {
150 | return (math_bits.Len64(x|1) + 6) / 7
151 | }
152 | func sozGenesis(x uint64) (n int) {
153 | return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63))))
154 | }
155 | func (m *GenesisState) Unmarshal(dAtA []byte) error {
156 | l := len(dAtA)
157 | iNdEx := 0
158 | for iNdEx < l {
159 | preIndex := iNdEx
160 | var wire uint64
161 | for shift := uint(0); ; shift += 7 {
162 | if shift >= 64 {
163 | return ErrIntOverflowGenesis
164 | }
165 | if iNdEx >= l {
166 | return io.ErrUnexpectedEOF
167 | }
168 | b := dAtA[iNdEx]
169 | iNdEx++
170 | wire |= uint64(b&0x7F) << shift
171 | if b < 0x80 {
172 | break
173 | }
174 | }
175 | fieldNum := int32(wire >> 3)
176 | wireType := int(wire & 0x7)
177 | if wireType == 4 {
178 | return fmt.Errorf("proto: GenesisState: wiretype end group for non-group")
179 | }
180 | if fieldNum <= 0 {
181 | return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire)
182 | }
183 | switch fieldNum {
184 | case 1:
185 | if wireType != 2 {
186 | return fmt.Errorf("proto: wrong wireType = %d for field PortId", wireType)
187 | }
188 | var stringLen uint64
189 | for shift := uint(0); ; shift += 7 {
190 | if shift >= 64 {
191 | return ErrIntOverflowGenesis
192 | }
193 | if iNdEx >= l {
194 | return io.ErrUnexpectedEOF
195 | }
196 | b := dAtA[iNdEx]
197 | iNdEx++
198 | stringLen |= uint64(b&0x7F) << shift
199 | if b < 0x80 {
200 | break
201 | }
202 | }
203 | intStringLen := int(stringLen)
204 | if intStringLen < 0 {
205 | return ErrInvalidLengthGenesis
206 | }
207 | postIndex := iNdEx + intStringLen
208 | if postIndex < 0 {
209 | return ErrInvalidLengthGenesis
210 | }
211 | if postIndex > l {
212 | return io.ErrUnexpectedEOF
213 | }
214 | m.PortId = string(dAtA[iNdEx:postIndex])
215 | iNdEx = postIndex
216 | default:
217 | iNdEx = preIndex
218 | skippy, err := skipGenesis(dAtA[iNdEx:])
219 | if err != nil {
220 | return err
221 | }
222 | if skippy < 0 {
223 | return ErrInvalidLengthGenesis
224 | }
225 | if (iNdEx + skippy) < 0 {
226 | return ErrInvalidLengthGenesis
227 | }
228 | if (iNdEx + skippy) > l {
229 | return io.ErrUnexpectedEOF
230 | }
231 | iNdEx += skippy
232 | }
233 | }
234 |
235 | if iNdEx > l {
236 | return io.ErrUnexpectedEOF
237 | }
238 | return nil
239 | }
240 | func skipGenesis(dAtA []byte) (n int, err error) {
241 | l := len(dAtA)
242 | iNdEx := 0
243 | depth := 0
244 | for iNdEx < l {
245 | var wire uint64
246 | for shift := uint(0); ; shift += 7 {
247 | if shift >= 64 {
248 | return 0, ErrIntOverflowGenesis
249 | }
250 | if iNdEx >= l {
251 | return 0, io.ErrUnexpectedEOF
252 | }
253 | b := dAtA[iNdEx]
254 | iNdEx++
255 | wire |= (uint64(b) & 0x7F) << shift
256 | if b < 0x80 {
257 | break
258 | }
259 | }
260 | wireType := int(wire & 0x7)
261 | switch wireType {
262 | case 0:
263 | for shift := uint(0); ; shift += 7 {
264 | if shift >= 64 {
265 | return 0, ErrIntOverflowGenesis
266 | }
267 | if iNdEx >= l {
268 | return 0, io.ErrUnexpectedEOF
269 | }
270 | iNdEx++
271 | if dAtA[iNdEx-1] < 0x80 {
272 | break
273 | }
274 | }
275 | case 1:
276 | iNdEx += 8
277 | case 2:
278 | var length int
279 | for shift := uint(0); ; shift += 7 {
280 | if shift >= 64 {
281 | return 0, ErrIntOverflowGenesis
282 | }
283 | if iNdEx >= l {
284 | return 0, io.ErrUnexpectedEOF
285 | }
286 | b := dAtA[iNdEx]
287 | iNdEx++
288 | length |= (int(b) & 0x7F) << shift
289 | if b < 0x80 {
290 | break
291 | }
292 | }
293 | if length < 0 {
294 | return 0, ErrInvalidLengthGenesis
295 | }
296 | iNdEx += length
297 | case 3:
298 | depth++
299 | case 4:
300 | if depth == 0 {
301 | return 0, ErrUnexpectedEndOfGroupGenesis
302 | }
303 | depth--
304 | case 5:
305 | iNdEx += 4
306 | default:
307 | return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
308 | }
309 | if iNdEx < 0 {
310 | return 0, ErrInvalidLengthGenesis
311 | }
312 | if depth == 0 {
313 | return iNdEx, nil
314 | }
315 | }
316 | return 0, io.ErrUnexpectedEOF
317 | }
318 |
319 | var (
320 | ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling")
321 | ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow")
322 | ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group")
323 | )
324 |
--------------------------------------------------------------------------------
/x/ibc-account/types/hook.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import sdk "github.com/cosmos/cosmos-sdk/types"
4 |
5 | type IBCAccountHooks interface {
6 | OnAccountCreated(ctx sdk.Context, sourcePort, sourceChannel string, address sdk.AccAddress)
7 | OnTxSucceeded(ctx sdk.Context, sourcePort, sourceChannel string, txHash []byte, txBytes []byte)
8 | OnTxFailed(ctx sdk.Context, sourcePort, sourceChannel string, txHash []byte, txBytes []byte)
9 | }
10 |
11 | type MultiIBCAccountHooks []IBCAccountHooks
12 |
13 | var (
14 | _ IBCAccountHooks = MultiIBCAccountHooks{}
15 | )
16 |
17 | func NewMultiIBCAccountHooks(hooks ...IBCAccountHooks) MultiIBCAccountHooks {
18 | return hooks
19 | }
20 |
21 | func (h MultiIBCAccountHooks) OnAccountCreated(ctx sdk.Context, sourcePort, sourceChannel string, address sdk.AccAddress) {
22 | for i := range h {
23 | h[i].OnAccountCreated(ctx, sourcePort, sourceChannel, address)
24 | }
25 | }
26 |
27 | func (h MultiIBCAccountHooks) OnTxSucceeded(ctx sdk.Context, sourcePort, sourceChannel string, txHash []byte, txBytes []byte) {
28 | for i := range h {
29 | h[i].OnTxSucceeded(ctx, sourcePort, sourceChannel, txHash, txBytes)
30 | }
31 | }
32 |
33 | func (h MultiIBCAccountHooks) OnTxFailed(ctx sdk.Context, sourcePort, sourceChannel string, txHash []byte, txBytes []byte) {
34 | for i := range h {
35 | h[i].OnTxFailed(ctx, sourcePort, sourceChannel, txHash, txBytes)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/x/ibc-account/types/keys.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import "fmt"
4 |
5 | const (
6 | // ModuleName defines the IBC transfer name
7 | ModuleName = "ibcaccount"
8 |
9 | // Version defines the current version the IBC tranfer
10 | // module supports
11 | Version = "ics27-1"
12 |
13 | PortID = "ibcaccount"
14 |
15 | StoreKey = ModuleName
16 | RouterKey = ModuleName
17 |
18 | // Key to store portID in our store
19 | PortKey = "portID"
20 |
21 | QuerierRoute = ModuleName
22 | )
23 |
24 | var (
25 | KeyPrefixRegisteredAccount = []byte("register")
26 | )
27 |
28 | func GetIdentifier(portID, channelID string) string {
29 | return fmt.Sprintf("%s/%s/", portID, channelID)
30 | }
31 |
--------------------------------------------------------------------------------
/x/ibc-account/types/packet.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 | )
6 |
7 | func (packet IBCAccountPacketData) GetBytes() []byte {
8 | return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&packet))
9 | }
10 |
11 | func (ack IBCAccountPacketAcknowledgement) GetBytes() []byte {
12 | return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&ack))
13 | }
14 |
--------------------------------------------------------------------------------
/x/ibc-account/types/querier.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | // query endpoints supported by the auth Querier
4 | const (
5 | QueryIBCAccount = "ibcaccount"
6 | QueryIBCAccountFromData = "ibcaccount-from-data"
7 | )
8 |
--------------------------------------------------------------------------------