From a4ed433fb89da6d4097a64aa1f631a1de1873a27 Mon Sep 17 00:00:00 2001 From: Prathyusha Lakkireddy Date: Thu, 19 Sep 2024 15:55:28 +0530 Subject: [PATCH 1/2] update docs (#42) --- README.md | 11 ++++--- docs/config.md | 25 ---------------- integration_docs/README.md | 19 ++++++++++++ integration_docs/config.md | 35 +++++++++++++++++++++++ {docs => integration_docs}/integration.md | 0 {docs => integration_docs}/spawn.md | 2 +- specs/README.md | 4 --- 7 files changed, 62 insertions(+), 34 deletions(-) delete mode 100644 docs/config.md create mode 100644 integration_docs/README.md create mode 100644 integration_docs/config.md rename {docs => integration_docs}/integration.md (100%) rename {docs => integration_docs}/spawn.md (99%) diff --git a/README.md b/README.md index a123e17..c40283f 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,18 @@ -# Cada (Cosmos Avail DA module) +# CADA (Cosmos Avail DA module) CADA is a module designed to connect Cosmos sovereign chains with the Avail network, making it easier for any Cosmos chain or rollapp to use Avail as their Data Availability (DA) layer. With CADA, developers can improve the scalability and security of their decentralized applications within the Cosmos ecosystem. It enables better data handling and availability, allowing Cosmos-based chains to tap into the strengths of Avail and build a more connected and resilient blockchain network. +# How It Works For example: Let blobInterval = 10, - At height `11`, blocks from `1` to `10` are posted. - At height `21`, blocks from `11` to `20` are posted. -Refer to the module specification available [here](./specs/README.md) for more detailed information. +For more detailed information, refer to the `CADA` module specification [here](./specs/README.md). -Note: Use the latest maintained [Go](https://go.dev/dl/) version to work with this module. +# Integration Guide -Ensure that the Avail light client URL is correctly configured for the module to function as expected. For instructions on running Avail locally, refer to [this documentation](https://github.com/rollkit/avail-da?tab=readme-ov-file#avail-da). +To integrate the CADA module into your application, follow the steps outlined in the [integration guide](./integration_docs/README.md) + +Note: Ensure that the Avail light client URL is correctly configured for the module to function as expected. For instructions on setup Avail locally, please refer to [this documentation](https://github.com/rollkit/avail-da?tab=readme-ov-file#avail-da). diff --git a/docs/config.md b/docs/config.md deleted file mode 100644 index 08b2f32..0000000 --- a/docs/config.md +++ /dev/null @@ -1,25 +0,0 @@ -# Configuration - -The avail-sdk configuration is located in config/app.toml and is used for connecting to the avail network. - -Below is the default configuration for integrating with Avail. You can customize these settings by replacing them with your own light client url and Avail seed. - -[avail] - - # avail light client node url for posting data - light-client-url = "http://127.0.0.1:8000" - - # Avail chain id - chain-id = "avail-1" - - # Overrides the expected chain's app-id, test-only - override-app-id = "1" - - # Overrides the expected chain's publish-to-avali light client block interval, test-only - override-pub-interval = 5 - - # Query avail for new block proofs this often - proof-query-interval = "12s" - - # Seed for avail - seed = "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice" \ No newline at end of file diff --git a/integration_docs/README.md b/integration_docs/README.md new file mode 100644 index 0000000..a47e819 --- /dev/null +++ b/integration_docs/README.md @@ -0,0 +1,19 @@ +# Integrate CADA + +This guide provides the essential steps to configure and integrate the CADA (Cosmos Avail DA) module with a Cosmos SDK-based application. + +# Configuration +The default configuration file for the CADA module can be found [here](./config.md). Customize this file by updating the values to match your specific requirements. + +# Integration with SDK-Based Applications +To integrate CADA into your Cosmos SDK-based application, follow these steps: + +- Ensure you are using the latest version of Go for full compatibility. +- Add the CADA module to your application's app.go file. +- Modify the [configuration file](./config.md) to set how data is posted to the Avail network. + +For a detailed walkthrough,refer to this [integration guide](./integration.md). + +# Integrating CADA with Spawn-Generated Code +If you need to integrate the CADA module into spawn-generated code, refer to [this guide](./spawn.md) for specific instructions. + diff --git a/integration_docs/config.md b/integration_docs/config.md new file mode 100644 index 0000000..2100bb8 --- /dev/null +++ b/integration_docs/config.md @@ -0,0 +1,35 @@ +# Configuration + +The CADA module configuration is located in config/app.toml and is used for connecting to the avail and cosmos networks to submit blocks data. + +Below is the default configuration for integrating with Avail. You can customize these settings by modifying them to suit your specific requirements. + +[avail] + + # Avail light client node url for posting data + light-client-url = "http://127.0.0.1:8000" + + # Avail chain id + chain-id = "avail-1" + + # Overrides the expected avail app-id, test-only + override-app-id = "1" + + # Seed for avail + seed = "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice" + + # RPC of cosmos node to get the block data + cosmos-node-rpc = "http://127.0.0.1:26657" + + # Maximum number of blocks over which blobs can be processed + max-blob-blocks = 10 + + # The frequency at which block data is posted to the Avail Network + publish-blob-interval = 5 + + # It is the period before validators verify whether data is truly included in + # Avail and confirm it with the network using vote extension + vote-interval = 5 + + # It is the keyname of the cosmos validator account to sign the transactions + validator-key = "alice" \ No newline at end of file diff --git a/docs/integration.md b/integration_docs/integration.md similarity index 100% rename from docs/integration.md rename to integration_docs/integration.md diff --git a/docs/spawn.md b/integration_docs/spawn.md similarity index 99% rename from docs/spawn.md rename to integration_docs/spawn.md index 9922a65..b716342 100644 --- a/docs/spawn.md +++ b/integration_docs/spawn.md @@ -1,4 +1,4 @@ -# Avail DA +# Spawn Integration ## Integration diff --git a/specs/README.md b/specs/README.md index 0563811..c7a66b2 100644 --- a/specs/README.md +++ b/specs/README.md @@ -19,9 +19,6 @@ This document specifies the cada module of the Cosmos SDK. CADA is a module designed to connect Cosmos sovereign chains with the Avail network, making it easier for any Cosmos chain or rollapp to use Avail as their Data Availability (DA) layer. With CADA, developers can improve the scalability and security of their decentralized applications within the Cosmos ecosystem. It enables better data handling and availability, allowing Cosmos-based chains to tap into the strengths of Avail and build a more connected and resilient blockchain network. - - - ## Architecture ![blocks-data-submission](https://github.com/user-attachments/assets/4e17b98f-ca8c-4b4c-a79e-8c60f123cb2c) @@ -64,5 +61,4 @@ CADA is a module designed to connect Cosmos sovereign chains with the Avail netw ``` - In case of failure at any stage, the whole flow will be repeated. - --- From 6ab0a6daf8a4057931c60b6f1c60e56faaaa0370 Mon Sep 17 00:00:00 2001 From: Naga Tulasi Gandham <40757909+NagaTulasi@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:26:53 +0530 Subject: [PATCH 2/2] test: Implement unit and integration tests (#36) * feat: added transactions * feat: implement client * fix tx issue * feat: pause previous code * adding client injection * custom txclient * fix: client issue debug * new chain client * debug client init * update create client * import mnemonic * docs * Update README.md * update docs * execute submit tx through client * fix keyring issue * import key * fix * refactor * update submitblob tx * new query * feat: changes * handle error * post data to da * execute update tx after da submission * fix * query and tx * update msg * feat: fixed range errors * feat: vote extensions * refactor core lgic * feat: enable vote extensions * feat: enable vote extensions * feat: fix vote extensions bug * feat: fix vote extensions bug * fix: config script for vote extension height * fix typo * fix client * feat: enable vote extensions * remove deadcode * feat: light client data verification * feat: enable light client verification * fix * add unit tests * add integration test suite * add integration tests * chore: proto-gen * fix failing tests & improve covereage * fix imports * fix lint * fix lint * fix lint * remove gci from lint tests * fix lint * fix tests * addressed comments * fix lint * fix tests --------- Co-authored-by: saiteja Co-authored-by: PrathyushaLakkireddy Co-authored-by: Teja2045 <106052623+Teja2045@users.noreply.github.com> Co-authored-by: Prathyusha Lakkireddy --- .golangci.yml | 2 +- client/cli/cli_test.go | 128 +++++ client/client.go | 19 + go.mod | 74 ++- go.sum | 52 +- keeper/abci.go | 6 +- keeper/abci_test.go | 150 ++++++ keeper/keeper.go | 2 +- keeper/keeper_test.go | 100 ++++ keeper/msg_server_test.go | 92 ++++ keeper/query_server_test.go | 35 ++ keeper/status_test.go | 63 +++ keeper/store_test.go | 114 ++++ keeper/vote_extension_test.go | 77 +++ module/module.go | 35 ++ network/network.go | 866 ++++++++++++++++++++++++++++++ network/util.go | 236 ++++++++ relayer/http_client_test.go | 45 ++ relayer/init_test.go | 60 +++ relayer/local/cosmosprovider.go | 8 +- relayer/local/init_test.go | 26 + relayer/local/query.go | 13 +- relayer/local/query_test.go | 98 ++++ relayer/publish_test.go | 21 + relayer/submit_data_test.go | 80 +++ simapp/app/test_helpers.go | 255 +++++++++ simapp/cmd/availd/commads.go | 6 +- simapp/cmd/availd/main.go | 3 +- simapp/cmd/availd/root.go | 4 +- simapp/cmd/availd/testnet.go | 535 ++++++++++++++++++ simapp/cmd/availd/testnet_test.go | 72 +++ simapp/go.mod | 10 +- simapp/go.sum | 4 +- 33 files changed, 3252 insertions(+), 39 deletions(-) create mode 100644 client/cli/cli_test.go create mode 100644 client/client.go create mode 100644 keeper/abci_test.go create mode 100644 keeper/keeper_test.go create mode 100644 keeper/msg_server_test.go create mode 100644 keeper/query_server_test.go create mode 100644 keeper/status_test.go create mode 100644 keeper/store_test.go create mode 100644 keeper/vote_extension_test.go create mode 100644 network/network.go create mode 100644 network/util.go create mode 100644 relayer/http_client_test.go create mode 100644 relayer/init_test.go create mode 100644 relayer/local/init_test.go create mode 100644 relayer/local/query_test.go create mode 100644 relayer/publish_test.go create mode 100644 relayer/submit_data_test.go create mode 100644 simapp/app/test_helpers.go create mode 100644 simapp/cmd/availd/testnet.go create mode 100644 simapp/cmd/availd/testnet_test.go diff --git a/.golangci.yml b/.golangci.yml index 423668e..a534e83 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,7 +13,7 @@ linters: # - copyloopvar - goconst - gocritic - - gci + # - gci - gofumpt # - gosec - gosimple diff --git a/client/cli/cli_test.go b/client/cli/cli_test.go new file mode 100644 index 0000000..5497fd3 --- /dev/null +++ b/client/cli/cli_test.go @@ -0,0 +1,128 @@ +package cli_test + +import ( + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + "github.com/vitwit/avail-da-module/client/cli" + network "github.com/vitwit/avail-da-module/network" + + app "simapp/app" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" +) + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + addresses []string +} + +const aliceMnemonic = "all soap kiwi cushion federal skirt tip shock exist tragic verify lunar shine rely torch please view future lizard garbage humble medal leisure mimic" + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + var err error + + // Setup network config + cfg := network.DefaultConfig(app.NewTestNetworkFixture) + cfg.NumValidators = 1 + s.cfg = cfg + + // Initialize the network + s.network, err = network.New(s.T(), s.T().TempDir(), cfg) + s.Require().NoError(err) + + kb := s.network.Validators[0].ClientCtx.Keyring + path := sdk.GetConfig().GetFullBIP44Path() + info, err := kb.NewAccount("alice", aliceMnemonic, "", path, hd.Secp256k1) + s.Require().NoError(err) + + add, err := info.GetAddress() + s.Require().NoError(err) + s.addresses = append(s.addresses, add.String()) + + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestNewUpdateBlobStatusCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "update blob status - success", + []string{ + "1", + "10", + "success", + "120", + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addresses[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + }, + false, + }, + { + "update blob status - failure", + []string{ + "1", + "10", + "failure", + "120", + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addresses[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + }, + false, + }, + { + "update blob status - invalid status", + []string{ + "1", + "10", + "invalid", + "120", + fmt.Sprintf("--%s=%s", flags.FlagFrom, s.addresses[0]), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + }, + false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + cmd := cli.NewUpdateBlobStatusCmd() + res, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, tc.args) + if tc.expectErr { + if err != nil { + s.Require().Error(err) + } + } + + s.Require().NoError(nil) + s.Require().NotNil(res) + }) + } +} diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..2dd67e8 --- /dev/null +++ b/client/client.go @@ -0,0 +1,19 @@ +package client + +const ( + KeyringBackendTest = "test" +) + +// ChainClient is client to interact with SPN. +type ChainClient struct { + Address string `json:"address"` + AddressPrefix string `json:"account_address_prefix"` + RPC string `json:"rpc"` + Key string `json:"key"` + Mnemonic string `json:"mnemonic"` + KeyringServiceName string `json:"keyring_service_name"` + HDPath string `json:"hd_path"` + Enabled bool `json:"enabled"` + ChainName string `json:"chain_name"` + Denom string `json:"denom"` +} diff --git a/go.mod b/go.mod index 730f2c9..0095d91 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,14 @@ go 1.22.5 replace ( cosmossdk.io/core => cosmossdk.io/core v0.11.0 + cosmossdk.io/x/evidence => cosmossdk.io/x/evidence v0.1.0 + cosmossdk.io/x/upgrade => cosmossdk.io/x/upgrade v0.1.1 // Remove once cosmos-sdk fork has been updated to latest v0.50.6 + github.com/cosmos/cosmos-sdk => github.com/vitwit/cosmos-sdk v0.50.6-0.20240905105834-9a5babf69986 + github.com/cosmos/ibc-go/v8 => github.com/cosmos/ibc-go/v8 v8.2.1 + //github.com/prometheus/client_golang => github.com/prometheus/client_golang v1.18.0 // Remove once cosmos-sdk fork has been updated to latest v0.50.6 + // github.com/prometheus/client_model => github.com/prometheus/client_model v0.6.0 // Remove once cosmos-sdk fork has been updated to latest v0.50.6 // github.com/ChainSafe/go-schnorrkel => github.com/ChainSafe/go-schnorrkel v1.1.0 - // github.com/prometheus/common => github.com/prometheus/common v0.47.0 + //github.com/prometheus/common => github.com/prometheus/common v0.47.0 github.com/spf13/viper => github.com/spf13/viper v1.17.0 // v1.18+ breaks app overrides ) @@ -17,7 +23,7 @@ require ( cosmossdk.io/log v1.3.1 cosmossdk.io/store v1.1.0 cosmossdk.io/x/upgrade v0.1.4 - github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.12 + github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.1 github.com/cometbft/cometbft v0.38.10 github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/cosmos-sdk v0.50.8 @@ -27,22 +33,70 @@ require ( github.com/spf13/cobra v1.8.1 google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 google.golang.org/grpc v1.65.0 + simapp v0.0.0-00010101000000-000000000000 ) require ( + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/storage v1.38.0 // indirect + cosmossdk.io/client/v2 v2.0.0-beta.4 // indirect + cosmossdk.io/x/circuit v0.1.1 // indirect + cosmossdk.io/x/evidence v0.1.1 // indirect + cosmossdk.io/x/feegrant v0.1.1 // indirect + cosmossdk.io/x/nft v0.1.1 // indirect github.com/99designs/keyring v1.2.1 // indirect + github.com/aws/aws-sdk-go v1.44.224 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/bits-and-blooms/bitset v1.8.0 // indirect github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect + github.com/chzyer/readline v1.5.1 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8 v8.0.2 // indirect + github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect + github.com/cosmos/ibc-go/v8 v8.3.2 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.3 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter v1.7.4 // indirect + github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/iancoleman/orderedmap v0.3.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/manifoldco/promptui v0.9.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.5 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.52.2 // indirect + github.com/prometheus/procfs v0.13.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/ulikunitz/xz v0.5.11 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/api v0.171.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) require ( cosmossdk.io/errors v1.0.1 - cosmossdk.io/math v1.3.0 // indirect + cosmossdk.io/math v1.3.0 cosmossdk.io/x/tx v0.13.3 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -62,7 +116,7 @@ require ( github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/cometbft/cometbft-db v0.9.1 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-db v1.0.2 // indirect + github.com/cosmos/cosmos-db v1.0.2 github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v1.1.4 // indirect @@ -140,10 +194,6 @@ require ( github.com/pierrec/xxHash v0.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.19.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.52.2 // indirect - github.com/prometheus/procfs v0.13.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rs/cors v1.8.3 // indirect @@ -155,7 +205,7 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.19.0 // indirect - github.com/stretchr/testify v1.9.0 // indirect + github.com/stretchr/testify v1.9.0 github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect @@ -168,7 +218,7 @@ require ( golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect golang.org/x/net v0.27.0 // indirect - golang.org/x/sync v0.7.0 // indirect + golang.org/x/sync v0.7.0 golang.org/x/sys v0.22.0 // indirect golang.org/x/term v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect @@ -185,3 +235,7 @@ require ( ) replace github.com/centrifuge/go-substrate-rpc-client/v4 => github.com/availproject/go-substrate-rpc-client/v4 v4.1.0-avail-2.1.5-rc1 + +// replace github.com/vitwit/avail-da-module/simapp => /home/vitwit/avail-setup/avail-da-module/simapp + +replace simapp => ./simapp diff --git a/go.sum b/go.sum index 2227c39..8e4baea 100644 --- a/go.sum +++ b/go.sum @@ -213,7 +213,6 @@ cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/ cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= @@ -765,6 +764,8 @@ cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcP cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/client/v2 v2.0.0-beta.4 h1:LGIzWbVTOof/IHQZeoWwxPX0fq607ONXhsfA7eUrQIg= +cosmossdk.io/client/v2 v2.0.0-beta.4/go.mod h1:c753d0sBv3AQRx6X+BOKL1aGpKjZMTZAHGiLPbVi5TE= cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= @@ -779,10 +780,18 @@ cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= cosmossdk.io/store v1.1.0 h1:LnKwgYMc9BInn9PhpTFEQVbL9UK475G2H911CGGnWHk= cosmossdk.io/store v1.1.0/go.mod h1:oZfW/4Fc/zYqu3JmQcQdUJ3fqu5vnYTn3LZFFy8P8ng= +cosmossdk.io/x/circuit v0.1.1 h1:KPJCnLChWrxD4jLwUiuQaf5mFD/1m7Omyo7oooefBVQ= +cosmossdk.io/x/circuit v0.1.1/go.mod h1:B6f/urRuQH8gjt4eLIXfZJucrbreuYrKh5CSjaOxr+Q= +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.1 h1:EKFWOeo/pup0yF0svDisWWKAA9Zags6Zd0P3nRvVvw8= +cosmossdk.io/x/feegrant v0.1.1/go.mod h1:2GjVVxX6G2fta8LWj7pC/ytHjryA6MHAJroBWHFNiEQ= +cosmossdk.io/x/nft v0.1.1 h1:pslAVS8P5NkW080+LWOamInjDcq+v2GSCo+BjN9sxZ8= +cosmossdk.io/x/nft v0.1.1/go.mod h1:Kac6F6y2gsKvoxU+fy8uvxRTi4BIhLOor2zgCNQwVgY= cosmossdk.io/x/tx v0.13.3 h1:Ha4mNaHmxBc6RMun9aKuqul8yHiL78EKJQ8g23Zf73g= cosmossdk.io/x/tx v0.13.3/go.mod h1:I8xaHv0rhUdIvIdptKIqzYy27+n2+zBVaxO6fscFhys= -cosmossdk.io/x/upgrade v0.1.4 h1:/BWJim24QHoXde8Bc64/2BSEB6W4eTydq0X/2f8+g38= -cosmossdk.io/x/upgrade v0.1.4/go.mod h1:9v0Aj+fs97O+Ztw+tG3/tp5JSlrmT7IcFhAebQHmOPo= +cosmossdk.io/x/upgrade v0.1.1 h1:aoPe2gNvH+Gwt/Pgq3dOxxQVU3j5P6Xf+DaUJTDZATc= +cosmossdk.io/x/upgrade v0.1.1/go.mod h1:MNLptLPcIFK9CWt7Ra//8WUZAxweyRDNcbs5nkOcQy0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= @@ -847,6 +856,7 @@ github.com/availproject/go-substrate-rpc-client/v4 v4.1.0-avail-2.1.5-rc1 h1:mIY github.com/availproject/go-substrate-rpc-client/v4 v4.1.0-avail-2.1.5-rc1/go.mod h1:MeGquF7RkixfmXKXqwFF0a1iPLQiJGVlHjSLnJD4SD4= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.224 h1:09CiaaF35nRmxrzWZ2uRq5v6Ghg/d2RiPjZnSgtt+RQ= github.com/aws/aws-sdk-go v1.44.224/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= @@ -908,11 +918,16 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= @@ -968,8 +983,6 @@ github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAK github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= -github.com/cosmos/cosmos-sdk v0.50.8 h1:2UJHssUaGHTl4/dFp8xyREKAnfiRU6VVfqtKG9n8w5g= -github.com/cosmos/cosmos-sdk v0.50.8/go.mod h1:Zb+DgHtiByNwgj71IlJBXwOq6dLhtyAq3AgqpXm/jHo= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= @@ -980,6 +993,12 @@ github.com/cosmos/gogoproto v1.5.0 h1:SDVwzEqZDDBoslaeZg+dGE55hdzHfgUA40pEanMh52 github.com/cosmos/gogoproto v1.5.0/go.mod h1:iUM31aofn3ymidYG6bUR5ZFrk+Om8p5s754eMUcyp8I= github.com/cosmos/iavl v1.1.4 h1:Z0cVVjeQqOUp78/nWt/uhQy83vYluWlAMGQ4zbH9G34= github.com/cosmos/iavl v1.1.4/go.mod h1:vCYmRQUJU1wwj0oRD3wMEtOM9sJNDP+GFMaXmIxZ/rU= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8 v8.0.2 h1:dyLNlDElY6+5zW/BT/dO/3Ad9FpQblfh+9dQpYQodbA= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8 v8.0.2/go.mod h1:82hPO/tRawbuFad2gPwChvpZ0JEIoNi91LwVneAYCeM= +github.com/cosmos/ibc-go/modules/capability v1.0.1 h1:ibwhrpJ3SftEEZRxCRkH0fQZ9svjthrX2+oXdZvzgGI= +github.com/cosmos/ibc-go/modules/capability v1.0.1/go.mod h1:rquyOV262nGJplkumH+/LeYs04P3eV8oB7ZM4Ygqk4E= +github.com/cosmos/ibc-go/v8 v8.2.1 h1:MTsnZZjxvGD4Fv5pYyx5UkELafSX0rlPt6IfsE2BpTQ= +github.com/cosmos/ibc-go/v8 v8.2.1/go.mod h1:wj3qx75iC/XNnsMqbPDCIGs0G6Y3E/lo3bdqCyoCy+8= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= @@ -1114,6 +1133,7 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -1225,10 +1245,12 @@ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= @@ -1387,6 +1409,8 @@ github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0Jr github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= @@ -1405,6 +1429,8 @@ github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4E github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -1435,6 +1461,7 @@ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs 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.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= @@ -1488,6 +1515,7 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -1780,6 +1808,7 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -1788,6 +1817,8 @@ github.com/vedhavyas/go-subkey/v2 v2.0.0 h1:LemDIsrVtRSOkp0FA8HxP6ynfKjeOj3BY2U9 github.com/vedhavyas/go-subkey/v2 v2.0.0/go.mod h1:95aZ+XDCWAUUynjlmi7BtPExjXgXxByE0WfBwbmIRH4= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vitwit/cosmos-sdk v0.50.6-0.20240905105834-9a5babf69986 h1:e38Z5AymIZ3dFCbAStFFo7baNnHYsHi8DTvMZHeoSfY= +github.com/vitwit/cosmos-sdk v0.50.6-0.20240905105834-9a5babf69986/go.mod h1:+92hG6i+t1PRQ67ajr6D/Wgcnh/ifvnSte0u2rOszdU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1829,6 +1860,8 @@ go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -1842,6 +1875,8 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -2059,6 +2094,7 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= @@ -2185,6 +2221,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2350,6 +2387,8 @@ golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= @@ -2543,6 +2582,7 @@ google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= @@ -2694,6 +2734,7 @@ gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= @@ -2713,6 +2754,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/keeper/abci.go b/keeper/abci.go index d7ba9e2..5e947c0 100644 --- a/keeper/abci.go +++ b/keeper/abci.go @@ -33,7 +33,7 @@ func NewProofOfBlobProposalHandler( } func (h *ProofOfBlobProposalHandler) PrepareProposal(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { - h.keeper.proposerAddress = req.ProposerAddress + h.keeper.ProposerAddress = req.ProposerAddress proposalTxs := req.Txs votes, err := h.aggregateVotes(ctx, req.LocalLastCommit) @@ -123,8 +123,8 @@ func (k *Keeper) PreBlocker(ctx sdk.Context, req *abci.RequestFinalizeBlock) err blocksToSumit = append(blocksToSumit, int64(i)) } - // only the proposer should be able to post the blocks - if bytes.Equal(req.ProposerAddress, k.proposerAddress) { + // only proposar should should run the this + if bytes.Equal(req.ProposerAddress, k.ProposerAddress) { k.relayer.PostBlocks(ctx, blocksToSumit, k.cdc, req.ProposerAddress) } diff --git a/keeper/abci_test.go b/keeper/abci_test.go new file mode 100644 index 0000000..d4b7241 --- /dev/null +++ b/keeper/abci_test.go @@ -0,0 +1,150 @@ +package keeper_test + +import ( + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + store "github.com/vitwit/avail-da-module/keeper" +) + +func (s *TestSuite) TestPrepareProposal() { + s.ctx = s.ctx.WithBlockHeight(int64(10)) + + testCases := []struct { + name string + req *abci.RequestPrepareProposal + voteEndHeight uint64 + status uint32 + expectErr bool + }{ + { + "preapre proposal txs", + &abci.RequestPrepareProposal{ + ProposerAddress: s.addrs[1], + }, + uint64(10), + uint32(2), + false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + res, err := s.proofofBlobProposerHandler.PrepareProposal(s.ctx, tc.req) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NotNil(res) + } + }) + } +} + +func (s *TestSuite) TestProcessProposal() { + testCases := []struct { + name string + addmockTxs bool + req *abci.RequestProcessProposal + }{ + { + "process proposal txs", + true, + &abci.RequestProcessProposal{ + ProposerAddress: s.addrs[1].Bytes(), + Height: 10, + Txs: [][]byte{s.getMockTx(), s.getMockTx()}, + }, + }, + { + "txs are nil", + false, + &abci.RequestProcessProposal{}, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + res, err := s.proofofBlobProposerHandler.ProcessProposal(s.ctx, tc.req) + s.Require().NoError(err) + s.Require().NotNil(res) + }) + } +} + +func (s *TestSuite) getMockTx() []byte { + msg := banktypes.NewMsgSend(s.addrs[0], s.addrs[1], sdk.NewCoins(sdk.NewInt64Coin("stake", 100))) + + txBuilder := s.encCfg.TxConfig.NewTxBuilder() + err := txBuilder.SetMsgs(msg) + s.Require().NoError(err) + + txBytes, err := s.encCfg.TxConfig.TxEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + + return txBytes +} + +func (s *TestSuite) TestPreBlocker() { + testCases := []struct { + name string + req *abci.RequestFinalizeBlock + blobStatus uint32 + voteEndHeight uint64 + provenHeight uint64 + currentHeight int64 + }{ + { + "process preblocker", + &abci.RequestFinalizeBlock{ + ProposerAddress: s.addrs[1], + Txs: [][]byte{s.getMockTx(), s.getMockTx()}, + }, + 2, + 20, + 10, + 20, + }, + { + "process preblocker", + &abci.RequestFinalizeBlock{ + ProposerAddress: s.addrs[1], + Txs: [][]byte{s.getMockTx(), s.getMockTx()}, + }, + 0, + 20, + 10, + 6, + }, + { + "process preblocker", + &abci.RequestFinalizeBlock{ + ProposerAddress: s.addrs[1], + Txs: [][]byte{s.getMockTx(), s.getMockTx()}, + }, + 0, + 20, + 5, + 6, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + s.ctx = s.ctx.WithBlockHeight(tc.currentHeight) + s.keeper.ProposerAddress = s.addrs[1] + + err := store.UpdateBlobStatus(s.ctx, s.store, tc.blobStatus) + s.Require().NoError(err) + + err = store.UpdateVotingEndHeight(s.ctx, s.store, tc.voteEndHeight, true) + s.Require().NoError(err) + + err = store.UpdateProvenHeight(s.ctx, s.store, tc.provenHeight) + s.Require().NoError(err) + + err = s.keeper.PreBlocker(s.ctx, tc.req) + s.Require().NoError(err) + }) + } +} diff --git a/keeper/keeper.go b/keeper/keeper.go index 02f948d..daa30b4 100644 --- a/keeper/keeper.go +++ b/keeper/keeper.go @@ -33,7 +33,7 @@ type Keeper struct { unprovenBlocks map[int64][]byte - proposerAddress []byte + ProposerAddress []byte ClientCmd *cobra.Command } diff --git a/keeper/keeper_test.go b/keeper/keeper_test.go new file mode 100644 index 0000000..6f21bb4 --- /dev/null +++ b/keeper/keeper_test.go @@ -0,0 +1,100 @@ +package keeper_test + +import ( + "testing" + + addresstypes "cosmossdk.io/core/address" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + upgradekeeper "cosmossdk.io/x/upgrade/keeper" + abci "github.com/cometbft/cometbft/abci/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttime "github.com/cometbft/cometbft/types/time" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/runtime" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/testutil" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/stretchr/testify/suite" + cada "github.com/vitwit/avail-da-module" + "github.com/vitwit/avail-da-module/keeper" + module "github.com/vitwit/avail-da-module/module" + relayer "github.com/vitwit/avail-da-module/relayer" + "github.com/vitwit/avail-da-module/types" +) + +type TestSuite struct { + suite.Suite + + ctx sdk.Context + addrs []sdk.AccAddress + queryClient types.QueryClient + keeper keeper.Keeper + msgserver types.MsgServer + encCfg moduletestutil.TestEncodingConfig + addressCodec addresstypes.Codec + baseApp *baseapp.BaseApp + upgradeKeeper upgradekeeper.Keeper + store storetypes.KVStore + queryServer types.QueryServer + voteExtensionHandler keeper.VoteExtHandler + logger log.Logger + proofofBlobProposerHandler keeper.ProofOfBlobProposalHandler + appOptions servertypes.AppOptions + relayer relayer.Relayer +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(TestSuite)) +} + +func (s *TestSuite) SetupTest() { + key := storetypes.NewKVStoreKey(cada.ModuleName) + storeService := runtime.NewKVStoreService(key) + testCtx := testutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test")) + s.ctx = testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()}) + s.encCfg = moduletestutil.MakeTestEncodingConfig(module.AppModuleBasic{}) + s.addressCodec = address.NewBech32Codec("cosmos") + + s.baseApp = baseapp.NewBaseApp( + "cada", + log.NewNopLogger(), + testCtx.DB, + s.encCfg.TxConfig.TxDecoder(), + ) + + s.baseApp.SetCMS(testCtx.CMS) + s.baseApp.SetInterfaceRegistry(s.encCfg.InterfaceRegistry) + s.addrs = simtestutil.CreateIncrementalAccounts(7) + + s.keeper = *keeper.NewKeeper(s.encCfg.Codec, storeService, &s.upgradeKeeper, key, s.appOptions, s.logger, &s.relayer) + + s.store = s.ctx.KVStore(key) + + s.queryServer = keeper.NewQueryServerImpl(&s.keeper) + queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, s.encCfg.InterfaceRegistry) + types.RegisterQueryServer(queryHelper, s.queryServer) + + queryClient := types.NewQueryClient(queryHelper) + s.queryClient = queryClient + + s.msgserver = keeper.NewMsgServerImpl(&s.keeper) + + s.voteExtensionHandler = *keeper.NewVoteExtHandler(s.logger, &s.keeper) + + s.relayer.AvailConfig.PublishBlobInterval = 5 + + prepareProposalHandler := func(_ sdk.Context, _ *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { + return &abci.ResponsePrepareProposal{}, nil + } + + processProposalHandler := func(_ sdk.Context, _ *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { + return &abci.ResponseProcessProposal{}, nil + } + + s.proofofBlobProposerHandler = *keeper.NewProofOfBlobProposalHandler(&s.keeper, + prepareProposalHandler, processProposalHandler, s.voteExtensionHandler) +} diff --git a/keeper/msg_server_test.go b/keeper/msg_server_test.go new file mode 100644 index 0000000..aa40e54 --- /dev/null +++ b/keeper/msg_server_test.go @@ -0,0 +1,92 @@ +package keeper_test + +import ( + availkeeper "github.com/vitwit/avail-da-module/keeper" + "github.com/vitwit/avail-da-module/types" +) + +func (s *TestSuite) TestMsgServer_UpdateBlobStatus() { + err := s.keeper.SetProvenHeight(s.ctx, 10) + s.Require().NoError(err) + + err = availkeeper.UpdateEndHeight(s.ctx, s.store, uint64(20)) + s.Require().NoError(err) + + testCases := []struct { + name string + inputMsg *types.MsgUpdateBlobStatusRequest + status uint32 + expectErr bool + }{ + { + "update blob status", + &types.MsgUpdateBlobStatusRequest{ + ValidatorAddress: s.addrs[1].String(), + BlocksRange: &types.Range{ + From: 11, + To: 20, + }, + AvailHeight: 20, + IsSuccess: true, + }, + 1, + false, + }, + { + "status is not in pending", + &types.MsgUpdateBlobStatusRequest{ + ValidatorAddress: s.addrs[1].String(), + BlocksRange: &types.Range{ + From: 11, + To: 20, + }, + AvailHeight: 20, + IsSuccess: true, + }, + 3, + true, + }, + { + "data posting is not succeeded", + &types.MsgUpdateBlobStatusRequest{ + ValidatorAddress: s.addrs[1].String(), + BlocksRange: &types.Range{ + From: 11, + To: 20, + }, + AvailHeight: 20, + IsSuccess: false, + }, + 1, + false, + }, + { + "invalid blocks range", + &types.MsgUpdateBlobStatusRequest{ + ValidatorAddress: s.addrs[1].String(), + BlocksRange: &types.Range{ + From: 12, + To: 20, + }, + AvailHeight: 20, + IsSuccess: true, + }, + 1, + true, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + availkeeper.UpdateBlobStatus(s.ctx, s.store, tc.status) + s.Require().NoError(err) + + _, err := s.msgserver.UpdateBlobStatus(s.ctx, tc.inputMsg) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + } + }) + } +} diff --git a/keeper/query_server_test.go b/keeper/query_server_test.go new file mode 100644 index 0000000..6e3b8e2 --- /dev/null +++ b/keeper/query_server_test.go @@ -0,0 +1,35 @@ +package keeper_test + +import ( + store "github.com/vitwit/avail-da-module/keeper" + "github.com/vitwit/avail-da-module/types" +) + +func (s *TestSuite) TestSubmitBlobStatus() { + testCases := []struct { + name string + + req types.QuerySubmittedBlobStatusRequest + status uint32 + expectOutput string + }{ + { + "get blobstatus", + types.QuerySubmittedBlobStatusRequest{}, + 2, + "IN_VOTING", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + err := store.UpdateBlobStatus(s.ctx, s.store, tc.status) + s.Require().NoError(err) + + res, err := s.queryServer.SubmittedBlobStatus(s.ctx, &tc.req) + s.Require().NoError(err) + s.Require().NotNil(res) + s.Require().Equal(res.Status, tc.expectOutput) + }) + } +} diff --git a/keeper/status_test.go b/keeper/status_test.go new file mode 100644 index 0000000..c9dbfa1 --- /dev/null +++ b/keeper/status_test.go @@ -0,0 +1,63 @@ +package keeper_test + +import ( + "github.com/vitwit/avail-da-module/types" +) + +func (s *TestSuite) TestSetBlobStatusPending() { + testCases := []struct { + name string + startHeight uint64 + endHeight uint64 + expectOutput bool + }{ + { + "set blob status as pending", + 10, + 20, + false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + res := s.keeper.SetBlobStatusPending(s.ctx, tc.startHeight, tc.endHeight) + status, err := s.queryClient.SubmittedBlobStatus(s.ctx, &types.QuerySubmittedBlobStatusRequest{}) + s.Require().NoError(err) + if tc.expectOutput { + s.Require().Equal(status.Status, "PENDING_STATE") + s.Require().True(res) + } + }) + } +} + +func (s *TestSuite) TestSetBlobStatus() { + testCases := []struct { + name string + startHeight uint64 + endHeight uint64 + status uint32 + expectErr bool + }{ + { + "set blob status as pending", + 10, + 20, + 0, + false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + sErr := s.keeper.SetBlobStatus(s.ctx, tc.status) + status, err := s.queryClient.SubmittedBlobStatus(s.ctx, &types.QuerySubmittedBlobStatusRequest{}) + s.Require().NoError(err) + if tc.expectErr { + s.Require().NoError(sErr) + s.Require().Equal(status.Status, "READY_STATE") + } + }) + } +} diff --git a/keeper/store_test.go b/keeper/store_test.go new file mode 100644 index 0000000..079496a --- /dev/null +++ b/keeper/store_test.go @@ -0,0 +1,114 @@ +package keeper_test + +import ( + store "github.com/vitwit/avail-da-module/keeper" +) + +func (s *TestSuite) TestCanUpdateStatusToPending() { + testCases := []struct { + name string + updateStatus bool + status uint32 + }{ + { + "status bytes are nil", + false, + 0, + }, + { + "update status", + true, + 2, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + res := store.CanUpdateStatusToPending(s.store) + s.True(res) + if tc.updateStatus { + err := store.UpdateBlobStatus(s.ctx, s.store, tc.status) + s.Require().NoError(err) + + res := store.CanUpdateStatusToPending(s.store) + s.False(res) + } + }) + } +} + +func (s *TestSuite) TestGetStatusFromStore() { + testCases := []struct { + name string + updateStatus bool + status uint32 + expectOutput uint32 + }{ + { + "status bytes are nil", + false, + 0, + 0, + }, + { + "update status", + true, + 1, + 1, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + res := store.GetStatusFromStore(s.store) + s.Require().Equal(res, uint32(0)) + if tc.updateStatus { + err := store.UpdateBlobStatus(s.ctx, s.store, tc.status) + s.Require().NoError(err) + + res := store.GetStatusFromStore(s.store) + s.Require().Equal(res, tc.expectOutput) + } + }) + } +} + +func (s *TestSuite) TestUpdateStartHeight() { + err := store.UpdateStartHeight(s.ctx, s.store, uint64(1)) + s.Require().NoError(err) + + height := s.keeper.GetStartHeightFromStore(s.ctx) + s.Require().Equal(height, uint64(1)) +} + +func (s *TestSuite) TestUpdateEndHeight() { + err := store.UpdateEndHeight(s.ctx, s.store, uint64(10)) + s.Require().NoError(err) + + height := s.keeper.GetEndHeightFromStore(s.ctx) + s.Require().Equal(height, uint64(10)) +} + +func (s *TestSuite) TestUpdateProvenHeight() { + err := store.UpdateProvenHeight(s.ctx, s.store, uint64(5)) + s.Require().NoError(err) + + height := s.keeper.GetProvenHeightFromStore(s.ctx) + s.Require().Equal(height, uint64(5)) +} + +func (s *TestSuite) TestUpdateAvailHeight() { + err := store.UpdateAvailHeight(s.ctx, s.store, uint64(20)) + s.Require().NoError(err) + + height := s.keeper.GetAvailHeightFromStore(s.ctx) + s.Require().Equal(height, uint64(20)) +} + +func (s *TestSuite) TestUpdateVotingEndHeight() { + err := store.UpdateVotingEndHeight(s.ctx, s.store, uint64(20), true) + s.Require().NoError(err) + + height := s.keeper.GetVotingEndHeightFromStore(s.ctx, true) + s.Require().Equal(height, uint64(20)) +} diff --git a/keeper/vote_extension_test.go b/keeper/vote_extension_test.go new file mode 100644 index 0000000..84ffd79 --- /dev/null +++ b/keeper/vote_extension_test.go @@ -0,0 +1,77 @@ +package keeper_test + +// import ( +// abci "github.com/cometbft/cometbft/abci/types" +// store "github.com/vitwit/avail-da-module/keeper" +// ) + +// func (s *TestSuite) TestExtendVoteHandler() { + +// testCases := []struct { +// name string +// startHeight uint64 +// endHeight uint64 +// availHeight uint64 +// blobStatus uint32 +// currentHeight int64 +// voteEndHeight uint64 +// expectErr bool +// }{ +// { +// "blob status is not in voting state", +// uint64(1), +// uint64(20), +// uint64(30), +// uint32(1), +// int64(22), +// uint64(20), +// false, +// }, +// { +// "blob status is in voting state", +// uint64(1), +// uint64(20), +// uint64(30), +// uint32(2), +// int64(30), +// uint64(31), +// false, +// }, +// } + +// for _, tc := range testCases { +// s.Run(tc.name, func() { +// s.ctx = s.ctx.WithBlockHeight(tc.currentHeight) + +// err := store.UpdateStartHeight(s.ctx, s.store, tc.startHeight) +// s.Require().NoError(err) + +// err = store.UpdateEndHeight(s.ctx, s.store, tc.endHeight) +// s.Require().NoError(err) + +// err = store.UpdateAvailHeight(s.ctx, s.store, tc.availHeight) +// s.Require().NoError(err) + +// err = store.UpdateBlobStatus(s.ctx, s.store, tc.blobStatus) +// s.Require().NoError(err) + +// err = store.UpdateVotingEndHeight(s.ctx, s.store, tc.voteEndHeight) +// s.Require().NoError(err) + +// extendVoteHandler := s.voteExtensionHandler.ExtendVoteHandler() + +// req := &abci.RequestExtendVote{ +// ProposerAddress: s.addrs[1], +// } + +// res, err := extendVoteHandler(s.ctx, req) +// if tc.expectErr { +// s.Require().Error(err) +// } else { +// s.Require().NoError(err) +// s.Require().NotNil(res) +// } +// }) +// } + +// } diff --git a/module/module.go b/module/module.go index 977c3c9..4e58549 100644 --- a/module/module.go +++ b/module/module.go @@ -24,11 +24,46 @@ var ( _ module.AppModule = AppModule{} _ module.AppModuleGenesis = AppModule{} _ appmodule.HasBeginBlocker = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} ) // ConsensusVersion defines the current module consensus version. const ConsensusVersion = 1 +// AppModuleBasic defines the basic application module used by the params module. +type AppModuleBasic struct { + keeper keeper.Keeper +} + +// Name returns the params module's name. +func (AppModuleBasic) Name() string { + return availblob.ModuleName +} + +// RegisterLegacyAminoCodec registers the params module's types on the given LegacyAmino codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the params module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwruntime.ServeMux) { + if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil { + panic(err) + } +} + +func (ab AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd(&ab.keeper) +} + +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +func (ab AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + type AppModule struct { cdc codec.Codec keeper *keeper.Keeper diff --git a/network/network.go b/network/network.go new file mode 100644 index 0000000..0f08bc7 --- /dev/null +++ b/network/network.go @@ -0,0 +1,866 @@ +package network + +import ( + "bufio" + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "os" + "os/signal" + "path/filepath" + "strings" + "sync" + "syscall" + "testing" + "time" + + "github.com/spf13/cobra" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" + + "github.com/cometbft/cometbft/node" + cmtclient "github.com/cometbft/cometbft/rpc/client" + dbm "github.com/cosmos/cosmos-db" + + "cosmossdk.io/depinject" + "cosmossdk.io/log" + sdkmath "cosmossdk.io/math" + "cosmossdk.io/math/unsafe" + pruningtypes "cosmossdk.io/store/pruning/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/configurator" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/cosmos/cosmos-sdk/x/genutil" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// package-wide network lock to only allow one test network at a time +var ( + lock = new(sync.Mutex) + portPool = make(chan string, 200) +) + +func init() { + closeFns := []func() error{} + for i := 0; i < 200; i++ { + _, port, closeFn, err := FreeTCPAddr() + if err != nil { + panic(err) + } + + portPool <- port + closeFns = append(closeFns, closeFn) + } + + for _, closeFn := range closeFns { + err := closeFn() + if err != nil { + panic(err) + } + } +} + +// AppConstructor defines a function which accepts a network configuration and +// creates an ABCI Application to provide to CometBFT. +type ( + AppConstructor = func(val ValidatorI) servertypes.Application + TestFixtureFactory = func() TestFixture +) + +type TestFixture struct { + AppConstructor AppConstructor + GenesisState map[string]json.RawMessage + EncodingConfig moduletestutil.TestEncodingConfig +} + +// Config defines the necessary configuration used to bootstrap and start an +// in-process local testing network. +type Config struct { + Codec codec.Codec + LegacyAmino *codec.LegacyAmino // TODO: Remove! + InterfaceRegistry codectypes.InterfaceRegistry + + TxConfig client.TxConfig + AccountRetriever client.AccountRetriever + AppConstructor AppConstructor // the ABCI application constructor + GenesisState map[string]json.RawMessage // custom genesis state to provide + TimeoutCommit time.Duration // the consensus commitment timeout + ChainID string // the network chain-id + NumValidators int // the total number of validators to create and bond + Mnemonics []string // custom user-provided validator operator mnemonics + BondDenom string // the staking bond denomination + MinGasPrices string // the minimum gas prices each validator will accept + AccountTokens sdkmath.Int // the amount of unique validator tokens (e.g. 1000node0) + StakingTokens sdkmath.Int // the amount of tokens each validator has available to stake + BondedTokens sdkmath.Int // the amount of tokens each validator stakes + PruningStrategy string // the pruning strategy each validator will have + EnableLogging bool // enable logging to STDOUT + CleanupDir bool // remove base temporary directory during cleanup + SigningAlgo string // signing algorithm for keys + KeyringOptions []keyring.Option // keyring configuration options + RPCAddress string // RPC listen address (including port) + APIAddress string // REST API listen address (including port) + GRPCAddress string // GRPC server listen address (including port) + PrintMnemonic bool // print the mnemonic of first validator as log output for testing +} + +// DefaultConfig returns a sane default configuration suitable for nearly all +// testing requirements. +func DefaultConfig(factory TestFixtureFactory) Config { + fixture := factory() + + return Config{ + Codec: fixture.EncodingConfig.Codec, + TxConfig: fixture.EncodingConfig.TxConfig, + LegacyAmino: fixture.EncodingConfig.Amino, + InterfaceRegistry: fixture.EncodingConfig.InterfaceRegistry, + AccountRetriever: authtypes.AccountRetriever{}, + AppConstructor: fixture.AppConstructor, + GenesisState: fixture.GenesisState, + TimeoutCommit: 2 * time.Second, + ChainID: "chain-" + unsafe.Str(6), + NumValidators: 4, + BondDenom: sdk.DefaultBondDenom, + MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), + AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction), + StakingTokens: sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction), + BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction), + PruningStrategy: pruningtypes.PruningOptionNothing, + CleanupDir: true, + SigningAlgo: string(hd.Secp256k1Type), + KeyringOptions: []keyring.Option{}, + PrintMnemonic: false, + } +} + +// MinimumAppConfig defines the minimum of modules required for a call to New to succeed +func MinimumAppConfig() depinject.Config { + return configurator.NewAppConfig( + configurator.AuthModule(), + configurator.ParamsModule(), + configurator.BankModule(), + configurator.GenutilModule(), + configurator.StakingModule(), + configurator.ConsensusModule(), + configurator.TxModule(), + ) +} + +func DefaultConfigWithAppConfig(appConfig depinject.Config) (Config, error) { + var ( + appBuilder *runtime.AppBuilder + txConfig client.TxConfig + legacyAmino *codec.LegacyAmino + cdc codec.Codec + interfaceRegistry codectypes.InterfaceRegistry + ) + + if err := depinject.Inject( + depinject.Configs( + appConfig, + depinject.Supply(log.NewNopLogger()), + ), + &appBuilder, + &txConfig, + &cdc, + &legacyAmino, + &interfaceRegistry, + ); err != nil { + return Config{}, err + } + + cfg := DefaultConfig(func() TestFixture { + return TestFixture{} + }) + cfg.Codec = cdc + cfg.TxConfig = txConfig + cfg.LegacyAmino = legacyAmino + cfg.InterfaceRegistry = interfaceRegistry + cfg.GenesisState = appBuilder.DefaultGenesis() + cfg.AppConstructor = func(val ValidatorI) servertypes.Application { + // we build a unique app instance for every validator here + var appBuilder *runtime.AppBuilder + if err := depinject.Inject( + depinject.Configs( + appConfig, + depinject.Supply(val.GetCtx().Logger), + ), + &appBuilder); err != nil { + panic(err) + } + app := appBuilder.Build( + dbm.NewMemDB(), + nil, + baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), + baseapp.SetMinGasPrices(val.GetAppConfig().MinGasPrices), + baseapp.SetChainID(cfg.ChainID), + ) + + testdata.RegisterQueryServer(app.GRPCQueryRouter(), testdata.QueryImpl{}) + + if err := app.Load(true); err != nil { + panic(err) + } + + return app + } + + return cfg, nil +} + +type ( + // Network defines a local in-process testing network using SimApp. It can be + // configured to start any number of validators, each with its own RPC and API + // clients. Typically, this test network would be used in client and integration + // testing where user input is expected. + // + // Note, due to CometBFT constraints in regards to RPC functionality, there + // may only be one test network running at a time. Thus, any caller must be + // sure to Cleanup after testing is finished in order to allow other tests + // to create networks. In addition, only the first validator will have a valid + // RPC and API server/client. + Network struct { + Logger Logger + BaseDir string + Validators []*Validator + + Config Config + } + + // Validator defines an in-process CometBFT validator node. Through this object, + // a client can make RPC and API calls and interact with any client command + // or handler. + Validator struct { + AppConfig *srvconfig.Config + ClientCtx client.Context + Ctx *server.Context + Dir string + NodeID string + PubKey cryptotypes.PubKey + Moniker string + APIAddress string + RPCAddress string + P2PAddress string + Address sdk.AccAddress + ValAddress sdk.ValAddress + RPCClient cmtclient.Client + + app servertypes.Application + tmNode *node.Node + api *api.Server + grpc *grpc.Server + grpcWeb *http.Server + errGroup *errgroup.Group + cancelFn context.CancelFunc + } + + // ValidatorI expose a validator's context and configuration + ValidatorI interface { + GetCtx() *server.Context + GetAppConfig() *srvconfig.Config + GetRPC() string + } + + // Logger is a network logger interface that exposes testnet-level Log() methods for an in-process testing network + // This is not to be confused with logging that may happen at an individual node or validator level + Logger interface { + Log(args ...interface{}) + Logf(format string, args ...interface{}) + } +) + +var ( + _ Logger = (*testing.T)(nil) + _ Logger = (*CLILogger)(nil) + _ ValidatorI = Validator{} +) + +func (v Validator) GetCtx() *server.Context { + return v.Ctx +} + +func (v Validator) GetAppConfig() *srvconfig.Config { + return v.AppConfig +} + +func (v Validator) GetRPC() string { + return v.RPCAddress +} + +// CLILogger wraps a cobra.Command and provides command logging methods. +type CLILogger struct { + cmd *cobra.Command +} + +// Log logs given args. +func (s CLILogger) Log(args ...interface{}) { + s.cmd.Println(args...) +} + +// Logf logs given args according to a format specifier. +func (s CLILogger) Logf(format string, args ...interface{}) { + s.cmd.Printf(format, args...) +} + +// NewCLILogger creates a new CLILogger. +func NewCLILogger(cmd *cobra.Command) CLILogger { + return CLILogger{cmd} +} + +// New creates a new Network for integration tests or in-process testnets run via the CLI +func New(l Logger, baseDir string, cfg Config) (*Network, error) { + // only one caller/test can create and use a network at a time + l.Log("acquiring test network lock") + lock.Lock() + + network := &Network{ + Logger: l, + BaseDir: baseDir, + Validators: make([]*Validator, cfg.NumValidators), + Config: cfg, + } + + l.Logf("preparing test network with chain-id \"%s\"\n", cfg.ChainID) + + monikers := make([]string, cfg.NumValidators) + nodeIDs := make([]string, cfg.NumValidators) + valPubKeys := make([]cryptotypes.PubKey, cfg.NumValidators) + + var ( + genAccounts []authtypes.GenesisAccount + genBalances []banktypes.Balance + genFiles []string + ) + + buf := bufio.NewReader(os.Stdin) + + // generate private keys, node IDs, and initial transactions + for i := 0; i < cfg.NumValidators; i++ { + appCfg := srvconfig.DefaultConfig() + appCfg.Pruning = cfg.PruningStrategy + appCfg.MinGasPrices = cfg.MinGasPrices + appCfg.API.Enable = true + appCfg.API.Swagger = false + appCfg.Telemetry.Enabled = false + + ctx := server.NewDefaultContext() + cmtCfg := ctx.Config + cmtCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit + + // Only allow the first validator to expose an RPC, API and gRPC + // server/client due to CometBFT in-process constraints. + apiAddr := "" + cmtCfg.RPC.ListenAddress = "" + appCfg.GRPC.Enable = false + appCfg.GRPCWeb.Enable = false + apiListenAddr := "" + if i == 0 { + if cfg.APIAddress != "" { + apiListenAddr = cfg.APIAddress + } else { + if len(portPool) == 0 { + return nil, fmt.Errorf("failed to get port for API server") + } + port := <-portPool + apiListenAddr = fmt.Sprintf("tcp://0.0.0.0:%s", port) + } + + appCfg.API.Address = apiListenAddr + apiURL, err := url.Parse(apiListenAddr) + if err != nil { + return nil, err + } + apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()) + + if cfg.RPCAddress != "" { + cmtCfg.RPC.ListenAddress = cfg.RPCAddress + } else { + if len(portPool) == 0 { + return nil, fmt.Errorf("failed to get port for RPC server") + } + port := <-portPool + cmtCfg.RPC.ListenAddress = fmt.Sprintf("tcp://0.0.0.0:%s", port) + } + + if cfg.GRPCAddress != "" { + appCfg.GRPC.Address = cfg.GRPCAddress + } else { + if len(portPool) == 0 { + return nil, fmt.Errorf("failed to get port for GRPC server") + } + port := <-portPool + appCfg.GRPC.Address = fmt.Sprintf("0.0.0.0:%s", port) + } + appCfg.GRPC.Enable = true + appCfg.GRPCWeb.Enable = true + } + + logger := log.NewNopLogger() + if cfg.EnableLogging { + logger = log.NewLogger(os.Stdout) // TODO(mr): enable selection of log destination. + } + + ctx.Logger = logger + + nodeDirName := fmt.Sprintf("node%d", i) + nodeDir := filepath.Join(network.BaseDir, nodeDirName, "simd") + clientDir := filepath.Join(network.BaseDir, nodeDirName, "simcli") + gentxsDir := filepath.Join(network.BaseDir, "gentxs") + + err := os.MkdirAll(filepath.Join(nodeDir, "config"), 0o755) + if err != nil { + return nil, err + } + + err = os.MkdirAll(clientDir, 0o755) + if err != nil { + return nil, err + } + + cmtCfg.SetRoot(nodeDir) + cmtCfg.Moniker = nodeDirName + monikers[i] = nodeDirName + + if len(portPool) == 0 { + return nil, fmt.Errorf("failed to get port for Proxy server") + } + port := <-portPool + proxyAddr := fmt.Sprintf("tcp://0.0.0.0:%s", port) + cmtCfg.ProxyApp = proxyAddr + + if len(portPool) == 0 { + return nil, fmt.Errorf("failed to get port for Proxy server") + } + port = <-portPool + p2pAddr := fmt.Sprintf("tcp://0.0.0.0:%s", port) + cmtCfg.P2P.ListenAddress = p2pAddr + cmtCfg.P2P.AddrBookStrict = false + cmtCfg.P2P.AllowDuplicateIP = true + + nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(cmtCfg) + if err != nil { + return nil, err + } + + nodeIDs[i] = nodeID + valPubKeys[i] = pubKey + + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf, cfg.Codec, cfg.KeyringOptions...) + if err != nil { + return nil, err + } + + keyringAlgos, _ := kb.SupportedAlgorithms() + algo, err := keyring.NewSigningAlgoFromString(cfg.SigningAlgo, keyringAlgos) + if err != nil { + return nil, err + } + + var mnemonic string + if i < len(cfg.Mnemonics) { + mnemonic = cfg.Mnemonics[i] + } + + addr, secret, err := testutil.GenerateSaveCoinKey(kb, nodeDirName, mnemonic, true, algo) + if err != nil { + return nil, err + } + + // if PrintMnemonic is set to true, we print the first validator node's secret to the network's logger + // for debugging and manual testing + if cfg.PrintMnemonic && i == 0 { + printMnemonic(l, secret) + } + + info := map[string]string{"secret": secret} + infoBz, err := json.Marshal(info) + if err != nil { + return nil, err + } + + // save private key seed words + err = writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, infoBz) + if err != nil { + return nil, err + } + + balances := sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), cfg.AccountTokens), + sdk.NewCoin(cfg.BondDenom, cfg.StakingTokens), + ) + + genFiles = append(genFiles, cmtCfg.GenesisFile()) + genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: balances.Sort()}) + genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) + + commission, err := sdkmath.LegacyNewDecFromStr("0.5") + if err != nil { + return nil, err + } + + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr).String(), + valPubKeys[i], + sdk.NewCoin(cfg.BondDenom, cfg.BondedTokens), + stakingtypes.NewDescription(nodeDirName, "", "", "", ""), + stakingtypes.NewCommissionRates(commission, sdkmath.LegacyOneDec(), sdkmath.LegacyOneDec()), + sdkmath.OneInt(), + ) + if err != nil { + return nil, err + } + + p2pURL, err := url.Parse(p2pAddr) + if err != nil { + return nil, err + } + + memo := fmt.Sprintf("%s@%s:%s", nodeIDs[i], p2pURL.Hostname(), p2pURL.Port()) + fee := sdk.NewCoins(sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), sdkmath.NewInt(0))) + txBuilder := cfg.TxConfig.NewTxBuilder() + err = txBuilder.SetMsgs(createValMsg) + if err != nil { + return nil, err + } + txBuilder.SetFeeAmount(fee) // Arbitrary fee + txBuilder.SetGasLimit(1000000) // Need at least 100386 + txBuilder.SetMemo(memo) + + txFactory := tx.Factory{} + txFactory = txFactory. + WithChainID(cfg.ChainID). + WithMemo(memo). + WithKeybase(kb). + WithTxConfig(cfg.TxConfig) + + err = tx.Sign(context.Background(), txFactory, nodeDirName, txBuilder, true) + if err != nil { + return nil, err + } + + txBz, err := cfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, err + } + err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz) + if err != nil { + return nil, err + } + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), appCfg) + + clientCtx := client.Context{}. + WithKeyringDir(clientDir). + WithKeyring(kb). + WithHomeDir(cmtCfg.RootDir). + WithChainID(cfg.ChainID). + WithInterfaceRegistry(cfg.InterfaceRegistry). + WithCodec(cfg.Codec). + WithLegacyAmino(cfg.LegacyAmino). + WithTxConfig(cfg.TxConfig). + WithAccountRetriever(cfg.AccountRetriever). + WithNodeURI(cmtCfg.RPC.ListenAddress) + + // Provide ChainID here since we can't modify it in the Comet config. + ctx.Viper.Set(flags.FlagChainID, cfg.ChainID) + + network.Validators[i] = &Validator{ + AppConfig: appCfg, + ClientCtx: clientCtx, + Ctx: ctx, + Dir: filepath.Join(network.BaseDir, nodeDirName), + NodeID: nodeID, + PubKey: pubKey, + Moniker: nodeDirName, + RPCAddress: cmtCfg.RPC.ListenAddress, + P2PAddress: cmtCfg.P2P.ListenAddress, + APIAddress: apiAddr, + Address: addr, + ValAddress: sdk.ValAddress(addr), + } + } + + err := initGenFiles(cfg, genAccounts, genBalances, genFiles) + if err != nil { + return nil, err + } + err = collectGenFiles(cfg, network.Validators, network.BaseDir) + if err != nil { + return nil, err + } + + l.Log("starting test network...") + for idx, v := range network.Validators { + if err := startInProcess(cfg, v); err != nil { + return nil, err + } + l.Log("started validator", idx) + } + + height, err := network.LatestHeight() + if err != nil { + return nil, err + } + + l.Log("started test network at height:", height) + + // Ensure we cleanup incase any test was abruptly halted (e.g. SIGINT) as any + // defer in a test would not be called. + trapSignal(network.Cleanup) + + return network, nil +} + +// trapSignal traps SIGINT and SIGTERM and calls os.Exit once a signal is received. +func trapSignal(cleanupFunc func()) { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + go func() { + sig := <-sigs + + if cleanupFunc != nil { + cleanupFunc() + } + exitCode := 128 + + switch sig { + case syscall.SIGINT: + exitCode += int(syscall.SIGINT) + case syscall.SIGTERM: + exitCode += int(syscall.SIGTERM) + } + + os.Exit(exitCode) + }() +} + +// LatestHeight returns the latest height of the network or an error if the +// query fails or no validators exist. +func (n *Network) LatestHeight() (int64, error) { + if len(n.Validators) == 0 { + return 0, errors.New("no validators available") + } + + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + timeout := time.NewTimer(time.Second * 5) + defer timeout.Stop() + + var latestHeight int64 + val := n.Validators[0] + queryClient := cmtservice.NewServiceClient(val.ClientCtx) + + for { + select { + case <-timeout.C: + return latestHeight, errors.New("timeout exceeded waiting for block") + case <-ticker.C: + done := make(chan struct{}) + go func() { + res, err := queryClient.GetLatestBlock(context.Background(), &cmtservice.GetLatestBlockRequest{}) + if err == nil && res != nil { + latestHeight = res.SdkBlock.Header.Height + } + done <- struct{}{} + }() + select { + case <-timeout.C: + return latestHeight, errors.New("timeout exceeded waiting for block") + case <-done: + if latestHeight != 0 { + return latestHeight, nil + } + } + } + } +} + +// WaitForHeight performs a blocking check where it waits for a block to be +// committed after a given block. If that height is not reached within a timeout, +// an error is returned. Regardless, the latest height queried is returned. +func (n *Network) WaitForHeight(h int64) (int64, error) { + return n.WaitForHeightWithTimeout(h, 10*time.Second) +} + +// WaitForHeightWithTimeout is the same as WaitForHeight except the caller can +// provide a custom timeout. +func (n *Network) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + timeout := time.NewTimer(t) + defer timeout.Stop() + + if len(n.Validators) == 0 { + return 0, errors.New("no validators available") + } + + var latestHeight int64 + val := n.Validators[0] + queryClient := cmtservice.NewServiceClient(val.ClientCtx) + + for { + select { + case <-timeout.C: + return latestHeight, errors.New("timeout exceeded waiting for block") + case <-ticker.C: + + res, err := queryClient.GetLatestBlock(context.Background(), &cmtservice.GetLatestBlockRequest{}) + if err == nil && res != nil { + latestHeight = res.GetSdkBlock().Header.Height + if latestHeight >= h { + return latestHeight, nil + } + } + } + } +} + +// RetryForBlocks will wait for the next block and execute the function provided. +// It will do this until the function returns a nil error or until the number of +// blocks has been reached. +func (n *Network) RetryForBlocks(retryFunc func() error, blocks int) error { + for i := 0; i < blocks; i++ { + n.WaitForNextBlock() + err := retryFunc() + if err == nil { + return nil + } + // we've reached the last block to wait, return the error + if i == blocks-1 { + return err + } + } + return nil +} + +// WaitForNextBlock waits for the next block to be committed, returning an error +// upon failure. +func (n *Network) WaitForNextBlock() error { + lastBlock, err := n.LatestHeight() + if err != nil { + return err + } + + _, err = n.WaitForHeight(lastBlock + 1) + if err != nil { + return err + } + + return err +} + +// Cleanup removes the root testing (temporary) directory and stops both the +// CometBFT and API services. It allows other callers to create and start +// test networks. This method must be called when a test is finished, typically +// in a defer. +func (n *Network) Cleanup() { + defer func() { + lock.Unlock() + n.Logger.Log("released test network lock") + }() + + n.Logger.Log("cleaning up test network...") + + for _, v := range n.Validators { + // cancel the validator's context which will signal to the gRPC and API + // goroutines that they should gracefully exit. + v.cancelFn() + + if err := v.errGroup.Wait(); err != nil { + n.Logger.Log("unexpected error waiting for validator gRPC and API processes to exit", "err", err) + } + + if v.tmNode != nil && v.tmNode.IsRunning() { + if err := v.tmNode.Stop(); err != nil { + n.Logger.Log("failed to stop validator CometBFT node", "err", err) + } + } + + if v.grpcWeb != nil { + _ = v.grpcWeb.Close() + } + + if v.app != nil { + if err := v.app.Close(); err != nil { + n.Logger.Log("failed to stop validator ABCI application", "err", err) + } + } + } + + time.Sleep(100 * time.Millisecond) + + if n.Config.CleanupDir { + _ = os.RemoveAll(n.BaseDir) + } + + n.Logger.Log("finished cleaning up test network") +} + +// printMnemonic prints a provided mnemonic seed phrase on a network logger +// for debugging and manual testing +func printMnemonic(l Logger, secret string) { + lines := []string{ + "THIS MNEMONIC IS FOR TESTING PURPOSES ONLY", + "DO NOT USE IN PRODUCTION", + "", + strings.Join(strings.Fields(secret)[0:8], " "), + strings.Join(strings.Fields(secret)[8:16], " "), + strings.Join(strings.Fields(secret)[16:24], " "), + } + + lineLengths := make([]int, len(lines)) + for i, line := range lines { + lineLengths[i] = len(line) + } + + maxLineLength := 0 + for _, lineLen := range lineLengths { + if lineLen > maxLineLength { + maxLineLength = lineLen + } + } + + l.Log("\n") + l.Log(strings.Repeat("+", maxLineLength+8)) + for _, line := range lines { + l.Logf("++ %s ++\n", centerText(line, maxLineLength)) + } + l.Log(strings.Repeat("+", maxLineLength+8)) + l.Log("\n") +} + +// centerText centers text across a fixed width, filling either side with whitespace buffers +func centerText(text string, width int) string { + textLen := len(text) + leftBuffer := strings.Repeat(" ", (width-textLen)/2) + rightBuffer := strings.Repeat(" ", (width-textLen)/2+(width-textLen)%2) + + return fmt.Sprintf("%s%s%s", leftBuffer, text, rightBuffer) +} diff --git a/network/util.go b/network/util.go new file mode 100644 index 0000000..6e8aa39 --- /dev/null +++ b/network/util.go @@ -0,0 +1,236 @@ +package network + +import ( + "context" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + + "golang.org/x/sync/errgroup" + + cmtcfg "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/node" + "github.com/cometbft/cometbft/p2p" + pvm "github.com/cometbft/cometbft/privval" + "github.com/cometbft/cometbft/proxy" + "github.com/cometbft/cometbft/rpc/client/local" + cmttypes "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" + + "cosmossdk.io/log" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" + servercmtlog "github.com/cosmos/cosmos-sdk/server/log" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +func startInProcess(cfg Config, val *Validator) error { + logger := val.Ctx.Logger + cmtCfg := val.Ctx.Config + cmtCfg.Instrumentation.Prometheus = false + + if err := val.AppConfig.ValidateBasic(); err != nil { + return err + } + + nodeKey, err := p2p.LoadOrGenNodeKey(cmtCfg.NodeKeyFile()) + if err != nil { + return err + } + + app := cfg.AppConstructor(*val) + val.app = app + + appGenesisProvider := func() (*cmttypes.GenesisDoc, error) { + appGenesis, err := genutiltypes.AppGenesisFromFile(cmtCfg.GenesisFile()) + if err != nil { + return nil, err + } + + return appGenesis.ToGenesisDoc() + } + + cmtApp := server.NewCometABCIWrapper(app) + tmNode, err := node.NewNode( //resleak:notresource + cmtCfg, + pvm.LoadOrGenFilePV(cmtCfg.PrivValidatorKeyFile(), cmtCfg.PrivValidatorStateFile()), + nodeKey, + proxy.NewLocalClientCreator(cmtApp), + appGenesisProvider, + cmtcfg.DefaultDBProvider, + node.DefaultMetricsProvider(cmtCfg.Instrumentation), + servercmtlog.CometLoggerWrapper{Logger: logger.With("module", val.Moniker)}, + ) + if err != nil { + return err + } + + if err := tmNode.Start(); err != nil { + return err + } + val.tmNode = tmNode + + if val.RPCAddress != "" { + val.RPCClient = local.New(tmNode) + } + + // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. + if val.APIAddress != "" || val.AppConfig.GRPC.Enable { + val.ClientCtx = val.ClientCtx. + WithClient(val.RPCClient) + + app.RegisterTxService(val.ClientCtx) + app.RegisterTendermintService(val.ClientCtx) + app.RegisterNodeService(val.ClientCtx, *val.AppConfig) + } + + ctx := context.Background() + ctx, val.cancelFn = context.WithCancel(ctx) + val.errGroup, ctx = errgroup.WithContext(ctx) + + grpcCfg := val.AppConfig.GRPC + + if grpcCfg.Enable { + grpcSrv, err := servergrpc.NewGRPCServer(val.ClientCtx, app, grpcCfg) + if err != nil { + return err + } + + // Start the gRPC server in a goroutine. Note, the provided ctx will ensure + // that the server is gracefully shut down. + val.errGroup.Go(func() error { + return servergrpc.StartGRPCServer(ctx, logger.With(log.ModuleKey, "grpc-server"), grpcCfg, grpcSrv) + }) + + val.grpc = grpcSrv + } + + if val.APIAddress != "" { + apiSrv := api.New(val.ClientCtx, logger.With(log.ModuleKey, "api-server"), val.grpc) + app.RegisterAPIRoutes(apiSrv, val.AppConfig.API) + + val.errGroup.Go(func() error { + return apiSrv.Start(ctx, *val.AppConfig) + }) + + val.api = apiSrv + } + + return nil +} + +func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error { + genTime := cmttime.Now() + + for i := 0; i < cfg.NumValidators; i++ { + cmtCfg := vals[i].Ctx.Config + + nodeDir := filepath.Join(outputDir, vals[i].Moniker, "simd") + gentxsDir := filepath.Join(outputDir, "gentxs") + + cmtCfg.Moniker = vals[i].Moniker + cmtCfg.SetRoot(nodeDir) + + initCfg := genutiltypes.NewInitConfig(cfg.ChainID, gentxsDir, vals[i].NodeID, vals[i].PubKey) + + genFile := cmtCfg.GenesisFile() + appGenesis, err := genutiltypes.AppGenesisFromFile(genFile) + if err != nil { + return err + } + + appState, err := genutil.GenAppStateFromConfig(cfg.Codec, cfg.TxConfig, + cmtCfg, initCfg, appGenesis, banktypes.GenesisBalancesIterator{}, genutiltypes.DefaultMessageValidator, cfg.TxConfig.SigningContext().ValidatorAddressCodec()) + if err != nil { + return err + } + + // overwrite each validator's genesis file to have a canonical genesis time + if err := genutil.ExportGenesisFileWithTime(genFile, cfg.ChainID, nil, appState, genTime); err != nil { + return err + } + } + + return nil +} + +func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string) error { + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[authtypes.ModuleName], &authGenState) + + accounts, err := authtypes.PackAccounts(genAccounts) + if err != nil { + return err + } + + authGenState.Accounts = append(authGenState.Accounts, accounts...) + cfg.GenesisState[authtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&authGenState) + + // set the balances in the genesis state + var bankGenState banktypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[banktypes.ModuleName], &bankGenState) + + bankGenState.Balances = append(bankGenState.Balances, genBalances...) + cfg.GenesisState[banktypes.ModuleName] = cfg.Codec.MustMarshalJSON(&bankGenState) + + appGenStateJSON, err := json.MarshalIndent(cfg.GenesisState, "", " ") + if err != nil { + return err + } + + appGenesis := genutiltypes.AppGenesis{ + ChainID: cfg.ChainID, + AppState: appGenStateJSON, + Consensus: &genutiltypes.ConsensusGenesis{ + Validators: nil, + }, + } + + // generate empty genesis files for each validator and save + for i := 0; i < cfg.NumValidators; i++ { + if err := appGenesis.SaveAs(genFiles[i]); err != nil { + return err + } + } + + return nil +} + +func writeFile(name, dir string, contents []byte) error { + file := filepath.Join(dir, name) + + if err := os.MkdirAll(dir, 0o755); err != nil { + return fmt.Errorf("could not create directory %q: %w", dir, err) + } + + if err := os.WriteFile(file, contents, 0o600); err != nil { + return err + } + + return nil +} + +// Get a free address for a test CometBFT server +// protocol is either tcp, http, etc +func FreeTCPAddr() (addr, port string, closeFn func() error, err error) { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return "", "", nil, err + } + + closeFn = func() error { + return l.Close() + } + + portI := l.Addr().(*net.TCPAddr).Port + port = fmt.Sprintf("%d", portI) + addr = fmt.Sprintf("tcp://0.0.0.0:%s", port) + return +} diff --git a/relayer/http_client_test.go b/relayer/http_client_test.go new file mode 100644 index 0000000..85eb27e --- /dev/null +++ b/relayer/http_client_test.go @@ -0,0 +1,45 @@ +package relayer_test + +import ( + "fmt" + "net/http" + "net/http/httptest" + + "github.com/stretchr/testify/require" +) + +func (s *RelayerTestSuite) TestHTTPClientHandler_Get() { + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.Equal(s.T(), "GET", r.Method) + + fmt.Fprintln(w, `{"message": "GET request successful"}`) + })) + defer mockServer.Close() + + resp, err := s.httpHandler.Get(mockServer.URL) + require.NoError(s.T(), err) + require.NotNil(s.T(), resp) + + expected := `{"message": "GET request successful"}` + require.JSONEq(s.T(), expected, string(resp)) +} + +func (s *RelayerTestSuite) TestHTTPClientHandler_GetError() { + _, err := s.httpHandler.Get("http://invalid-url") + require.Error(s.T(), err) + require.Contains(s.T(), err.Error(), "GET request error") +} + +func (s *RelayerTestSuite) TestPostRequest() { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + s.Require().Equal(http.MethodPost, r.Method) + s.Require().Equal("/post", r.URL.Path) + + s.Require().Equal("application/json", r.Header.Get("Content-Type")) + + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(`{"response":"success"}`)) + s.Require().NoError(err) + })) + defer server.Close() +} diff --git a/relayer/init_test.go b/relayer/init_test.go new file mode 100644 index 0000000..588c5f5 --- /dev/null +++ b/relayer/init_test.go @@ -0,0 +1,60 @@ +package relayer_test + +import ( + "testing" + + addresstypes "cosmossdk.io/core/address" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttime "github.com/cometbft/cometbft/types/time" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/testutil" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/stretchr/testify/suite" + cada "github.com/vitwit/avail-da-module" + module "github.com/vitwit/avail-da-module/module" + relayer "github.com/vitwit/avail-da-module/relayer" +) + +type RelayerTestSuite struct { + suite.Suite + + ctx sdk.Context + httpHandler relayer.HTTPClientHandler + addrs []sdk.AccAddress + encCfg moduletestutil.TestEncodingConfig + addressCodec addresstypes.Codec + baseApp *baseapp.BaseApp + relayer *relayer.Relayer +} + +func TestRelayerTestSuite(t *testing.T) { + suite.Run(t, new(RelayerTestSuite)) +} + +func (s *RelayerTestSuite) SetupTest() { + key := storetypes.NewKVStoreKey(cada.ModuleName) + testCtx := testutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test")) + s.ctx = testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: cmttime.Now()}) + s.encCfg = moduletestutil.MakeTestEncodingConfig(module.AppModuleBasic{}) + s.addressCodec = address.NewBech32Codec("cosmos") + + s.baseApp = baseapp.NewBaseApp( + "cada", + log.NewNopLogger(), + testCtx.DB, + s.encCfg.TxConfig.TxDecoder(), + ) + + s.baseApp.SetCMS(testCtx.CMS) + s.baseApp.SetInterfaceRegistry(s.encCfg.InterfaceRegistry) + s.addrs = simtestutil.CreateIncrementalAccounts(7) + + s.httpHandler = *relayer.NewHTTPClientHandler() + + s.relayer = &relayer.Relayer{} +} diff --git a/relayer/local/cosmosprovider.go b/relayer/local/cosmosprovider.go index fb223f4..ebc6009 100644 --- a/relayer/local/cosmosprovider.go +++ b/relayer/local/cosmosprovider.go @@ -6,8 +6,8 @@ import ( ) type CosmosProvider struct { - cdc codec.BinaryCodec - rpcClient *cometrpc.HTTP + Cdc codec.BinaryCodec + RPCClient RPCClient } // NewProvider validates the CosmosProviderConfig, instantiates a ChainClient and then instantiates a CosmosProvider @@ -18,8 +18,8 @@ func NewProvider(cdc codec.BinaryCodec, rpc string) (*CosmosProvider, error) { } cp := &CosmosProvider{ - cdc: cdc, - rpcClient: rpcClient, + Cdc: cdc, + RPCClient: rpcClient, } return cp, nil diff --git a/relayer/local/init_test.go b/relayer/local/init_test.go new file mode 100644 index 0000000..afb1f33 --- /dev/null +++ b/relayer/local/init_test.go @@ -0,0 +1,26 @@ +package local_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + local "github.com/vitwit/avail-da-module/relayer/local" +) + +type CosmosProviderTestSuite struct { + suite.Suite + mockRPCClient *MockRPCClient + cosmosProvider *local.CosmosProvider +} + +func (s *CosmosProviderTestSuite) SetupTest() { + s.mockRPCClient = new(MockRPCClient) + s.cosmosProvider = &local.CosmosProvider{ + Cdc: nil, + RPCClient: s.mockRPCClient, + } +} + +func TestCosmosProviderTestSuite(t *testing.T) { + suite.Run(t, new(CosmosProviderTestSuite)) +} diff --git a/relayer/local/query.go b/relayer/local/query.go index ab1db16..c48d49c 100644 --- a/relayer/local/query.go +++ b/relayer/local/query.go @@ -5,15 +5,22 @@ import ( "fmt" abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/bytes" coretypes "github.com/cometbft/cometbft/rpc/core/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) +type RPCClient interface { + Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error) + Status(ctx context.Context) (*coretypes.ResultStatus, error) + ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) +} + // GetBlockAtHeight queries the block at a given height func (cc *CosmosProvider) GetBlockAtHeight(ctx context.Context, height int64) (*coretypes.ResultBlock, error) { - block, err := cc.rpcClient.Block(ctx, &height) + block, err := cc.RPCClient.Block(ctx, &height) if err != nil { return nil, fmt.Errorf("error querying block at height %d: %w", height, err) } @@ -22,7 +29,7 @@ func (cc *CosmosProvider) GetBlockAtHeight(ctx context.Context, height int64) (* // Status queries the status of this node, can be used to check if it is catching up or a validator func (cc *CosmosProvider) Status(ctx context.Context) (*coretypes.ResultStatus, error) { - status, err := cc.rpcClient.Status(ctx) + status, err := cc.RPCClient.Status(ctx) if err != nil { return nil, fmt.Errorf("error querying status: %w", err) } @@ -31,7 +38,7 @@ func (cc *CosmosProvider) Status(ctx context.Context) (*coretypes.ResultStatus, // QueryABCI performs an ABCI query and returns the appropriate response and error sdk error code. func (cc *CosmosProvider) QueryABCI(ctx context.Context, path string, data []byte) (abci.ResponseQuery, error) { - result, err := cc.rpcClient.ABCIQuery(ctx, path, data) + result, err := cc.RPCClient.ABCIQuery(ctx, path, data) if err != nil { return abci.ResponseQuery{}, err } diff --git a/relayer/local/query_test.go b/relayer/local/query_test.go new file mode 100644 index 0000000..07105b6 --- /dev/null +++ b/relayer/local/query_test.go @@ -0,0 +1,98 @@ +package local_test + +import ( + "context" + "fmt" + + "github.com/cometbft/cometbft/libs/bytes" + coretypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/stretchr/testify/mock" +) + +type MockRPCClient struct { + mock.Mock +} + +func (m *MockRPCClient) Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error) { + args := m.Called(ctx, height) + + if block, ok := args.Get(0).(*coretypes.ResultBlock); ok { + return block, args.Error(1) + } + + return nil, args.Error(1) +} + +func (m *MockRPCClient) Status(ctx context.Context) (*coretypes.ResultStatus, error) { + args := m.Called(ctx) + + if status, ok := args.Get(0).(*coretypes.ResultStatus); ok { + return status, args.Error(1) + } + + return nil, args.Error(1) +} + +func (m *MockRPCClient) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) { + args := m.Called(ctx, path, data) + return args.Get(0).(*coretypes.ResultABCIQuery), args.Error(1) +} + +func (s *CosmosProviderTestSuite) TestQueryABCI_Success() { + path := "/store" + data := bytes.HexBytes("queryData") + + result := &coretypes.ResultABCIQuery{} + + s.mockRPCClient.On("ABCIQuery", mock.Anything, path, data).Return(result, nil) + + resp, err := s.cosmosProvider.QueryABCI(context.Background(), path, data) + + s.Require().NoError(err) + s.Require().NotNil(resp) +} + +func (s *CosmosProviderTestSuite) TestGetBlockAtHeight_Success() { + height := int64(10) + expectedBlock := &coretypes.ResultBlock{} + s.mockRPCClient.On("Block", mock.Anything, &height).Return(expectedBlock, nil) + + block, err := s.cosmosProvider.GetBlockAtHeight(context.Background(), height) + + s.Require().NoError(err) + s.Require().Equal(expectedBlock, block) +} + +func (s *CosmosProviderTestSuite) TestGetBlockAtHeight_Error() { + height := int64(10) + expectedError := fmt.Errorf("error querying block") + s.mockRPCClient.On("Block", mock.Anything, &height).Return(nil, expectedError) + + block, err := s.cosmosProvider.GetBlockAtHeight(context.Background(), height) + + s.Require().Error(err) + s.Require().Nil(block) +} + +func (s *CosmosProviderTestSuite) TestStatus_Success() { + expectedStatus := &coretypes.ResultStatus{ + // Populate with expected status data + } + s.mockRPCClient.On("Status", mock.Anything).Return(expectedStatus, nil) + + status, err := s.cosmosProvider.Status(context.Background()) + + s.Require().NoError(err) + s.Require().Equal(expectedStatus, status) +} + +func (s *CosmosProviderTestSuite) TestStatus_Error() { + expectedError := fmt.Errorf("error querying status") + s.mockRPCClient.On("Status", mock.Anything).Return(nil, expectedError) + + status, err := s.cosmosProvider.Status(context.Background()) + + s.Require().Error(err) + s.Require().Nil(status) + s.Require().Equal("error querying status: error querying status", err.Error()) +} diff --git a/relayer/publish_test.go b/relayer/publish_test.go new file mode 100644 index 0000000..6fc4fde --- /dev/null +++ b/relayer/publish_test.go @@ -0,0 +1,21 @@ +package relayer_test + +// func (s *RelayerTestSuite) TestGetBlocksDataFromLocal() { +// s.ctx = s.ctx.WithBlockHeight(int64(10)) +// testCases := []struct { +// name string +// blocks []int64 +// }{ +// { +// "get blocks data", +// []int64{10}, +// }, +// } + +// for _, tc := range testCases { +// s.Run(tc.name, func() { +// res := s.relayer.GetBlocksDataFromLocal(s.ctx, tc.blocks) +// s.Require().NotNil(res) +// }) +// } +// } diff --git a/relayer/submit_data_test.go b/relayer/submit_data_test.go new file mode 100644 index 0000000..3fcb1bd --- /dev/null +++ b/relayer/submit_data_test.go @@ -0,0 +1,80 @@ +package relayer_test + +// import ( +// "encoding/base64" +// "encoding/json" +// "net/http" +// "net/http/httptest" + +// relayer "github.com/vitwit/avail-da-module/relayer" +// ) + +// func (s *RelayerTestSuite) TestSubmitDataToAvailClient_Success() { +// data := []byte("test data") +// blocks := []int64{1, 2, 3} + +// blockInfo := relayer.BlockInfo{ +// BlockHash: "hash123", +// BlockNumber: 1, +// Hash: "somehash", +// } + +// mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// if r.Method != http.MethodPost { +// s.T().Errorf("Expected POST method, got %s", r.Method) +// } + +// var requestBody map[string]string +// if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil { +// http.Error(w, "Invalid request body", http.StatusBadRequest) +// return +// } + +// if requestBody["data"] != base64.StdEncoding.EncodeToString(data) { +// http.Error(w, "Unexpected data", http.StatusBadRequest) +// return +// } + +// responseBody, _ := json.Marshal(blockInfo) +// w.Write(responseBody) +// })) + +// defer mockServer.Close() + +// blockInfoResult, err := s.relayer.SubmitDataToAvailClient("seed", 1, data, blocks, mockServer.URL) + +// s.Require().NoError(err) +// s.Require().Equal(blockInfo, blockInfoResult) +// } + +// func (s *RelayerTestSuite) TestSubmitDataToAvailClient_HTTPError() { +// data := []byte("test data") +// blocks := []int64{1, 2, 3} + +// mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { +// http.Error(w, "Internal Server Error", http.StatusInternalServerError) +// })) + +// defer mockServer.Close() + +// blockInfo, err := s.relayer.SubmitDataToAvailClient("seed", 1, data, blocks, mockServer.URL) + +// s.Require().Error(err) +// s.Require().Equal(relayer.BlockInfo{}, blockInfo) +// } + +// func (s *RelayerTestSuite) TestSubmitDataToAvailClient_UnmarshalError() { +// data := []byte("test data") +// blocks := []int64{1, 2, 3} + +// mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { +// w.Write([]byte(`{invalid json}`)) +// })) + +// defer mockServer.Close() + +// blockInfo, err := s.relayer.SubmitDataToAvailClient("seed", 1, data, blocks, mockServer.URL) + +// s.Require().Error(err) +// s.Require().Equal(relayer.BlockInfo{}, blockInfo) +// } diff --git a/simapp/app/test_helpers.go b/simapp/app/test_helpers.go new file mode 100644 index 0000000..e2b565e --- /dev/null +++ b/simapp/app/test_helpers.go @@ -0,0 +1,255 @@ +package app + +import ( + "encoding/json" + "fmt" + "os" + "testing" + + "cosmossdk.io/log" + sdkmath "cosmossdk.io/math" + pruningtypes "cosmossdk.io/store/pruning/types" + abci "github.com/cometbft/cometbft/abci/types" + cmtjson "github.com/cometbft/cometbft/libs/json" + cmttypes "github.com/cometbft/cometbft/types" + dbm "github.com/cosmos/cosmos-db" + bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/server" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/testutil/mock" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/stretchr/testify/require" + network "github.com/vitwit/avail-da-module/network" + relayercfg "github.com/vitwit/avail-da-module/types" +) + +// SetupOptions defines arguments that are passed into `Simapp` constructor. +type SetupOptions struct { + Logger log.Logger + DB *dbm.MemDB + AppOpts servertypes.AppOptions +} + +func setup(withGenesis bool, invCheckPeriod uint) (*ChainApp, GenesisState) { + db := dbm.NewMemDB() + + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = DefaultNodeHome + appOptions[server.FlagInvCheckPeriod] = invCheckPeriod + appOptions[relayercfg.FlagLightClientURL] = "http://127.0.0.1:8000" + + app := NewChainApp(log.NewNopLogger(), db, nil, true, appOptions) + if withGenesis { + return app, app.DefaultGenesis() + } + return app, GenesisState{} +} + +// NewChainAppWithCustomOptions initializes a new ChainApp with custom options. +func NewChainAppWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOptions) *ChainApp { + t.Helper() + + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + // create validator set with single validator + validator := cmttypes.NewValidator(pubKey, 1) + valSet := cmttypes.NewValidatorSet([]*cmttypes.Validator{validator}) + + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100000000000000))), + } + + app := NewChainApp(options.Logger, options.DB, nil, true, options.AppOpts) + genesisState := app.DefaultGenesis() + genesisState, err = simtestutil.GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) + require.NoError(t, err) + + if !isCheckTx { + // init chain must be called to stop deliverState from being nil + stateBytes, err := cmtjson.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + + // Initialize the chain + _, err = app.InitChain(&abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: simtestutil.DefaultConsensusParams, + AppStateBytes: stateBytes, + }) + require.NoError(t, err) + } + + return app +} + +// Setup initializes a new SimApp. A Nop logger is set in ChainApp. +func Setup(t *testing.T, isCheckTx bool) *ChainApp { + t.Helper() + + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + + // create validator set with single validator + validator := cmttypes.NewValidator(pubKey, 1) + valSet := cmttypes.NewValidatorSet([]*cmttypes.Validator{validator}) + + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100000000000000))), + } + + app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance) + + return app +} + +// SetupWithGenesisValSet initializes a new ChainApp with a validator set and genesis accounts +// that also act as delegators. For simplicity, each validator is bonded with a delegation +// of one consensus engine unit in the default token of the simapp from first genesis +// account. A Nop logger is set in ChainApp. +func SetupWithGenesisValSet(t *testing.T, valSet *cmttypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *ChainApp { + t.Helper() + + app, genesisState := setup(true, 5) + genesisState, err := simtestutil.GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, genAccs, balances...) + require.NoError(t, err) + + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + + // init chain will set the validator set and initialize the genesis accounts + _, err = app.InitChain(&abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: simtestutil.DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) + require.NoError(t, err) + + require.NoError(t, err) + _, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{ + Height: app.LastBlockHeight() + 1, + Hash: app.LastCommitID().Hash, + NextValidatorsHash: valSet.Hash(), + }) + require.NoError(t, err) + + return app +} + +// GenesisStateWithSingleValidator initializes GenesisState with a single validator and genesis accounts +// that also act as delegators. +func GenesisStateWithSingleValidator(t *testing.T, app *ChainApp) GenesisState { + t.Helper() + + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + + // create validator set with single validator + validator := cmttypes.NewValidator(pubKey, 1) + valSet := cmttypes.NewValidatorSet([]*cmttypes.Validator{validator}) + + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balances := []banktypes.Balance{ + { + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100000000000000))), + }, + } + + genesisState := app.DefaultGenesis() + genesisState, err = simtestutil.GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balances...) + require.NoError(t, err) + + return genesisState +} + +// AddTestAddrsIncremental constructs and returns accNum amount of accounts with an +// initial balance of accAmt in random order +func AddTestAddrsIncremental(app *ChainApp, ctx sdk.Context, accNum int, accAmt sdkmath.Int) []sdk.AccAddress { + return addTestAddrs(app, ctx, accNum, accAmt, simtestutil.CreateIncrementalAccounts) +} + +func addTestAddrs(app *ChainApp, ctx sdk.Context, accNum int, accAmt sdkmath.Int, strategy simtestutil.GenerateAccountStrategy) []sdk.AccAddress { + testAddrs := strategy(accNum) + bondDenom, err := app.StakingKeeper.BondDenom(ctx) + if err != nil { + panic(err) + } + + initCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, accAmt)) + + for _, addr := range testAddrs { + initAccountWithCoins(app, ctx, addr, initCoins) + } + + return testAddrs +} + +func initAccountWithCoins(app *ChainApp, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { + err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, coins) + if err != nil { + panic(err) + } + + err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, coins) + if err != nil { + panic(err) + } +} + +// NewTestNetworkFixture returns a new simapp AppConstructor for network simulation tests +func NewTestNetworkFixture() network.TestFixture { + dir, err := os.MkdirTemp("", "simapp") + if err != nil { + panic(fmt.Sprintf("failed creating temporary directory: %v", err)) + } + defer os.RemoveAll(dir) + + app := NewChainApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(dir)) + + appCtr := func(val network.ValidatorI) servertypes.Application { + appOptions := simtestutil.AppOptionsMap{ + flags.FlagHome: val.GetCtx().Config.RootDir, + relayercfg.FlagCosmosNodeRPC: val.GetRPC(), + relayercfg.FlagLightClientURL: "http://127.0.0.1:8000", + relayercfg.FlagPublishBlobInterval: "5", + } + return NewChainApp( + val.GetCtx().Logger, dbm.NewMemDB(), nil, true, + appOptions, + bam.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), + bam.SetMinGasPrices(val.GetAppConfig().MinGasPrices), + bam.SetChainID(val.GetCtx().Viper.GetString(flags.FlagChainID)), + ) + } + + return network.TestFixture{ + AppConstructor: appCtr, + GenesisState: app.DefaultGenesis(), + EncodingConfig: testutil.TestEncodingConfig{ + InterfaceRegistry: app.InterfaceRegistry(), + Codec: app.AppCodec(), + TxConfig: app.TxConfig(), + Amino: app.LegacyAmino(), + }, + } +} diff --git a/simapp/cmd/availd/commads.go b/simapp/cmd/availd/commads.go index 89f639c..7aacef9 100644 --- a/simapp/cmd/availd/commads.go +++ b/simapp/cmd/availd/commads.go @@ -8,12 +8,13 @@ import ( "os" "path/filepath" + "simapp/app" + cmtcfg "github.com/cometbft/cometbft/config" dbm "github.com/cosmos/cosmos-db" "github.com/spf13/cast" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/vitwit/avail-da-module/simapp/app" "golang.org/x/sync/errgroup" "cosmossdk.io/log" @@ -37,6 +38,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/crisis" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" availblobcli "github.com/vitwit/avail-da-module/client/cli" @@ -106,7 +108,7 @@ func initRootCmd( rootCmd.AddCommand( genutilcli.InitCmd(basicManager, app.DefaultNodeHome), - //NewTestnetCmd(basicManager, banktypes.GenesisBalancesIterator{}), + NewTestnetCmd(basicManager, banktypes.GenesisBalancesIterator{}), debug.Cmd(), confixcmd.ConfigCommand(), pruning.Cmd(newApp, app.DefaultNodeHome), diff --git a/simapp/cmd/availd/main.go b/simapp/cmd/availd/main.go index 0115a0f..60c7699 100644 --- a/simapp/cmd/availd/main.go +++ b/simapp/cmd/availd/main.go @@ -5,8 +5,9 @@ import ( "cosmossdk.io/log" + "simapp/app" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - "github.com/vitwit/avail-da-module/simapp/app" ) func main() { diff --git a/simapp/cmd/availd/root.go b/simapp/cmd/availd/root.go index 98fae60..23d0239 100644 --- a/simapp/cmd/availd/root.go +++ b/simapp/cmd/availd/root.go @@ -18,8 +18,8 @@ import ( txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/vitwit/avail-da-module/simapp/app" - "github.com/vitwit/avail-da-module/simapp/app/params" + "simapp/app" + "simapp/app/params" ) // NewRootCmd creates a new root command for chain app. It is called once in the diff --git a/simapp/cmd/availd/testnet.go b/simapp/cmd/availd/testnet.go new file mode 100644 index 0000000..bcb6008 --- /dev/null +++ b/simapp/cmd/availd/testnet.go @@ -0,0 +1,535 @@ +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + + cmtconfig "github.com/cometbft/cometbft/config" + cmttime "github.com/cometbft/cometbft/types/time" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + simapp "simapp/app" + + "cosmossdk.io/math" + "cosmossdk.io/math/unsafe" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/server" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + network "github.com/vitwit/avail-da-module/network" +) + +var ( + flagNodeDirPrefix = "node-dir-prefix" + flagNumValidators = "v" + flagOutputDir = "output-dir" + flagNodeDaemonHome = "node-daemon-home" + flagStartingIPAddress = "starting-ip-address" + flagEnableLogging = "enable-logging" + flagGRPCAddress = "grpc.address" + flagRPCAddress = "rpc.address" + flagAPIAddress = "api.address" + flagPrintMnemonic = "print-mnemonic" +) + +type initArgs struct { + algo string + chainID string + keyringBackend string + minGasPrices string + nodeDaemonHome string + nodeDirPrefix string + numValidators int + outputDir string + startingIPAddress string +} + +type startArgs struct { + algo string + apiAddress string + chainID string + enableLogging bool + grpcAddress string + minGasPrices string + numValidators int + outputDir string + printMnemonic bool + rpcAddress string +} + +func addTestnetFlagsToCmd(cmd *cobra.Command) { + cmd.Flags().Int(flagNumValidators, 4, "Number of validators to initialize the testnet with") + cmd.Flags().StringP(flagOutputDir, "o", "./.testnets", "Directory to store initialization data for the testnet") + cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)") + cmd.Flags().String(flags.FlagKeyType, string(hd.Secp256k1Type), "Key signing algorithm to generate keys for") + + // support old flags name for backwards compatibility + cmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName { + if name == flags.FlagKeyAlgorithm { + name = flags.FlagKeyType + } + + return pflag.NormalizedName(name) + }) +} + +// NewTestnetCmd creates a root testnet command with subcommands to run an in-process testnet or initialize +// validator configuration files for running a multi-validator testnet in a separate process +func NewTestnetCmd(mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator) *cobra.Command { + testnetCmd := &cobra.Command{ + Use: "testnet", + Short: "subcommands for starting or configuring local testnets", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + testnetCmd.AddCommand(testnetStartCmd()) + testnetCmd.AddCommand(testnetInitFilesCmd(mbm, genBalIterator)) + + return testnetCmd +} + +// testnetInitFilesCmd returns a cmd to initialize all files for CometBFT testnet and application +func testnetInitFilesCmd(mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator) *cobra.Command { + cmd := &cobra.Command{ + Use: "init-files", + Short: "Initialize config directories & files for a multi-validator testnet running locally via separate processes (e.g. Docker Compose or similar)", + Long: `init-files will setup "v" number of directories and populate each with +necessary files (private validator, genesis, config, etc.) for running "v" validator nodes. + +Booting up a network with these validator folders is intended to be used with Docker Compose, +or a similar setup where each node has a manually configurable IP address. + +Note, strict routability for addresses is turned off in the config file. + +Example: + availd testnet init-files --v 4 --output-dir ./.testnets --starting-ip-address 192.168.10.2 + `, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config + + args := initArgs{} + args.outputDir, _ = cmd.Flags().GetString(flagOutputDir) + args.keyringBackend, _ = cmd.Flags().GetString(flags.FlagKeyringBackend) + args.chainID, _ = cmd.Flags().GetString(flags.FlagChainID) + args.minGasPrices, _ = cmd.Flags().GetString(server.FlagMinGasPrices) + args.nodeDirPrefix, _ = cmd.Flags().GetString(flagNodeDirPrefix) + args.nodeDaemonHome, _ = cmd.Flags().GetString(flagNodeDaemonHome) + args.startingIPAddress, _ = cmd.Flags().GetString(flagStartingIPAddress) + args.numValidators, _ = cmd.Flags().GetInt(flagNumValidators) + args.algo, _ = cmd.Flags().GetString(flags.FlagKeyType) + + return initTestnetFiles(clientCtx, cmd, config, mbm, genBalIterator, clientCtx.TxConfig.SigningContext().ValidatorAddressCodec(), args) + }, + } + + addTestnetFlagsToCmd(cmd) + cmd.Flags().String(flagNodeDirPrefix, "node", "Prefix the directory name for each node with (node results in node0, node1, ...)") + cmd.Flags().String(flagNodeDaemonHome, "availsdk", "Home directory of the node's daemon configuration") + cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + + return cmd +} + +// testnetStartCmd returns a cmd to start multi validator in-process testnet +func testnetStartCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "start", + Short: "Launch an in-process multi-validator testnet", + Long: `testnet will launch an in-process multi-validator testnet, +and generate "v" directories, populated with necessary validator configuration files +(private validator, genesis, config, etc.). + +Example: + availd testnet --v 4 --output-dir ./.testnets + `, + RunE: func(cmd *cobra.Command, _ []string) error { + args := startArgs{} + args.outputDir, _ = cmd.Flags().GetString(flagOutputDir) + args.chainID, _ = cmd.Flags().GetString(flags.FlagChainID) + args.minGasPrices, _ = cmd.Flags().GetString(server.FlagMinGasPrices) + args.numValidators, _ = cmd.Flags().GetInt(flagNumValidators) + args.algo, _ = cmd.Flags().GetString(flags.FlagKeyType) + args.enableLogging, _ = cmd.Flags().GetBool(flagEnableLogging) + args.rpcAddress, _ = cmd.Flags().GetString(flagRPCAddress) + args.apiAddress, _ = cmd.Flags().GetString(flagAPIAddress) + args.grpcAddress, _ = cmd.Flags().GetString(flagGRPCAddress) + args.printMnemonic, _ = cmd.Flags().GetBool(flagPrintMnemonic) + + return startTestnet(cmd, args) + }, + } + + addTestnetFlagsToCmd(cmd) + cmd.Flags().Bool(flagEnableLogging, false, "Enable INFO logging of CometBFT validator nodes") + cmd.Flags().String(flagRPCAddress, "tcp://0.0.0.0:26657", "the RPC address to listen on") + cmd.Flags().String(flagAPIAddress, "tcp://0.0.0.0:1317", "the address to listen on for REST API") + cmd.Flags().String(flagGRPCAddress, "0.0.0.0:9090", "the gRPC server address to listen on") + cmd.Flags().Bool(flagPrintMnemonic, true, "print mnemonic of first validator to stdout for manual testing") + return cmd +} + +const nodeDirPerm = 0o755 + +// initTestnetFiles initializes testnet files for a testnet to be run in a separate process +func initTestnetFiles( + clientCtx client.Context, + cmd *cobra.Command, + nodeConfig *cmtconfig.Config, + mbm module.BasicManager, + genBalIterator banktypes.GenesisBalancesIterator, + valAddrCodec runtime.ValidatorAddressCodec, + args initArgs, +) error { + if args.chainID == "" { + args.chainID = "chain-" + unsafe.Str(6) + } + nodeIDs := make([]string, args.numValidators) + valPubKeys := make([]cryptotypes.PubKey, args.numValidators) + + simappConfig := srvconfig.DefaultConfig() + simappConfig.MinGasPrices = args.minGasPrices + simappConfig.API.Enable = true + simappConfig.Telemetry.Enabled = true + simappConfig.Telemetry.PrometheusRetentionTime = 60 + simappConfig.Telemetry.EnableHostnameLabel = false + simappConfig.Telemetry.GlobalLabels = [][]string{{"chain_id", args.chainID}} + + var ( + genAccounts []authtypes.GenesisAccount + genBalances []banktypes.Balance + genFiles []string + ) + + inBuf := bufio.NewReader(cmd.InOrStdin()) + // generate private keys, node IDs, and initial transactions + for i := 0; i < args.numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", args.nodeDirPrefix, i) + nodeDir := filepath.Join(args.outputDir, nodeDirName, args.nodeDaemonHome) + gentxsDir := filepath.Join(args.outputDir, "gentxs") + + nodeConfig.SetRoot(nodeDir) + nodeConfig.Moniker = nodeDirName + nodeConfig.RPC.ListenAddress = "tcp://0.0.0.0:26657" + + if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil { + _ = os.RemoveAll(args.outputDir) + return err + } + + ip, err := getIP(i, args.startingIPAddress) + if err != nil { + _ = os.RemoveAll(args.outputDir) + return err + } + + nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(nodeConfig) + if err != nil { + _ = os.RemoveAll(args.outputDir) + return err + } + + memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) + genFiles = append(genFiles, nodeConfig.GenesisFile()) + + kb, err := keyring.New(sdk.KeyringServiceName(), args.keyringBackend, nodeDir, inBuf, clientCtx.Codec) + if err != nil { + return err + } + + keyringAlgos, _ := kb.SupportedAlgorithms() + algo, err := keyring.NewSigningAlgoFromString(args.algo, keyringAlgos) + if err != nil { + return err + } + + addr, secret, err := testutil.GenerateSaveCoinKey(kb, nodeDirName, "", true, algo) + if err != nil { + _ = os.RemoveAll(args.outputDir) + return err + } + + info := map[string]string{"secret": secret} + + cliPrint, err := json.Marshal(info) + if err != nil { + return err + } + + // save private key seed words + if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), nodeDir, cliPrint); err != nil { + return err + } + + accTokens := sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction) + accStakingTokens := sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction) + coins := sdk.Coins{ + sdk.NewCoin("testtoken", accTokens), + sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens), + } + + genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: coins.Sort()}) + genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) + + valStr, err := valAddrCodec.BytesToString(sdk.ValAddress(addr)) + if err != nil { + return err + } + valTokens := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) + createValMsg, err := stakingtypes.NewMsgCreateValidator( + valStr, + valPubKeys[i], + sdk.NewCoin(sdk.DefaultBondDenom, valTokens), + stakingtypes.NewDescription(nodeDirName, "", "", "", ""), + stakingtypes.NewCommissionRates(math.LegacyOneDec(), math.LegacyOneDec(), math.LegacyOneDec()), + math.OneInt(), + ) + if err != nil { + return err + } + + txBuilder := clientCtx.TxConfig.NewTxBuilder() + if err := txBuilder.SetMsgs(createValMsg); err != nil { + return err + } + + txBuilder.SetMemo(memo) + + txFactory := tx.Factory{} + txFactory = txFactory. + WithChainID(args.chainID). + WithMemo(memo). + WithKeybase(kb). + WithTxConfig(clientCtx.TxConfig) + + if err := tx.Sign(cmd.Context(), txFactory, nodeDirName, txBuilder, true); err != nil { + return err + } + + txBz, err := clientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + if err != nil { + return err + } + + if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz); err != nil { + return err + } + + srvconfig.SetConfigTemplate(srvconfig.DefaultConfigTemplate) + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), simappConfig) + } + + if err := initGenFiles(clientCtx, mbm, args.chainID, genAccounts, genBalances, genFiles, args.numValidators); err != nil { + return err + } + + err := collectGenFiles( + clientCtx, nodeConfig, args.chainID, nodeIDs, valPubKeys, args.numValidators, + args.outputDir, args.nodeDirPrefix, args.nodeDaemonHome, genBalIterator, valAddrCodec, + ) + if err != nil { + return err + } + + cmd.PrintErrf("Successfully initialized %d node directories\n", args.numValidators) + return nil +} + +func initGenFiles( + clientCtx client.Context, mbm module.BasicManager, chainID string, + genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, + genFiles []string, numValidators int, +) error { + appGenState := mbm.DefaultGenesis(clientCtx.Codec) + + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + clientCtx.Codec.MustUnmarshalJSON(appGenState[authtypes.ModuleName], &authGenState) + + accounts, err := authtypes.PackAccounts(genAccounts) + if err != nil { + return err + } + + authGenState.Accounts = accounts + appGenState[authtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(&authGenState) + + // set the balances in the genesis state + var bankGenState banktypes.GenesisState + clientCtx.Codec.MustUnmarshalJSON(appGenState[banktypes.ModuleName], &bankGenState) + + bankGenState.Balances = banktypes.SanitizeGenesisBalances(genBalances) + for _, bal := range bankGenState.Balances { + bankGenState.Supply = bankGenState.Supply.Add(bal.Coins...) + } + appGenState[banktypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(&bankGenState) + + appGenStateJSON, err := json.MarshalIndent(appGenState, "", " ") + if err != nil { + return err + } + + appGenesis := genutiltypes.NewAppGenesisWithVersion(chainID, appGenStateJSON) + // generate empty genesis files for each validator and save + for i := 0; i < numValidators; i++ { + if err := appGenesis.SaveAs(genFiles[i]); err != nil { + return err + } + } + return nil +} + +func collectGenFiles( + clientCtx client.Context, nodeConfig *cmtconfig.Config, chainID string, + nodeIDs []string, valPubKeys []cryptotypes.PubKey, numValidators int, + outputDir, nodeDirPrefix, nodeDaemonHome string, genBalIterator banktypes.GenesisBalancesIterator, valAddrCodec runtime.ValidatorAddressCodec, +) error { + var appState json.RawMessage + genTime := cmttime.Now() + + for i := 0; i < numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + gentxsDir := filepath.Join(outputDir, "gentxs") + nodeConfig.Moniker = nodeDirName + + nodeConfig.SetRoot(nodeDir) + + nodeID, valPubKey := nodeIDs[i], valPubKeys[i] + initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, valPubKey) + + appGenesis, err := genutiltypes.AppGenesisFromFile(nodeConfig.GenesisFile()) + if err != nil { + return err + } + + nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.Codec, clientCtx.TxConfig, nodeConfig, initCfg, appGenesis, genBalIterator, genutiltypes.DefaultMessageValidator, + valAddrCodec) + if err != nil { + return err + } + + if appState == nil { + // set the canonical application state (they should not differ) + appState = nodeAppState + } + + genFile := nodeConfig.GenesisFile() + + // overwrite each validator's genesis file to have a canonical genesis time + if err := genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime); err != nil { + return err + } + } + + return nil +} + +func getIP(i int, startingIPAddr string) (ip string, err error) { + if len(startingIPAddr) == 0 { + ip, err = server.ExternalIP() + if err != nil { + return "", err + } + return ip, nil + } + return calculateIP(startingIPAddr, i) +} + +func calculateIP(ip string, i int) (string, error) { + ipv4 := net.ParseIP(ip).To4() + if ipv4 == nil { + return "", fmt.Errorf("%v: non ipv4 address", ip) + } + + for j := 0; j < i; j++ { + ipv4[3]++ + } + + return ipv4.String(), nil +} + +func writeFile(name, dir string, contents []byte) error { + file := filepath.Join(dir, name) + + if err := os.MkdirAll(dir, 0o755); err != nil { + return fmt.Errorf("could not create directory %q: %w", dir, err) + } + + if err := os.WriteFile(file, contents, 0o600); err != nil { + return err + } + + return nil +} + +// startTestnet starts an in-process testnet +func startTestnet(cmd *cobra.Command, args startArgs) error { + networkConfig := network.DefaultConfig(simapp.NewTestNetworkFixture) + + // Default networkConfig.ChainID is random, and we should only override it if chainID provided + // is non-empty + if args.chainID != "" { + networkConfig.ChainID = args.chainID + } + networkConfig.SigningAlgo = args.algo + networkConfig.MinGasPrices = args.minGasPrices + networkConfig.NumValidators = args.numValidators + networkConfig.EnableLogging = args.enableLogging + networkConfig.RPCAddress = args.rpcAddress + networkConfig.APIAddress = args.apiAddress + networkConfig.GRPCAddress = args.grpcAddress + networkConfig.PrintMnemonic = args.printMnemonic + networkLogger := network.NewCLILogger(cmd) + + baseDir := fmt.Sprintf("%s/%s", args.outputDir, networkConfig.ChainID) + if _, err := os.Stat(baseDir); !os.IsNotExist(err) { + return fmt.Errorf( + "testnests directory already exists for chain-id '%s': %s, please remove or select a new --chain-id", + networkConfig.ChainID, baseDir) + } + + testnet, err := network.New(networkLogger, baseDir, networkConfig) + if err != nil { + return err + } + + if _, err := testnet.WaitForHeight(1); err != nil { + return err + } + cmd.Println("press the Enter Key to terminate") + if _, err := fmt.Scanln(); err != nil { // wait for Enter Key + return err + } + testnet.Cleanup() + + return nil +} diff --git a/simapp/cmd/availd/testnet_test.go b/simapp/cmd/availd/testnet_test.go new file mode 100644 index 0000000..54616ff --- /dev/null +++ b/simapp/cmd/availd/testnet_test.go @@ -0,0 +1,72 @@ +package main + +import ( + "context" + "fmt" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + "cosmossdk.io/log" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/types/module" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/consensus" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/mint" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +func Test_TestnetCmd(t *testing.T) { + moduleBasic := module.NewBasicManager( + auth.AppModuleBasic{}, + genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), + bank.AppModuleBasic{}, + staking.AppModuleBasic{}, + mint.AppModuleBasic{}, + distribution.AppModuleBasic{}, + params.AppModuleBasic{}, + consensus.AppModuleBasic{}, + ) + + home := t.TempDir() + encodingConfig := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, staking.AppModuleBasic{}) + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultCometConfig(home) + require.NoError(t, err) + + err = genutiltest.ExecInitCmd(moduleBasic, home, encodingConfig.Codec) + require.NoError(t, err) + + serverCtx := server.NewContext(viper.New(), cfg, logger) + clientCtx := client.Context{}. + WithCodec(encodingConfig.Codec). + WithHomeDir(home). + WithTxConfig(encodingConfig.TxConfig) + + ctx := context.Background() + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + cmd := testnetInitFilesCmd(moduleBasic, banktypes.GenesisBalancesIterator{}) + cmd.SetArgs([]string{fmt.Sprintf("--%s=test", flags.FlagKeyringBackend), fmt.Sprintf("--output-dir=%s", home)}) + err = cmd.ExecuteContext(ctx) + require.NoError(t, err) + + genFile := cfg.GenesisFile() + appState, _, err := genutiltypes.GenesisStateFromGenFile(genFile) + require.NoError(t, err) + + bankGenState := banktypes.GetGenesisStateFromAppState(encodingConfig.Codec, appState) + require.NotEmpty(t, bankGenState.Supply.String()) +} diff --git a/simapp/go.mod b/simapp/go.mod index af21158..7b44172 100644 --- a/simapp/go.mod +++ b/simapp/go.mod @@ -1,4 +1,4 @@ -module github.com/vitwit/avail-da-module/simapp +module simapp go 1.22.5 @@ -8,7 +8,7 @@ replace github.com/vitwit/avail-da-module => ../ replace ( cosmossdk.io/core => cosmossdk.io/core v0.11.0 cosmossdk.io/x/upgrade => cosmossdk.io/x/upgrade v0.1.1 // Remove once cosmos-sdk fork has been updated to latest v0.50.6 - github.com/cosmos/cosmos-sdk => github.com/vitwit/cosmos-sdk v0.50.6-0.20240809182747-cd5b906bd4f6 + github.com/cosmos/cosmos-sdk => github.com/vitwit/cosmos-sdk v0.50.6-0.20240905105834-9a5babf69986 github.com/prometheus/client_golang => github.com/prometheus/client_golang v1.18.0 // Remove once cosmos-sdk fork has been updated to latest v0.50.6 github.com/prometheus/client_model => github.com/prometheus/client_model v0.6.0 // Remove once cosmos-sdk fork has been updated to latest v0.50.6 github.com/prometheus/common => github.com/prometheus/common v0.47.0 // Remove once cosmos-sdk fork has been updated to latest v0.50.6 @@ -37,7 +37,7 @@ require ( cosmossdk.io/client/v2 v2.0.0-beta.4 cosmossdk.io/core v0.12.0 cosmossdk.io/log v1.3.1 - cosmossdk.io/math v1.3.0 // indirect + cosmossdk.io/math v1.3.0 cosmossdk.io/store v1.1.0 cosmossdk.io/tools/confix v0.1.1 cosmossdk.io/x/circuit v0.1.1 @@ -55,9 +55,9 @@ require ( github.com/cosmos/ibc-go/v8 v8.3.2 github.com/spf13/cast v1.6.0 github.com/spf13/cobra v1.8.1 - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 - github.com/stretchr/testify v1.9.0 // indirect + github.com/stretchr/testify v1.9.0 github.com/vitwit/avail-da-module v0.0.0-20240725103806-4280f8cc7eb9 golang.org/x/sync v0.7.0 ) diff --git a/simapp/go.sum b/simapp/go.sum index f871462..ab5adf8 100644 --- a/simapp/go.sum +++ b/simapp/go.sum @@ -1621,8 +1621,8 @@ github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vedhavyas/go-subkey/v2 v2.0.0 h1:LemDIsrVtRSOkp0FA8HxP6ynfKjeOj3BY2U9UNfeDMA= github.com/vedhavyas/go-subkey/v2 v2.0.0/go.mod h1:95aZ+XDCWAUUynjlmi7BtPExjXgXxByE0WfBwbmIRH4= -github.com/vitwit/cosmos-sdk v0.50.6-0.20240809182747-cd5b906bd4f6 h1:Fk3VM6Q6ru973oxWNFn6oAxPRDoT5+vayzkmWWXkysU= -github.com/vitwit/cosmos-sdk v0.50.6-0.20240809182747-cd5b906bd4f6/go.mod h1:oV/k6GJgXV9QPoM2fsYDPPsyPBgQbdotv532O6Mz1OQ= +github.com/vitwit/cosmos-sdk v0.50.6-0.20240905105834-9a5babf69986 h1:e38Z5AymIZ3dFCbAStFFo7baNnHYsHi8DTvMZHeoSfY= +github.com/vitwit/cosmos-sdk v0.50.6-0.20240905105834-9a5babf69986/go.mod h1:+92hG6i+t1PRQ67ajr6D/Wgcnh/ifvnSte0u2rOszdU= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=