├── .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 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/.vuepress/public/logo-bw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 24 | 25 | 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 | --------------------------------------------------------------------------------