diff --git a/changelog.md b/changelog.md index d46ab575da..6a33895cf9 100644 --- a/changelog.md +++ b/changelog.md @@ -8,10 +8,12 @@ - [#3770](https://github.com/ignite/cli/pull/3770) Add `scaffold configs` and `scaffold params` commands - [#3985](https://github.com/ignite/cli/pull/3985) Make some `cmd` pkg functions public - [#3967](https://github.com/ignite/cli/issues/3967) Add HD wallet parameters `address index` and `account number` to the chain account config +- [#3660](https://github.com/ignite/cli/pull/3660) Add ability to scaffold ICS consumer chain - [#4004](https://github.com/ignite/cli/pull/4004) Remove all import placeholders using the `xast` pkg ### Changes +- [#4013](https://github.com/ignite/cli/pull/4013) Bump `cosmos-sdk` to `v0.50.5` - [#3993](https://github.com/ignite/cli/pull/3993) Oracle scaffolding was deprecated and has been removed - [#3959](https://github.com/ignite/cli/pull/3959) Remove app name prefix from the `.gitignore` file - [#3962](https://github.com/ignite/cli/pull/3962) Rename all RPC endpoints and autocli commands generated for `map`/`list`/`single` types diff --git a/docs/docs/08-references/02-config.md b/docs/docs/08-references/02-config.md index 1eb4aa7f8f..34347b9565 100644 --- a/docs/docs/08-references/02-config.md +++ b/docs/docs/08-references/02-config.md @@ -12,6 +12,26 @@ to describe the development environment for your blockchain. Only a default set of parameters is provided. If more nuanced configuration is required, you can add these parameters to the `config.yml` file. +## Validation + +Ignite uses the `validation` field to determine the kind of validation +of your blockchain. There are currently two supported kinds of validation: + +- `sovereign` which is the standard kind of validation where your blockchain + has its own validator set. This is the default value when this field is not + in the config file. +- `consumer` indicates your blockchain is a consumer chain, in the sense of + Replicated Security. That means it doesn't have a validator set, but + inherits the one of a provider chain. + +While the `sovereign` chain is the default validation when you run the `ignite scaffold +chain`, to scaffold a consumer chain, you have to run `ignite scaffold chain +--consumer`. + +This field is, at this time of writing, only used by Ignite at the genesis +generation step, because the genesis of a sovereign chain and a consumer chain +are different. + ## Accounts A list of user accounts created during genesis of the blockchain. diff --git a/go.mod b/go.mod index 631bb1969c..53dcd59a5b 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ replace ( ) require ( - cosmossdk.io/math v1.2.0 + cosmossdk.io/math v1.3.0 cosmossdk.io/x/evidence v0.1.0 cosmossdk.io/x/feegrant v0.1.0 cosmossdk.io/x/upgrade v0.1.0 @@ -33,7 +33,7 @@ require ( github.com/charmbracelet/lipgloss v0.6.0 github.com/cockroachdb/errors v1.11.1 github.com/cometbft/cometbft v0.38.5 - github.com/cosmos/cosmos-sdk v0.50.4 + github.com/cosmos/cosmos-sdk v0.50.5 github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/gogoproto v1.4.11 github.com/cosmos/ibc-go/modules/capability v1.0.0 @@ -73,19 +73,19 @@ require ( github.com/rs/cors v1.10.1 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/tbruyelle/mdgofmt v0.1.3 github.com/vektra/mockery/v2 v2.36.1 go.etcd.io/bbolt v1.3.8 - golang.org/x/exp v0.0.0-20240213143201-ec583247a57a + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 golang.org/x/mod v0.15.0 golang.org/x/sync v0.6.0 golang.org/x/term v0.17.0 golang.org/x/text v0.14.0 golang.org/x/tools v0.18.0 golang.org/x/vuln v1.0.1 - google.golang.org/grpc v1.60.1 - google.golang.org/protobuf v1.32.0 + google.golang.org/grpc v1.62.0 + google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v2 v2.4.0 mvdan.cc/gofumpt v0.5.0 sigs.k8s.io/yaml v1.4.0 @@ -104,7 +104,7 @@ require ( cosmossdk.io/errors v1.0.1 // indirect cosmossdk.io/log v1.3.1 // indirect cosmossdk.io/store v1.0.2 // indirect - cosmossdk.io/x/tx v0.13.0 // indirect + cosmossdk.io/x/tx v0.13.1 // indirect dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/4meepo/tagalign v1.3.3 // indirect @@ -176,7 +176,7 @@ require ( github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect github.com/cosiner/argv v0.1.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-db v1.0.0 // indirect + github.com/cosmos/cosmos-db v1.0.2 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.4 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v1.0.1 // indirect @@ -254,7 +254,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.1.0 // indirect github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect @@ -272,7 +272,7 @@ require ( github.com/google/go-dap v0.9.1 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 // indirect - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect @@ -315,7 +315,7 @@ require ( github.com/kisielk/errcheck v1.6.3 // indirect github.com/kisielk/gotool v1.0.0 // indirect github.com/kkHAIKE/contextcheck v1.1.4 // indirect - github.com/klauspost/compress v1.17.6 // indirect + github.com/klauspost/compress v1.17.7 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -418,7 +418,7 @@ require ( github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect @@ -464,9 +464,9 @@ require ( golang.org/x/net v0.21.0 // indirect golang.org/x/sys v0.17.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index d1f67502b6..e2603dbfc6 100644 --- a/go.sum +++ b/go.sum @@ -19,23 +19,23 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM= -cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -65,8 +65,8 @@ cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= -cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= -cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= +cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= +cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= cosmossdk.io/store v1.0.2 h1:lSg5BTvJBHUDwswNNyeh4K/CbqiHER73VU4nDNb8uk0= cosmossdk.io/store v1.0.2/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= cosmossdk.io/x/circuit v0.1.0 h1:IAej8aRYeuOMritczqTlljbUVHq1E85CpBqaCTwYgXs= @@ -75,8 +75,8 @@ cosmossdk.io/x/evidence v0.1.0 h1:J6OEyDl1rbykksdGynzPKG5R/zm6TacwW2fbLTW4nCk= cosmossdk.io/x/evidence v0.1.0/go.mod h1:hTaiiXsoiJ3InMz1uptgF0BnGqROllAN8mwisOMMsfw= cosmossdk.io/x/feegrant v0.1.0 h1:c7s3oAq/8/UO0EiN1H5BIjwVntujVTkYs35YPvvrdQk= cosmossdk.io/x/feegrant v0.1.0/go.mod h1:4r+FsViJRpcZif/yhTn+E0E6OFfg4n0Lx+6cCtnZElU= -cosmossdk.io/x/tx v0.13.0 h1:8lzyOh3zONPpZv2uTcUmsv0WTXy6T1/aCVDCqShmpzU= -cosmossdk.io/x/tx v0.13.0/go.mod h1:CpNQtmoqbXa33/DVxWQNx5Dcnbkv2xGUhL7tYQ5wUsY= +cosmossdk.io/x/tx v0.13.1 h1:Mg+EMp67Pz+NukbJqYxuo8uRp7N/a9uR+oVS9pONtj8= +cosmossdk.io/x/tx v0.13.1/go.mod h1:CBCU6fsRVz23QGFIQBb1DNX2DztJCf3jWyEkHY2nJQ0= cosmossdk.io/x/upgrade v0.1.0 h1:z1ZZG4UL9ICTNbJDYZ6jOnF9GdEK9wyoEFi4BUScHXE= cosmossdk.io/x/upgrade v0.1.0/go.mod h1:/6jjNGbiPCNtmA1N+rBtP601sr0g4ZXuj3yC6ClPCGY= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= @@ -354,12 +354,12 @@ github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg= github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= -github.com/cosmos/cosmos-db v1.0.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0E= -github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= +github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= github.com/cosmos/cosmos-proto v1.0.0-beta.4 h1:aEL7tU/rLOmxZQ9z4i7mzxcLbSCY48OdY7lIWTLG7oU= github.com/cosmos/cosmos-proto v1.0.0-beta.4/go.mod h1:oeB+FyVzG3XrQJbJng0EnV8Vljfk9XvTIpGILNU/9Co= -github.com/cosmos/cosmos-sdk v0.50.4 h1:hQT5/+Z1XXNF7skaPq0i247Ts2dzzqg/j2WO/BPHSto= -github.com/cosmos/cosmos-sdk v0.50.4/go.mod h1:UbShFs6P8Ly29xxJvkNGaNaL/UGj5a686NRtb1Cqra0= +github.com/cosmos/cosmos-sdk v0.50.5 h1:MOEi+DKYgW67YaPgB+Pf+nHbD3V9S/ayitRKJYLfGIA= +github.com/cosmos/cosmos-sdk v0.50.5/go.mod h1:oV/k6GJgXV9QPoM2fsYDPPsyPBgQbdotv532O6Mz1OQ= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= @@ -677,8 +677,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -760,8 +760,8 @@ github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -964,8 +964,8 @@ github.com/kkHAIKE/contextcheck v1.1.4/go.mod h1:1+i/gWqokIa+dm31mqGLZhZJ7Uh44DJ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= -github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -1415,8 +1415,9 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1427,8 +1428,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= @@ -1611,8 +1613,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 h1:jWGQJV4niP+CCmFW9ekjA9Zx8vYORzOUH2/Nl5WPuLQ= @@ -2002,12 +2004,12 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= -google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s= -google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= +google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 h1:x9PwdEgd11LgK+orcck69WVRo7DezSO4VUMPI4xpc8A= +google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -2030,8 +2032,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= +google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2046,8 +2048,9 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/ignite/cmd/scaffold_chain.go b/ignite/cmd/scaffold_chain.go index b2deb9ec6d..5d90ce6a07 100644 --- a/ignite/cmd/scaffold_chain.go +++ b/ignite/cmd/scaffold_chain.go @@ -14,6 +14,7 @@ const ( flagMinimal = "minimal" flagNoDefaultModule = "no-module" flagSkipGit = "skip-git" + flagIsConsumer = "consumer" tplScaffoldChainSuccess = ` ⭐️ Successfully created a new blockchain '%[1]v'. @@ -84,6 +85,9 @@ about Cosmos SDK on https://docs.cosmos.network c.Flags().StringSlice(flagModuleConfigs, []string{}, "add module configs") c.Flags().Bool(flagSkipGit, false, "skip Git repository initialization") c.Flags().Bool(flagMinimal, false, "create a minimal blockchain (with the minimum required Cosmos SDK modules)") + c.Flags().Bool(flagIsConsumer, false, "scafffold an ICS consumer chain") + // Cannot have both minimal and consumer flag + c.MarkFlagsMutuallyExclusive(flagIsConsumer, flagMinimal) return c } @@ -99,6 +103,7 @@ func scaffoldChainHandler(cmd *cobra.Command, args []string) error { noDefaultModule, _ = cmd.Flags().GetBool(flagNoDefaultModule) skipGit, _ = cmd.Flags().GetBool(flagSkipGit) minimal, _ = cmd.Flags().GetBool(flagMinimal) + isConsumer, _ = cmd.Flags().GetBool(flagIsConsumer) params, _ = cmd.Flags().GetStringSlice(flagParams) moduleConfigs, _ = cmd.Flags().GetStringSlice(flagModuleConfigs) ) @@ -116,7 +121,7 @@ func scaffoldChainHandler(cmd *cobra.Command, args []string) error { return err } - appdir, err := scaffolder.Init( + appDir, err := scaffolder.Init( cmd.Context(), cacheStorage, placeholder.New(), @@ -126,6 +131,7 @@ func scaffoldChainHandler(cmd *cobra.Command, args []string) error { noDefaultModule, skipGit, minimal, + isConsumer, params, moduleConfigs, ) @@ -133,7 +139,7 @@ func scaffoldChainHandler(cmd *cobra.Command, args []string) error { return err } - path, err := xfilepath.RelativePath(appdir) + path, err := xfilepath.RelativePath(appDir) if err != nil { return err } diff --git a/ignite/config/chain/base/config.go b/ignite/config/chain/base/config.go index 6e288e05ba..9d7b27cae9 100644 --- a/ignite/config/chain/base/config.go +++ b/ignite/config/chain/base/config.go @@ -153,15 +153,29 @@ type Host struct { API string `yaml:"api"` } +// Validation describes the kind of validation the chain has. +type Validation string + +const ( + // ValidationSovereign is when the chain has his own validator set. + // Note that an empty string is also considered as a sovereign validation, + // because this is the default value. + ValidationSovereign = "sovereign" + // ValidationConsumer is when the chain is validated by a provider chain. + // Such chain is called a consumer chain. + ValidationConsumer = "consumer" +) + // Config defines a struct with the fields that are common to all config versions. type Config struct { - Version version.Version `yaml:"version"` - Build Build `yaml:"build,omitempty"` - Accounts []Account `yaml:"accounts"` - Faucet Faucet `yaml:"faucet,omitempty"` - Client Client `yaml:"client,omitempty"` - Genesis xyaml.Map `yaml:"genesis,omitempty"` - Minimal bool `yaml:"minimal,omitempty"` + Validation Validation `yaml:"validation,omitempty"` + Version version.Version `yaml:"version"` + Build Build `yaml:"build,omitempty"` + Accounts []Account `yaml:"accounts"` + Faucet Faucet `yaml:"faucet,omitempty"` + Client Client `yaml:"client,omitempty"` + Genesis xyaml.Map `yaml:"genesis,omitempty"` + Minimal bool `yaml:"minimal,omitempty"` } // GetVersion returns the config version. @@ -174,6 +188,14 @@ func (c Config) IsChainMinimal() bool { return c.Minimal } +func (c Config) IsSovereignChain() bool { + return c.Validation == "" || c.Validation == ValidationSovereign +} + +func (c Config) IsConsumerChain() bool { + return c.Validation == ValidationConsumer +} + // SetDefaults assigns default values to empty config fields. func (c *Config) SetDefaults() error { return mergo.Merge(c, DefaultConfig()) diff --git a/ignite/internal/plugin/consumer.go b/ignite/internal/plugin/consumer.go new file mode 100644 index 0000000000..03f2b5ef70 --- /dev/null +++ b/ignite/internal/plugin/consumer.go @@ -0,0 +1,39 @@ +package plugininternal + +import ( + "context" + "strconv" + + "github.com/ignite/cli/v28/ignite/pkg/errors" + "github.com/ignite/cli/v28/ignite/services/plugin" +) + +// TODO use released version of app-consumer. +const consumerPlugin = "github.com/ignite/apps/consumer" + +// ConsumerWriteGenesis writes validators in the consumer module genesis. +// NOTE(tb): Using a plugin for this task avoids having the interchain-security +// dependency in Ignite. +func ConsumerWriteGenesis(ctx context.Context, c plugin.Chainer) error { + _, err := Execute(ctx, consumerPlugin, []string{"writeGenesis"}, plugin.WithChain(c)) + if err != nil { + return errors.Errorf("execute consumer plugin 'writeGenesis': %w", err) + } + return nil +} + +// ConsumerIsInitialized returns true if the consumer chain's genesis c has +// a consumer module entry with an initial validator set. +// NOTE(tb): Using a plugin for this task avoids having the interchain-security +// dependency in Ignite. +func ConsumerIsInitialized(ctx context.Context, c plugin.Chainer) (bool, error) { + out, err := Execute(ctx, consumerPlugin, []string{"isInitialized"}, plugin.WithChain(c)) + if err != nil { + return false, errors.Errorf("execute consumer plugin 'isInitialized': %w", err) + } + b, err := strconv.ParseBool(out) + if err != nil { + return false, errors.Errorf("invalid consumer plugin 'isInitialized' output, got '%s': %w", out, err) + } + return b, nil +} diff --git a/ignite/internal/plugin/consumer_test.go b/ignite/internal/plugin/consumer_test.go new file mode 100644 index 0000000000..1bb5954f3d --- /dev/null +++ b/ignite/internal/plugin/consumer_test.go @@ -0,0 +1,134 @@ +package plugininternal + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ignite/cli/v28/ignite/services/plugin" + "github.com/ignite/cli/v28/ignite/services/plugin/mocks" +) + +func TestConsumerPlugin(t *testing.T) { + tests := []struct { + name string + args []string + setup func(*testing.T, string) + expectedOutput string + expectedError string + }{ + { + name: "fail: missing arg", + expectedError: "missing argument", + }, + { + name: "fail: invalid arg", + args: []string{"xxx"}, + expectedError: "invalid argument \"xxx\"", + }, + { + name: "fail: writeGenesis w/o priv_validator_key.json", + args: []string{"writeGenesis"}, + expectedError: "open .*/config/priv_validator_key.json: no such file or directory", + }, + { + name: "fail: writeFenesis w/o genesis.json", + args: []string{"writeGenesis"}, + setup: func(t *testing.T, path string) { + // Add priv_validator_key.json to path + bz, err := os.ReadFile("testdata/consumer/config/priv_validator_key.json") + require.NoError(t, err) + err = os.WriteFile(filepath.Join(path, "config", "priv_validator_key.json"), bz, 0o777) + require.NoError(t, err) + }, + expectedError: ".*/config/genesis.json does not exist, run `init` first", + }, + + { + name: "ok: writeGenesis", + args: []string{"writeGenesis"}, + setup: func(t *testing.T, path string) { + // Add priv_validator_key.json to path + bz, err := os.ReadFile("testdata/consumer/config/priv_validator_key.json") + require.NoError(t, err) + err = os.WriteFile(filepath.Join(path, "config", "priv_validator_key.json"), bz, 0o777) + require.NoError(t, err) + + // Add genesis.json to path + bz, err = os.ReadFile("testdata/consumer/config/genesis.json") + require.NoError(t, err) + err = os.WriteFile(filepath.Join(path, "config", "genesis.json"), bz, 0o777) + require.NoError(t, err) + }, + }, + { + name: "ok: isInitialized returns false", + args: []string{"isInitialized"}, + expectedOutput: "false", + }, + { + name: "ok: isInitialized returns true", + args: []string{"isInitialized"}, + setup: func(t *testing.T, path string) { + // isInitialized returns true if there's a consumer genesis with an + // InitialValSet length != 0 + // Add priv_validator_key.json to path + bz, err := os.ReadFile("testdata/consumer/config/priv_validator_key.json") + require.NoError(t, err) + err = os.WriteFile(filepath.Join(path, "config", "priv_validator_key.json"), bz, 0o777) + require.NoError(t, err) + + // Add genesis.json to path + bz, err = os.ReadFile("testdata/consumer/config/genesis.json") + require.NoError(t, err) + err = os.WriteFile(filepath.Join(path, "config", "genesis.json"), bz, 0o777) + require.NoError(t, err) + + // Call writeGenesis to create the genesis + chainer := mocks.NewChainerInterface(t) + chainer.EXPECT().ID().Return("id", nil).Maybe() + chainer.EXPECT().AppPath().Return("apppath").Maybe() + chainer.EXPECT().ConfigPath().Return("configpath").Maybe() + chainer.EXPECT().Home().Return(path, nil).Maybe() + chainer.EXPECT().RPCPublicAddress().Return("rpcPublicAddress", nil).Maybe() + _, err = Execute(context.Background(), consumerPlugin, []string{"writeGenesis"}, plugin.WithChain(chainer)) + require.NoError(t, err) + }, + expectedOutput: "true", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + homePath := t.TempDir() + err := os.MkdirAll(filepath.Join(homePath, "config"), 0o777) + require.NoError(t, err) + chainer := mocks.NewChainerInterface(t) + chainer.EXPECT().ID().Return("id", nil).Maybe() + chainer.EXPECT().AppPath().Return("apppath").Maybe() + chainer.EXPECT().ConfigPath().Return("configpath").Maybe() + chainer.EXPECT().Home().Return(homePath, nil).Maybe() + chainer.EXPECT().RPCPublicAddress().Return("rpcPublicAddress", nil).Maybe() + if tt.setup != nil { + tt.setup(t, homePath) + } + + out, err := Execute( + context.Background(), + consumerPlugin, + tt.args, + plugin.WithChain(chainer), + ) + + if tt.expectedError != "" { + require.Error(t, err) + require.Regexp(t, tt.expectedError, err.Error()) + return + } + require.NoError(t, err) + require.Equal(t, tt.expectedOutput, out) + }) + } +} diff --git a/ignite/internal/plugin/execute_test.go b/ignite/internal/plugin/execute_test.go index c7fba3fb45..a76c8795a9 100644 --- a/ignite/internal/plugin/execute_test.go +++ b/ignite/internal/plugin/execute_test.go @@ -15,10 +15,10 @@ import ( func TestPluginExecute(t *testing.T) { tests := []struct { - name string - pluginPath string - expectedOut string - expectedError string + name string + pluginPath string + expectedOutput string + expectedError string }{ { name: "fail: plugin doesnt exist", @@ -26,12 +26,12 @@ func TestPluginExecute(t *testing.T) { expectedError: "local app path \"/not/exists\" not found: stat /not/exists: no such file or directory", }, { - name: "ok: plugin execute ok ", - pluginPath: "testdata/execute_ok", - expectedOut: "ok args=[arg1 arg2] chainid=id appPath=apppath configPath=configpath home=home rpcAddress=rpcPublicAddress\n", + name: "ok: plugin execute ok", + pluginPath: "testdata/execute_ok", + expectedOutput: "ok args=[arg1 arg2] chainid=id appPath=apppath configPath=configpath home=home rpcAddress=rpcPublicAddress\n", }, { - name: "ok: plugin execute fail ", + name: "ok: plugin execute fail", pluginPath: "testdata/execute_fail", expectedError: "fail", }, @@ -64,7 +64,7 @@ func TestPluginExecute(t *testing.T) { return } require.NoError(t, err) - require.Equal(t, tt.expectedOut, out) + require.Equal(t, tt.expectedOutput, out) }) } } diff --git a/ignite/internal/plugin/testdata/consumer/config/genesis.json b/ignite/internal/plugin/testdata/consumer/config/genesis.json new file mode 100644 index 0000000000..49a3950dda --- /dev/null +++ b/ignite/internal/plugin/testdata/consumer/config/genesis.json @@ -0,0 +1,9 @@ +{ + "app_name": "test", + "app_version": "", + "genesis_time": "2024-01-19T10:27:44.742750573Z", + "chain_id": "test", + "initial_height": 1, + "app_hash": null, + "app_state": {} +} diff --git a/ignite/internal/plugin/testdata/consumer/config/priv_validator_key.json b/ignite/internal/plugin/testdata/consumer/config/priv_validator_key.json new file mode 100644 index 0000000000..ec5122805f --- /dev/null +++ b/ignite/internal/plugin/testdata/consumer/config/priv_validator_key.json @@ -0,0 +1,11 @@ +{ + "address": "2D3C15095E5EAA318CAEDE1C2D02C77581584751", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "uBOT+dDuUvXjJrkfwMNrS4bRT4/O+fBnpwfYpR6n1Wk=" + }, + "priv_key": { + "type": "tendermint/PrivKeyEd25519", + "value": "HovIzTJTGMrQx5oBikjfypyMZYF9QP5MxS+S+S/3QYq4E5P50O5S9eMmuR/Aw2tLhtFPj8758GenB9ilHqfVaQ==" + } +} \ No newline at end of file diff --git a/ignite/services/chain/build.go b/ignite/services/chain/build.go index 77b54016a2..97848d9e19 100644 --- a/ignite/services/chain/build.go +++ b/ignite/services/chain/build.go @@ -29,6 +29,7 @@ const ( releaseChecksumKey = "release_checksum" modChecksumKey = "go_mod_checksum" buildDirchangeCacheNamespace = "build.dirchange" + consumerDevel = "consumer_devel" ) // Build builds and installs app binaries. @@ -72,6 +73,17 @@ func (c *Chain) build( } } + cfg, err := c.Config() + if err != nil { + return err + } + if cfg.IsConsumerChain() { + // When building a non-release consumer chain (which is the case for this + // build() method), enable consumerDevel (see templates consumer_devel and + // consumer_final for more info). + buildTags = append(buildTags, consumerDevel) + } + buildFlags, err := c.preBuild(ctx, cacheStorage, buildTags...) if err != nil { return err diff --git a/ignite/services/chain/init.go b/ignite/services/chain/init.go index 60117e2ca0..ea89936f63 100644 --- a/ignite/services/chain/init.go +++ b/ignite/services/chain/init.go @@ -9,6 +9,7 @@ import ( "github.com/imdario/mergo" chainconfig "github.com/ignite/cli/v29/ignite/config/chain" + "github.com/ignite/cli/v29/ignite/internal/plugin" chaincmdrunner "github.com/ignite/cli/v29/ignite/pkg/chaincmd/runner" "github.com/ignite/cli/v29/ignite/pkg/cliui/view/accountview" "github.com/ignite/cli/v29/ignite/pkg/confile" @@ -171,11 +172,22 @@ func (c *Chain) InitAccounts(ctx context.Context, cfg *chainconfig.Config) error c.ev.SendView(accounts, events.ProgressFinish()) // 0 length validator set when using network config - if len(cfg.Validators) != 0 { - _, err = c.IssueGentx(ctx, createValidatorFromConfig(cfg)) + if len(cfg.Validators) == 0 { + return nil } - - return err + if cfg.IsConsumerChain() { + err := plugininternal.ConsumerWriteGenesis(ctx, c) + if err != nil { + return err + } + } else { + // Sovereign chain writes validators in gentxs. + _, err := c.IssueGentx(ctx, createValidatorFromConfig(cfg)) + if err != nil { + return err + } + } + return nil } // IssueGentx generates a gentx from the validator information in chain config and imports it in the chain genesis. @@ -196,12 +208,22 @@ func (c Chain) IssueGentx(ctx context.Context, v Validator) (string, error) { } // IsInitialized checks if the chain is initialized. -// The check is performed by checking if the gentx dir exists in the config. +// The check is performed by checking if the gentx dir exists in the config, +// unless c is a consumer chain, in which case the check relies on checking if +// the consumer genesis module is filled with validators. func (c *Chain) IsInitialized() (bool, error) { home, err := c.Home() if err != nil { return false, err } + cfg, err := c.Config() + if err != nil { + return false, err + } + if cfg.IsConsumerChain() { + return plugininternal.ConsumerIsInitialized(context.Background(), c) + } + gentxDir := filepath.Join(home, "config", "gentx") if _, err := os.Stat(gentxDir); os.IsNotExist(err) { diff --git a/ignite/services/scaffolder/init.go b/ignite/services/scaffolder/init.go index 73ee3ed134..3ece392fbc 100644 --- a/ignite/services/scaffolder/init.go +++ b/ignite/services/scaffolder/init.go @@ -25,7 +25,7 @@ func Init( cacheStorage cache.Storage, tracer *placeholder.Tracer, root, name, addressPrefix string, - noDefaultModule, skipGit, minimal bool, + noDefaultModule, skipGit, minimal, isConsumerChain bool, params, moduleConfigs []string, ) (path string, err error) { pathInfo, err := gomodulepath.Parse(name) @@ -46,8 +46,18 @@ func Init( path = filepath.Join(root, appFolder) // create the project - err = generate(ctx, tracer, pathInfo, addressPrefix, path, noDefaultModule, minimal, params, moduleConfigs) - if err != nil { + if err := generate( + ctx, + tracer, + pathInfo, + addressPrefix, + path, + noDefaultModule, + minimal, + isConsumerChain, + params, + moduleConfigs, + ); err != nil { return "", err } @@ -73,7 +83,7 @@ func generate( pathInfo gomodulepath.Path, addressPrefix, absRoot string, - noDefaultModule, minimal bool, + noDefaultModule, minimal, isConsumerChain bool, params, moduleConfigs []string, ) error { // Parse params with the associated type @@ -103,6 +113,7 @@ func generate( BinaryNamePrefix: pathInfo.Root, AddressPrefix: addressPrefix, IsChainMinimal: minimal, + IsConsumerChain: isConsumerChain, }) if err != nil { return err diff --git a/ignite/services/scaffolder/module.go b/ignite/services/scaffolder/module.go index db76513152..9d31d6478b 100644 --- a/ignite/services/scaffolder/module.go +++ b/ignite/services/scaffolder/module.go @@ -56,6 +56,7 @@ var ( "account": {}, "block": {}, "broadcast": {}, + "consumer": {}, // ICS consumer module "encode": {}, "multisign": {}, "sign": {}, diff --git a/ignite/templates/app/app.go b/ignite/templates/app/app.go index 62cb3ffa46..25e9cf4eb1 100644 --- a/ignite/templates/app/app.go +++ b/ignite/templates/app/app.go @@ -19,6 +19,9 @@ var ( //go:embed files-minimal/* files-minimal/**/* filesMinimal embed.FS + + //go:embed files-consumer/* files-consumer/**/* + filesConsumer embed.FS ) const ( @@ -32,27 +35,35 @@ func NewGenerator(opts *Options) (*genny.Generator, error) { if err != nil { return nil, errors.Errorf("generator sub: %w", err) } - g := genny.New() - var excludePrefix []string + var ( + includePrefix = opts.IncludePrefixes + excludePrefix []string + overridesFS = make(map[string]embed.FS) + ) if opts.IsChainMinimal { // minimal chain does not have ibc excludePrefix = append(excludePrefix, ibcConfig) + overridesFS["files-minimal"] = filesMinimal + } + if opts.IsConsumerChain { + overridesFS["files-consumer"] = filesConsumer } - if err := g.SelectiveFS(subfs, opts.IncludePrefixes, nil, excludePrefix, nil); err != nil { + g := genny.New() + if err := g.SelectiveFS(subfs, includePrefix, nil, excludePrefix, nil); err != nil { return g, errors.Errorf("generator fs: %w", err) } - if opts.IsChainMinimal { - // Remove "files-minimal/" prefix - subfs, err := fs.Sub(filesMinimal, "files-minimal") + for prefix, embed := range overridesFS { + // Remove prefix + subfs, err := fs.Sub(embed, prefix) if err != nil { - return nil, errors.Errorf("generator sub minimal: %w", err) + return g, errors.Errorf("generator sub %s: %w", prefix, err) } - // Override files from "files" with the ones from "files-minimal" + // Override files from "files" with the ones from embed if err := g.FS(subfs); err != nil { - return g, errors.Errorf("generator fs minimal: %w", err) + return g, errors.Errorf("generator fs %s: %w", prefix, err) } } @@ -62,6 +73,7 @@ func NewGenerator(opts *Options) (*genny.Generator, error) { ctx.Set("GitHubPath", opts.GitHubPath) ctx.Set("BinaryNamePrefix", opts.BinaryNamePrefix) ctx.Set("AddressPrefix", opts.AddressPrefix) + ctx.Set("IsConsumerChain", opts.IsConsumerChain) ctx.Set("DepTools", cosmosgen.DepTools()) ctx.Set("IsChainMinimal", opts.IsChainMinimal) diff --git a/ignite/templates/app/files-consumer/app/ante_handler.go.plush b/ignite/templates/app/files-consumer/app/ante_handler.go.plush new file mode 100644 index 0000000000..de1c4ecca8 --- /dev/null +++ b/ignite/templates/app/files-consumer/app/ante_handler.go.plush @@ -0,0 +1,73 @@ +package app + +import ( + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/log" + circuitante "cosmossdk.io/x/circuit/ante" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + ibcante "github.com/cosmos/ibc-go/v8/modules/core/ante" + ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper" + consumerante "github.com/cosmos/interchain-security/v3/app/consumer/ante" + ibcconsumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" +) + +// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC channel keeper. +type HandlerOptions struct { + ante.HandlerOptions + + IBCKeeper *ibckeeper.Keeper + ConsumerKeeper ibcconsumerkeeper.Keeper + CircuitKeeper circuitante.CircuitBreaker +} + +func NewAnteHandler(options HandlerOptions, logger log.Logger) (sdk.AnteHandler, error) { + if options.AccountKeeper == nil { + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler") + } + if options.BankKeeper == nil { + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler") + } + if options.SignModeHandler == nil { + return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder") + } + + sigGasConsumer := options.SigGasConsumer + if sigGasConsumer == nil { + sigGasConsumer = ante.DefaultSigVerificationGasConsumer + } + + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), + circuitante.NewCircuitBreakerDecorator(options.CircuitKeeper), + ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker), + consumerante.NewDisabledModulesDecorator("/cosmos.evidence", "/cosmos.slashing"), + ante.NewValidateBasicDecorator(), + ante.NewTxTimeoutHeightDecorator(), + ante.NewValidateMemoDecorator(options.AccountKeeper), + ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), + ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker), + // SetPubKeyDecorator must be called before all signature verification decorators + ante.NewSetPubKeyDecorator(options.AccountKeeper), + ante.NewValidateSigCountDecorator(options.AccountKeeper), + ante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer), + ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), + ante.NewIncrementSequenceDecorator(options.AccountKeeper), + ibcante.NewRedundantRelayDecorator(options.IBCKeeper), + } + + // This constant depends on build tag. + // When true the consumer ante decorator is not added. This decorator rejects + // all non-IBC messages until the CCV channel is established with the + // provider chain. While this is useful for production, for development it's + // more convenient to avoid it so the consumer chain can be tested without + // a provider chain and a relayer running on top. + if !ConsumerSkipMsgFilter { + anteDecorators = append(anteDecorators, consumerante.NewMsgFilterDecorator(options.ConsumerKeeper)) + } else { + logger.Error("WARNING: BUILT WITH skip_ccv_msg_filter. THIS IS NOT A PRODUCTION BUILD") + } + + return sdk.ChainAnteDecorators(anteDecorators...), nil +} diff --git a/ignite/templates/app/files-consumer/app/app.go.plush b/ignite/templates/app/files-consumer/app/app.go.plush new file mode 100644 index 0000000000..fac3700686 --- /dev/null +++ b/ignite/templates/app/files-consumer/app/app.go.plush @@ -0,0 +1,463 @@ +package app + +import ( + "io" + "os" + "path/filepath" + "fmt" + + "cosmossdk.io/depinject" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + circuitkeeper "cosmossdk.io/x/circuit/keeper" + evidencekeeper "cosmossdk.io/x/evidence/keeper" + feegrantkeeper "cosmossdk.io/x/feegrant/keeper" + upgradekeeper "cosmossdk.io/x/upgrade/keeper" + _ "cosmossdk.io/api/cosmos/tx/config/v1" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/auth" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/bank" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/consensus" // import for side-effects + _ "cosmossdk.io/x/circuit" // import for side-effects + _ "cosmossdk.io/x/evidence" // import for side-effects + _ "cosmossdk.io/x/feegrant/module" // import for side-effects + _ "cosmossdk.io/x/nft/module" // import for side-effects + nftkeeper "cosmossdk.io/x/nft/keeper" + _ "cosmossdk.io/x/upgrade" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/auth/vesting" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/authz/module" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/crisis" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/group/module" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/params" // import for side-effects + _ "github.com/cosmos/cosmos-sdk/x/slashing" // import for side-effects + _ "github.com/cosmos/ibc-go/modules/capability" // import for side-effects + _ "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts" // import for side-effects + _ "github.com/cosmos/ibc-go/v8/modules/apps/29-fee" // import for side-effects + dbm "github.com/cosmos/cosmos-db" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + consensuskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + groupkeeper "github.com/cosmos/cosmos-sdk/x/group/keeper" + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" + icacontrollerkeeper "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/keeper" + icahostkeeper "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/host/keeper" + ibcfeekeeper "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/keeper" + ibctransferkeeper "github.com/cosmos/ibc-go/v8/modules/apps/transfer/keeper" + ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + ibcconsumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" + ibcconsumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + // this line is used by starport scaffolding # stargate/app/moduleImport + + "<%= ModulePath %>/docs" +) + +const ( + AccountAddressPrefix = "<%= AddressPrefix %>" + Name = "<%= BinaryNamePrefix %>" +) + +var ( + // DefaultNodeHome default home directories for the application daemon + DefaultNodeHome string +) + +var ( + _ runtime.AppI = (*App)(nil) + _ servertypes.Application = (*App)(nil) +) + +// App extends an ABCI application, but with most of its parameters exported. +// They are exported for convenience in creating helper functions, as object +// capabilities aren't needed for testing. +type App struct { + *runtime.App + legacyAmino *codec.LegacyAmino + appCodec codec.Codec + txConfig client.TxConfig + interfaceRegistry codectypes.InterfaceRegistry + + // keepers + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + ConsensusParamsKeeper consensuskeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + ConsumerKeeper ibcconsumerkeeper.Keeper + CrisisKeeper *crisiskeeper.Keeper + UpgradeKeeper *upgradekeeper.Keeper + ParamsKeeper paramskeeper.Keeper + AuthzKeeper authzkeeper.Keeper + EvidenceKeeper evidencekeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper + GroupKeeper groupkeeper.Keeper + NFTKeeper nftkeeper.Keeper + CircuitBreakerKeeper circuitkeeper.Keeper + + // IBC + IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + CapabilityKeeper *capabilitykeeper.Keeper + IBCFeeKeeper ibcfeekeeper.Keeper + ICAControllerKeeper icacontrollerkeeper.Keeper + ICAHostKeeper icahostkeeper.Keeper + TransferKeeper ibctransferkeeper.Keeper + + // Scoped IBC + ScopedIBCKeeper capabilitykeeper.ScopedKeeper + ScopedIBCTransferKeeper capabilitykeeper.ScopedKeeper + ScopedICAControllerKeeper capabilitykeeper.ScopedKeeper + ScopedICAHostKeeper capabilitykeeper.ScopedKeeper + + // this line is used by starport scaffolding # stargate/app/keeperDeclaration + + // simulation manager + sm *module.SimulationManager +} + +func init() { + userHomeDir, err := os.UserHomeDir() + if err != nil { + panic(err) + } + + DefaultNodeHome = filepath.Join(userHomeDir, "."+Name) +} + +// AppConfig returns the default app config. +func AppConfig() depinject.Config { + return depinject.Configs( + appConfig, + // Loads the app config from a YAML file. + // appconfig.LoadYAML(AppConfigYAML), + depinject.Supply( + // supply custom module basics + map[string]module.AppModuleBasic{ + genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), + // this line is used by starport scaffolding # stargate/appConfig/moduleBasic + }, + ), + ) +} + +// New returns a reference to an initialized App. +func New( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + appOpts servertypes.AppOptions, + baseAppOptions ...func(*baseapp.BaseApp), +) (*App, error) { + var ( + app = &App{} + appBuilder *runtime.AppBuilder + + // merge the AppConfig and other configuration in one config + appConfig = depinject.Configs( + AppConfig(), + depinject.Supply( + // Supply the application options + appOpts, + // Supply with IBC keeper getter for the IBC modules with App Wiring. + // The IBC Keeper cannot be passed because it has not been initiated yet. + // Passing the getter, the app IBC Keeper will always be accessible. + // This needs to be removed after IBC supports App Wiring. + app.GetIBCKeeper, + app.GetCapabilityScopedKeeper, + // Supply the consumer keeper for the consumer module + &app.ConsumerKeeper, + // Supply the logger + logger, + + // ADVANCED CONFIGURATION + // + // AUTH + // + // For providing a custom function required in auth to generate custom account types + // add it below. By default the auth module uses simulation.RandomGenesisAccounts. + // + // authtypes.RandomGenesisAccountsFn(simulation.RandomGenesisAccounts), + + // For providing a custom a base account type add it below. + // By default the auth module uses authtypes.ProtoBaseAccount(). + // + // func() sdk.AccountI { return authtypes.ProtoBaseAccount() }, + // + // For providing a different address codec, add it below. + // By default the auth module uses a Bech32 address codec, + // with the prefix defined in the auth module configuration. + // + // func() address.Codec { return <- custom address codec type -> } + + // + // STAKING + // + // For provinding a different validator and consensus address codec, add it below. + // By default the staking module uses the bech32 prefix provided in the auth config, + // and appends "valoper" and "valcons" for validator and consensus addresses respectively. + // When providing a custom address codec in auth, custom address codecs must be provided here as well. + // + // func() runtime.ValidatorAddressCodec { return <- custom validator address codec type -> } + // func() runtime.ConsensusAddressCodec { return <- custom consensus address codec type -> } + + // + // MINT + // + + // For providing a custom inflation function for x/mint add here your + // custom function that implements the minttypes.InflationCalculationFn + // interface. + ), + ) + ) + + if err := depinject.Inject(appConfig, + &appBuilder, + &app.appCodec, + &app.legacyAmino, + &app.txConfig, + &app.interfaceRegistry, + &app.AccountKeeper, + &app.BankKeeper, + &app.ConsensusParamsKeeper, + &app.SlashingKeeper, + &app.CrisisKeeper, + &app.UpgradeKeeper, + &app.ParamsKeeper, + &app.AuthzKeeper, + &app.EvidenceKeeper, + &app.FeeGrantKeeper, + &app.NFTKeeper, + &app.GroupKeeper, + &app.CircuitBreakerKeeper, + // this line is used by starport scaffolding # stargate/app/keeperDefinition + ); err != nil { + panic(err) + } + + // Below we could construct and set an application specific mempool and + // ABCI 1.0 PrepareProposal and ProcessProposal handlers. These defaults are + // already set in the SDK's BaseApp, this shows an example of how to override + // them. + // + // Example: + // + // app.App = appBuilder.Build(...) + // nonceMempool := mempool.NewSenderNonceMempool() + // abciPropHandler := NewDefaultProposalHandler(nonceMempool, app.App.BaseApp) + // + // app.App.BaseApp.SetMempool(nonceMempool) + // app.App.BaseApp.SetPrepareProposal(abciPropHandler.PrepareProposalHandler()) + // app.App.BaseApp.SetProcessProposal(abciPropHandler.ProcessProposalHandler()) + // + // Alternatively, you can construct BaseApp options, append those to + // baseAppOptions and pass them to the appBuilder. + // + // Example: + // + // prepareOpt = func(app *baseapp.BaseApp) { + // abciPropHandler := baseapp.NewDefaultProposalHandler(nonceMempool, app) + // app.SetPrepareProposal(abciPropHandler.PrepareProposalHandler()) + // } + // baseAppOptions = append(baseAppOptions, prepareOpt) + // + // create and set vote extension handler + // voteExtOp := func(bApp *baseapp.BaseApp) { + // voteExtHandler := NewVoteExtensionHandler() + // voteExtHandler.SetHandlers(bApp) + // } + + app.App = appBuilder.Build(db, traceStore, baseAppOptions...) + + // Register legacy modules + if err := app.registerIBCModules(appOpts); err != nil { + return nil, err + } + + // register streaming services + if err := app.RegisterStreamingServices(appOpts, app.kvStoreKeys()); err != nil { + return nil, err + } + + /**** Module Options ****/ + + app.ModuleManager.RegisterInvariants(app.CrisisKeeper) + + // create the simulation manager and define the order of the modules for deterministic simulations + // + // NOTE: this is not required apps that don't use the simulator for fuzz testing transactions + overrideModules := map[string]module.AppModuleSimulation{ + authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + } + app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules) + + app.sm.RegisterStoreDecoders() + + // TODO theres probably a better way to SetAnteHandler with app wiring + anteHandler, err := NewAnteHandler( + HandlerOptions{ + HandlerOptions: ante.HandlerOptions{ + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + FeegrantKeeper: app.FeeGrantKeeper, + SignModeHandler: app.txConfig.SignModeHandler(), + SigGasConsumer: ante.DefaultSigVerificationGasConsumer, + }, + IBCKeeper: app.IBCKeeper, + ConsumerKeeper: app.ConsumerKeeper, + CircuitKeeper: &app.CircuitBreakerKeeper, + }, + logger, + ) + if err != nil { + panic(fmt.Errorf("failed to create AnteHandler: %w", err)) + } + app.SetAnteHandler(anteHandler) + + // A custom InitChainer can be set if extra pre-init-genesis logic is required. + // By default, when using app wiring enabled module, this is not required. + // For instance, the upgrade module will set automatically the module version map in its init genesis thanks to app wiring. + // However, when registering a module manually (i.e. that does not support app wiring), the module version map + // must be set manually as follow. The upgrade module will de-duplicate the module version map. + // + // app.SetInitChainer(func(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) { + // app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()) + // return app.App.InitChainer(ctx, req) + // }) + + if err := app.Load(loadLatest); err != nil { + return nil, err + } + + return app, nil +} + +// LegacyAmino returns App's amino codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *App) LegacyAmino() *codec.LegacyAmino { + return app.legacyAmino +} + +// AppCodec returns App's app codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *App) AppCodec() codec.Codec { + return app.appCodec +} + +// GetKey returns the KVStoreKey for the provided store key. +func (app *App) GetKey(storeKey string) *storetypes.KVStoreKey { + kvStoreKey, ok := app.UnsafeFindStoreKey(storeKey).(*storetypes.KVStoreKey) + if !ok { + return nil + } + return kvStoreKey +} + +// GetMemKey returns the MemoryStoreKey for the provided store key. +func (app *App) GetMemKey(storeKey string) *storetypes.MemoryStoreKey { + key, ok := app.UnsafeFindStoreKey(storeKey).(*storetypes.MemoryStoreKey) + if !ok { + return nil + } + + return key +} + +// kvStoreKeys returns all the kv store keys registered inside App. +func (app *App) kvStoreKeys() map[string]*storetypes.KVStoreKey { + keys := make(map[string]*storetypes.KVStoreKey) + for _, k := range app.GetStoreKeys() { + if kv, ok := k.(*storetypes.KVStoreKey); ok { + keys[kv.Name()] = kv + } + } + + return keys +} + +// GetSubspace returns a param subspace for a given module name. +func (app *App) GetSubspace(moduleName string) paramstypes.Subspace { + subspace, _ := app.ParamsKeeper.GetSubspace(moduleName) + return subspace +} + +// GetIBCKeeper returns the IBC keeper. +func (app *App) GetIBCKeeper() *ibckeeper.Keeper { + return app.IBCKeeper +} + +// GetCapabilityScopedKeeper returns the capability scoped keeper. +func (app *App) GetCapabilityScopedKeeper(moduleName string) capabilitykeeper.ScopedKeeper { + return app.CapabilityKeeper.ScopeToModule(moduleName) +} + +// SimulationManager implements the SimulationApp interface. +func (app *App) SimulationManager() *module.SimulationManager { + return app.sm +} + +// RegisterAPIRoutes registers all application module routes with the provided +// API server. +func (app *App) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { + app.App.RegisterAPIRoutes(apiSvr, apiConfig) + // register swagger API in app.go so that other applications can override easily + if err := server.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil { + panic(err) + } + + // register app's OpenAPI routes. + docs.RegisterOpenAPIService(Name, apiSvr.Router) +} + +// GetMaccPerms returns a copy of the module account permissions +// +// NOTE: This is solely to be used for testing purposes. +func GetMaccPerms() map[string][]string { + dup := make(map[string][]string) + for _, perms := range moduleAccPerms { + dup[perms.Account] = perms.Permissions + } + return dup +} + +// BlockedAddresses returns all the app's blocked account addresses. +func BlockedAddresses() map[string]bool { + result := make(map[string]bool) + if len(blockAccAddrs) > 0 { + for _, addr := range blockAccAddrs { + result[addr] = true + } + } else { + for addr := range GetMaccPerms() { + result[addr] = true + } + } + // Remove the fee-pool from the group of blocked recipient addresses in bank + // this is required for the consumer chain to be able to send tokens to + // the provider chain + delete(result, authtypes.NewModuleAddress(ibcconsumertypes.ConsumerToSendToProviderName).String()) + return result +} diff --git a/ignite/templates/app/files-consumer/app/app_config.go.plush b/ignite/templates/app/files-consumer/app/app_config.go.plush new file mode 100644 index 0000000000..669b0fc6cc --- /dev/null +++ b/ignite/templates/app/files-consumer/app/app_config.go.plush @@ -0,0 +1,252 @@ +package app + +import ( + "time" + + runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1" + appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1" + authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1" + authzmodulev1 "cosmossdk.io/api/cosmos/authz/module/v1" + bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1" + circuitmodulev1 "cosmossdk.io/api/cosmos/circuit/module/v1" + consensusmodulev1 "cosmossdk.io/api/cosmos/consensus/module/v1" + crisismodulev1 "cosmossdk.io/api/cosmos/crisis/module/v1" + evidencemodulev1 "cosmossdk.io/api/cosmos/evidence/module/v1" + feegrantmodulev1 "cosmossdk.io/api/cosmos/feegrant/module/v1" + genutilmodulev1 "cosmossdk.io/api/cosmos/genutil/module/v1" + groupmodulev1 "cosmossdk.io/api/cosmos/group/module/v1" + nftmodulev1 "cosmossdk.io/api/cosmos/nft/module/v1" + paramsmodulev1 "cosmossdk.io/api/cosmos/params/module/v1" + slashingmodulev1 "cosmossdk.io/api/cosmos/slashing/module/v1" + txconfigv1 "cosmossdk.io/api/cosmos/tx/config/v1" + upgrademodulev1 "cosmossdk.io/api/cosmos/upgrade/module/v1" + vestingmodulev1 "cosmossdk.io/api/cosmos/vesting/module/v1" + "cosmossdk.io/core/appconfig" + circuittypes "cosmossdk.io/x/circuit/types" + evidencetypes "cosmossdk.io/x/evidence/types" + "cosmossdk.io/x/feegrant" + "cosmossdk.io/x/nft" + upgradetypes "cosmossdk.io/x/upgrade/types" + "github.com/cosmos/cosmos-sdk/runtime" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/group" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types" + ibcfeetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types" + ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" + "google.golang.org/protobuf/types/known/durationpb" + ibcconsumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + // this line is used by starport scaffolding # stargate/app/moduleImport +) + +var ( + // NOTE: The genutils module must occur after staking so that pools are + // properly initialized with tokens from genesis accounts. + // NOTE: The genutils module must also occur after auth so that it can access the params from auth. + // NOTE: Capability module must occur first so that it can initialize any capabilities + // so that other modules that want to create or claim capabilities afterwards in InitChain + // can do so safely. + genesisModuleOrder = []string{ + // cosmos-sdk/ibc modules + capabilitytypes.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + slashingtypes.ModuleName, + crisistypes.ModuleName, + ibcexported.ModuleName, + genutiltypes.ModuleName, + evidencetypes.ModuleName, + authz.ModuleName, + ibctransfertypes.ModuleName, + icatypes.ModuleName, + ibcfeetypes.ModuleName, + feegrant.ModuleName, + paramstypes.ModuleName, + upgradetypes.ModuleName, + vestingtypes.ModuleName, + circuittypes.ModuleName, + nft.ModuleName, + group.ModuleName, + consensustypes.ModuleName, + circuittypes.ModuleName, + ibcconsumertypes.ModuleName, + // chain modules + // this line is used by starport scaffolding # stargate/app/initGenesis + } + + // During begin block slashing happens after distr.BeginBlocker so that + // there is nothing left over in the validator fee pool, so as to keep the + // CanWithdrawInvariant invariant. + // NOTE: staking module is required if HistoricalEntries param > 0 + // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) + beginBlockers = []string{ + // cosmos sdk modules + slashingtypes.ModuleName, + evidencetypes.ModuleName, + ibcconsumertypes.ModuleName, + authz.ModuleName, + genutiltypes.ModuleName, + // ibc modules + capabilitytypes.ModuleName, + ibcexported.ModuleName, + ibctransfertypes.ModuleName, + icatypes.ModuleName, + ibcfeetypes.ModuleName, + // chain modules + // this line is used by starport scaffolding # stargate/app/beginBlockers + } + + endBlockers = []string{ + // cosmos sdk modules + crisistypes.ModuleName, + ibcconsumertypes.ModuleName, + feegrant.ModuleName, + group.ModuleName, + genutiltypes.ModuleName, + // ibc modules + ibcexported.ModuleName, + ibctransfertypes.ModuleName, + capabilitytypes.ModuleName, + icatypes.ModuleName, + ibcfeetypes.ModuleName, + // chain modules + // this line is used by starport scaffolding # stargate/app/endBlockers + } + + preBlockers = []string{ + upgradetypes.ModuleName, + // this line is used by starport scaffolding # stargate/app/preBlockers + } + + // module account permissions + moduleAccPerms = []*authmodulev1.ModuleAccountPermission{ + {Account: authtypes.FeeCollectorName}, + {Account: icatypes.ModuleName}, + {Account: ibcconsumertypes.ConsumerRedistributeName}, + {Account: ibcconsumertypes.ConsumerToSendToProviderName}, + {Account: nft.ModuleName}, + {Account: ibctransfertypes.ModuleName, Permissions: []string{authtypes.Minter, authtypes.Burner}}, + {Account: ibcfeetypes.ModuleName}, + {Account: icatypes.ModuleName}, + // this line is used by starport scaffolding # stargate/app/maccPerms + } + + // blocked account addresses + blockAccAddrs = []string{ + authtypes.FeeCollectorName, + // We allow the following module accounts to receive funds: + // govtypes.ModuleName + } + + // appConfig application configuration (used by depinject) + appConfig = appconfig.Compose(&appv1alpha1.Config{ + Modules: []*appv1alpha1.ModuleConfig{ + { + Name: runtime.ModuleName, + Config: appconfig.WrapAny(&runtimev1alpha1.Module{ + AppName: Name, + PreBlockers: preBlockers, + BeginBlockers: beginBlockers, + EndBlockers: endBlockers, + InitGenesis: genesisModuleOrder, + OverrideStoreKeys: []*runtimev1alpha1.StoreKeyConfig{ + { + ModuleName: authtypes.ModuleName, + KvStoreKey: "acc", + }, + }, + // When ExportGenesis is not specified, the export genesis module order + // is equal to the init genesis order + // ExportGenesis: genesisModuleOrder, + // Uncomment if you want to set a custom migration order here. + // OrderMigrations: nil, + }), + }, + { + Name: authtypes.ModuleName, + Config: appconfig.WrapAny(&authmodulev1.Module{ + Bech32Prefix: AccountAddressPrefix, + ModuleAccountPermissions: moduleAccPerms, + // By default modules authority is the governance module. This is configurable with the following: + // Authority: "group", // A custom module authority can be set using a module name + // Authority: "cosmos1cwwv22j5ca08ggdv9c2uky355k908694z577tv", // or a specific address + }), + }, + { + Name: nft.ModuleName, + Config: appconfig.WrapAny(&nftmodulev1.Module{}), + }, + { + Name: vestingtypes.ModuleName, + Config: appconfig.WrapAny(&vestingmodulev1.Module{}), + }, + { + Name: banktypes.ModuleName, + Config: appconfig.WrapAny(&bankmodulev1.Module{ + BlockedModuleAccountsOverride: blockAccAddrs, + }), + }, + { + Name: slashingtypes.ModuleName, + Config: appconfig.WrapAny(&slashingmodulev1.Module{}), + }, + { + Name: paramstypes.ModuleName, + Config: appconfig.WrapAny(¶msmodulev1.Module{}), + }, + { + Name: "tx", + Config: appconfig.WrapAny(&txconfigv1.Config{}), + }, + { + Name: genutiltypes.ModuleName, + Config: appconfig.WrapAny(&genutilmodulev1.Module{}), + }, + { + Name: authz.ModuleName, + Config: appconfig.WrapAny(&authzmodulev1.Module{}), + }, + { + Name: upgradetypes.ModuleName, + Config: appconfig.WrapAny(&upgrademodulev1.Module{}), + }, + { + Name: evidencetypes.ModuleName, + Config: appconfig.WrapAny(&evidencemodulev1.Module{}), + }, + { + Name: group.ModuleName, + Config: appconfig.WrapAny(&groupmodulev1.Module{ + MaxExecutionPeriod: durationpb.New(time.Second * 1209600), + MaxMetadataLen: 255, + }), + }, + { + Name: feegrant.ModuleName, + Config: appconfig.WrapAny(&feegrantmodulev1.Module{}), + }, + { + Name: crisistypes.ModuleName, + Config: appconfig.WrapAny(&crisismodulev1.Module{}), + }, + { + Name: consensustypes.ModuleName, + Config: appconfig.WrapAny(&consensusmodulev1.Module{}), + }, + { + Name: circuittypes.ModuleName, + Config: appconfig.WrapAny(&circuitmodulev1.Module{}), + }, + // this line is used by starport scaffolding # stargate/app/moduleConfig + }, + }) +) diff --git a/ignite/templates/app/files-consumer/app/consumer_devel.go.plush b/ignite/templates/app/files-consumer/app/consumer_devel.go.plush new file mode 100644 index 0000000000..96029aa81b --- /dev/null +++ b/ignite/templates/app/files-consumer/app/consumer_devel.go.plush @@ -0,0 +1,7 @@ +//go:build consumer_devel + +package app + +const ( + ConsumerSkipMsgFilter = true +) diff --git a/ignite/templates/app/files-consumer/app/consumer_final.go.plush b/ignite/templates/app/files-consumer/app/consumer_final.go.plush new file mode 100644 index 0000000000..fa77783545 --- /dev/null +++ b/ignite/templates/app/files-consumer/app/consumer_final.go.plush @@ -0,0 +1,7 @@ +//go:build !consumer_devel + +package app + +const ( + ConsumerSkipMsgFilter = false +) diff --git a/ignite/templates/app/files-consumer/app/export.go.plush b/ignite/templates/app/files-consumer/app/export.go.plush new file mode 100644 index 0000000000..a7b07935fa --- /dev/null +++ b/ignite/templates/app/files-consumer/app/export.go.plush @@ -0,0 +1,71 @@ +package app + +import ( + "encoding/json" + "fmt" + + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmtypes "github.com/cometbft/cometbft/types" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ExportAppStateAndValidators exports the state of the application for a genesis +// file. +func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs, modulesToExport []string) (servertypes.ExportedApp, error) { + // as if they could withdraw from the start of the next block + ctx := app.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()}) + + // We export at last height + 1, because that's the height at which + // CometBFT will start InitChain. + height := app.LastBlockHeight() + 1 + if forZeroHeight { + height = 0 + app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) + } + + genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport) + if err != nil { + return servertypes.ExportedApp{}, err + } + + appState, err := json.MarshalIndent(genState, "", " ") + if err != nil { + return servertypes.ExportedApp{}, err + } + + validators, err := app.GetValidatorSet(ctx) + if err != nil { + return servertypes.ExportedApp{}, err + } + + return servertypes.ExportedApp{ + AppState: appState, + Validators: validators, + Height: height, + ConsensusParams: app.BaseApp.GetConsensusParams(ctx), + }, err +} + +// prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// +// in favor of export at a block height +func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { + /* Just to be safe, assert the invariants on current state. */ + app.CrisisKeeper.AssertInvariants(ctx) +} + +// GetValidatorSet returns a slice of bonded validators. +func (app *App) GetValidatorSet(ctx sdk.Context) ([]tmtypes.GenesisValidator, error) { + cVals := app.ConsumerKeeper.GetAllCCValidator(ctx) + if len(cVals) == 0 { + return nil, fmt.Errorf("empty validator set") + } + + vals := []tmtypes.GenesisValidator{} + for _, v := range cVals { + vals = append(vals, tmtypes.GenesisValidator{Address: v.Address, Power: v.Power}) + } + return vals, nil +} diff --git a/ignite/templates/app/files-consumer/app/ibc.go.plush b/ignite/templates/app/files-consumer/app/ibc.go.plush new file mode 100644 index 0000000000..5c8c756d8b --- /dev/null +++ b/ignite/templates/app/files-consumer/app/ibc.go.plush @@ -0,0 +1,241 @@ +package app + +import ( + "cosmossdk.io/core/appmodule" + storetypes "cosmossdk.io/store/types" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/ibc-go/modules/capability" + capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" + capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + icamodule "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts" + icacontroller "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller" + icacontrollerkeeper "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/keeper" + icacontrollertypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/types" + icahost "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/host" + icahostkeeper "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/host/keeper" + icahosttypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types" + ibcfee "github.com/cosmos/ibc-go/v8/modules/apps/29-fee" + ibcfeekeeper "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/keeper" + ibcfeetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types" + ibctransfer "github.com/cosmos/ibc-go/v8/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v8/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v8/modules/core" + ibcclienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" // nolint:staticcheck // Deprecated: params key table is needed for params migration + ibcconnectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" + porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper" + solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine" + ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" + ibcconsumer "github.com/cosmos/interchain-security/v3/x/ccv/consumer" + ibcconsumerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/consumer/keeper" + ibcconsumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + ibcccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" + // this line is used by starport scaffolding # ibc/app/import +) + +// registerIBCModules register IBC keepers and non dependency inject modules. +func (app *App) registerIBCModules(appOpts servertypes.AppOptions) error { + // set up non depinject support modules store keys + if err := app.RegisterStores( + storetypes.NewKVStoreKey(capabilitytypes.StoreKey), + storetypes.NewKVStoreKey(ibcexported.StoreKey), + storetypes.NewKVStoreKey(ibctransfertypes.StoreKey), + storetypes.NewKVStoreKey(ibcfeetypes.StoreKey), + storetypes.NewKVStoreKey(icahosttypes.StoreKey), + storetypes.NewKVStoreKey(ibcccvtypes.StoreKey), + storetypes.NewKVStoreKey(icacontrollertypes.StoreKey), + storetypes.NewMemoryStoreKey(capabilitytypes.MemStoreKey), + storetypes.NewTransientStoreKey(paramstypes.TStoreKey), + ); err != nil { + return err + } + + // register the key tables for legacy param subspaces + keyTable := ibcclienttypes.ParamKeyTable() + keyTable.RegisterParamSet(&ibcconnectiontypes.Params{}) + app.ParamsKeeper.Subspace(ibcexported.ModuleName).WithKeyTable(keyTable) + app.ParamsKeeper.Subspace(ibctransfertypes.ModuleName).WithKeyTable(ibctransfertypes.ParamKeyTable()) + app.ParamsKeeper.Subspace(icacontrollertypes.SubModuleName).WithKeyTable(icacontrollertypes.ParamKeyTable()) + app.ParamsKeeper.Subspace(ibcccvtypes.ModuleName).WithKeyTable(ibcccvtypes.ParamKeyTable()) + app.ParamsKeeper.Subspace(icahosttypes.SubModuleName).WithKeyTable(icahosttypes.ParamKeyTable()) + + // add capability keeper and ScopeToModule for ibc module + app.CapabilityKeeper = capabilitykeeper.NewKeeper( + app.AppCodec(), + app.GetKey(capabilitytypes.StoreKey), + app.GetMemKey(capabilitytypes.MemStoreKey), + ) + + // add capability keeper and ScopeToModule for ibc module + scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibcexported.ModuleName) + scopedIBCTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) + scopedICAControllerKeeper := app.CapabilityKeeper.ScopeToModule(icacontrollertypes.SubModuleName) + scopedICAHostKeeper := app.CapabilityKeeper.ScopeToModule(icahosttypes.SubModuleName) + scopedIBCConsumerKeeper := app.CapabilityKeeper.ScopeToModule(ibcccvtypes.ModuleName) + + // pre-initialize ConsumerKeeper to satisfy ibckeeper.NewKeeper + // which would panic on nil or zero keeper + // ConsumerKeeper implements StakingKeeper but all function calls result in no-ops so this is safe + // communication over IBC is not affected by these changes + app.ConsumerKeeper = ibcconsumerkeeper.NewNonZeroKeeper( + app.appCodec, + app.GetKey(ibcccvtypes.StoreKey), + app.GetSubspace(ibcccvtypes.ModuleName), + ) + + // Create IBC keeper + app.IBCKeeper = ibckeeper.NewKeeper( + app.appCodec, + app.GetKey(ibcexported.StoreKey), + app.GetSubspace(ibcexported.ModuleName), + app.ConsumerKeeper, + app.UpgradeKeeper, + scopedIBCKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // initialize the actual consumer keeper + app.ConsumerKeeper = ibcconsumerkeeper.NewKeeper( + app.appCodec, + app.GetKey(ibcccvtypes.StoreKey), + app.GetSubspace(ibcccvtypes.ModuleName), + scopedIBCConsumerKeeper, + app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.PortKeeper, + app.IBCKeeper.ConnectionKeeper, + app.IBCKeeper.ClientKeeper, + app.SlashingKeeper, + app.BankKeeper, + app.AccountKeeper, + &app.TransferKeeper, + app.IBCKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + authcodec.NewBech32Codec(sdk.Bech32PrefixValAddr), + authcodec.NewBech32Codec(sdk.Bech32PrefixConsAddr), + ) + + // register slashing module Slashing hooks to the consumer keeper + app.ConsumerKeeper = *app.ConsumerKeeper.SetHooks(app.SlashingKeeper.Hooks()) + consumerModule := ibcconsumer.NewAppModule(app.ConsumerKeeper, app.GetSubspace(ibcccvtypes.ModuleName)) + + app.IBCFeeKeeper = ibcfeekeeper.NewKeeper( + app.appCodec, app.GetKey(ibcfeetypes.StoreKey), + app.IBCKeeper.ChannelKeeper, // may be replaced with IBC middleware + app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, + ) + + // Create IBC transfer keeper + app.TransferKeeper = ibctransferkeeper.NewKeeper( + app.appCodec, + app.GetKey(ibctransfertypes.StoreKey), + app.GetSubspace(ibctransfertypes.ModuleName), + app.IBCFeeKeeper, + app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.PortKeeper, + app.AccountKeeper, + app.BankKeeper, + scopedIBCTransferKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // Create interchain account keepers + app.ICAHostKeeper = icahostkeeper.NewKeeper( + app.appCodec, + app.GetKey(icahosttypes.StoreKey), + app.GetSubspace(icahosttypes.SubModuleName), + app.IBCFeeKeeper, // use ics29 fee as ics4Wrapper in middleware stack + app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.PortKeeper, + app.AccountKeeper, + scopedICAHostKeeper, + app.MsgServiceRouter(), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper( + app.appCodec, + app.GetKey(icacontrollertypes.StoreKey), + app.GetSubspace(icacontrollertypes.SubModuleName), + app.IBCFeeKeeper, // use ics29 fee as ics4Wrapper in middleware stack + app.IBCKeeper.ChannelKeeper, + app.IBCKeeper.PortKeeper, + scopedICAControllerKeeper, + app.MsgServiceRouter(), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // Create IBC modules with ibcfee middleware + transferIBCModule := ibcfee.NewIBCMiddleware(ibctransfer.NewIBCModule(app.TransferKeeper), app.IBCFeeKeeper) + + // integration point for custom authentication modules + var noAuthzModule porttypes.IBCModule + icaControllerIBCModule := ibcfee.NewIBCMiddleware( + icacontroller.NewIBCMiddleware(noAuthzModule, app.ICAControllerKeeper), + app.IBCFeeKeeper, + ) + + icaHostIBCModule := ibcfee.NewIBCMiddleware(icahost.NewIBCModule(app.ICAHostKeeper), app.IBCFeeKeeper) + + // Create static IBC router, add transfer route, then set and seal it + ibcRouter := porttypes.NewRouter(). + AddRoute(ibctransfertypes.ModuleName, transferIBCModule). + AddRoute(icacontrollertypes.SubModuleName, icaControllerIBCModule). + AddRoute(ibcccvtypes.ModuleName, consumerModule). + AddRoute(icahosttypes.SubModuleName, icaHostIBCModule) + + // this line is used by starport scaffolding # ibc/app/module + + app.IBCKeeper.SetRouter(ibcRouter) + + app.ScopedIBCKeeper = scopedIBCKeeper + app.ScopedIBCTransferKeeper = scopedIBCTransferKeeper + app.ScopedICAHostKeeper = scopedICAHostKeeper + app.ScopedICAControllerKeeper = scopedICAControllerKeeper + + // register IBC modules + if err := app.RegisterModules( + ibc.NewAppModule(app.IBCKeeper), + ibctransfer.NewAppModule(app.TransferKeeper), + ibcfee.NewAppModule(app.IBCFeeKeeper), + icamodule.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper), + capability.NewAppModule(app.appCodec, *app.CapabilityKeeper, false), + ibctm.NewAppModule(), + solomachine.NewAppModule(), + consumerModule, + ); err != nil { + return err + } + + return nil +} + +// Since the IBC modules don't support dependency injection, we need to +// manually register the modules on the client side. +// This needs to be removed after IBC supports App Wiring. +func RegisterIBC(registry cdctypes.InterfaceRegistry) map[string]appmodule.AppModule { + modules := map[string]appmodule.AppModule{ + ibcexported.ModuleName: ibc.AppModule{}, + ibctransfertypes.ModuleName: ibctransfer.AppModule{}, + ibcfeetypes.ModuleName: ibcfee.AppModule{}, + icatypes.ModuleName: icamodule.AppModule{}, + capabilitytypes.ModuleName: capability.AppModule{}, + ibctm.ModuleName: ibctm.AppModule{}, + solomachine.ModuleName: solomachine.AppModule{}, + ibcconsumertypes.ModuleName: ibcconsumer.AppModule{}, + } + + for name, m := range modules { + module.CoreAppModuleBasicAdaptor(name, m).RegisterInterfaces(registry) + } + + return modules +} diff --git a/ignite/templates/app/files-consumer/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush b/ignite/templates/app/files-consumer/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush new file mode 100644 index 0000000000..b54a5b5395 --- /dev/null +++ b/ignite/templates/app/files-consumer/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush @@ -0,0 +1,183 @@ +package cmd + +import ( + "os" + "strings" + + "cosmossdk.io/client/v2/autocli" + clientv2keyring "cosmossdk.io/client/v2/autocli/keyring" + "cosmossdk.io/core/address" + "cosmossdk.io/depinject" + "cosmossdk.io/log" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/config" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/tx" + txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config" + "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + + "<%= ModulePath %>/app" +) + +// NewRootCmd creates a new root command for <%= BinaryNamePrefix %>d. It is called once in the main function. +func NewRootCmd() *cobra.Command { + initSDKConfig() + + var ( + txConfigOpts tx.ConfigOptions + autoCliOpts autocli.AppOptions + moduleBasicManager module.BasicManager + clientCtx client.Context + ) + + if err := depinject.Inject( + depinject.Configs(app.AppConfig(), + depinject.Supply( + log.NewNopLogger(), + ), + depinject.Provide( + ProvideClientContext, + ProvideKeyring, + ProvideStakingKeeper, + ), + ), + &txConfigOpts, + &autoCliOpts, + &moduleBasicManager, + &clientCtx, + ); err != nil { + panic(err) + } + + rootCmd := &cobra.Command{ + Use: app.Name + "d", + Short: "Start <%= AppName %> node", + SilenceErrors: true, + PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { + // set the default command outputs + cmd.SetOut(cmd.OutOrStdout()) + cmd.SetErr(cmd.ErrOrStderr()) + + clientCtx = clientCtx.WithCmdContext(cmd.Context()) + clientCtx, err := client.ReadPersistentCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } + + clientCtx, err = config.ReadFromClientConfig(clientCtx) + if err != nil { + return err + } + + // This needs to go after ReadFromClientConfig, as that function + // sets the RPC client needed for SIGN_MODE_TEXTUAL. + txConfigOpts.EnabledSignModes = append(txConfigOpts.EnabledSignModes, signing.SignMode_SIGN_MODE_TEXTUAL) + txConfigOpts.TextualCoinMetadataQueryFn = txmodule.NewGRPCCoinMetadataQueryFn(clientCtx) + txConfigWithTextual, err := tx.NewTxConfigWithOptions( + codec.NewProtoCodec(clientCtx.InterfaceRegistry), + txConfigOpts, + ) + if err != nil { + return err + } + + clientCtx = clientCtx.WithTxConfig(txConfigWithTextual) + if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil { + return err + } + + if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil { + return err + } + + customAppTemplate, customAppConfig := initAppConfig() + customCMTConfig := initCometBFTConfig() + + return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customCMTConfig) + }, + } + +<%= if (!IsChainMinimal) { %> + // Since the IBC modules don't support dependency injection, we need to + // manually register the modules on the client side. + // This needs to be removed after IBC supports App Wiring. + ibcModules := app.RegisterIBC(clientCtx.InterfaceRegistry) + for name, mod := range ibcModules { + moduleBasicManager[name] = module.CoreAppModuleBasicAdaptor(name, mod) + autoCliOpts.Modules[name] = mod + } +<% } %> + initRootCmd(rootCmd, clientCtx.TxConfig, moduleBasicManager) + + overwriteFlagDefaults(rootCmd, map[string]string{ + flags.FlagChainID: strings.ReplaceAll(app.Name, "-", ""), + flags.FlagKeyringBackend: "test", + }) + + if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil { + panic(err) + } + + return rootCmd +} + +func overwriteFlagDefaults(c *cobra.Command, defaults map[string]string) { + set := func(s *pflag.FlagSet, key, val string) { + if f := s.Lookup(key); f != nil { + f.DefValue = val + _ = f.Value.Set(val) + } + } + for key, val := range defaults { + set(c.Flags(), key, val) + set(c.PersistentFlags(), key, val) + } + for _, c := range c.Commands() { + overwriteFlagDefaults(c, defaults) + } +} + +func ProvideClientContext( + appCodec codec.Codec, + interfaceRegistry codectypes.InterfaceRegistry, + txConfig client.TxConfig, + legacyAmino *codec.LegacyAmino, +) client.Context { + clientCtx := client.Context{}. + WithCodec(appCodec). + WithInterfaceRegistry(interfaceRegistry). + WithTxConfig(txConfig). + WithLegacyAmino(legacyAmino). + WithInput(os.Stdin). + WithAccountRetriever(types.AccountRetriever{}). + WithHomeDir(app.DefaultNodeHome). + WithViper(app.Name) // env variable prefix + + // Read the config again to overwrite the default values with the values from the config file + clientCtx, _ = config.ReadFromClientConfig(clientCtx) + + return clientCtx +} + +func ProvideKeyring(clientCtx client.Context, addressCodec address.Codec) (clientv2keyring.Keyring, error) { + kb, err := client.NewKeyringFromBackend(clientCtx, clientCtx.Keyring.Backend()) + if err != nil { + return nil, err + } + + return keyring.NewAutoCLIKeyring(kb) +} + +// This is a hack for root.go, needs to be reverted eventually. +func ProvideStakingKeeper() stakingkeeper.Keeper { + return stakingkeeper.Keeper{} +} \ No newline at end of file diff --git a/ignite/templates/app/files/app/app.go.plush b/ignite/templates/app/files/app/app.go.plush index 2673a020f2..a7b997459e 100644 --- a/ignite/templates/app/files/app/app.go.plush +++ b/ignite/templates/app/files/app/app.go.plush @@ -45,7 +45,6 @@ import ( "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" - testdata_pulsar "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" diff --git a/ignite/templates/app/files/app/ibc.go.plush b/ignite/templates/app/files/app/ibc.go.plush index 9e255b0cab..9d46ba8cbc 100644 --- a/ignite/templates/app/files/app/ibc.go.plush +++ b/ignite/templates/app/files/app/ibc.go.plush @@ -172,8 +172,8 @@ func (app *App) registerIBCModules(appOpts servertypes.AppOptions) error { ibcfee.NewAppModule(app.IBCFeeKeeper), icamodule.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper), capability.NewAppModule(app.appCodec, *app.CapabilityKeeper, false), - ibctm.AppModule{}, - solomachine.AppModule{}, + ibctm.NewAppModule(), + solomachine.NewAppModule(), ); err != nil { return err } diff --git a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush index 8bb54e2f73..685899a07d 100644 --- a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush +++ b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/commands.go.plush @@ -31,8 +31,6 @@ import ( func initRootCmd( rootCmd *cobra.Command, txConfig client.TxConfig, - interfaceRegistry codectypes.InterfaceRegistry, - appCodec codec.Codec, basicManager module.BasicManager, ) { rootCmd.AddCommand( diff --git a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush index 46122b9c13..f49f6a3050 100644 --- a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush +++ b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush @@ -114,7 +114,7 @@ func NewRootCmd() *cobra.Command { autoCliOpts.Modules[name] = mod } <% } %> - initRootCmd(rootCmd, clientCtx.TxConfig, clientCtx.InterfaceRegistry, clientCtx.Codec, moduleBasicManager) + initRootCmd(rootCmd, clientCtx.TxConfig, moduleBasicManager) overwriteFlagDefaults(rootCmd, map[string]string{ flags.FlagChainID: strings.ReplaceAll(app.Name, "-", ""), @@ -161,7 +161,7 @@ func ProvideClientContext( WithViper(app.Name) // env variable prefix // Read the config again to overwrite the default values with the values from the config file - clientCtx, _ = config.ReadFromClientConfig(clientCtx) + clientCtx, _ = config.ReadDefaultValuesFromDefaultClientConfig(clientCtx) return clientCtx } diff --git a/ignite/templates/app/files/config.yml b/ignite/templates/app/files/config.yml.plush similarity index 75% rename from ignite/templates/app/files/config.yml rename to ignite/templates/app/files/config.yml.plush index 868e0e1f31..7a0bcb8594 100644 --- a/ignite/templates/app/files/config.yml +++ b/ignite/templates/app/files/config.yml.plush @@ -1,5 +1,6 @@ version: 1 -accounts: +validation: <%= if (IsConsumerChain) { %>consumer<% } else { %>sovereign<% } %> +accounts: - name: alice coins: - 20000token diff --git a/ignite/templates/app/files/go.mod.plush b/ignite/templates/app/files/go.mod.plush index 144347eebf..db5e037b8b 100644 --- a/ignite/templates/app/files/go.mod.plush +++ b/ignite/templates/app/files/go.mod.plush @@ -23,25 +23,28 @@ require ( cosmossdk.io/x/feegrant v0.1.0 cosmossdk.io/x/nft v0.1.0 cosmossdk.io/x/upgrade v0.1.1 - github.com/bufbuild/buf v1.28.1 + <%= if (IsConsumerChain) { %> + github.com/cosmos/interchain-security/v3 v3.2.0-consumer-rc0.0.20231123140529-1819e73f6197 + <% } %> + github.com/bufbuild/buf v1.30.0 github.com/cometbft/cometbft v0.38.5 - github.com/cosmos/cosmos-db v1.0.0 + github.com/cosmos/cosmos-db v1.0.2 github.com/cosmos/cosmos-proto v1.0.0-beta.4 - github.com/cosmos/cosmos-sdk v0.50.4 + github.com/cosmos/cosmos-sdk v0.50.5 github.com/cosmos/gogoproto v1.4.11 github.com/cosmos/ibc-go/modules/capability v1.0.0 github.com/cosmos/ibc-go/v8 v8.1.0 - github.com/golang/protobuf v1.5.3 + github.com/golang/protobuf v1.5.4 github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 - github.com/stretchr/testify v1.8.4 - golang.org/x/tools v0.18.0 - google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f - google.golang.org/grpc v1.60.1 + github.com/stretchr/testify v1.9.0 + golang.org/x/tools v0.19.0 + google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 + google.golang.org/grpc v1.62.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 - google.golang.org/protobuf v1.32.0 -) + google.golang.org/protobuf v1.33.0 +) \ No newline at end of file diff --git a/ignite/templates/app/options.go b/ignite/templates/app/options.go index 0176c02db1..c60b966de5 100644 --- a/ignite/templates/app/options.go +++ b/ignite/templates/app/options.go @@ -11,6 +11,7 @@ type Options struct { // IncludePrefixes is used to filter the files to include from the generator IncludePrefixes []string IsChainMinimal bool + IsConsumerChain bool } // Validate that options are usable. diff --git a/integration/app/cmd_app_test.go b/integration/app/cmd_app_test.go index f6b87ebfa9..ca28db1195 100644 --- a/integration/app/cmd_app_test.go +++ b/integration/app/cmd_app_test.go @@ -211,3 +211,15 @@ func TestGenerateAppWithEmptyModule(t *testing.T) { app.EnsureSteady() } + +func TestGenerateAConsumerApp(t *testing.T) { + var ( + env = envtest.New(t) + app = env.Scaffold("github.com/test/blog", "--consumer") + ) + + _, statErr := os.Stat(filepath.Join(app.SourcePath(), "x", "blog")) + require.False(t, os.IsNotExist(statErr), "the default module should be scaffolded") + + app.EnsureSteady() +}