From fd62df914f91c9c26ff75a09ef10b4b9ba946a22 Mon Sep 17 00:00:00 2001 From: Giuseppe Natale <12249307+giunatale@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:48:57 +0100 Subject: [PATCH] feat: add `x/photon` module (#57) This PR adds the `x/photon` module to the main branch, which implements the corresponding ADR (separate PR #34) Closes #44 --------- Co-authored-by: Thomas Bruyelle Co-authored-by: Albert Le Batteux --- CHANGELOG.md | 8 + Makefile | 18 +- UPGRADING.md | 306 ++++- ante/ante.go | 8 +- app/app.go | 4 +- app/keepers/keepers.go | 12 + app/keepers/keys.go | 2 + app/modules.go | 12 +- app/params/config.go | 20 + app/params/params.go | 7 - app/upgrades/v2/constants.go | 23 + app/upgrades/v2/upgrades.go | 66 + cmd/atomoned/cmd/bech32_convert.go | 3 +- cmd/atomoned/cmd/config.go | 23 +- contrib/devdeps/go.mod | 1 + contrib/devdeps/go.sum | 2 + contrib/devdeps/tools.go | 5 +- proto/atomone/photon/v1/genesis.proto | 13 + proto/atomone/photon/v1/photon.proto | 16 + proto/atomone/photon/v1/query.proto | 42 + proto/atomone/photon/v1/tx.proto | 66 + tests/e2e/{chain.go => chain_test.go} | 2 + tests/e2e/e2e_bank_test.go | 23 +- tests/e2e/e2e_distribution_test.go | 15 +- tests/e2e/e2e_exec_test.go | 122 +- tests/e2e/e2e_feegrant_test.go | 3 - tests/e2e/e2e_gov_test.go | 73 +- tests/e2e/e2e_ibc_test.go | 12 +- tests/e2e/e2e_photon_test.go | 58 + tests/e2e/e2e_setup_test.go | 74 +- tests/e2e/e2e_staking_test.go | 9 +- tests/e2e/e2e_test.go | 8 + tests/e2e/e2e_vesting_test.go | 45 +- tests/e2e/{genesis.go => genesis_test.go} | 13 +- tests/e2e/{http_util.go => http_util_test.go} | 0 tests/e2e/query_test.go | 46 +- tests/e2e/scripts/hermes_bootstrap.sh | 8 +- tests/e2e/{util.go => util_test.go} | 0 tests/e2e/{validator.go => validator_test.go} | 0 x/photon/ante/ante.go | 71 ++ x/photon/ante/ante_test.go | 240 ++++ x/photon/client/cli/query.go | 74 ++ x/photon/client/cli/tx.go | 55 + x/photon/genesis.go | 24 + x/photon/genesis_test.go | 24 + x/photon/keeper/grpc_query.go | 35 + x/photon/keeper/grpc_query_test.go | 64 + x/photon/keeper/keeper.go | 56 + x/photon/keeper/msg_server.go | 110 ++ x/photon/keeper/msg_server_test.go | 185 +++ x/photon/keeper/params.go | 29 + x/photon/keeper/params_test.go | 19 + x/photon/module.go | 153 +++ x/photon/module_simulation.go | 30 + x/photon/simulation/decoder.go | 16 + x/photon/simulation/genesis.go | 47 + x/photon/simulation/operations.go | 89 ++ x/photon/simulation/proposals.go | 43 + x/photon/testutil/expected_keepers_mocks.go | 194 +++ x/photon/testutil/keeper.go | 54 + x/photon/types/codec.go | 34 + x/photon/types/const.go | 10 + x/photon/types/errors.go | 14 + x/photon/types/events.go | 9 + x/photon/types/expected_keepers.go | 26 + x/photon/types/genesis.go | 19 + x/photon/types/genesis.pb.go | 323 +++++ x/photon/types/genesis_test.go | 37 + x/photon/types/keys.go | 14 + x/photon/types/msgs.go | 76 ++ x/photon/types/msgs_test.go | 112 ++ x/photon/types/params.go | 27 + x/photon/types/photon.pb.go | 369 ++++++ x/photon/types/query.pb.go | 878 +++++++++++++ x/photon/types/query.pb.gw.go | 218 ++++ x/photon/types/tx.pb.go | 1083 +++++++++++++++++ x/photon/types/types.go | 1 + 77 files changed, 5849 insertions(+), 181 deletions(-) create mode 100644 app/params/config.go delete mode 100644 app/params/params.go create mode 100644 app/upgrades/v2/constants.go create mode 100644 app/upgrades/v2/upgrades.go create mode 100644 proto/atomone/photon/v1/genesis.proto create mode 100644 proto/atomone/photon/v1/photon.proto create mode 100644 proto/atomone/photon/v1/query.proto create mode 100644 proto/atomone/photon/v1/tx.proto rename tests/e2e/{chain.go => chain_test.go} (97%) create mode 100644 tests/e2e/e2e_photon_test.go rename tests/e2e/{genesis.go => genesis_test.go} (93%) rename tests/e2e/{http_util.go => http_util_test.go} (100%) rename tests/e2e/{util.go => util_test.go} (100%) rename tests/e2e/{validator.go => validator_test.go} (100%) create mode 100644 x/photon/ante/ante.go create mode 100644 x/photon/ante/ante_test.go create mode 100644 x/photon/client/cli/query.go create mode 100644 x/photon/client/cli/tx.go create mode 100644 x/photon/genesis.go create mode 100644 x/photon/genesis_test.go create mode 100644 x/photon/keeper/grpc_query.go create mode 100644 x/photon/keeper/grpc_query_test.go create mode 100644 x/photon/keeper/keeper.go create mode 100644 x/photon/keeper/msg_server.go create mode 100644 x/photon/keeper/msg_server_test.go create mode 100644 x/photon/keeper/params.go create mode 100644 x/photon/keeper/params_test.go create mode 100644 x/photon/module.go create mode 100644 x/photon/module_simulation.go create mode 100644 x/photon/simulation/decoder.go create mode 100644 x/photon/simulation/genesis.go create mode 100644 x/photon/simulation/operations.go create mode 100644 x/photon/simulation/proposals.go create mode 100644 x/photon/testutil/expected_keepers_mocks.go create mode 100644 x/photon/testutil/keeper.go create mode 100644 x/photon/types/codec.go create mode 100644 x/photon/types/const.go create mode 100644 x/photon/types/errors.go create mode 100644 x/photon/types/events.go create mode 100644 x/photon/types/expected_keepers.go create mode 100644 x/photon/types/genesis.go create mode 100644 x/photon/types/genesis.pb.go create mode 100644 x/photon/types/genesis_test.go create mode 100644 x/photon/types/keys.go create mode 100644 x/photon/types/msgs.go create mode 100644 x/photon/types/msgs_test.go create mode 100644 x/photon/types/params.go create mode 100644 x/photon/types/photon.pb.go create mode 100644 x/photon/types/query.pb.go create mode 100644 x/photon/types/query.pb.gw.go create mode 100644 x/photon/types/tx.pb.go create mode 100644 x/photon/types/types.go diff --git a/CHANGELOG.md b/CHANGELOG.md index c9f32741..170c9764 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,14 @@ ### STATE BREAKING +## v2.0.0 + +*Release date* + +### FEATURES + +- Add the photon module and use photon as the only fee denom [#57](https://github.com/atomone-hub/atomone/pull/57) + ## v1.0.0 *Sep 26th, 2024* diff --git a/Makefile b/Makefile index 4e549775..bb6901ce 100644 --- a/Makefile +++ b/Makefile @@ -216,7 +216,13 @@ docker-build-hermes: docker-build-all: docker-build-debug docker-build-hermes -.PHONY: docker-build-debug docker-build-hermes docker-build-all +mockgen_cmd=$(rundep) github.com/golang/mock/mockgen + +mocks-gen: + $(mockgen_cmd) -source=x/gov/testutil/expected_keepers.go -package testutil -destination x/gov/testutil/expected_keepers_mocks.go + $(mockgen_cmd) -source=x/photon/types/expected_keepers.go -package testutil -destination x/photon/testutil/expected_keepers_mocks.go + +.PHONY: docker-build-debug docker-build-hermes docker-build-all mocks-gen ############################################################################### ### Linting ### @@ -261,14 +267,16 @@ update-swagger-docs: proto-swagger-gen start-localnet-ci: build rm -rf ~/.atomoned-liveness - ./build/atomoned init liveness --chain-id liveness --home ~/.atomoned-liveness + ./build/atomoned init liveness --default-denom uatone --chain-id liveness --home ~/.atomoned-liveness ./build/atomoned config chain-id liveness --home ~/.atomoned-liveness ./build/atomoned config keyring-backend test --home ~/.atomoned-liveness ./build/atomoned keys add val --home ~/.atomoned-liveness - ./build/atomoned genesis add-genesis-account val 10000000000000000000000000stake --home ~/.atomoned-liveness --keyring-backend test - ./build/atomoned genesis gentx val 1000000000stake --home ~/.atomoned-liveness --chain-id liveness + ./build/atomoned genesis add-genesis-account val 1000000000000uatone --home ~/.atomoned-liveness --keyring-backend test + ./build/atomoned keys add user --home ~/.atomoned-liveness + ./build/atomoned genesis add-genesis-account user 1000000000uatone --home ~/.atomoned-liveness --keyring-backend test + ./build/atomoned genesis gentx val 1000000000uatone --home ~/.atomoned-liveness --chain-id liveness ./build/atomoned genesis collect-gentxs --home ~/.atomoned-liveness - sed -i.bak'' 's/minimum-gas-prices = ""/minimum-gas-prices = "0uatone"/' ~/.atomoned-liveness/config/app.toml + sed -i.bak 's#^minimum-gas-prices = .*#minimum-gas-prices = "0.001uatone,0.001uphoton"#g' ~/.atomoned-liveness/config/app.toml ./build/atomoned start --home ~/.atomoned-liveness --x-crisis-skip-assert-invariants .PHONY: start-localnet-ci diff --git a/UPGRADING.md b/UPGRADING.md index 9e314479..d80e8ba7 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,6 +1,23 @@ # Upgrading AtomOne -This guide provides instructions for upgrading to specific versions of AtomOne. +This guide provides instructions for upgrading AtomOne from v1.x to v2.x. + +This document describes the steps for validators and full node operators, to +upgrade successfully for the AtomOne v2 release. + +For more details on the release, please see the [release notes][v2]. + +**Validators** will have to change their configuration to allow the PHOTON +denom (`uphoton`) for the fees, **before** the upgrade, see [Validator config +change](#validator-config-change). + +**Relayer Operators** will also need to update their configuration to use the +PHOTON denom for the fees, but this time **after** the upgrade, see [Relayer +config change](#relayer-config-change) section. + +## Release Binary + +Please use the correct release binary: `v2.0.0`. ## Go version bump @@ -18,3 +35,290 @@ AtomOne. For example, to run `make build` : ``` $ GOROOT=$(go1.22.10 env GOROOT) PATH=$GOROOT/bin:$PATH make build ``` + +## Instructions + +- [Upgrading AtomOne](#upgrading-atomeone) + - [Release Binary](#release-binary) + - [Go version bump](#go-version-dump) + - [Instructions](#instructions) + - [On-chain governance proposal attains consensus](#on-chain-governance-proposal-attains-consensus) + - [Upgrade date](#upgrade-date) + - [Preparing for the upgrade](#preparing-for-the-upgrade) + - [System requirements](#system-requirements) + - [Backups](#backups) + - [Testing](#testing) + - [Current runtime](#current-runtime) + - [Target runtime](#target-runtime) + - [Upgrade steps](#upgrade-steps) + - [Validator config change](#validator-config-change) + - [Relayer config change](#relayer-config-change) + - [Method I: Manual Upgrade](#method-i-manual-upgrade) + - [Method II: Upgrade using Cosmovisor](#method-ii-upgrade-using-cosmovisor) + - [Manually preparing the binary](#manually-preparing-the-binary) + - [Preparation](#preparation) + - [Auto-Downloading the AtomOne binary](#auto-downloading-the-gaia-binary) + - [Expected upgrade result](#expected-upgrade-result) + - [Upgrade duration](#upgrade-duration) + - [Rollback plan](#rollback-plan) + - [Communications](#communications) + - [Risks](#risks) + +## On-chain governance proposal attains consensus + +Once a software upgrade governance proposal is submitted to the Cosmos Hub, +both a reference to this proposal and an `UPGRADE_HEIGHT` are added to the +[release notes][v2]. +If and when this proposal reaches consensus, the upgrade height will be used to +halt the "old" chain binaries. You can check the proposal on one of the block +explorers or using the `atomoned` CLI tool. +Neither core developers nor core funding entities control the governance. + +## Upgrade date + +The date/time of the upgrade is subject to change as blocks are not generated +at a constant interval. You can stay up-to-date by checking the estimated +estimated time until the block is produced one of the block explorers (e.g. +https://www.mintscan.io/atomone/blocks/`UPGRADE_HEIGHT`). + +## Preparing for the upgrade + +### System requirements + +### Backups + +Prior to the upgrade, validators are encouraged to take a full data snapshot. +Snapshotting depends heavily on infrastructure, but generally this can be done +by backing up the `.atomone` directory. +If you use Cosmovisor to upgrade, by default, Cosmovisor will backup your data +upon upgrade. See below [upgrade using cosmovisor](#method-ii-upgrade-using-cosmovisor) +section. + +It is critically important for validator operators to back-up the +`.atomone/data/priv_validator_state.json` file after stopping the atomoned +process. This file is updated every block as your validator participates in +consensus rounds. It is a critical file needed to prevent double-signing, in +case the upgrade fails and the previous chain needs to be restarted. + +### Current runtime + +The AtomOne mainnet network, `atomone-1`, is currently running [AtomOne +v1.0.0][v1]. We anticipate that operators who are running on v1.0.0, will be +able to upgrade successfully. Validators are expected to ensure that their +systems are up to date and capable of performing the upgrade. This includes +running the correct binary and if building from source, building with the +appropriate `go` version. + +### Target runtime + +The AtomOne mainnet network, `atomone-1`, will run **[AtomOne v2.0.0][v2]**. +Operators _**MUST**_ use this version post-upgrade to remain connected to the +network. The new version requires `go v1.21` to build successfully. + +## Upgrade steps + +There are 2 major ways to upgrade a node: + +- Manual upgrade +- Upgrade using [Cosmovisor](https://pkg.go.dev/cosmossdk.io/tools/cosmovisor) + - Either by manually preparing the new binary + - Or by using the auto-download functionality (this is not yet recommended) + +If you prefer to use Cosmovisor to upgrade, some preparation work is needed +before upgrade. + +### Validator config change + +**AtomOne v2.0.0** introduces `photon` as the only fee token, so it requires a +modification of the validator configuration, namely the `minimum-gas-prices` +which must contain the `uphoton` denom in addition to the `uatone` denom (so +both denom). This setting is located in the `$ATOMONE_HOME/config/app.toml` +file. + +For example, considering this existing setting: +```toml +minimum-gas-prices = "0.001uatone" +``` +Before upgrading, the setting should be changed to: +```toml +minimum-gas-prices = "0.001uatone,0.001uphoton" +``` + +For validators that have `authz` transactions submitted periodically, the tx +fee denom would need to be updated as well. + +### Relayer config change + +Similarly to the validator config change, any running relayers would have to +change the gas price denom for the AtomOne chain, from `uatone` to `uphoton`. + +For Hermes relayers, this setting is located in the `~/.hermes/config.toml` +file. + +For example, considerng the existing setting: +```toml +[[ chain ]] +id = 'atomone-1' +(...) +gas_price = { price = 0.001, denom = 'uatone' } +``` +Once the chain is upgraded, the setting should be changed to: +```toml +[[ chain ]] +id = 'atomone-1' +(...) +gas_price = { price = 0.001, denom = 'uphoton' } +``` + +Note that unlike the validator config change which still accepts `uatone` for +fees, this change should be done **after** the upgrade because it is restricted +to `uphoton` which has no supply before the upgrade. + +### Method I: Manual Upgrade + +Make sure **AtomOne v1.0.0** is installed by either downloading a [compatible +binary][v1], or building from source. Check the required version to build this +binary in the `Makefile`. + +Run AtomOne v1.0.0 till upgrade height, the node will panic: + +```shell +ERR UPGRADE "v2" NEEDED at height: : upgrade to v2 and applying upgrade "v2" at height: +``` + +Stop the node, and switch the binary to **AtomOne v2.0.0** and re-start by +`atomone start`. + +It may take several minutes to a few hours until validators with a total sum +voting power > 2/3 to complete their node upgrades. After that, the chain can +continue to produce blocks. + +### Method II: Upgrade using Cosmovisor + +#### Manually preparing the binary + +##### Preparation + +- Install the latest version of Cosmovisor (`1.5.0`): + +```shell +go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest +cosmovisor version +# cosmovisor version: v1.5.0 +``` + +- Create a `cosmovisor` folder inside `$ATOMONE_HOME` and move AtomOne `v1.0.0` +into `$ATOMONE_HOME/cosmovisor/genesis/bin`: + +```shell +mkdir -p $ATOMONE_HOME/cosmovisor/genesis/bin +cp $(which atomoned) $ATOMONE_HOME/cosmovisor/genesis/bin +``` + +- Build AtomOne `v2.0.0`, and move atomoned `v2.0.0` to + `$ATOMONE_HOME/cosmovisor/upgrades/v2/bin` + +```shell +mkdir -p $ATOMONE_HOME/cosmovisor/upgrades/v2/bin +cp $(which atomoned) $ATOMONE_HOME/cosmovisor/upgrades/v2/bin +``` + +At this moment, you should have the following structure: + +```shell +. +├── current -> genesis or upgrades/ +├── genesis +│ └── bin +│ └── atomoned # old: v1.0.0 +└── upgrades + └── v2 + └── bin + └── atomoned # new: v2.0.0 +``` + +- Export the environmental variables: + +```shell +export DAEMON_NAME=atomoned +# please change to your own gaia home dir +# please note `DAEMON_HOME` has to be absolute path +export DAEMON_HOME=$ATOMONE_HOME +export DAEMON_RESTART_AFTER_UPGRADE=true +``` + +- Start the node: + +```shell +cosmovisor run start --x-crisis-skip-assert-invariants --home $DAEMON_HOME +``` + +Skipping the invariant checks can be used to decreases the upgrade time +significantly, but it is done at the expense of verifying state validity and +must be done cautiously. + +#### Auto-Downloading the AtomOne binary + +**This method is not recommended!** + +## Expected upgrade result + +When the upgrade block height is reached, AtomOne will panic and stop: + +This may take a few minutes to a few hours. +After upgrade, the chain will continue to produce blocks when validators with a +total sum voting power > 2/3 complete their node upgrades. + +## Upgrade duration + +The upgrade may take a few minutes to several hours to complete because +atomone-1 participants operate globally with differing operating hours and it +may take some time for operators to upgrade their binaries and connect to the +network. + +## Rollback plan + +During the network upgrade, core Cosmos teams will be keeping an ever vigilant +eye and communicating with operators on the status of their upgrades. During +this time, the core teams will listen to operator needs to determine if the +upgrade is experiencing unintended challenges. In the event of unexpected +challenges, the core teams, after conferring with operators and attaining +social consensus, may choose to declare that the upgrade will be skipped. + +Steps to skip this upgrade proposal are simply to resume the `atomone-1` +network with the (downgraded) v1.0.0 binary using the following command: + +```shell +atomoned start --unsafe-skip-upgrade +``` + +Note: There is no particular need to restore a state snapshot prior to the +upgrade height, unless specifically directed by core Cosmos teams. + +Important: A social consensus decision to skip the upgrade will be based solely +on technical merits, thereby respecting and maintaining the decentralized +governance process of the upgrade proposal's successful YES vote. + +## Communications + +Operators are encouraged to join the `#validate-private` channel +of the AtomOne (unofficial) Discord. This channel is the primary communication +tool for operators to ask questions, report upgrade status, report technical +issues, and to build social consensus should the need arise. This channel is +restricted to known operators and requires verification beforehand. Requests to +join the `#validator-private` channel can be sent to the `#support` channel. + +## Risks + +As a validator performing the upgrade procedure on your consensus nodes carries +a heightened risk of double-signing and being slashed. The most important piece +of this procedure is verifying your software version and genesis file hash +before starting your validator and signing. + +The riskiest thing a validator can do is discover that they made a mistake and +repeat the upgrade procedure again during the network startup. If you discover +a mistake in the process, the best thing to do is wait for the network to start +before correcting it. + +[v1]: https://github.com/atomone-hub/atomone/releases/tag/v1.0.0 +[v2]: https://github.com/atomone-hub/atomone/releases/tag/v2.0.0 diff --git a/ante/ante.go b/ante/ante.go index 5696b989..48ff0980 100644 --- a/ante/ante.go +++ b/ante/ante.go @@ -12,6 +12,8 @@ import ( stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" atomoneerrors "github.com/atomone-hub/atomone/types/errors" + photonante "github.com/atomone-hub/atomone/x/photon/ante" + photonkeeper "github.com/atomone-hub/atomone/x/photon/keeper" ) // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC @@ -21,6 +23,7 @@ type HandlerOptions struct { Codec codec.BinaryCodec IBCkeeper *ibckeeper.Keeper StakingKeeper *stakingkeeper.Keeper + PhotonKeeper *photonkeeper.Keeper TxFeeChecker ante.TxFeeChecker } @@ -37,10 +40,12 @@ func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) { if opts.IBCkeeper == nil { return nil, errorsmod.Wrap(atomoneerrors.ErrLogic, "IBC keeper is required for AnteHandler") } - if opts.StakingKeeper == nil { return nil, errorsmod.Wrap(atomoneerrors.ErrNotFound, "staking param store is required for AnteHandler") } + if opts.PhotonKeeper == nil { + return nil, errorsmod.Wrap(atomoneerrors.ErrNotFound, "photon keeper is required for AnteHandler") + } sigGasConsumer := opts.SigGasConsumer if sigGasConsumer == nil { @@ -54,6 +59,7 @@ func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) { ante.NewValidateMemoDecorator(opts.AccountKeeper), ante.NewConsumeGasForTxSizeDecorator(opts.AccountKeeper), NewGovVoteDecorator(opts.Codec, opts.StakingKeeper), + photonante.NewValidateFeeDecorator(opts.PhotonKeeper), ante.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, opts.TxFeeChecker), ante.NewSetPubKeyDecorator(opts.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators ante.NewValidateSigCountDecorator(opts.AccountKeeper), diff --git a/app/app.go b/app/app.go index 7f72e80a..33439649 100644 --- a/app/app.go +++ b/app/app.go @@ -51,6 +51,7 @@ import ( "github.com/atomone-hub/atomone/app/keepers" "github.com/atomone-hub/atomone/app/params" "github.com/atomone-hub/atomone/app/upgrades" + v2 "github.com/atomone-hub/atomone/app/upgrades/v2" govtypes "github.com/atomone-hub/atomone/x/gov/types" ) @@ -58,7 +59,7 @@ var ( // DefaultNodeHome default home directories for the application daemon DefaultNodeHome string - Upgrades = []upgrades.Upgrade{} + Upgrades = []upgrades.Upgrade{v2.Upgrade} ) var ( @@ -221,6 +222,7 @@ func NewAtomOneApp( Codec: appCodec, IBCkeeper: app.IBCKeeper, StakingKeeper: app.StakingKeeper, + PhotonKeeper: app.PhotonKeeper, // If TxFeeChecker is nil the default ante TxFeeChecker is used TxFeeChecker: nil, }, diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 0881c34e..ee3b243a 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -62,6 +62,8 @@ import ( govtypes "github.com/atomone-hub/atomone/x/gov/types" govv1 "github.com/atomone-hub/atomone/x/gov/types/v1" govv1beta1 "github.com/atomone-hub/atomone/x/gov/types/v1beta1" + photonkeeper "github.com/atomone-hub/atomone/x/photon/keeper" + photontypes "github.com/atomone-hub/atomone/x/photon/types" ) type AppKeepers struct { @@ -90,6 +92,7 @@ type AppKeepers struct { FeeGrantKeeper feegrantkeeper.Keeper AuthzKeeper authzkeeper.Keeper ConsensusParamsKeeper consensusparamkeeper.Keeper + PhotonKeeper *photonkeeper.Keeper // Modules ICAModule ica.AppModule @@ -208,6 +211,15 @@ func NewAppKeeper( authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) + appKeepers.PhotonKeeper = photonkeeper.NewKeeper( + appCodec, + appKeepers.keys[photontypes.StoreKey], + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + appKeepers.BankKeeper, + appKeepers.AccountKeeper, + appKeepers.StakingKeeper, + ) + appKeepers.MintKeeper = mintkeeper.NewKeeper( appCodec, appKeepers.keys[minttypes.StoreKey], diff --git a/app/keepers/keys.go b/app/keepers/keys.go index 651f6198..6ebd4963 100644 --- a/app/keepers/keys.go +++ b/app/keepers/keys.go @@ -23,6 +23,7 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" govtypes "github.com/atomone-hub/atomone/x/gov/types" + photontypes "github.com/atomone-hub/atomone/x/photon/types" ) func (appKeepers *AppKeepers) GenerateKeys() { @@ -47,6 +48,7 @@ func (appKeepers *AppKeepers) GenerateKeys() { feegrant.StoreKey, authzkeeper.StoreKey, consensusparamtypes.StoreKey, + photontypes.StoreKey, ) // Define transient store keys diff --git a/app/modules.go b/app/modules.go index 121b6a0c..8517a575 100644 --- a/app/modules.go +++ b/app/modules.go @@ -48,6 +48,8 @@ import ( "github.com/atomone-hub/atomone/x/gov" govclient "github.com/atomone-hub/atomone/x/gov/client" govtypes "github.com/atomone-hub/atomone/x/gov/types" + "github.com/atomone-hub/atomone/x/photon" + photontypes "github.com/atomone-hub/atomone/x/photon/types" ) var maccPerms = map[string][]string{ @@ -58,8 +60,8 @@ var maccPerms = map[string][]string{ stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, govtypes.ModuleName: {authtypes.Burner}, - // liquiditytypes.ModuleName: {authtypes.Minter, authtypes.Burner}, - ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + photontypes.ModuleName: {authtypes.Minter, authtypes.Burner}, } // ModuleBasics defines the module BasicManager is in charge of setting up basic, @@ -85,6 +87,7 @@ var ModuleBasics = module.NewBasicManager( sdkparams.AppModuleBasic{}, crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, + photon.AppModuleBasic{}, feegrantmodule.AppModuleBasic{}, authzmodule.AppModuleBasic{}, ibc.AppModuleBasic{}, @@ -121,6 +124,7 @@ func appModules( slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + photon.NewAppModule(appCodec, *app.PhotonKeeper, app.BankKeeper, app.AccountKeeper, app.StakingKeeper), upgrade.NewAppModule(app.UpgradeKeeper), evidence.NewAppModule(app.EvidenceKeeper), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), @@ -150,6 +154,7 @@ func simulationModules( gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + photon.NewAppModule(appCodec, *app.PhotonKeeper, app.BankKeeper, app.AccountKeeper, app.StakingKeeper), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), sdkparams.NewAppModule(app.ParamsKeeper), @@ -185,6 +190,7 @@ func orderBeginBlockers() []string { stakingtypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, + photontypes.ModuleName, govtypes.ModuleName, crisistypes.ModuleName, ibcexported.ModuleName, @@ -218,6 +224,7 @@ func orderEndBlockers() []string { capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, + photontypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, minttypes.ModuleName, @@ -248,6 +255,7 @@ func orderInitBlockers() []string { distrtypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, + photontypes.ModuleName, slashingtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, diff --git a/app/params/config.go b/app/params/config.go new file mode 100644 index 00000000..f12e7b0f --- /dev/null +++ b/app/params/config.go @@ -0,0 +1,20 @@ +package params + +const ( + BondDenom = "uatone" + + Bech32PrefixAccAddr = "atone" +) + +var ( + // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key. + Bech32PrefixAccPub = Bech32PrefixAccAddr + "pub" + // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address. + Bech32PrefixValAddr = Bech32PrefixAccAddr + "valoper" + // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key. + Bech32PrefixValPub = Bech32PrefixAccAddr + "valoperpub" + // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address. + Bech32PrefixConsAddr = Bech32PrefixAccAddr + "valcons" + // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key. + Bech32PrefixConsPub = Bech32PrefixAccAddr + "valconspub" +) diff --git a/app/params/params.go b/app/params/params.go deleted file mode 100644 index b6aa5fb5..00000000 --- a/app/params/params.go +++ /dev/null @@ -1,7 +0,0 @@ -package params - -// Simulation parameter constants -const ( - StakePerAccount = "stake_per_account" - InitiallyBondedValidators = "initially_bonded_validators" -) diff --git a/app/upgrades/v2/constants.go b/app/upgrades/v2/constants.go new file mode 100644 index 00000000..2b184a3d --- /dev/null +++ b/app/upgrades/v2/constants.go @@ -0,0 +1,23 @@ +package v2 + +import ( + store "github.com/cosmos/cosmos-sdk/store/types" + + "github.com/atomone-hub/atomone/app/upgrades" + photontypes "github.com/atomone-hub/atomone/x/photon/types" +) + +const ( + UpgradeName = "v2" +) + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: CreateUpgradeHandler, + StoreUpgrades: store.StoreUpgrades{ + Added: []string{ + // new module added in v2 + photontypes.ModuleName, + }, + }, +} diff --git a/app/upgrades/v2/upgrades.go b/app/upgrades/v2/upgrades.go new file mode 100644 index 00000000..95930246 --- /dev/null +++ b/app/upgrades/v2/upgrades.go @@ -0,0 +1,66 @@ +package v2 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + "github.com/atomone-hub/atomone/app/keepers" +) + +// CreateUpgradeHandler returns a upgrade handler for AtomOne v2 +// which executes the following migrations: +// - add new denom metadata for photon in the bank module store. +func CreateUpgradeHandler( + mm *module.Manager, + configurator module.Configurator, + keepers *keepers.AppKeepers, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + ctx.Logger().Info("Starting module migrations...") + // RunMigrations will detect the add of the photon module, will initiate + // its genesis and will fill the versionMap with its consensus version. + vm, err := mm.RunMigrations(ctx, configurator, vm) + if err != nil { + return vm, err + } + // Add the photon denom metadata to the bank module store + setPhotonDenomMetadata(ctx, keepers.BankKeeper) + ctx.Logger().Info("Upgrade complete") + return vm, nil + } +} + +func setPhotonDenomMetadata(ctx sdk.Context, bk bankkeeper.Keeper) { + ctx.Logger().Info("Adding photon denom metadata...") + bk.SetDenomMetaData(ctx, banktypes.Metadata{ + Base: "uphoton", + Display: "photon", + Name: "AtomOne Photon", + Symbol: "PHOTON", + Description: "The fee token of AtomOne Hub", + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: "uphoton", + Exponent: 0, + Aliases: []string{ + "microphoton", + }, + }, + { + Denom: "mphoton", + Exponent: 3, + Aliases: []string{ + "milliphoton", + }, + }, + { + Denom: "photon", + Exponent: 6, + }, + }, + }) + ctx.Logger().Info("Photon denom metadata added") +} diff --git a/cmd/atomoned/cmd/bech32_convert.go b/cmd/atomoned/cmd/bech32_convert.go index ba464594..a954173a 100644 --- a/cmd/atomoned/cmd/bech32_convert.go +++ b/cmd/atomoned/cmd/bech32_convert.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/cobra" + appparams "github.com/atomone-hub/atomone/app/params" addressutil "github.com/atomone-hub/atomone/pkg/address" ) @@ -41,7 +42,7 @@ Example: }, } - cmd.Flags().StringP(flagBech32Prefix, "p", "atone", "Bech32 Prefix to encode to") + cmd.Flags().StringP(flagBech32Prefix, "p", appparams.Bech32PrefixAccAddr, "Bech32 Prefix to encode to") return cmd } diff --git a/cmd/atomoned/cmd/config.go b/cmd/atomoned/cmd/config.go index b309abbf..ccde3feb 100644 --- a/cmd/atomoned/cmd/config.go +++ b/cmd/atomoned/cmd/config.go @@ -2,29 +2,14 @@ package cmd import ( sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ( - Bech32PrefixAccAddr = "atone" -) -var ( - // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key. - Bech32PrefixAccPub = Bech32PrefixAccAddr + "pub" - // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address. - Bech32PrefixValAddr = Bech32PrefixAccAddr + "valoper" - // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key. - Bech32PrefixValPub = Bech32PrefixAccAddr + "valoperpub" - // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address. - Bech32PrefixConsAddr = Bech32PrefixAccAddr + "valcons" - // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key. - Bech32PrefixConsPub = Bech32PrefixAccAddr + "valconspub" + appparams "github.com/atomone-hub/atomone/app/params" ) func InitSDKConfig() { cfg := sdk.GetConfig() - cfg.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub) - cfg.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub) - cfg.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub) + cfg.SetBech32PrefixForAccount(appparams.Bech32PrefixAccAddr, appparams.Bech32PrefixAccPub) + cfg.SetBech32PrefixForValidator(appparams.Bech32PrefixValAddr, appparams.Bech32PrefixValPub) + cfg.SetBech32PrefixForConsensusNode(appparams.Bech32PrefixConsAddr, appparams.Bech32PrefixConsPub) cfg.Seal() } diff --git a/contrib/devdeps/go.mod b/contrib/devdeps/go.mod index ce449b3d..9108d315 100644 --- a/contrib/devdeps/go.mod +++ b/contrib/devdeps/go.mod @@ -3,6 +3,7 @@ module github.com/atomone-hub/atomone/contrib/devdeps go 1.22 require ( + github.com/golang/mock v1.6.0 github.com/golangci/golangci-lint v1.56.0 github.com/goreleaser/goreleaser v1.25.1 github.com/rakyll/statik v0.1.7 diff --git a/contrib/devdeps/go.sum b/contrib/devdeps/go.sum index fc89d4b8..9a710cb9 100644 --- a/contrib/devdeps/go.sum +++ b/contrib/devdeps/go.sum @@ -486,6 +486,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/contrib/devdeps/tools.go b/contrib/devdeps/tools.go index 9628e6eb..5a4fad17 100644 --- a/contrib/devdeps/tools.go +++ b/contrib/devdeps/tools.go @@ -3,12 +3,15 @@ package devdeps import ( - // required for formatting, linting, pls. + // formatting _ "mvdan.cc/gofumpt" // linter _ "github.com/golangci/golangci-lint/cmd/golangci-lint" + // mocks + _ "github.com/golang/mock/mockgen" + // for releases _ "github.com/goreleaser/goreleaser" diff --git a/proto/atomone/photon/v1/genesis.proto b/proto/atomone/photon/v1/genesis.proto new file mode 100644 index 00000000..8c06bb80 --- /dev/null +++ b/proto/atomone/photon/v1/genesis.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package atomone.photon.v1; + +import "gogoproto/gogo.proto"; +import "atomone/photon/v1/photon.proto"; +import "amino/amino.proto"; + +option go_package = "github.com/atomone-hub/atomone/x/photon/types"; + +// GenesisState defines the x/photon module's genesis state. +message GenesisState { + Params params = 1 [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; +} diff --git a/proto/atomone/photon/v1/photon.proto b/proto/atomone/photon/v1/photon.proto new file mode 100644 index 00000000..159b7d38 --- /dev/null +++ b/proto/atomone/photon/v1/photon.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package atomone.photon.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/atomone-hub/atomone/x/photon/types"; + +// Params defines the parameters for the x/photon module. +message Params { + // Allow to mint photon or not + bool mint_disabled = 1; + // tx_fee_exceptions holds the msg type urls that are allowed to use some + // different tx fee coins than photon. + // A wildcard "*" can be used to allow all transactions to use any fee denom. + repeated string tx_fee_exceptions = 2; +} diff --git a/proto/atomone/photon/v1/query.proto b/proto/atomone/photon/v1/query.proto new file mode 100644 index 00000000..9e9bc6e6 --- /dev/null +++ b/proto/atomone/photon/v1/query.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; +package atomone.photon.v1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "atomone/photon/v1/photon.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "amino/amino.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/atomone-hub/atomone/x/photon/types"; + +// Query defines the gRPC querier service. +service Query { + // Parameters queries the parameters of the module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/atomone/photon/v1/params"; + } + // ConversionRate queries the photon's conversion rate + rpc ConversionRate(QueryConversionRateRequest) returns (QueryConversionRateResponse) { + option (google.api.http).get = "/atomone/photon/v1/conversion_rate"; + } +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is response type for the Query/Params RPC method. +message QueryParamsResponse { + // params holds all the parameters of this module. + Params params = 1 [ (gogoproto.nullable) = false ]; +} + +// QueryConversionRateRequest is request type for the Query/ConversionRate RPC method. +message QueryConversionRateRequest {} + +// QueryConversionRateResponse is response type for the Query/ConversionRate RPC method. +message QueryConversionRateResponse { + // conversion_rate represents the factor used to convert atone to photon. + string conversion_rate = 1 [ (cosmos_proto.scalar) = "cosmos.Dec" ]; +} diff --git a/proto/atomone/photon/v1/tx.proto b/proto/atomone/photon/v1/tx.proto new file mode 100644 index 00000000..924b4bae --- /dev/null +++ b/proto/atomone/photon/v1/tx.proto @@ -0,0 +1,66 @@ +syntax = "proto3"; +package atomone.photon.v1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/msg/v1/msg.proto"; +import "amino/amino.proto"; +import "atomone/photon/v1/photon.proto"; + +option go_package = "github.com/atomone-hub/atomone/x/photon/types"; + +// Msg defines the Msg service. +service Msg { + option (cosmos.msg.v1.service) = true; + + // MintPhoton defines a method to burn atone and mint photons. + rpc MintPhoton(MsgMintPhoton) returns (MsgMintPhotonResponse); + + // UpdateParams defines a governance operation for updating the x/photon + // module parameters. The authority is defined in the keeper. + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgMintPhoton defines an sdk.Msg for burning atone and minting photons. +message MsgMintPhoton { + option (cosmos.msg.v1.signer) = "to_address"; + option (amino.name) = "atomone/photon/v1/MsgMintPhoton"; + + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string to_address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + cosmos.base.v1beta1.Coin amount = 2 + [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; +} + +message MsgMintPhotonResponse { + cosmos.base.v1beta1.Coin minted = 1 [ + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true + ]; + // conversion_rate represents the factor used to convert atone to photon. + string conversion_rate = 2 [ (cosmos_proto.scalar) = "cosmos.Dec" ]; +} + +// MsgUpdateParams is the Msg/UpdateParams request type. +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + option (amino.name) = "atomone/x/photon/v1/MsgUpdateParams"; + + // authority is the address that controls the module (defaults to x/gov unless + // overwritten). + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // params defines the x/gov parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 + [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +message MsgUpdateParamsResponse {} + diff --git a/tests/e2e/chain.go b/tests/e2e/chain_test.go similarity index 97% rename from tests/e2e/chain.go rename to tests/e2e/chain_test.go index 8c9e313e..c0dcad7d 100644 --- a/tests/e2e/chain.go +++ b/tests/e2e/chain_test.go @@ -27,6 +27,7 @@ import ( atomoneparams "github.com/atomone-hub/atomone/app/params" govv1types "github.com/atomone-hub/atomone/x/gov/types/v1" govv1beta1types "github.com/atomone-hub/atomone/x/gov/types/v1beta1" + photontypes "github.com/atomone-hub/atomone/x/photon/types" ) const ( @@ -61,6 +62,7 @@ func init() { upgradetypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) distribtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) ibctransfertypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + photontypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) cdc = encodingConfig.Marshaler txConfig = encodingConfig.TxConfig diff --git a/tests/e2e/e2e_bank_test.go b/tests/e2e/e2e_bank_test.go index 3d576c96..05fdf1d0 100644 --- a/tests/e2e/e2e_bank_test.go +++ b/tests/e2e/e2e_bank_test.go @@ -47,7 +47,7 @@ func (s *IntegrationTestSuite) testBankTokenTransfer() { ) // alice sends tokens to bob - s.execBankSend(s.chainA, valIdx, alice.String(), bob.String(), tokenAmount.String(), standardFees.String(), false) + s.execBankSend(s.chainA, valIdx, alice.String(), bob.String(), tokenAmount.String(), false) // check that the transfer was successful s.Require().Eventually( @@ -58,7 +58,7 @@ func (s *IntegrationTestSuite) testBankTokenTransfer() { afterBobUAtoneBalance, err = getSpecificBalance(chainEndpoint, bob.String(), uatoneDenom) s.Require().NoError(err) - decremented := beforeAliceUAtoneBalance.Sub(tokenAmount).Sub(standardFees).IsEqual(afterAliceUAtoneBalance) + decremented := beforeAliceUAtoneBalance.Sub(tokenAmount).IsEqual(afterAliceUAtoneBalance) incremented := beforeBobUAtoneBalance.Add(tokenAmount).IsEqual(afterBobUAtoneBalance) return decremented && incremented @@ -71,7 +71,8 @@ func (s *IntegrationTestSuite) testBankTokenTransfer() { beforeAliceUAtoneBalance, beforeBobUAtoneBalance = afterAliceUAtoneBalance, afterBobUAtoneBalance // alice sends tokens to bob and charlie, at once - s.execBankMultiSend(s.chainA, valIdx, alice.String(), []string{bob.String(), charlie.String()}, tokenAmount.String(), standardFees.String(), false) + s.execBankMultiSend(s.chainA, valIdx, alice.String(), + []string{bob.String(), charlie.String()}, tokenAmount.String(), false) s.Require().Eventually( func() bool { @@ -85,7 +86,7 @@ func (s *IntegrationTestSuite) testBankTokenTransfer() { s.Require().NoError(err) // assert alice's account gets decremented the amount of tokens twice - decremented := beforeAliceUAtoneBalance.Sub(tokenAmount).Sub(tokenAmount).Sub(standardFees).IsEqual(afterAliceUAtoneBalance) + decremented := beforeAliceUAtoneBalance.Sub(tokenAmount).Sub(tokenAmount).IsEqual(afterAliceUAtoneBalance) incremented := beforeBobUAtoneBalance.Add(tokenAmount).IsEqual(afterBobUAtoneBalance) && beforeCharlieUAtoneBalance.Add(tokenAmount).IsEqual(afterCharlieUAtoneBalance) @@ -95,4 +96,18 @@ func (s *IntegrationTestSuite) testBankTokenTransfer() { time.Second, ) }) + + s.Run("send tokens with atone fees", func() { + var ( + valIdx = 0 + c = s.chainA + ) + alice, _ := c.genesisAccounts[1].keyInfo.GetAddress() + bob, _ := c.genesisAccounts[2].keyInfo.GetAddress() + + // alice sends tokens to bob should fail because doesn't use photons for the fees. + atoneFees := sdk.NewCoin(uatoneDenom, standardFees.Amount) + s.execBankSend(s.chainA, valIdx, alice.String(), bob.String(), + tokenAmount.String(), true, withKeyValue(flagFees, atoneFees)) + }) } diff --git a/tests/e2e/e2e_distribution_test.go b/tests/e2e/e2e_distribution_test.go index e86af008..a6fff97b 100644 --- a/tests/e2e/e2e_distribution_test.go +++ b/tests/e2e/e2e_distribution_test.go @@ -21,7 +21,6 @@ func (s *IntegrationTestSuite) testDistribution() { delegatorAddress, _ := s.chainA.genesisAccounts[2].keyInfo.GetAddress() newWithdrawalAddress, _ := s.chainA.genesisAccounts[3].keyInfo.GetAddress() - fees := sdk.NewCoin(uatoneDenom, sdk.NewInt(1000)) beforeBalance, err := getSpecificBalance(chainEndpoint, newWithdrawalAddress.String(), uatoneDenom) s.Require().NoError(err) @@ -29,7 +28,7 @@ func (s *IntegrationTestSuite) testDistribution() { beforeBalance = sdk.NewCoin(uatoneDenom, sdk.NewInt(0)) } - s.execSetWithdrawAddress(s.chainA, 0, fees.String(), delegatorAddress.String(), newWithdrawalAddress.String(), atomoneHomePath) + s.execSetWithdrawAddress(s.chainA, 0, delegatorAddress.String(), newWithdrawalAddress.String(), atomoneHomePath) // Verify s.Require().Eventually( @@ -76,17 +75,15 @@ func (s *IntegrationTestSuite) fundCommunityPool() { beforeDistUatoneBalance = sdk.NewInt64Coin(uatoneDenom, 0) } - s.execDistributionFundCommunityPool(s.chainA, 0, sender.String(), tokenAmount.String(), standardFees.String()) - - // there are still tokens being added to the community pool through block production rewards but they should be less than 500 tokens - marginOfErrorForBlockReward := sdk.NewInt64Coin(uatoneDenom, 500) + s.execDistributionFundCommunityPool(s.chainA, 0, sender.String(), tokenAmount.String()) s.Require().Eventually( func() bool { - afterDistPhotonBalance, err := getSpecificBalance(chainAAPIEndpoint, distModuleAddress, tokenAmount.Denom) - s.Require().NoErrorf(err, "Error getting balance: %s", afterDistPhotonBalance) + afterDistUatoneBalance, err := getSpecificBalance(chainAAPIEndpoint, distModuleAddress, tokenAmount.Denom) + s.Require().NoErrorf(err, "Error getting balance: %s", afterDistUatoneBalance) - return beforeDistUatoneBalance.Add(tokenAmount.Add(standardFees)).Sub(afterDistPhotonBalance).IsLT(marginOfErrorForBlockReward) + // check if the balance is increased by the tokenAmount + return beforeDistUatoneBalance.Add(tokenAmount).IsLT(afterDistUatoneBalance) }, 15*time.Second, time.Second, diff --git a/tests/e2e/e2e_exec_test.go b/tests/e2e/e2e_exec_test.go index 5f61bf34..f712b845 100644 --- a/tests/e2e/e2e_exec_test.go +++ b/tests/e2e/e2e_exec_test.go @@ -15,6 +15,7 @@ import ( rpchttp "github.com/cometbft/cometbft/rpc/client/http" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -24,6 +25,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" govtypes "github.com/atomone-hub/atomone/x/gov/types" + photontypes "github.com/atomone-hub/atomone/x/photon/types" ) const ( @@ -158,7 +160,7 @@ func (s *IntegrationTestSuite) execVestingTx( //nolint:unused atomoneCommand = append(atomoneCommand, fmt.Sprintf("--%s=%v", flag, value)) } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, 0, s.defaultExecValidation(c, 0)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, 0, nil) s.T().Logf("successfully %s with %v", method, args) } @@ -182,7 +184,7 @@ func (s *IntegrationTestSuite) execUnjail( ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - s.T().Logf("Executing atomoned slashing unjail %s with options: %v", c.id, opt) + s.T().Logf("Executing atomoned slashing unjail %s with options: %v", c.id, opts) atomoneCommand := []string{ atomonedBinary, txCommand, @@ -195,7 +197,7 @@ func (s *IntegrationTestSuite) execUnjail( atomoneCommand = append(atomoneCommand, fmt.Sprintf("--%s=%v", flag, value)) } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, 0, s.defaultExecValidation(c, 0)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, 0, nil) s.T().Logf("successfully unjail with options %v", opt) } @@ -222,7 +224,7 @@ func (s *IntegrationTestSuite) execFeeGrant(c *chain, valIdx int, granter, grant atomoneCommand = append(atomoneCommand, fmt.Sprintf("--%s=%v", flag, value)) } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) } func (s *IntegrationTestSuite) execFeeGrantRevoke(c *chain, valIdx int, granter, grantee string, opt ...flagOption) { @@ -247,7 +249,7 @@ func (s *IntegrationTestSuite) execFeeGrantRevoke(c *chain, valIdx int, granter, atomoneCommand = append(atomoneCommand, fmt.Sprintf("--%s=%v", flag, value)) } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) } func (s *IntegrationTestSuite) execBankSend( @@ -255,13 +257,11 @@ func (s *IntegrationTestSuite) execBankSend( valIdx int, from, to, - amt, - fees string, + amt string, expectErr bool, opt ...flagOption, ) { // TODO remove the hardcode opt after refactor, all methods should accept custom flags - opt = append(opt, withKeyValue(flagFees, fees)) opt = append(opt, withKeyValue(flagFrom, from)) opts := applyOptions(c.id, opt) @@ -293,12 +293,10 @@ func (s *IntegrationTestSuite) execBankMultiSend( from string, to []string, amt string, - fees string, expectErr bool, opt ...flagOption, ) { // TODO remove the hardcode opt after refactor, all methods should accept custom flags - opt = append(opt, withKeyValue(flagFees, fees)) opt = append(opt, withKeyValue(flagFrom, from)) opts := applyOptions(c.id, opt) @@ -344,7 +342,7 @@ func (s *IntegrationTestSuite) execBankSendBatch( //nolint:unused for i := range txs { s.T().Logf(txs[i].log) - s.execBankSend(c, valIdx, txs[i].from, txs[i].to, txs[i].amt, txs[i].fees, txs[i].expectErr) + s.execBankSend(c, valIdx, txs[i].from, txs[i].to, txs[i].amt, txs[i].expectErr) if !txs[i].expectErr { if !txs[i].expectErr { sucessBankSendCount++ @@ -355,7 +353,7 @@ func (s *IntegrationTestSuite) execBankSendBatch( //nolint:unused return sucessBankSendCount } -func (s *IntegrationTestSuite) execWithdrawAllRewards(c *chain, valIdx int, payee, fees string, expectErr bool) { //nolint:unused +func (s *IntegrationTestSuite) execWithdrawAllRewards(c *chain, valIdx int, payee string, expectErr bool) { //nolint:unused ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -365,7 +363,7 @@ func (s *IntegrationTestSuite) execWithdrawAllRewards(c *chain, valIdx int, paye distributiontypes.ModuleName, "withdraw-all-rewards", fmt.Sprintf("--%s=%s", flags.FlagFrom, payee), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, fees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), "--keyring-backend=test", "--output=json", @@ -375,7 +373,7 @@ func (s *IntegrationTestSuite) execWithdrawAllRewards(c *chain, valIdx int, paye s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.expectErrExecValidation(c, valIdx, expectErr)) } -func (s *IntegrationTestSuite) execDistributionFundCommunityPool(c *chain, valIdx int, from, amt, fees string) { +func (s *IntegrationTestSuite) execDistributionFundCommunityPool(c *chain, valIdx int, from, amt string) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -389,17 +387,17 @@ func (s *IntegrationTestSuite) execDistributionFundCommunityPool(c *chain, valId amt, fmt.Sprintf("--%s=%s", flags.FlagFrom, from), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), - fmt.Sprintf("--%s=%s", flags.FlagFees, fees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), "--keyring-backend=test", "--output=json", "-y", } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Logf("Successfully funded community pool") } -func (s *IntegrationTestSuite) runGovExec(c *chain, valIdx int, submitterAddr, govCommand string, proposalFlags []string, fees string) { +func (s *IntegrationTestSuite) runGovExec(c *chain, valIdx int, submitterAddr, govCommand string, proposalFlags []string) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -413,7 +411,7 @@ func (s *IntegrationTestSuite) runGovExec(c *chain, valIdx int, submitterAddr, g generalFlags := []string{ fmt.Sprintf("--%s=%s", flags.FlagFrom, submitterAddr), fmt.Sprintf("--%s=%s", flags.FlagGas, "300000"), // default 200000 isn't enough - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, fees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), "--keyring-backend=test", "--output=json", @@ -422,7 +420,7 @@ func (s *IntegrationTestSuite) runGovExec(c *chain, valIdx int, submitterAddr, g atomoneCommand = concatFlags(atomoneCommand, proposalFlags, generalFlags) s.T().Logf("Executing atomoned tx gov %s on chain %s", govCommand, c.id) - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Logf("Successfully executed %s", govCommand) } @@ -471,7 +469,7 @@ func (s *IntegrationTestSuite) runGovExec(c *chain, valIdx int, submitterAddr, g // }) // } -func (s *IntegrationTestSuite) execDelegate(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home, delegateFees string) { //nolint:unparam +func (s *IntegrationTestSuite) execDelegate(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home string) { //nolint:unparam ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -487,18 +485,18 @@ func (s *IntegrationTestSuite) execDelegate(c *chain, valIdx int, amount, valOpe amount, fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), "--keyring-backend=test", fmt.Sprintf("--%s=%s", flags.FlagHome, home), "--output=json", "-y", } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Logf("%s successfully delegated %s to %s", delegatorAddr, amount, valOperAddress) } -func (s *IntegrationTestSuite) execUnbondDelegation(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home, delegateFees string) { +func (s *IntegrationTestSuite) execUnbondDelegation(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home string) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -513,18 +511,18 @@ func (s *IntegrationTestSuite) execUnbondDelegation(c *chain, valIdx int, amount amount, fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), "--keyring-backend=test", fmt.Sprintf("--%s=%s", flags.FlagHome, home), "--output=json", "-y", } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Logf("%s successfully undelegated %s to %s", delegatorAddr, amount, valOperAddress) } -func (s *IntegrationTestSuite) execCancelUnbondingDelegation(c *chain, valIdx int, amount, valOperAddress, creationHeight, delegatorAddr, home, delegateFees string) { +func (s *IntegrationTestSuite) execCancelUnbondingDelegation(c *chain, valIdx int, amount, valOperAddress, creationHeight, delegatorAddr, home string) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -540,19 +538,19 @@ func (s *IntegrationTestSuite) execCancelUnbondingDelegation(c *chain, valIdx in creationHeight, fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), "--keyring-backend=test", fmt.Sprintf("--%s=%s", flags.FlagHome, home), "--output=json", "-y", } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Logf("%s successfully canceled unbonding %s to %s", delegatorAddr, amount, valOperAddress) } func (s *IntegrationTestSuite) execRedelegate(c *chain, valIdx int, amount, originalValOperAddress, - newValOperAddress, delegatorAddr, home, delegateFees string, + newValOperAddress, delegatorAddr, home string, ) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -570,14 +568,14 @@ func (s *IntegrationTestSuite) execRedelegate(c *chain, valIdx int, amount, orig fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), fmt.Sprintf("--%s=%s", flags.FlagGas, "300000"), // default 200000 isn't enough - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), "--keyring-backend=test", fmt.Sprintf("--%s=%s", flags.FlagHome, home), "--output=json", "-y", } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Logf("%s successfully redelegated %s from %s to %s", delegatorAddr, amount, originalValOperAddress, newValOperAddress) } @@ -615,7 +613,6 @@ func (s *IntegrationTestSuite) getLatestBlockTime(c *chain, valIdx int) time.Tim func (s *IntegrationTestSuite) execSetWithdrawAddress( c *chain, valIdx int, - fees, delegatorAddress, newWithdrawalAddress, homePath string, @@ -631,7 +628,7 @@ func (s *IntegrationTestSuite) execSetWithdrawAddress( "set-withdraw-addr", newWithdrawalAddress, fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddress), - fmt.Sprintf("--%s=%s", flags.FlagFees, fees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), fmt.Sprintf("--%s=%s", flags.FlagHome, homePath), "--keyring-backend=test", @@ -639,7 +636,7 @@ func (s *IntegrationTestSuite) execSetWithdrawAddress( "-y", } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Logf("Successfully set new distribution withdrawal address for %s to %s", delegatorAddress, newWithdrawalAddress) } @@ -661,7 +658,7 @@ func (s *IntegrationTestSuite) execWithdrawReward( "withdraw-rewards", validatorAddress, fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddress), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, "300uatone"), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), fmt.Sprintf("--%s=%s", flags.FlagGas, "auto"), fmt.Sprintf("--%s=%s", flags.FlagGasAdjustment, "1.5"), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), @@ -671,13 +668,13 @@ func (s *IntegrationTestSuite) execWithdrawReward( "-y", } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Logf("Successfully withdrew distribution rewards for delegator %s from validator %s", delegatorAddress, validatorAddress) } func (s *IntegrationTestSuite) executeAtomoneTxCommand(ctx context.Context, c *chain, atomoneCommand []string, valIdx int, validation func([]byte, []byte) bool) { if validation == nil { - validation = s.defaultExecValidation(s.chainA, 0) + validation = s.defaultExecValidation(s.chainA, 0, nil) } var ( outBuf bytes.Buffer @@ -756,28 +753,28 @@ func (s *IntegrationTestSuite) executeHermesCommand(ctx context.Context, hermesC func (s *IntegrationTestSuite) expectErrExecValidation(chain *chain, valIdx int, expectErr bool) func([]byte, []byte) bool { return func(stdOut []byte, stdErr []byte) bool { var txResp sdk.TxResponse - gotErr := cdc.UnmarshalJSON(stdOut, &txResp) != nil - if gotErr { - s.Require().True(expectErr) + err := cdc.UnmarshalJSON(stdOut, &txResp) + if !expectErr { + s.Require().NoError(err, "stdOut='%s' stdErr='%s'", string(stdOut), string(stdErr)) } endpoint := fmt.Sprintf("http://%s", s.valResources[chain.id][valIdx].GetHostPort("1317/tcp")) // wait for the tx to be committed on chain s.Require().Eventuallyf( func() bool { - gotErr := queryAtomOneTx(endpoint, txResp.TxHash) != nil + gotErr := queryAtomOneTx(endpoint, txResp.TxHash, nil) != nil return gotErr == expectErr }, time.Minute, time.Second, - "stdOut: %s, stdErr: %s", + "stdOut='%s', stdErr='%s'", string(stdOut), string(stdErr), ) return true } } -func (s *IntegrationTestSuite) defaultExecValidation(chain *chain, valIdx int) func([]byte, []byte) bool { +func (s *IntegrationTestSuite) defaultExecValidation(chain *chain, valIdx int, msgResp codec.ProtoMarshaler) func([]byte, []byte) bool { return func(stdOut []byte, stdErr []byte) bool { var txResp sdk.TxResponse if err := cdc.UnmarshalJSON(stdOut, &txResp); err != nil { @@ -787,7 +784,7 @@ func (s *IntegrationTestSuite) defaultExecValidation(chain *chain, valIdx int) f endpoint := fmt.Sprintf("http://%s", s.valResources[chain.id][valIdx].GetHostPort("1317/tcp")) s.Require().Eventually( func() bool { - err := queryAtomOneTx(endpoint, txResp.TxHash) + err := queryAtomOneTx(endpoint, txResp.TxHash, msgResp) if isErrNotFound(err) { // tx not processed yet, continue return false @@ -806,7 +803,7 @@ func (s *IntegrationTestSuite) defaultExecValidation(chain *chain, valIdx int) f } } -func (s *IntegrationTestSuite) executeValidatorBond(c *chain, valIdx int, valOperAddress, delegatorAddr, home, delegateFees string) { //nolint:unused +func (s *IntegrationTestSuite) executeValidatorBond(c *chain, valIdx int, valOperAddress, delegatorAddr, home string) { //nolint:unused ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -820,17 +817,48 @@ func (s *IntegrationTestSuite) executeValidatorBond(c *chain, valIdx int, valOpe valOperAddress, fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), "--keyring-backend=test", fmt.Sprintf("--%s=%s", flags.FlagHome, home), "--output=json", "-y", } - s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Logf("%s successfully executed validator bond tx to %s", delegatorAddr, valOperAddress) } +func (s *IntegrationTestSuite) execPhotonMint( + c *chain, + valIdx int, + from, + amt string, + opt ...flagOption, +) (resp photontypes.MsgMintPhotonResponse) { + opt = append(opt, withKeyValue(flagFrom, from)) + opts := applyOptions(c.id, opt) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("minting photon from %s from %s on chain %s", amt, from, c.id) + + atomoneCommand := []string{ + atomonedBinary, + txCommand, + photontypes.ModuleName, + "mint", + amt, + "-y", + } + for flag, value := range opts { + atomoneCommand = append(atomoneCommand, fmt.Sprintf("--%s=%v", flag, value)) + } + + s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, s.defaultExecValidation(c, valIdx, &resp)) + return +} + // signTxFileOnline signs a transaction file using the atomoned tx sign command // the from flag is used to specify the keyring account to sign the transaction // the from account must be registered in the keyring and exist on chain (have a balance or be a genesis account) diff --git a/tests/e2e/e2e_feegrant_test.go b/tests/e2e/e2e_feegrant_test.go index ace8fbd1..9fee26db 100644 --- a/tests/e2e/e2e_feegrant_test.go +++ b/tests/e2e/e2e_feegrant_test.go @@ -49,7 +49,6 @@ func (s *IntegrationTestSuite) testFeeGrant() { bob.String(), Address(), tokenAmount.String(), - standardFees.String(), false, withKeyValue(flagFeeGranter, alice.String()), ) @@ -67,7 +66,6 @@ func (s *IntegrationTestSuite) testFeeGrant() { bob.String(), Address(), tokenAmount.String(), - standardFees.String(), true, withKeyValue(flagFeeGranter, alice.String()), ) @@ -97,7 +95,6 @@ func (s *IntegrationTestSuite) testFeeGrant() { charlie.String(), Address(), tokenAmount.String(), - standardFees.String(), true, withKeyValue(flagFeeGranter, alice.String()), ) diff --git a/tests/e2e/e2e_gov_test.go b/tests/e2e/e2e_gov_test.go index 83a4d188..d14096fd 100644 --- a/tests/e2e/e2e_gov_test.go +++ b/tests/e2e/e2e_gov_test.go @@ -16,6 +16,7 @@ import ( govtypes "github.com/atomone-hub/atomone/x/gov/types" govtypesv1 "github.com/atomone-hub/atomone/x/gov/types/v1" govtypesv1beta1 "github.com/atomone-hub/atomone/x/gov/types/v1beta1" + photontypes "github.com/atomone-hub/atomone/x/photon/types" ) /* @@ -128,7 +129,7 @@ func (s *IntegrationTestSuite) testGovCommunityPoolSpend() { sender := senderAddress.String() recipientAddress, _ := s.chainA.validators[1].keyInfo.GetAddress() recipient := recipientAddress.String() - sendAmount := sdk.NewCoin(uatoneDenom, sdk.NewInt(10000000)) // 10uatone + sendAmount := sdk.NewInt64Coin(uatoneDenom, 10_000_000) // 10atone s.writeGovCommunitySpendProposal(s.chainA, sendAmount, recipient) beforeRecipientBalance, err := getSpecificBalance(chainAAPIEndpoint, recipient, uatoneDenom) @@ -177,6 +178,40 @@ func (s *IntegrationTestSuite) testGovParamChange() { newParams := s.queryStakingParams(chainAAPIEndpoint) s.Assert().NotEqual(oldMaxValidator, newParams.Params.MaxValidators) }) + + s.Run("photon param change", func() { + // check existing params + chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + senderAddress, _ := s.chainA.validators[0].keyInfo.GetAddress() + sender := senderAddress.String() + params := s.queryPhotonParams(chainAAPIEndpoint) + // toggle param mint_disabled + oldMintDisabled := params.Params.MintDisabled + s.Require().False(oldMintDisabled, "expected photon param mint disabled to be false") + params.Params.MintDisabled = true + + s.writePhotonParamChangeProposal(s.chainA, params.Params) + // Gov tests may be run in arbitrary order, each test must increment proposalCounter to have the correct proposal id to submit and query + proposalCounter++ + submitGovFlags := []string{configFile(proposalParamChangeFilename)} + depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} + voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes"} + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "atomone.photon.v1.MsgUpdateParams", submitGovFlags, depositGovFlags, voteGovFlags, "vote") + + newParams := s.queryPhotonParams(chainAAPIEndpoint) + s.Assert().True(newParams.Params.MintDisabled, "expected photon param mint disabled to be true") + + // Revert change or mint photon test will fail + params.Params.MintDisabled = false + proposalCounter++ + depositGovFlags = []string{strconv.Itoa(proposalCounter), depositAmount.String()} + voteGovFlags = []string{strconv.Itoa(proposalCounter), "yes"} + s.writePhotonParamChangeProposal(s.chainA, params.Params) + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "atomone.photon.v1.MsgUpdateParams", submitGovFlags, depositGovFlags, voteGovFlags, "vote") + + newParams = s.queryPhotonParams(chainAAPIEndpoint) + s.Require().False(newParams.Params.MintDisabled, "expected photon param mint disabled to be false") + }) } func (s *IntegrationTestSuite) testGovConstitutionAmendment() { @@ -212,7 +247,7 @@ func (s *IntegrationTestSuite) submitLegacyGovProposal(chainAAPIEndpoint, sender // min deposit of 1000uatone is required in e2e tests, otherwise the gov antehandler causes the proposal to be dropped sflags := submitFlags if withDeposit { - sflags = append(sflags, "--deposit=1000uatone") + sflags = append(sflags, "--deposit="+initialDepositAmount.String()) } s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, "submit-legacy-proposal", sflags, govtypesv1beta1.StatusDepositPeriod) s.T().Logf("Depositing Gov Proposal: %s", proposalType) @@ -278,7 +313,7 @@ func (s *IntegrationTestSuite) verifyChainPassesUpgradeHeight(c *chain, valIdx i } func (s *IntegrationTestSuite) submitGovCommand(chainAAPIEndpoint, sender string, proposalID int, govCommand string, proposalFlags []string, expectedSuccessStatus govtypesv1beta1.ProposalStatus) { - s.runGovExec(s.chainA, 0, sender, govCommand, proposalFlags, standardFees.String()) + s.runGovExec(s.chainA, 0, sender, govCommand, proposalFlags) s.Require().Eventually( func() bool { @@ -303,14 +338,38 @@ func (s *IntegrationTestSuite) writeStakingParamChangeProposal(c *chain, params "params": %s } ], - "deposit": "100uatone", + "deposit": "%s", "proposer": "Proposing staking param change", "metadata": "", "title": "Change in staking params", "summary": "summary" } ` - propMsgBody := fmt.Sprintf(template, govModuleAddress, cdc.MustMarshalJSON(¶ms)) + propMsgBody := fmt.Sprintf(template, govModuleAddress, cdc.MustMarshalJSON(¶ms), initialDepositAmount) + err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalParamChangeFilename), []byte(propMsgBody)) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) writePhotonParamChangeProposal(c *chain, params photontypes.Params) { + govModuleAddress := authtypes.NewModuleAddress(govtypes.ModuleName).String() + + template := ` + { + "messages":[ + { + "@type": "/atomone.photon.v1.MsgUpdateParams", + "authority": "%s", + "params": %s + } + ], + "deposit": "%s", + "proposer": "Proposing photon param change", + "metadata": "", + "title": "Change in photon params", + "summary": "summary" + } + ` + propMsgBody := fmt.Sprintf(template, govModuleAddress, cdc.MustMarshalJSON(¶ms), initialDepositAmount) err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalParamChangeFilename), []byte(propMsgBody)) s.Require().NoError(err) } @@ -328,14 +387,14 @@ func (s *IntegrationTestSuite) writeGovConstitutionAmendmentProposal(c *chain, a "amendment": "%s" } ], - "deposit": "100uatone", + "deposit": "%s", "proposer": "Proposing validator address", "metadata": "Constitution Amendment", "title": "Constitution Amendment", "summary": "summary" } ` - propMsgBody := fmt.Sprintf(template, govModuleAddress, amendment) + propMsgBody := fmt.Sprintf(template, govModuleAddress, amendment, initialDepositAmount) err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalConstitutionAmendmentFilename), []byte(propMsgBody)) s.Require().NoError(err) } diff --git a/tests/e2e/e2e_ibc_test.go b/tests/e2e/e2e_ibc_test.go index 0765cfb0..1e043b47 100644 --- a/tests/e2e/e2e_ibc_test.go +++ b/tests/e2e/e2e_ibc_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "strconv" "strings" "time" @@ -13,7 +12,7 @@ import ( ) //nolint:unparam -func (s *IntegrationTestSuite) sendIBC(c *chain, valIdx int, sender, recipient, token, fees, note string) { +func (s *IntegrationTestSuite) sendIBC(c *chain, valIdx int, sender, recipient, token, note string) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -27,7 +26,7 @@ func (s *IntegrationTestSuite) sendIBC(c *chain, valIdx int, sender, recipient, recipient, token, fmt.Sprintf("--from=%s", sender), - fmt.Sprintf("--%s=%s", flags.FlagFees, fees), + fmt.Sprintf("--%s=%s", flags.FlagFees, standardFees.String()), fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), // fmt.Sprintf("--%s=%s", flags.FlagNote, note), fmt.Sprintf("--memo=%s", note), @@ -37,7 +36,7 @@ func (s *IntegrationTestSuite) sendIBC(c *chain, valIdx int, sender, recipient, "-y", } s.T().Logf("sending %s from %s (%s) to %s (%s) with memo %s", token, s.chainA.id, sender, s.chainB.id, recipient, note) - s.executeAtomoneTxCommand(ctx, c, ibcCmd, valIdx, s.defaultExecValidation(c, valIdx)) + s.executeAtomoneTxCommand(ctx, c, ibcCmd, valIdx, s.defaultExecValidation(c, valIdx, nil)) s.T().Log("successfully sent IBC tokens") } @@ -228,8 +227,7 @@ func (s *IntegrationTestSuite) testIBCTokenTransfer() { } } - tokenAmt := 3300000000 - s.sendIBC(s.chainA, 0, sender, recipient, strconv.Itoa(tokenAmt)+uatoneDenom, standardFees.String(), "") + s.sendIBC(s.chainA, 0, sender, recipient, tokenAmount.String(), "") pass := s.hermesClearPacket(hermesConfigWithGasPrices, s.chainA.id, transferChannel) s.Require().True(pass) @@ -246,7 +244,7 @@ func (s *IntegrationTestSuite) testIBCTokenTransfer() { for _, c := range balances { if strings.Contains(c.Denom, "ibc/") { ibcStakeDenom = c.Denom - s.Require().Equal((int64(tokenAmt) + beforeBalance), c.Amount.Int64()) + s.Require().Equal(tokenAmount.Amount.Int64()+beforeBalance, c.Amount.Int64()) break } } diff --git a/tests/e2e/e2e_photon_test.go b/tests/e2e/e2e_photon_test.go new file mode 100644 index 00000000..bf39dbab --- /dev/null +++ b/tests/e2e/e2e_photon_test.go @@ -0,0 +1,58 @@ +package e2e + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (s *IntegrationTestSuite) testMintPhoton() { + subtest := func(fees sdk.Coin) func() { + return func() { + var ( + c = s.chainA + valIdx = 0 + chainEndpoint = fmt.Sprintf("http://%s", s.valResources[c.id][valIdx].GetHostPort("1317/tcp")) + ) + alice, _ := c.genesisAccounts[1].keyInfo.GetAddress() + beforeBalance, err := queryAtomOneAllBalances(chainEndpoint, alice.String()) + s.Require().NoError(err) + beforeSupply := s.queryBankSupply(chainEndpoint) + conversionRate := s.queryPhotonConversionRate(chainEndpoint) + s.Require().Positive(conversionRate.MustFloat64()) + burnedAtoneAmt := sdk.NewInt64Coin(uatoneDenom, 1_000_000) + + resp := s.execPhotonMint(s.chainA, valIdx, alice.String(), burnedAtoneAmt.String(), + withKeyValue(flagFees, fees), + ) + + expectedBalance := beforeBalance. + Sub(burnedAtoneAmt). // remove burned atones + Add(resp.Minted). // add minted photons + Sub(fees) // remove fees + + afterBalance, err := queryAtomOneAllBalances(chainEndpoint, alice.String()) + s.Require().NoError(err) + s.Require().Equal(expectedBalance.String(), afterBalance.String()) + var ( + _, beforeUphotonSupply = beforeSupply.Find(uphotonDenom) + expectedUphotonSupply = beforeUphotonSupply.Add(resp.Minted) + afterSupply = s.queryBankSupply(chainEndpoint) + _, afterUphotonSupply = afterSupply.Find(uphotonDenom) + ) + s.Require().Equal(expectedUphotonSupply.String(), afterUphotonSupply.String()) + // For atone supply assertion we must take into account inflation and so + // we except the final supply to be greater or equal than the initial + // supply - the burned atones. + var ( + _, beforeUatoneSupply = beforeSupply.Find(uatoneDenom) + _, afterUatoneSupply = afterSupply.Find(uatoneDenom) + ) + s.Require().True(afterUatoneSupply.IsGTE(beforeUatoneSupply.Sub(burnedAtoneAmt)), + "after supply should be >= than initial %s supply", uatoneDenom) + } + } + s.Run("mint photon", subtest(standardFees)) + atoneFees := sdk.NewCoin(uatoneDenom, standardFees.Amount) + s.Run("mint photon with atone fees", subtest(atoneFees)) +} diff --git a/tests/e2e/e2e_setup_test.go b/tests/e2e/e2e_setup_test.go index bf51d8c6..302a6601 100644 --- a/tests/e2e/e2e_setup_test.go +++ b/tests/e2e/e2e_setup_test.go @@ -1,7 +1,6 @@ package e2e import ( - "bytes" "context" "encoding/json" "fmt" @@ -42,7 +41,9 @@ import ( genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + appparams "github.com/atomone-hub/atomone/app/params" govtypes "github.com/atomone-hub/atomone/x/gov/types" + photontypes "github.com/atomone-hub/atomone/x/photon/types" ) const ( @@ -51,10 +52,8 @@ const ( queryCommand = "query" keysCommand = "keys" atomoneHomePath = "/home/nonroot/.atomone" - photonDenom = "photon" - uatoneDenom = "uatone" - stakeDenom = "stake" - initBalanceStr = "110000000000stake,100000000000000000photon,100000000000000000uatone" + uatoneDenom = appparams.BondDenom + uphotonDenom = photontypes.Denom minGasPrice = "0.00001" gas = 200000 govProposalBlockBuffer int64 = 35 @@ -78,12 +77,17 @@ const ( var ( runInCI = os.Getenv("GITHUB_ACTIONS") == "true" atomoneConfigPath = filepath.Join(atomoneHomePath, "config") - stakingAmount = sdk.NewInt(100000000000) - stakingAmountCoin = sdk.NewCoin(uatoneDenom, stakingAmount) - tokenAmount = sdk.NewCoin(uatoneDenom, sdk.NewInt(3300000000)) // 3,300uatom - standardFees = sdk.NewCoin(uatoneDenom, sdk.NewInt(330000)) // 0.33uatom - depositAmount = sdk.NewCoin(uatoneDenom, sdk.NewInt(330000000)) // 3,300uatom - proposalCounter = 0 + initBalance = sdk.NewCoins( + sdk.NewInt64Coin(uatoneDenom, 10_000_000_000_000), // 10,000,000atone + sdk.NewInt64Coin(uphotonDenom, 10_000_000_000), // 10,000photon + ) + initBalanceStr = initBalance.String() + stakingAmountCoin = sdk.NewInt64Coin(uatoneDenom, 6_000_000_000_000) // 6,000,000atone + tokenAmount = sdk.NewInt64Coin(uatoneDenom, 100_000_000) // 100atone + standardFees = sdk.NewInt64Coin(uphotonDenom, 330_000) // 0.33photon + depositAmount = sdk.NewInt64Coin(uatoneDenom, 1_000_000_000) // 1,000atone + initialDepositAmount = sdk.NewInt64Coin(uatoneDenom, 100_000_000) // 100atone + proposalCounter = 0 ) type IntegrationTestSuite struct { @@ -113,6 +117,14 @@ func TestIntegrationTestSuite(t *testing.T) { } func (s *IntegrationTestSuite) SetupSuite() { + defer func() { + if s.T().Failed() { + defer s.TearDownSuite() + s.saveChainLogs(s.chainA) + s.saveChainLogs(s.chainB) + } + }() + s.T().Log("setting up e2e integration test suite...") var err error @@ -348,7 +360,7 @@ func (s *IntegrationTestSuite) addGenesisVestingAndJailedAccounts( } jailedValidatorBalances := banktypes.Balance{ Address: jailedValAcc.String(), - Coins: sdk.NewCoins(tokenAmount), + Coins: initBalance, } stakingModuleBalances := banktypes.Balance{ Address: authtypes.NewModuleAddress(stakingtypes.NotBondedPoolName).String(), @@ -516,7 +528,8 @@ func (s *IntegrationTestSuite) initValidatorConfigs(c *chain) { appConfig := srvconfig.DefaultConfig() appConfig.API.Enable = true appConfig.API.Address = "tcp://0.0.0.0:1317" - appConfig.MinGasPrices = fmt.Sprintf("%s%s", minGasPrice, uatoneDenom) + appConfig.MinGasPrices = fmt.Sprintf("%s%s,%s%s", minGasPrice, uatoneDenom, + minGasPrice, uphotonDenom) appConfig.GRPC.Address = "0.0.0.0:9090" srvconfig.SetConfigTemplate(srvconfig.DefaultConfigTemplate) @@ -589,22 +602,28 @@ func (s *IntegrationTestSuite) runValidators(c *chain, portOffset int) { nodeReadyTimeout, time.Second, ) { - // Print first container logs in case no blocks are produced - var b bytes.Buffer - err := s.dkrPool.Client.Logs(docker.LogsOptions{ - Container: s.valResources[c.id][0].Container.ID, - OutputStream: &b, - ErrorStream: &b, - Stdout: true, - Stderr: true, - }) - if err == nil { - s.T().Logf("Node logs: %s", b.String()) - } s.T().Fatalf("AtomOne node failed to produce blocks. Is docker image %q up-to-date?", dockerImage) } } +func (s *IntegrationTestSuite) saveChainLogs(c *chain) { + f, err := os.CreateTemp("", c.id) + if err != nil { + s.T().Fatal(err) + } + defer f.Close() + err = s.dkrPool.Client.Logs(docker.LogsOptions{ + Container: s.valResources[c.id][0].Container.ID, + OutputStream: f, + ErrorStream: f, + Stdout: true, + Stderr: true, + }) + if err == nil { + s.T().Logf("See chain %s log file %s", c.id, f.Name()) + } +} + func noRestart(config *docker.HostConfig) { // in this case we don't want the nodes to restart on failure config.RestartPolicy = docker.RestartPolicy{ @@ -696,14 +715,15 @@ func (s *IntegrationTestSuite) writeGovCommunitySpendProposal(c *chain, amount s }] } ], - "deposit": "100uatone", + "deposit": "%s", "proposer": "Proposing validator address", "metadata": "Community Pool Spend", "title": "Fund Team!", "summary": "summary" } ` - propMsgBody := fmt.Sprintf(template, govModuleAddress, recipient, amount.Denom, amount.Amount.String()) + propMsgBody := fmt.Sprintf(template, govModuleAddress, recipient, + amount.Denom, amount.Amount.String(), initialDepositAmount.String()) err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalCommunitySpendFilename), []byte(propMsgBody)) s.Require().NoError(err) } diff --git a/tests/e2e/e2e_staking_test.go b/tests/e2e/e2e_staking_test.go index 92599726..ee4766b7 100644 --- a/tests/e2e/e2e_staking_test.go +++ b/tests/e2e/e2e_staking_test.go @@ -25,8 +25,6 @@ func (s *IntegrationTestSuite) testStaking() { delegatorAddress, _ := s.chainA.genesisAccounts[2].keyInfo.GetAddress() - fees := sdk.NewCoin(uatoneDenom, sdk.NewInt(1)) - existingDelegation := sdk.ZeroDec() res, err := queryDelegation(chainEndpoint, validatorAddressA, delegatorAddress.String()) if err == nil { @@ -37,7 +35,7 @@ func (s *IntegrationTestSuite) testStaking() { delegation := sdk.NewCoin(uatoneDenom, delegationAmount) // 500 atom // Alice delegate uatone to Validator A - s.execDelegate(s.chainA, 0, delegation.String(), validatorAddressA, delegatorAddress.String(), atomoneHomePath, fees.String()) + s.execDelegate(s.chainA, 0, delegation.String(), validatorAddressA, delegatorAddress.String(), atomoneHomePath) // Validate delegation successful s.Require().Eventually( @@ -56,7 +54,7 @@ func (s *IntegrationTestSuite) testStaking() { redelegation := sdk.NewCoin(uatoneDenom, redelegationAmount) // 250 atom // Alice re-delegate half of her uatone delegation from Validator A to Validator B - s.execRedelegate(s.chainA, 0, redelegation.String(), validatorAddressA, validatorAddressB, delegatorAddress.String(), atomoneHomePath, fees.String()) + s.execRedelegate(s.chainA, 0, redelegation.String(), validatorAddressA, validatorAddressB, delegatorAddress.String(), atomoneHomePath) // Validate re-delegation successful s.Require().Eventually( @@ -93,7 +91,7 @@ func (s *IntegrationTestSuite) testStaking() { ) // Alice unbonds all her uatone delegation from Validator A - s.execUnbondDelegation(s.chainA, 0, currDelegation.String(), validatorAddressA, delegatorAddress.String(), atomoneHomePath, fees.String()) + s.execUnbondDelegation(s.chainA, 0, currDelegation.String(), validatorAddressA, delegatorAddress.String(), atomoneHomePath) var ubdDelegationEntry types.UnbondingDelegationEntry @@ -121,7 +119,6 @@ func (s *IntegrationTestSuite) testStaking() { strconv.Itoa(int(ubdDelegationEntry.CreationHeight)), delegatorAddress.String(), atomoneHomePath, - fees.String(), ) // validate that unbonding delegation was successfully canceled diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index c3acf929..a2f53e63 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -13,6 +13,7 @@ var ( runStakingAndDistributionTest = true runVestingTest = true runRestInterfacesTest = true + runPhotonTest = true ) func (s *IntegrationTestSuite) TestRestInterfaces() { @@ -96,3 +97,10 @@ func (s *IntegrationTestSuite) TestVesting() { s.testContinuousVestingAccount(chainAAPI) // s.testPeriodicVestingAccount(chainAAPI) TODO: add back when v0.45 adds the missing CLI command. } + +func (s *IntegrationTestSuite) TestPhoton() { + if !runPhotonTest { + s.T().Skip() + } + s.testMintPhoton() +} diff --git a/tests/e2e/e2e_vesting_test.go b/tests/e2e/e2e_vesting_test.go index 01989da0..614e5695 100644 --- a/tests/e2e/e2e_vesting_test.go +++ b/tests/e2e/e2e_vesting_test.go @@ -31,12 +31,14 @@ type ( ) var ( - genesisVestingKeys = []string{continuousVestingKey, delayedVestingKey, lockedVestingKey, periodicVestingKey} - vestingAmountVested = sdk.NewCoin(uatoneDenom, sdk.NewInt(99900000000)) - vestingAmount = sdk.NewCoin(uatoneDenom, sdk.NewInt(350000)) - vestingBalance = sdk.NewCoins(vestingAmountVested).Add(vestingAmount) + genesisVestingKeys = []string{continuousVestingKey, delayedVestingKey, lockedVestingKey, periodicVestingKey} + vestingAmountVested = sdk.NewCoin(uatoneDenom, sdk.NewInt(99900000000)) + vestingAmount = sdk.NewCoins( + sdk.NewInt64Coin(uatoneDenom, 350_000), + sdk.NewInt64Coin(uphotonDenom, 10_000_000_000), + ) + vestingBalance = sdk.NewCoins(vestingAmountVested).Add(vestingAmount...) vestingDelegationAmount = sdk.NewCoin(uatoneDenom, sdk.NewInt(500000000)) - vestingDelegationFees = sdk.NewCoin(uatoneDenom, sdk.NewInt(1)) ) func (s *IntegrationTestSuite) testDelayedVestingAccount(api string) { @@ -60,7 +62,7 @@ func (s *IntegrationTestSuite) testDelayedVestingAccount(api string) { // Delegate coins should succeed s.execDelegate(chain, valIdx, vestingDelegationAmount.String(), valOpAddr, - vestingDelayedAcc.String(), atomoneHomePath, vestingDelegationFees.String()) + vestingDelayedAcc.String(), atomoneHomePath) // Validate delegation successful s.Require().Eventually( @@ -85,8 +87,7 @@ func (s *IntegrationTestSuite) testDelayedVestingAccount(api string) { valIdx, vestingDelayedAcc.String(), Address(), - balance.Sub(standardFees).String(), - standardFees.String(), + balance.String(), true, ) waitTime = acc.EndTime - time.Now().Unix() + vestingTxDelay @@ -101,8 +102,7 @@ func (s *IntegrationTestSuite) testDelayedVestingAccount(api string) { valIdx, vestingDelayedAcc.String(), Address(), - balance.Sub(standardFees).String(), - standardFees.String(), + balance.String(), false, ) }) @@ -129,7 +129,7 @@ func (s *IntegrationTestSuite) testContinuousVestingAccount(api string) { // Delegate coins should succeed s.execDelegate(chain, valIdx, vestingDelegationAmount.String(), - valOpAddr, continuousVestingAcc.String(), atomoneHomePath, vestingDelegationFees.String()) + valOpAddr, continuousVestingAcc.String(), atomoneHomePath) // Validate delegation successful s.Require().Eventually( @@ -154,8 +154,7 @@ func (s *IntegrationTestSuite) testContinuousVestingAccount(api string) { valIdx, continuousVestingAcc.String(), Address(), - balance.Sub(standardFees).String(), - standardFees.String(), + balance.String(), true, ) waitStartTime = acc.StartTime - time.Now().Unix() + vestingTxDelay @@ -172,8 +171,7 @@ func (s *IntegrationTestSuite) testContinuousVestingAccount(api string) { valIdx, continuousVestingAcc.String(), Address(), - balance.Sub(standardFees).String(), - standardFees.String(), + balance.String(), true, ) waitEndTime = acc.EndTime - time.Now().Unix() + vestingTxDelay @@ -188,8 +186,7 @@ func (s *IntegrationTestSuite) testContinuousVestingAccount(api string) { valIdx, continuousVestingAcc.String(), Address(), - balance.Sub(standardFees).String(), - standardFees.String(), + balance.String(), false, ) }) @@ -239,8 +236,7 @@ func (s *IntegrationTestSuite) testPeriodicVestingAccount(api string) { //nolint valIdx, periodicVestingAddr, Address(), - balance.Sub(standardFees).String(), - standardFees.String(), + balance.String(), true, ) waitStartTime = acc.StartTime - time.Now().Unix() + vestingTxDelay @@ -258,8 +254,7 @@ func (s *IntegrationTestSuite) testPeriodicVestingAccount(api string) { //nolint valIdx, periodicVestingAddr, Address(), - balance.Sub(standardFees).String(), - standardFees.String(), + balance.String(), true, ) waitFirstPeriod = firstPeriod - time.Now().Unix() + vestingTxDelay @@ -268,7 +263,7 @@ func (s *IntegrationTestSuite) testPeriodicVestingAccount(api string) { //nolint // Delegate coins should succeed s.execDelegate(chain, valIdx, vestingDelegationAmount.String(), valOpAddr, - periodicVestingAddr, atomoneHomePath, vestingDelegationFees.String()) + periodicVestingAddr, atomoneHomePath) // Validate delegation successful s.Require().Eventually( @@ -291,8 +286,7 @@ func (s *IntegrationTestSuite) testPeriodicVestingAccount(api string) { //nolint valIdx, periodicVestingAddr, Address(), - balance.Sub(standardFees).String(), - standardFees.String(), + balance.String(), false, ) @@ -309,8 +303,7 @@ func (s *IntegrationTestSuite) testPeriodicVestingAccount(api string) { //nolint valIdx, periodicVestingAddr, Address(), - balance.Sub(standardFees).String(), - standardFees.String(), + balance.String(), false, ) } diff --git a/tests/e2e/genesis.go b/tests/e2e/genesis_test.go similarity index 93% rename from tests/e2e/genesis.go rename to tests/e2e/genesis_test.go index ac7a401c..3348cddb 100644 --- a/tests/e2e/genesis.go +++ b/tests/e2e/genesis_test.go @@ -17,6 +17,7 @@ import ( 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" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" govtypes "github.com/atomone-hub/atomone/x/gov/types" @@ -165,8 +166,16 @@ func modifyGenesis(path, moniker, amountStr string, addrAll []sdk.AccAddress, de } appState[stakingtypes.ModuleName] = stakingGenStateBz + var mintGenState minttypes.GenesisState + cdc.MustUnmarshalJSON(appState[minttypes.ModuleName], &mintGenState) + mintGenState.Params.MintDenom = denom + mintGenStateBz, err := cdc.MarshalJSON(&mintGenState) + if err != nil { + return fmt.Errorf("failed to marshal mint genesis state: %s", err) + } + appState[minttypes.ModuleName] = mintGenStateBz + // Refactor to separate method - amnt := sdk.NewInt(10000) quorum, _ := sdk.NewDecFromStr("0.000000000000000001") threshold, _ := sdk.NewDecFromStr("0.000000000000000001") lawQuorum, _ := sdk.NewDecFromStr("0.000000000000000001") @@ -179,7 +188,7 @@ func modifyGenesis(path, moniker, amountStr string, addrAll []sdk.AccAddress, de govGenState := govv1.NewGenesisState(1, govv1.NewParams( - sdk.NewCoins(sdk.NewCoin(denom, amnt)), maxDepositPeriod, + sdk.NewCoins(depositAmount), maxDepositPeriod, votingPeriod, quorum.String(), threshold.String(), amendmentsQuorum.String(), amendmentsThreshold.String(), lawQuorum.String(), lawThreshold.String(), diff --git a/tests/e2e/http_util.go b/tests/e2e/http_util_test.go similarity index 100% rename from tests/e2e/http_util.go rename to tests/e2e/http_util_test.go diff --git a/tests/e2e/query_test.go b/tests/e2e/query_test.go index de8288f6..c9a1a4cb 100644 --- a/tests/e2e/query_test.go +++ b/tests/e2e/query_test.go @@ -1,11 +1,13 @@ package e2e import ( + "encoding/hex" "fmt" "io" "net/http" "strings" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -17,10 +19,11 @@ import ( govtypesv1 "github.com/atomone-hub/atomone/x/gov/types/v1" govtypesv1beta1 "github.com/atomone-hub/atomone/x/gov/types/v1beta1" + photontypes "github.com/atomone-hub/atomone/x/photon/types" ) // queryAtomOneTx returns an error if the tx is not found or is failed. -func queryAtomOneTx(endpoint, txHash string) error { +func queryAtomOneTx(endpoint, txHash string, msgResp codec.ProtoMarshaler) error { body, err := httpGet(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", endpoint, txHash)) if err != nil { return err @@ -33,6 +36,20 @@ func queryAtomOneTx(endpoint, txHash string) error { if resp.TxResponse.Code != 0 { return fmt.Errorf("tx %s is failed with code=%d log='%s'", txHash, resp.TxResponse.Code, resp.TxResponse.RawLog) } + if msgResp != nil { + // msgResp is provided, try to decode the tx response + data, err := hex.DecodeString(resp.TxResponse.Data) + if err != nil { + return err + } + var txMsgData sdk.TxMsgData + if err := cdc.Unmarshal(data, &txMsgData); err != nil { + return err + } + if err := cdc.Unmarshal(txMsgData.MsgResponses[0].Value, msgResp); err != nil { + return err + } + } return nil } @@ -65,6 +82,15 @@ func queryAtomOneAllBalances(endpoint, addr string) (sdk.Coins, error) { return balancesResp.Balances, nil } +func (s *IntegrationTestSuite) queryBankSupply(endpoint string) sdk.Coins { + body, err := httpGet(fmt.Sprintf("%s/cosmos/bank/v1beta1/supply", endpoint)) + s.Require().NoError(err) + var resp banktypes.QueryTotalSupplyResponse + err = cdc.UnmarshalJSON(body, &resp) + s.Require().NoError(err) + return resp.Supply +} + func queryStakingParams(endpoint string) (stakingtypes.QueryParamsResponse, error) { //nolint:unused body, err := httpGet(fmt.Sprintf("%s/cosmos/staking/v1beta1/params", endpoint)) if err != nil { @@ -278,3 +304,21 @@ func (s *IntegrationTestSuite) queryConstitution(endpoint string) govtypesv1.Que s.Require().NoError(err) return res } + +func (s *IntegrationTestSuite) queryPhotonConversionRate(endpoint string) sdk.Dec { + body, err := httpGet(fmt.Sprintf("%s/atomone/photon/v1/conversion_rate", endpoint)) + s.Require().NoError(err) + var resp photontypes.QueryConversionRateResponse + err = cdc.UnmarshalJSON(body, &resp) + s.Require().NoError(err) + return sdk.MustNewDecFromStr(resp.ConversionRate) +} + +func (s *IntegrationTestSuite) queryPhotonParams(endpoint string) photontypes.QueryParamsResponse { + body, err := httpGet(fmt.Sprintf("%s/atomone/photon/v1/params", endpoint)) + s.Require().NoError(err) + var res photontypes.QueryParamsResponse + err = cdc.UnmarshalJSON(body, &res) + s.Require().NoError(err) + return res +} diff --git a/tests/e2e/scripts/hermes_bootstrap.sh b/tests/e2e/scripts/hermes_bootstrap.sh index 5b56fb91..145254f4 100755 --- a/tests/e2e/scripts/hermes_bootstrap.sh +++ b/tests/e2e/scripts/hermes_bootstrap.sh @@ -53,7 +53,7 @@ account_prefix = 'atone' key_name = 'rly01-gaia-a' store_prefix = 'ibc' max_gas = 6000000 -gas_price = { price = 0.00001, denom = 'uatone' } +gas_price = { price = 0.00001, denom = 'uphoton' } gas_multiplier = 1.2 clock_drift = '1m' # to accomdate docker containers trusting_period = '14days' @@ -69,7 +69,7 @@ account_prefix = 'atone' key_name = 'rly01-gaia-b' store_prefix = 'ibc' max_gas = 6000000 -gas_price = { price = 0.00001, denom = 'uatone' } +gas_price = { price = 0.00001, denom = 'uphoton' } gas_multiplier = 1.2 clock_drift = '1m' # to accomdate docker containers trusting_period = '14days' @@ -120,7 +120,7 @@ account_prefix = 'atone' key_name = 'rly01-gaia-a' store_prefix = 'ibc' max_gas = 6000000 -gas_price = { price = 0, denom = 'uatone' } +gas_price = { price = 0, denom = 'uphoton' } gas_multiplier = 1.2 clock_drift = '1m' # to accommodate docker containers trusting_period = '14days' @@ -136,7 +136,7 @@ account_prefix = 'atone' key_name = 'rly01-gaia-b' store_prefix = 'ibc' max_gas = 6000000 -gas_price = { price = 0, denom = 'uatone' } +gas_price = { price = 0, denom = 'uphoton' } gas_multiplier = 1.2 clock_drift = '1m' # to accommodate docker containers trusting_period = '14days' diff --git a/tests/e2e/util.go b/tests/e2e/util_test.go similarity index 100% rename from tests/e2e/util.go rename to tests/e2e/util_test.go diff --git a/tests/e2e/validator.go b/tests/e2e/validator_test.go similarity index 100% rename from tests/e2e/validator.go rename to tests/e2e/validator_test.go diff --git a/x/photon/ante/ante.go b/x/photon/ante/ante.go new file mode 100644 index 00000000..e0d3d80f --- /dev/null +++ b/x/photon/ante/ante.go @@ -0,0 +1,71 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/atomone-hub/atomone/x/photon/keeper" + "github.com/atomone-hub/atomone/x/photon/types" +) + +var _ sdk.AnteDecorator = ValidateFeeDecorator{} + +type ValidateFeeDecorator struct { + k *keeper.Keeper +} + +func NewValidateFeeDecorator(k *keeper.Keeper) ValidateFeeDecorator { + return ValidateFeeDecorator{k: k} +} + +// AnteHandle implements the sdk.AnteDecorator interface. +// It returns an error if the tx fee denom is not photon, with some exceptions: +// - tx has no fees or 0 fees. +// - tx messages' type URLs match the `TxFeeExceptions` field of the +// [types.Params]. +func (vfd ValidateFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") //nolint:staticcheck + } + feeCoins := feeTx.GetFee() + if feeCoins.IsZero() { + // Skip if no fees + return next(ctx, tx, simulate) + } + + if allowsAnyTxFee(tx, vfd.k.GetParams(ctx).TxFeeExceptions) { + // Skip if tx is declared in TxFeeExceptions (any fee coins are allowed). + return next(ctx, tx, simulate) + } + + if len(feeCoins) > 1 { + return ctx, types.ErrTooManyFeeCoins + } + if feeDenom := feeCoins[0].Denom; feeDenom != types.Denom { + // feeDenom not allowed + return ctx, sdkerrors.Wrapf(types.ErrInvalidFeeToken, "fee denom %s not allowed", feeDenom) //nolint:staticcheck + } + // feeDenom photon is allowed + return next(ctx, tx, simulate) +} + +// allowsAnyTxFee returns true if all tx messages type URL are presents in +// txFeeExceptions, or if it starts with a wildcard "*". +func allowsAnyTxFee(tx sdk.Tx, txFeeExceptions []string) bool { + if len(txFeeExceptions) > 0 && txFeeExceptions[0] == "*" { + // wildcard detected, all tx fees are allowed. + return true + } + var anyTxFeeMsgCount int + for _, msg := range tx.GetMsgs() { + msgTypeURL := sdk.MsgTypeURL(msg) + for _, exception := range txFeeExceptions { + if exception == msgTypeURL { + anyTxFeeMsgCount++ + break + } + } + } + return anyTxFeeMsgCount == len(tx.GetMsgs()) +} diff --git a/x/photon/ante/ante_test.go b/x/photon/ante/ante_test.go new file mode 100644 index 00000000..6f0d4c66 --- /dev/null +++ b/x/photon/ante/ante_test.go @@ -0,0 +1,240 @@ +package ante + +import ( + "testing" + + "gotest.tools/v3/assert" + + appparams "github.com/atomone-hub/atomone/app/params" + "github.com/atomone-hub/atomone/x/photon/testutil" + "github.com/atomone-hub/atomone/x/photon/types" + "github.com/stretchr/testify/require" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx" +) + +func TestValidateFeeDecorator(t *testing.T) { + tests := []struct { + name string + tx sdk.Tx + expectedError string + }{ + { + name: "ok: no fee", + tx: &tx.Tx{ + AuthInfo: &tx.AuthInfo{ + Fee: &tx.Fee{}, + }, + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgMintPhoton{}), + }, + }, + }, + }, + { + name: "ok: tx MsgMintPhoton accepts any fee denom bc declared in txFeeExceptions", + tx: &tx.Tx{ + AuthInfo: &tx.AuthInfo{ + Fee: &tx.Fee{ + Amount: sdk.NewCoins( + sdk.NewInt64Coin(appparams.BondDenom, 1), + sdk.NewInt64Coin("xxx", 1), + ), + }, + }, + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgMintPhoton{}), + }, + }, + }, + }, + { + name: "ok: MsgUpdateParams fee uphoton", + tx: &tx.Tx{ + AuthInfo: &tx.AuthInfo{ + Fee: &tx.Fee{ + Amount: sdk.NewCoins(sdk.NewInt64Coin(types.Denom, 1)), + }, + }, + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgUpdateParams{}), + }, + }, + }, + }, + { + name: "fail: MsgUpdateParams fee uatone", + tx: &tx.Tx{ + AuthInfo: &tx.AuthInfo{ + Fee: &tx.Fee{ + Amount: sdk.NewCoins(sdk.NewInt64Coin(appparams.BondDenom, 1)), + }, + }, + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgUpdateParams{}), + }, + }, + }, + expectedError: "fee denom uatone not allowed: invalid fee token", + }, + { + name: "fail: MsgUpdateParams fee xxx", + tx: &tx.Tx{ + AuthInfo: &tx.AuthInfo{ + Fee: &tx.Fee{ + Amount: sdk.NewCoins(sdk.NewInt64Coin("xxx", 1)), + }, + }, + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgUpdateParams{}), + }, + }, + }, + expectedError: "fee denom xxx not allowed: invalid fee token", + }, + { + name: "fail: MsgUpdateParams multiple fee denom", + tx: &tx.Tx{ + AuthInfo: &tx.AuthInfo{ + Fee: &tx.Fee{ + Amount: sdk.NewCoins( + sdk.NewInt64Coin(appparams.BondDenom, 1), + sdk.NewInt64Coin("xxx", 1), + ), + }, + }, + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgUpdateParams{}), + }, + }, + }, + expectedError: "too many fee coins, only accepts fees in one denom", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k, _, ctx := testutil.SetupPhotonKeeper(t) + k.SetParams(ctx, types.DefaultParams()) + var ( + nextInvoked bool + next = func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { + nextInvoked = true + return ctx, nil + } + vfd = NewValidateFeeDecorator(k) + ) + + _, err := vfd.AnteHandle(ctx, tt.tx, false, next) + + if tt.expectedError != "" { + require.EqualError(t, err, tt.expectedError) + return + } + require.NoError(t, err) + require.True(t, nextInvoked, "next is not invoked") + }) + } +} + +func TestAllowsAnyTxFee(t *testing.T) { + tests := []struct { + name string + tx sdk.Tx + txFeeExceptions []string + expectedRes bool + }{ + { + name: "wildcard fee execptions", + tx: &tx.Tx{ + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgUpdateParams{}), + }, + }, + }, + txFeeExceptions: []string{"*"}, + expectedRes: true, + }, + { + name: "empty fee execptions", + tx: &tx.Tx{ + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgMintPhoton{}), + }, + }, + }, + txFeeExceptions: nil, + expectedRes: false, + }, + { + name: "one message match txFeeExceptions", + tx: &tx.Tx{ + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgMintPhoton{}), + }, + }, + }, + txFeeExceptions: []string{sdk.MsgTypeURL(&types.MsgMintPhoton{})}, + expectedRes: true, + }, + { + name: "multiple messages not all match txFeeExceptions", + tx: &tx.Tx{ + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgUpdateParams{}), + codectypes.UnsafePackAny(&types.MsgMintPhoton{}), + }, + }, + }, + txFeeExceptions: []string{sdk.MsgTypeURL(&types.MsgMintPhoton{})}, + expectedRes: false, + }, + { + name: "multiple same messages match txFeeExceptions", + tx: &tx.Tx{ + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgMintPhoton{}), + codectypes.UnsafePackAny(&types.MsgMintPhoton{}), + }, + }, + }, + txFeeExceptions: []string{sdk.MsgTypeURL(&types.MsgMintPhoton{})}, + expectedRes: true, + }, + { + name: "multiple different messages match txFeeExceptions", + tx: &tx.Tx{ + Body: &tx.TxBody{ + Messages: []*codectypes.Any{ + codectypes.UnsafePackAny(&types.MsgMintPhoton{}), + codectypes.UnsafePackAny(&types.MsgUpdateParams{}), + }, + }, + }, + txFeeExceptions: []string{ + sdk.MsgTypeURL(&types.MsgMintPhoton{}), + sdk.MsgTypeURL(&types.MsgUpdateParams{}), + }, + expectedRes: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := allowsAnyTxFee(tt.tx, tt.txFeeExceptions) + + assert.Equal(t, tt.expectedRes, res) + }) + } +} diff --git a/x/photon/client/cli/query.go b/x/photon/client/cli/query.go new file mode 100644 index 00000000..4198438d --- /dev/null +++ b/x/photon/client/cli/query.go @@ -0,0 +1,74 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + + "github.com/atomone-hub/atomone/x/photon/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(queryRoute string) *cobra.Command { + // Group photon queries under a subcommand + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetQueryParamsCmd(), + GetQueryConversionRateCmd(), + ) + return cmd +} + +func GetQueryParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "shows the parameters of the module", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +func GetQueryConversionRateCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "conversion-rate", + Short: "shows the atone to photon conversion rate", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.ConversionRate(cmd.Context(), &types.QueryConversionRateRequest{}) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/photon/client/cli/tx.go b/x/photon/client/cli/tx.go new file mode 100644 index 00000000..c26df243 --- /dev/null +++ b/x/photon/client/cli/tx.go @@ -0,0 +1,55 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/atomone-hub/atomone/x/photon/types" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + cmd.AddCommand(GetTxMintPhotonCmd()) + return cmd +} + +func GetTxMintPhotonCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "mint [amount]", + Short: "Broadcast MintPhoton message which burns [amount] and mint photons.", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + toBurn, err := sdk.ParseCoinNormalized(args[0]) + if err != nil { + return err + } + msg := types.NewMsgMintPhoton( + clientCtx.GetFromAddress(), + toBurn, + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/photon/genesis.go b/x/photon/genesis.go new file mode 100644 index 00000000..ff6bb8ab --- /dev/null +++ b/x/photon/genesis.go @@ -0,0 +1,24 @@ +package photon + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/atomone-hub/atomone/x/photon/keeper" + "github.com/atomone-hub/atomone/x/photon/types" +) + +// InitGenesis initializes the module's state from a provided genesis state. +func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { + if err := k.SetParams(ctx, genState.Params); err != nil { + panic(fmt.Sprintf("%s module params has not been set", types.ModuleName)) + } +} + +// ExportGenesis returns the module's exported genesis +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { + genesis := types.DefaultGenesis() + genesis.Params = k.GetParams(ctx) + return genesis +} diff --git a/x/photon/genesis_test.go b/x/photon/genesis_test.go new file mode 100644 index 00000000..fc1a670c --- /dev/null +++ b/x/photon/genesis_test.go @@ -0,0 +1,24 @@ +package photon_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/atomone-hub/atomone/x/photon" + "github.com/atomone-hub/atomone/x/photon/testutil" + "github.com/atomone-hub/atomone/x/photon/types" +) + +func TestGenesis(t *testing.T) { + genesisState := types.GenesisState{ + Params: types.DefaultParams(), + } + k, _, ctx := testutil.SetupPhotonKeeper(t) + + photon.InitGenesis(ctx, *k, genesisState) + got := photon.ExportGenesis(ctx, *k) + + require.NotNil(t, got) + require.Equal(t, genesisState, *got) +} diff --git a/x/photon/keeper/grpc_query.go b/x/photon/keeper/grpc_query.go new file mode 100644 index 00000000..813bc7b5 --- /dev/null +++ b/x/photon/keeper/grpc_query.go @@ -0,0 +1,35 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/atomone-hub/atomone/x/photon/types" +) + +var _ types.QueryServer = Keeper{} + +func (k Keeper) Params(goCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil +} + +// ConversionRate returns the staking denom to photon conversion ratio. +func (k Keeper) ConversionRate(goCtx context.Context, req *types.QueryConversionRateRequest) (*types.QueryConversionRateResponse, error) { + var ( + ctx = sdk.UnwrapSDKContext(goCtx) + bondDenom = k.stakingKeeper.BondDenom(ctx) + stakingDenomSupply = k.bankKeeper.GetSupply(ctx, bondDenom).Amount.ToLegacyDec() + uphotonSupply = k.bankKeeper.GetSupply(ctx, types.Denom).Amount.ToLegacyDec() + cr = k.conversionRate(ctx, stakingDenomSupply, uphotonSupply) + ) + return &types.QueryConversionRateResponse{ConversionRate: cr.String()}, nil +} diff --git a/x/photon/keeper/grpc_query_test.go b/x/photon/keeper/grpc_query_test.go new file mode 100644 index 00000000..764950bc --- /dev/null +++ b/x/photon/keeper/grpc_query_test.go @@ -0,0 +1,64 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + + appparams "github.com/atomone-hub/atomone/app/params" + "github.com/atomone-hub/atomone/x/photon/testutil" + "github.com/atomone-hub/atomone/x/photon/types" + "github.com/stretchr/testify/require" +) + +func TestParamsQuery(t *testing.T) { + k, _, ctx := testutil.SetupPhotonKeeper(t) + params := types.DefaultParams() + k.SetParams(ctx, params) + + resp, err := k.Params(ctx, &types.QueryParamsRequest{}) + + require.NoError(t, err) + require.Equal(t, &types.QueryParamsResponse{Params: params}, resp) +} + +func TestConversionRateQuery(t *testing.T) { + tests := []struct { + name string + uatoneSupply int64 + uphotonSupply int64 + expectedResponse *types.QueryConversionRateResponse + }{ + { + name: "nominal case", + uatoneSupply: 100_000_000_000_000, // 100,000,000atone + uphotonSupply: 100_000_000_000, // 100,000photon + expectedResponse: &types.QueryConversionRateResponse{ + ConversionRate: "9.999000000000000000", + }, + }, + { + name: "max supply of photon exceeded", + uatoneSupply: 100_000_000_000_000, // 100,000,000atone + uphotonSupply: types.MaxSupply + 1, + expectedResponse: &types.QueryConversionRateResponse{ + ConversionRate: "0.000000000000000000", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k, m, ctx := testutil.SetupPhotonKeeper(t) + m.StakingKeeper.EXPECT().BondDenom(ctx).Return(appparams.BondDenom) + m.BankKeeper.EXPECT().GetSupply(ctx, appparams.BondDenom). + Return(sdk.NewInt64Coin(appparams.BondDenom, tt.uatoneSupply)) + m.BankKeeper.EXPECT().GetSupply(ctx, types.Denom). + Return(sdk.NewInt64Coin(appparams.BondDenom, tt.uphotonSupply)) + + resp, err := k.ConversionRate(ctx, &types.QueryConversionRateRequest{}) + + require.NoError(t, err) + require.Equal(t, tt.expectedResponse, resp) + }) + } +} diff --git a/x/photon/keeper/keeper.go b/x/photon/keeper/keeper.go new file mode 100644 index 00000000..b3f019c8 --- /dev/null +++ b/x/photon/keeper/keeper.go @@ -0,0 +1,56 @@ +package keeper + +import ( + "fmt" + + "github.com/cometbft/cometbft/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/atomone-hub/atomone/x/photon/types" +) + +type Keeper struct { + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + authority string + + bankKeeper types.BankKeeper + accountKeeper types.AccountKeeper + stakingKeeper types.StakingKeeper +} + +func NewKeeper( + cdc codec.BinaryCodec, + storeKey storetypes.StoreKey, + authority string, + bankKeeper types.BankKeeper, + accountKeeper types.AccountKeeper, + stakingKeeper types.StakingKeeper, +) *Keeper { + return &Keeper{ + cdc: cdc, + storeKey: storeKey, + authority: authority, + bankKeeper: bankKeeper, + accountKeeper: accountKeeper, + stakingKeeper: stakingKeeper, + } +} + +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// conversionRate returns the conversion rate for converting bond denom to +// photon. +func (k Keeper) conversionRate(_ sdk.Context, bondDenomSupply, uphotonSupply sdk.Dec) sdk.Dec { + remainMintableUphotons := sdk.NewDec(types.MaxSupply).Sub(uphotonSupply) + if remainMintableUphotons.IsNegative() { + // If for any reason the max supply is exceeded, avoid returning a negative number + return sdk.ZeroDec() + } + return remainMintableUphotons.Quo(bondDenomSupply) +} diff --git a/x/photon/keeper/msg_server.go b/x/photon/keeper/msg_server.go new file mode 100644 index 00000000..82464980 --- /dev/null +++ b/x/photon/keeper/msg_server.go @@ -0,0 +1,110 @@ +package keeper + +import ( + "context" + + "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + + govtypes "github.com/atomone-hub/atomone/x/gov/types" + "github.com/atomone-hub/atomone/x/photon/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +// MintPhoton implements the MsgServer.MintPhoton method. +func (k msgServer) MintPhoton(goCtx context.Context, msg *types.MsgMintPhoton) (*types.MsgMintPhotonResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + params := k.GetParams(ctx) + if params.MintDisabled { + return nil, types.ErrMintDisabled + } + + // Ensure burned amount denom is bond denom + bondDenom := k.stakingKeeper.BondDenom(ctx) + if msg.Amount.Denom != bondDenom { + return nil, types.ErrBurnInvalidDenom + } + // Compute photons to mint + var ( + bondDenomSupply = k.bankKeeper.GetSupply(ctx, bondDenom).Amount.ToLegacyDec() + uphotonSupply = k.bankKeeper.GetSupply(ctx, types.Denom).Amount.ToLegacyDec() + conversionRate = k.conversionRate(ctx, bondDenomSupply, uphotonSupply) + bondDenomToBurn = msg.Amount + uphotonToMint = bondDenomToBurn.Amount.ToLegacyDec().Mul(conversionRate).RoundInt() + ) + // If no photon to mint, do not burn bondDenomToBurn, returns an error + // this could happen due to rounding + if uphotonToMint.IsZero() { + return nil, types.ErrZeroMintPhotons + } + + // Burn/Mint phase: + // 1) move ATONEs from msg signer address to this module address + // 2) burn ATONEs from this module address + // 3) mint PHOTONs into this module address + // 4) move PHOTONs from this module address to msg signer address + var ( + coinsToBurn = sdk.NewCoins(bondDenomToBurn) + coinsToMint = sdk.NewCoins(sdk.NewCoin(types.Denom, uphotonToMint)) + ) + // 1) Send atone to photon module for burn + to, err := sdk.AccAddressFromBech32(msg.ToAddress) + if err != nil { + return nil, err + } + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, to, types.ModuleName, coinsToBurn); err != nil { + return nil, err + } + // 2) Burn atone + if err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, coinsToBurn); err != nil { + return nil, err + } + + // 3) Mint photons + if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, coinsToMint); err != nil { + return nil, err + } + // 4) Send minted photon to account + if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, to, coinsToMint); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeMintPhoton, + sdk.NewAttribute(types.AttributeKeyBurned, coinsToBurn.String()), + sdk.NewAttribute(types.AttributeKeyMinted, coinsToMint.String()), + ), + }) + + return &types.MsgMintPhotonResponse{ + Minted: coinsToMint[0], + ConversionRate: conversionRate.String(), + }, nil +} + +// UpdateParams implements the MsgServer.UpdateParams method. +func (k msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { + if k.authority != msg.Authority { + return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.authority, msg.Authority) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + if err := k.SetParams(ctx, msg.Params); err != nil { + return nil, err + } + + return &types.MsgUpdateParamsResponse{}, nil +} diff --git a/x/photon/keeper/msg_server_test.go b/x/photon/keeper/msg_server_test.go new file mode 100644 index 00000000..34f35047 --- /dev/null +++ b/x/photon/keeper/msg_server_test.go @@ -0,0 +1,185 @@ +package keeper_test + +import ( + "math" + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + appparams "github.com/atomone-hub/atomone/app/params" + "github.com/atomone-hub/atomone/x/photon/testutil" + "github.com/atomone-hub/atomone/x/photon/types" +) + +func TestMsgServerMintPhoton(t *testing.T) { + var ( + toAddress = sdk.AccAddress("test1") + atoneSupply int64 = 107_775_332 * 1_000_000 // From genesis + ) + tests := []struct { + name string + params types.Params + msg *types.MsgMintPhoton + setup func(sdk.Context, testutil.Mocks) + expectedErr string + expectedResponse *types.MsgMintPhotonResponse + }{ + { + name: "fail: mint disabled", + params: types.Params{MintDisabled: true}, + msg: &types.MsgMintPhoton{}, + expectedErr: "photon mint disabled", + }, + { + name: "fail: empty Amount field", + params: types.Params{MintDisabled: false}, + msg: &types.MsgMintPhoton{}, + setup: func(ctx sdk.Context, m testutil.Mocks) { + m.StakingKeeper.EXPECT().BondDenom(ctx).Return(appparams.BondDenom) + }, + expectedErr: "invalid burned amount denom: expected bond denom", + }, + { + name: "fail: invalid Amount field denom", + params: types.Params{MintDisabled: false}, + msg: &types.MsgMintPhoton{ + Amount: sdk.NewInt64Coin("xxx", 42), + }, + setup: func(ctx sdk.Context, m testutil.Mocks) { + m.StakingKeeper.EXPECT().BondDenom(ctx).Return(appparams.BondDenom) + }, + expectedErr: "invalid burned amount denom: expected bond denom", + }, + { + name: "fail: photon_supply=max", + params: types.Params{MintDisabled: false}, + msg: &types.MsgMintPhoton{ + ToAddress: toAddress.String(), + Amount: sdk.NewInt64Coin(appparams.BondDenom, 1), + }, + setup: func(ctx sdk.Context, m testutil.Mocks) { + m.StakingKeeper.EXPECT().BondDenom(ctx).Return(appparams.BondDenom) + m.BankKeeper.EXPECT().GetSupply(ctx, appparams.BondDenom). + Return(sdk.NewInt64Coin(appparams.BondDenom, atoneSupply)) + m.BankKeeper.EXPECT().GetSupply(ctx, types.Denom).Return(sdk.NewInt64Coin(types.Denom, types.MaxSupply)) + }, + expectedErr: "no mintable photon after rounding, try higher burn", + }, + { + name: "fail: atone_supply >> photon_supply", + params: types.Params{MintDisabled: false}, + msg: &types.MsgMintPhoton{ + ToAddress: toAddress.String(), + Amount: sdk.NewInt64Coin(appparams.BondDenom, 1), + }, + setup: func(ctx sdk.Context, m testutil.Mocks) { + m.StakingKeeper.EXPECT().BondDenom(ctx).Return(appparams.BondDenom) + m.BankKeeper.EXPECT().GetSupply(ctx, appparams.BondDenom). + Return(sdk.NewInt64Coin(appparams.BondDenom, math.MaxInt)) + m.BankKeeper.EXPECT().GetSupply(ctx, types.Denom).Return(sdk.NewInt64Coin(types.Denom, 0)) + }, + expectedErr: "no mintable photon after rounding, try higher burn", + }, + { + name: "ok: photon_supply=0", + params: types.Params{MintDisabled: false}, + msg: &types.MsgMintPhoton{ + ToAddress: toAddress.String(), + Amount: sdk.NewInt64Coin(appparams.BondDenom, 1), + }, + setup: func(ctx sdk.Context, m testutil.Mocks) { + m.StakingKeeper.EXPECT().BondDenom(ctx).Return(appparams.BondDenom) + m.BankKeeper.EXPECT().GetSupply(ctx, appparams.BondDenom). + Return(sdk.NewInt64Coin(appparams.BondDenom, atoneSupply)) + m.BankKeeper.EXPECT().GetSupply(ctx, types.Denom).Return(sdk.NewInt64Coin(types.Denom, 0)) + m.BankKeeper.EXPECT().SendCoinsFromAccountToModule( + ctx, toAddress, types.ModuleName, + sdk.NewCoins(sdk.NewInt64Coin(appparams.BondDenom, 1)), + ) + m.BankKeeper.EXPECT().BurnCoins(ctx, types.ModuleName, + sdk.NewCoins(sdk.NewInt64Coin(appparams.BondDenom, 1)), + ) + m.BankKeeper.EXPECT().MintCoins(ctx, types.ModuleName, + sdk.NewCoins(sdk.NewInt64Coin(types.Denom, 9)), + ) + m.BankKeeper.EXPECT().SendCoinsFromModuleToAccount( + ctx, types.ModuleName, toAddress, + sdk.NewCoins(sdk.NewInt64Coin(types.Denom, 9)), + ) + }, + expectedResponse: &types.MsgMintPhotonResponse{ + Minted: sdk.NewInt64Coin(types.Denom, 9), + ConversionRate: "9.278561071841560182", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ms, k, mocks, ctx := testutil.SetupMsgServer(t) + k.SetParams(ctx, tt.params) + if tt.setup != nil { + tt.setup(ctx, mocks) + } + + resp, err := ms.MintPhoton(ctx, tt.msg) + + if tt.expectedErr != "" { + require.EqualError(t, err, tt.expectedErr) + return + } + require.NoError(t, err) + require.Equal(t, resp, tt.expectedResponse) + }) + } +} + +func TestMsgServerUpdateParams(t *testing.T) { + tests := []struct { + name string + msg *types.MsgUpdateParams + expectedErr string + }{ + { + name: "empty authority field", + msg: &types.MsgUpdateParams{ + Authority: "", + Params: types.Params{MintDisabled: true}, + }, + expectedErr: "invalid authority; expected cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn, got : expected gov account as only signer for proposal message", + }, + { + name: "invalid authority field", + msg: &types.MsgUpdateParams{ + Authority: "xxx", + Params: types.Params{MintDisabled: true}, + }, + expectedErr: "invalid authority; expected cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn, got xxx: expected gov account as only signer for proposal message", + }, + { + name: "ok", + msg: &types.MsgUpdateParams{ + Authority: "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", + Params: types.Params{MintDisabled: true}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ms, k, _, ctx := testutil.SetupMsgServer(t) + params := types.DefaultParams() + k.SetParams(ctx, params) + + _, err := ms.UpdateParams(ctx, tt.msg) + + if tt.expectedErr != "" { + require.EqualError(t, err, tt.expectedErr) + return + } + require.NoError(t, err) + got := k.GetParams(ctx) + require.Equal(t, got, tt.msg.Params) + }) + } +} diff --git a/x/photon/keeper/params.go b/x/photon/keeper/params.go new file mode 100644 index 00000000..70f97576 --- /dev/null +++ b/x/photon/keeper/params.go @@ -0,0 +1,29 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/atomone-hub/atomone/x/photon/types" +) + +// GetParams get all parameters as types.Params +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ParamsKey) + if bz == nil { + return params + } + k.cdc.MustUnmarshal(bz, ¶ms) + return params +} + +// SetParams set the params +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error { + store := ctx.KVStore(k.storeKey) + bz, err := k.cdc.Marshal(¶ms) + if err != nil { + return err + } + store.Set(types.ParamsKey, bz) + return nil +} diff --git a/x/photon/keeper/params_test.go b/x/photon/keeper/params_test.go new file mode 100644 index 00000000..d5ac4195 --- /dev/null +++ b/x/photon/keeper/params_test.go @@ -0,0 +1,19 @@ +package keeper_test + +import ( + "testing" + + "github.com/atomone-hub/atomone/x/photon/testutil" + "github.com/atomone-hub/atomone/x/photon/types" + "github.com/stretchr/testify/require" +) + +func TestGetParams(t *testing.T) { + k, _, ctx := testutil.SetupPhotonKeeper(t) + params := types.DefaultParams() + + k.SetParams(ctx, params) + got := k.GetParams(ctx) + + require.EqualValues(t, params, got) +} diff --git a/x/photon/module.go b/x/photon/module.go new file mode 100644 index 00000000..a55339bd --- /dev/null +++ b/x/photon/module.go @@ -0,0 +1,153 @@ +package photon + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/atomone-hub/atomone/x/photon/client/cli" + "github.com/atomone-hub/atomone/x/photon/keeper" + "github.com/atomone-hub/atomone/x/photon/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement. +type AppModuleBasic struct { + cdc codec.Codec +} + +func NewAppModuleBasic(cdc codec.Codec) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Name returns the name of the module as a string +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +// RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message +func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(reg) +} + +// DefaultGenesis returns a default GenesisState for the module, marshalled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// ValidateGenesis used to validate the GenesisState, given in its json.RawMessage form +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil { + panic(err) + } +} + +// GetTxCmd returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd(types.StoreKey) +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper + accountKeeper types.AccountKeeper + bankKeeper types.BankKeeper + stakingKeeper types.StakingKeeper +} + +func NewAppModule( + cdc codec.Codec, + keeper keeper.Keeper, + bankKeeper types.BankKeeper, + accountKeeper types.AccountKeeper, + stakingKeeper types.StakingKeeper, +) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + bankKeeper: bankKeeper, + accountKeeper: accountKeeper, + stakingKeeper: stakingKeeper, + } +} + +// RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// RegisterInvariants registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted) +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// InitGenesis performs the module's genesis initialization. It returns no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { + var genState types.GenesisState + // Initialize global index to index in genesis state + cdc.MustUnmarshalJSON(gs, &genState) + + InitGenesis(ctx, am.keeper, genState) + + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + genState := ExportGenesis(ctx, am.keeper) + return cdc.MustMarshalJSON(genState) +} + +// ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1 +func (AppModule) ConsensusVersion() uint64 { return 1 } + +// BeginBlock contains the logic that is automatically triggered at the beginning of each block +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock contains the logic that is automatically triggered at the end of each block +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} diff --git a/x/photon/module_simulation.go b/x/photon/module_simulation.go new file mode 100644 index 00000000..27fa21fb --- /dev/null +++ b/x/photon/module_simulation.go @@ -0,0 +1,30 @@ +package photon + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + + "github.com/atomone-hub/atomone/x/photon/simulation" + "github.com/atomone-hub/atomone/x/photon/types" +) + +// GenerateGenesisState creates a randomized GenState of the module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RegisterStoreDecoder registers a decoder. +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) +} + +func (AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg { + return simulation.ProposalMsgs() +} + +// WeightedOperations returns the all the gov module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return simulation.WeightedOperations(simState.AppParams, simState.Cdc, + am.accountKeeper, am.bankKeeper, am.stakingKeeper, am.keeper) +} diff --git a/x/photon/simulation/decoder.go b/x/photon/simulation/decoder.go new file mode 100644 index 00000000..9b52c7c2 --- /dev/null +++ b/x/photon/simulation/decoder.go @@ -0,0 +1,16 @@ +package simulation + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/kv" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding gov type. +func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + panic(fmt.Sprintf("invalid photon key prefix %X", kvA.Key[:1])) + } +} diff --git a/x/photon/simulation/genesis.go b/x/photon/simulation/genesis.go new file mode 100644 index 00000000..5786fa91 --- /dev/null +++ b/x/photon/simulation/genesis.go @@ -0,0 +1,47 @@ +package simulation + +import ( + "math/rand" + + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/atomone-hub/atomone/x/photon/types" +) + +const ( + MintDisabled = "mint_disabled" + TxFeeExceptions = "tx_fee_exceptions" +) + +// GenMintDisabled returns a randomized MintDisabled param. +func GenMintDisabled(r *rand.Rand) bool { + return r.Int63n(101) <= 15 // 15% chance of mint being disabled +} + +// GenTxFeeExceptions returns a wildcard to allow all transactions to use any +// fee denom. +// This is needed because other modules' simulations do not allow the fee coins +// to be changed, so w/o this configuration all transactions would fail. +func GenTxFeeExceptions(r *rand.Rand) []string { + return []string{"*"} +} + +// RandomizedGenState generates a random GenesisState for gov +func RandomizedGenState(simState *module.SimulationState) { + var mintDisabled bool + simState.AppParams.GetOrGenerate( + simState.Cdc, MintDisabled, &mintDisabled, simState.Rand, + func(r *rand.Rand) { mintDisabled = GenMintDisabled(r) }, + ) + var txFeeExceptions []string + simState.AppParams.GetOrGenerate( + simState.Cdc, TxFeeExceptions, &txFeeExceptions, simState.Rand, + func(r *rand.Rand) { txFeeExceptions = GenTxFeeExceptions(r) }, + ) + + photonGenesis := types.NewGenesisState( + types.NewParams(mintDisabled, txFeeExceptions), + ) + + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(photonGenesis) +} diff --git a/x/photon/simulation/operations.go b/x/photon/simulation/operations.go new file mode 100644 index 00000000..e0986ab9 --- /dev/null +++ b/x/photon/simulation/operations.go @@ -0,0 +1,89 @@ +package simulation + +import ( + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + + "github.com/atomone-hub/atomone/x/photon/keeper" + "github.com/atomone-hub/atomone/x/photon/types" +) + +// Photon message types +var ( + TypeMsgMintPhoton = sdk.MsgTypeURL(&types.MsgMintPhoton{}) +) + +// Simulation operation weights constants +// +//nolint:gosec // these are not hard-coded credentials. +const ( + OpWeightMsgMintPhoton = "op_weight_msg_mint_photon" + + DefaultWeightMsgMintPhoton = 100 +) + +// WeightedOperations returns all the operations from the module with their respective weights +func WeightedOperations(appParams simtypes.AppParams, cdc codec.JSONCodec, + ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, k keeper.Keeper, +) simulation.WeightedOperations { + var weightMsgMintPhoton int + appParams.GetOrGenerate(cdc, OpWeightMsgMintPhoton, &weightMsgMintPhoton, nil, + func(_ *rand.Rand) { + weightMsgMintPhoton = DefaultWeightMsgMintPhoton + }, + ) + + return simulation.WeightedOperations{ + simulation.NewWeightedOperation( + weightMsgMintPhoton, + SimulateMsgMintPhoton(ak, bk, sk, k), + ), + } +} + +func SimulateMsgMintPhoton( + ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, k keeper.Keeper, +) simtypes.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + // Check if mint is disabled + if k.GetParams(ctx).MintDisabled { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgMintPhoton, "mint is disabled"), nil, nil + } + toAddress, _ := simtypes.RandomAcc(r, accs) + acc := ak.GetAccount(ctx, toAddress.Address) + spendable := bk.SpendableCoins(ctx, acc.GetAddress()) + if len(spendable) == 0 { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgMintPhoton, "no spendable coins"), nil, nil + } + bondDenom := sk.BondDenom(ctx) + ok, amount := spendable.Find(bondDenom) + if !ok { + return simtypes.NoOpMsg(types.ModuleName, TypeMsgMintPhoton, "no bond denom in spendable coins"), nil, nil + } + + msg := types.NewMsgMintPhoton(toAddress.Address, amount) + txCtx := simulation.OperationInput{ + R: r, + App: app, + TxGen: moduletestutil.MakeTestEncodingConfig().TxConfig, + Cdc: nil, + Msg: msg, + MsgType: TypeMsgMintPhoton, + Context: ctx, + SimAccount: toAddress, + AccountKeeper: ak, + Bankkeeper: bk, + ModuleName: types.ModuleName, + CoinsSpentInMsg: spendable, + } + + return simulation.GenAndDeliverTxWithRandFees(txCtx) + } +} diff --git a/x/photon/simulation/proposals.go b/x/photon/simulation/proposals.go new file mode 100644 index 00000000..0533219f --- /dev/null +++ b/x/photon/simulation/proposals.go @@ -0,0 +1,43 @@ +package simulation + +import ( + "math/rand" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/address" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" + + "github.com/atomone-hub/atomone/x/photon/types" +) + +// Simulation operation weights constants +const ( + DefaultWeightMsgUpdateParams int = 100 + + OpWeightMsgUpdateParams = "op_weight_msg_update_params" //nolint:gosec +) + +// ProposalMsgs defines the module weighted proposals' contents +func ProposalMsgs() []simtypes.WeightedProposalMsg { + return []simtypes.WeightedProposalMsg{ + simulation.NewWeightedProposalMsg( + OpWeightMsgUpdateParams, + DefaultWeightMsgUpdateParams, + SimulateMsgUpdateParams, + ), + } +} + +// SimulateMsgUpdateParams returns a random MsgUpdateParams +func SimulateMsgUpdateParams(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg { + // use the default gov module account address as authority + var authority sdk.AccAddress = address.Module("gov") + + params := types.DefaultParams() + params.MintDisabled = r.Intn(2) == 0 + return &types.MsgUpdateParams{ + Authority: authority.String(), + Params: params, + } +} diff --git a/x/photon/testutil/expected_keepers_mocks.go b/x/photon/testutil/expected_keepers_mocks.go new file mode 100644 index 00000000..fe91341c --- /dev/null +++ b/x/photon/testutil/expected_keepers_mocks.go @@ -0,0 +1,194 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: x/photon/types/expected_keepers.go + +// Package testutil is a generated GoMock package. +package testutil + +import ( + reflect "reflect" + + types "github.com/cosmos/cosmos-sdk/types" + types0 "github.com/cosmos/cosmos-sdk/x/auth/types" + gomock "github.com/golang/mock/gomock" +) + +// MockAccountKeeper is a mock of AccountKeeper interface. +type MockAccountKeeper struct { + ctrl *gomock.Controller + recorder *MockAccountKeeperMockRecorder +} + +// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper. +type MockAccountKeeperMockRecorder struct { + mock *MockAccountKeeper +} + +// NewMockAccountKeeper creates a new mock instance. +func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper { + mock := &MockAccountKeeper{ctrl: ctrl} + mock.recorder = &MockAccountKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { + return m.recorder +} + +// GetAccount mocks base method. +func (m *MockAccountKeeper) GetAccount(ctx types.Context, addr types.AccAddress) types0.AccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccount", ctx, addr) + ret0, _ := ret[0].(types0.AccountI) + return ret0 +} + +// GetAccount indicates an expected call of GetAccount. +func (mr *MockAccountKeeperMockRecorder) GetAccount(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), ctx, addr) +} + +// MockStakingKeeper is a mock of StakingKeeper interface. +type MockStakingKeeper struct { + ctrl *gomock.Controller + recorder *MockStakingKeeperMockRecorder +} + +// MockStakingKeeperMockRecorder is the mock recorder for MockStakingKeeper. +type MockStakingKeeperMockRecorder struct { + mock *MockStakingKeeper +} + +// NewMockStakingKeeper creates a new mock instance. +func NewMockStakingKeeper(ctrl *gomock.Controller) *MockStakingKeeper { + mock := &MockStakingKeeper{ctrl: ctrl} + mock.recorder = &MockStakingKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperMockRecorder { + return m.recorder +} + +// BondDenom mocks base method. +func (m *MockStakingKeeper) BondDenom(arg0 types.Context) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BondDenom", arg0) + ret0, _ := ret[0].(string) + return ret0 +} + +// BondDenom indicates an expected call of BondDenom. +func (mr *MockStakingKeeperMockRecorder) BondDenom(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BondDenom", reflect.TypeOf((*MockStakingKeeper)(nil).BondDenom), arg0) +} + +// MockBankKeeper is a mock of BankKeeper interface. +type MockBankKeeper struct { + ctrl *gomock.Controller + recorder *MockBankKeeperMockRecorder +} + +// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper. +type MockBankKeeperMockRecorder struct { + mock *MockBankKeeper +} + +// NewMockBankKeeper creates a new mock instance. +func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper { + mock := &MockBankKeeper{ctrl: ctrl} + mock.recorder = &MockBankKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { + return m.recorder +} + +// BurnCoins mocks base method. +func (m *MockBankKeeper) BurnCoins(ctx types.Context, moduleName string, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BurnCoins", ctx, moduleName, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// BurnCoins indicates an expected call of BurnCoins. +func (mr *MockBankKeeperMockRecorder) BurnCoins(ctx, moduleName, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BurnCoins", reflect.TypeOf((*MockBankKeeper)(nil).BurnCoins), ctx, moduleName, amt) +} + +// GetSupply mocks base method. +func (m *MockBankKeeper) GetSupply(ctx types.Context, denom string) types.Coin { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSupply", ctx, denom) + ret0, _ := ret[0].(types.Coin) + return ret0 +} + +// GetSupply indicates an expected call of GetSupply. +func (mr *MockBankKeeperMockRecorder) GetSupply(ctx, denom interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSupply", reflect.TypeOf((*MockBankKeeper)(nil).GetSupply), ctx, denom) +} + +// MintCoins mocks base method. +func (m *MockBankKeeper) MintCoins(ctx types.Context, moduleName string, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MintCoins", ctx, moduleName, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// MintCoins indicates an expected call of MintCoins. +func (mr *MockBankKeeperMockRecorder) MintCoins(ctx, moduleName, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MintCoins", reflect.TypeOf((*MockBankKeeper)(nil).MintCoins), ctx, moduleName, amt) +} + +// SendCoinsFromAccountToModule mocks base method. +func (m *MockBankKeeper) SendCoinsFromAccountToModule(ctx types.Context, senderAddr types.AccAddress, recipientModule string, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromAccountToModule", ctx, senderAddr, recipientModule, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromAccountToModule indicates an expected call of SendCoinsFromAccountToModule. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromAccountToModule), ctx, senderAddr, recipientModule, amt) +} + +// SendCoinsFromModuleToAccount mocks base method. +func (m *MockBankKeeper) SendCoinsFromModuleToAccount(ctx types.Context, senderModule string, recipientAddr types.AccAddress, amt types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendCoinsFromModuleToAccount", ctx, senderModule, recipientAddr, amt) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendCoinsFromModuleToAccount indicates an expected call of SendCoinsFromModuleToAccount. +func (mr *MockBankKeeperMockRecorder) SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCoinsFromModuleToAccount", reflect.TypeOf((*MockBankKeeper)(nil).SendCoinsFromModuleToAccount), ctx, senderModule, recipientAddr, amt) +} + +// SpendableCoins mocks base method. +func (m *MockBankKeeper) SpendableCoins(ctx types.Context, addr types.AccAddress) types.Coins { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SpendableCoins", ctx, addr) + ret0, _ := ret[0].(types.Coins) + return ret0 +} + +// SpendableCoins indicates an expected call of SpendableCoins. +func (mr *MockBankKeeperMockRecorder) SpendableCoins(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpendableCoins", reflect.TypeOf((*MockBankKeeper)(nil).SpendableCoins), ctx, addr) +} diff --git a/x/photon/testutil/keeper.go b/x/photon/testutil/keeper.go new file mode 100644 index 00000000..c101c697 --- /dev/null +++ b/x/photon/testutil/keeper.go @@ -0,0 +1,54 @@ +package testutil + +import ( + "testing" + + "github.com/golang/mock/gomock" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmtime "github.com/cometbft/cometbft/types/time" + + "github.com/cosmos/cosmos-sdk/testutil" + 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" + + govtypes "github.com/atomone-hub/atomone/x/gov/types" + "github.com/atomone-hub/atomone/x/photon/keeper" + "github.com/atomone-hub/atomone/x/photon/types" +) + +type Mocks struct { + AccountKeeper *MockAccountKeeper + BankKeeper *MockBankKeeper + StakingKeeper *MockStakingKeeper +} + +func SetupMsgServer(t *testing.T) (types.MsgServer, *keeper.Keeper, Mocks, sdk.Context) { + t.Helper() + k, m, ctx := SetupPhotonKeeper(t) + return keeper.NewMsgServerImpl(*k), k, m, ctx +} + +func SetupPhotonKeeper(t *testing.T) ( + *keeper.Keeper, + Mocks, + sdk.Context, +) { + t.Helper() + ctrl := gomock.NewController(t) + m := Mocks{ + AccountKeeper: NewMockAccountKeeper(ctrl), + BankKeeper: NewMockBankKeeper(ctrl), + StakingKeeper: NewMockStakingKeeper(ctrl), + } + + key := sdk.NewKVStoreKey(types.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: tmtime.Now()}) + encCfg := moduletestutil.MakeTestEncodingConfig() + types.RegisterInterfaces(encCfg.InterfaceRegistry) + // banktypes.RegisterInterfaces(encCfg.InterfaceRegistry) + authority := authtypes.NewModuleAddress(govtypes.ModuleName).String() + return keeper.NewKeeper(encCfg.Codec, key, authority, m.BankKeeper, m.AccountKeeper, m.StakingKeeper), m, ctx +} diff --git a/x/photon/types/codec.go b/x/photon/types/codec.go new file mode 100644 index 00000000..9cd81f5f --- /dev/null +++ b/x/photon/types/codec.go @@ -0,0 +1,34 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + legacy.RegisterAminoMsg(cdc, &MsgMintPhoton{}, "atomone/photon/v1/MsgMintPhoton") + legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "atomone/x/photon/v1/MsgUpdateParams") + cdc.RegisterConcrete(&Params{}, "atomone/photon/v1/Params", nil) +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgMintPhoton{}, &MsgUpdateParams{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + sdk.RegisterLegacyAminoCodec(amino) +} diff --git a/x/photon/types/const.go b/x/photon/types/const.go new file mode 100644 index 00000000..2761cf36 --- /dev/null +++ b/x/photon/types/const.go @@ -0,0 +1,10 @@ +package types + +const ( + + // Denom name + Denom = "uphoton" + + // Photon max supply is 1B + MaxSupply int64 = 1_000_000_000 * 1_000_000 +) diff --git a/x/photon/types/errors.go b/x/photon/types/errors.go new file mode 100644 index 00000000..b758e0c7 --- /dev/null +++ b/x/photon/types/errors.go @@ -0,0 +1,14 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// x/photon module sentinel errors +var ( + ErrMintDisabled = sdkerrors.Register(ModuleName, 1, "photon mint disabled") //nolint:staticcheck + ErrBurnInvalidDenom = sdkerrors.Register(ModuleName, 2, "invalid burned amount denom: expected bond denom") //nolint:staticcheck + ErrZeroMintPhotons = sdkerrors.Register(ModuleName, 3, "no mintable photon after rounding, try higher burn") //nolint:staticcheck + ErrTooManyFeeCoins = sdkerrors.Register(ModuleName, 5, "too many fee coins, only accepts fees in one denom") //nolint:staticcheck + ErrInvalidFeeToken = sdkerrors.Register(ModuleName, 6, "invalid fee token") //nolint:staticcheck +) diff --git a/x/photon/types/events.go b/x/photon/types/events.go new file mode 100644 index 00000000..6e75aad2 --- /dev/null +++ b/x/photon/types/events.go @@ -0,0 +1,9 @@ +package types + +// Photon module event types +const ( + EventTypeMintPhoton = "mint_photon" + + AttributeKeyBurned = "burned" + AttributeKeyMinted = "minted" +) diff --git a/x/photon/types/expected_keepers.go b/x/photon/types/expected_keepers.go new file mode 100644 index 00000000..335be719 --- /dev/null +++ b/x/photon/types/expected_keepers.go @@ -0,0 +1,26 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// AccountKeeper defines the expected account keeper used for simulations (noalias) +type AccountKeeper interface { + GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI +} + +// StakingKeeper defines the expected staking keeper. +type StakingKeeper interface { + BondDenom(sdk.Context) string +} + +// BankKeeper defines the expected interface needed to retrieve account balances. +type BankKeeper interface { + SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + GetSupply(ctx sdk.Context, denom string) sdk.Coin +} diff --git a/x/photon/types/genesis.go b/x/photon/types/genesis.go new file mode 100644 index 00000000..420b6e5f --- /dev/null +++ b/x/photon/types/genesis.go @@ -0,0 +1,19 @@ +package types + +// NewGenesisState creates a new genesis state for the governance module +func NewGenesisState(params Params) *GenesisState { + return &GenesisState{ + Params: params, + } +} + +// DefaultGenesis returns the default genesis state +func DefaultGenesis() *GenesisState { + return NewGenesisState(DefaultParams()) +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + return gs.Params.ValidateBasic() +} diff --git a/x/photon/types/genesis.pb.go b/x/photon/types/genesis.pb.go new file mode 100644 index 00000000..c1da2dda --- /dev/null +++ b/x/photon/types/genesis.pb.go @@ -0,0 +1,323 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: atomone/photon/v1/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the x/photon module's genesis state. +type GenesisState struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_bd52513321c28864, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "atomone.photon.v1.GenesisState") +} + +func init() { proto.RegisterFile("atomone/photon/v1/genesis.proto", fileDescriptor_bd52513321c28864) } + +var fileDescriptor_bd52513321c28864 = []byte{ + // 213 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0x2c, 0xc9, 0xcf, + 0xcd, 0xcf, 0x4b, 0xd5, 0x2f, 0xc8, 0xc8, 0x2f, 0xc9, 0xcf, 0xd3, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, + 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x84, 0x2a, 0xd0, + 0x83, 0x28, 0xd0, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xea, 0x83, 0x58, + 0x10, 0x85, 0x52, 0x72, 0x98, 0x26, 0x41, 0xb5, 0x40, 0xe4, 0x05, 0x13, 0x73, 0x33, 0xf3, 0xf2, + 0xf5, 0xc1, 0x24, 0x44, 0x48, 0xc9, 0x87, 0x8b, 0xc7, 0x1d, 0x62, 0x59, 0x70, 0x49, 0x62, 0x49, + 0xaa, 0x90, 0x0d, 0x17, 0x5b, 0x41, 0x62, 0x51, 0x62, 0x6e, 0xb1, 0x04, 0xa3, 0x02, 0xa3, 0x06, + 0xb7, 0x91, 0xa4, 0x1e, 0x86, 0xe5, 0x7a, 0x01, 0x60, 0x05, 0x4e, 0x9c, 0x27, 0xee, 0xc9, 0x33, + 0xac, 0x78, 0xbe, 0x41, 0x8b, 0x31, 0x08, 0xaa, 0xc7, 0xc9, 0xfd, 0xc4, 0x23, 0x39, 0xc6, 0x0b, + 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, + 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x74, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, + 0xf5, 0xa1, 0x26, 0xea, 0x66, 0x94, 0x26, 0xc1, 0xd8, 0xfa, 0x15, 0x30, 0x37, 0x97, 0x54, 0x16, + 0xa4, 0x16, 0x27, 0xb1, 0x81, 0x5d, 0x67, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x05, 0xe6, 0x1f, + 0xfb, 0x1c, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/photon/types/genesis_test.go b/x/photon/types/genesis_test.go new file mode 100644 index 00000000..4f845ea1 --- /dev/null +++ b/x/photon/types/genesis_test.go @@ -0,0 +1,37 @@ +package types_test + +import ( + "testing" + + "github.com/atomone-hub/atomone/x/photon/types" + "github.com/stretchr/testify/require" +) + +func TestGenesisState_Validate(t *testing.T) { + tests := []struct { + desc string + genState *types.GenesisState + valid bool + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + valid: true, + }, + { + desc: "valid genesis state", + genState: &types.GenesisState{}, + valid: true, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/x/photon/types/keys.go b/x/photon/types/keys.go new file mode 100644 index 00000000..35f2e724 --- /dev/null +++ b/x/photon/types/keys.go @@ -0,0 +1,14 @@ +package types + +const ( + // ModuleName defines the module name + ModuleName = "photon" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName +) + +var ParamsKey = []byte{0x00} diff --git a/x/photon/types/msgs.go b/x/photon/types/msgs.go new file mode 100644 index 00000000..eb18dc20 --- /dev/null +++ b/x/photon/types/msgs.go @@ -0,0 +1,76 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/atomone-hub/atomone/x/gov/types" +) + +var _, _ sdk.Msg = &MsgMintPhoton{}, &MsgUpdateParams{} + +func NewMsgMintPhoton(toAddr sdk.AccAddress, amount sdk.Coin) *MsgMintPhoton { + return &MsgMintPhoton{ + ToAddress: toAddr.String(), + Amount: amount, + } +} + +func (msg *MsgMintPhoton) Route() string { + return RouterKey +} + +func (msg *MsgMintPhoton) Type() string { return sdk.MsgTypeURL(msg) } + +func (msg *MsgMintPhoton) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.ToAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgMintPhoton) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgMintPhoton) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.ToAddress); err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid toAddress: %s", err) //nolint:staticcheck + } + if err := msg.Amount.Validate(); err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "invalid coin to burn: %s", err) //nolint:staticcheck + } + if !msg.Amount.IsPositive() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "coin to burn must be positive") //nolint:staticcheck + } + return nil +} + +// Route implements the sdk.Msg interface. +func (msg MsgUpdateParams) Route() string { return types.RouterKey } + +// Type implements the sdk.Msg interface. +func (msg MsgUpdateParams) Type() string { return sdk.MsgTypeURL(&msg) } + +// ValidateBasic implements the sdk.Msg interface. +func (msg MsgUpdateParams) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Authority); err != nil { + return sdkerrors.ErrInvalidAddress.Wrapf("invalid authority address: %s", err) + } + + return msg.Params.ValidateBasic() +} + +// GetSignBytes returns the message bytes to sign over. +func (msg MsgUpdateParams) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(&msg) + return sdk.MustSortJSON(bz) +} + +// GetSigners returns the expected signers for a MsgUpdateParams. +func (msg MsgUpdateParams) GetSigners() []sdk.AccAddress { + authority, _ := sdk.AccAddressFromBech32(msg.Authority) + return []sdk.AccAddress{authority} +} diff --git a/x/photon/types/msgs_test.go b/x/photon/types/msgs_test.go new file mode 100644 index 00000000..28b7f83a --- /dev/null +++ b/x/photon/types/msgs_test.go @@ -0,0 +1,112 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + appparams "github.com/atomone-hub/atomone/app/params" +) + +func TestMsgMintPhoton_ValidateBasic(t *testing.T) { + tests := []struct { + name string + msg MsgMintPhoton + err error + }{ + { + name: "fail: invalid toAddress", + msg: MsgMintPhoton{ + ToAddress: "invalid_address", + }, + err: sdkerrors.ErrInvalidAddress, + }, + { + name: "fail: negative amount", + msg: MsgMintPhoton{ + ToAddress: sdk.AccAddress("test1").String(), + Amount: sdk.Coin{ + Denom: appparams.BondDenom, + Amount: sdk.NewInt(-1), + }, + }, + err: sdkerrors.ErrInvalidCoins, + }, + { + name: "fail: not positive amount", + msg: MsgMintPhoton{ + ToAddress: sdk.AccAddress("test1").String(), + Amount: sdk.Coin{ + Denom: appparams.BondDenom, + Amount: sdk.NewInt(0), + }, + }, + err: sdkerrors.ErrInvalidCoins, + }, + { + name: "ok", + msg: MsgMintPhoton{ + ToAddress: sdk.AccAddress("test1").String(), + Amount: sdk.NewInt64Coin(appparams.BondDenom, 1), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.msg.ValidateBasic() + if tt.err != nil { + require.ErrorIs(t, err, tt.err) + return + } + require.NoError(t, err) + }) + } +} + +func TestMsgMintPhoton_GetSignBytes(t *testing.T) { + msg := MsgMintPhoton{ + ToAddress: "my_addr", + Amount: sdk.NewInt64Coin("uatone", 1), + } + + bz := msg.GetSignBytes() + + expectedSignedBytes := `{ + "type": "atomone/photon/v1/MsgMintPhoton", + "value": { + "amount": { + "amount":"1", + "denom": "uatone" + }, + "to_address": "my_addr" + } + }` + require.JSONEq(t, expectedSignedBytes, string(bz)) +} + +func TestMsgUpdateParams_GetSignBytes(t *testing.T) { + msg := MsgUpdateParams{ + Authority: "authority", + Params: Params{ + MintDisabled: true, + TxFeeExceptions: []string{"tx1", "tx2"}, + }, + } + + bz := msg.GetSignBytes() + + expectedSignedBytes := `{ + "type": "atomone/x/photon/v1/MsgUpdateParams", + "value": { + "authority":"authority", + "params": { + "mint_disabled":true, + "tx_fee_exceptions": ["tx1","tx2"] + } + } + }` + require.JSONEq(t, expectedSignedBytes, string(bz)) +} diff --git a/x/photon/types/params.go b/x/photon/types/params.go new file mode 100644 index 00000000..a4555887 --- /dev/null +++ b/x/photon/types/params.go @@ -0,0 +1,27 @@ +package types + +// NewParams creates a new Params instance +func NewParams(mintDisabled bool, txFeeExceptions []string) Params { + return Params{ + MintDisabled: mintDisabled, + TxFeeExceptions: txFeeExceptions, + } +} + +const ( + defaultMintDisabled = false +) + +// NOTE(tb): Not possible to use `sdk.MsgTypeURL(types.MsgMintPhoton{})` +// instead of plain text because at this step the msg is not registered yet. +var defaultTxFeeExceptions = []string{"/atomone.photon.v1.MsgMintPhoton"} + +// DefaultParams returns a default set of parameters +func DefaultParams() Params { + return NewParams(defaultMintDisabled, defaultTxFeeExceptions) +} + +// Validate validates the set of params +func (p Params) ValidateBasic() error { + return nil +} diff --git a/x/photon/types/photon.pb.go b/x/photon/types/photon.pb.go new file mode 100644 index 00000000..9853d43b --- /dev/null +++ b/x/photon/types/photon.pb.go @@ -0,0 +1,369 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: atomone/photon/v1/photon.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Params defines the parameters for the x/photon module. +type Params struct { + // Allow to mint photon or not + MintDisabled bool `protobuf:"varint,1,opt,name=mint_disabled,json=mintDisabled,proto3" json:"mint_disabled,omitempty"` + // tx_fee_exceptions holds the msg type urls that are allowed to use some + // different tx fee coins than photon. + // A wildcard "*" can be used to allow all transactions to use any fee denom. + TxFeeExceptions []string `protobuf:"bytes,2,rep,name=tx_fee_exceptions,json=txFeeExceptions,proto3" json:"tx_fee_exceptions,omitempty"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_37449d2fb4799465, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetMintDisabled() bool { + if m != nil { + return m.MintDisabled + } + return false +} + +func (m *Params) GetTxFeeExceptions() []string { + if m != nil { + return m.TxFeeExceptions + } + return nil +} + +func init() { + proto.RegisterType((*Params)(nil), "atomone.photon.v1.Params") +} + +func init() { proto.RegisterFile("atomone/photon/v1/photon.proto", fileDescriptor_37449d2fb4799465) } + +var fileDescriptor_37449d2fb4799465 = []byte{ + // 212 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0x2c, 0xc9, 0xcf, + 0xcd, 0xcf, 0x4b, 0xd5, 0x2f, 0xc8, 0xc8, 0x2f, 0xc9, 0xcf, 0xd3, 0x2f, 0x33, 0x84, 0xb2, 0xf4, + 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x04, 0xa1, 0xf2, 0x7a, 0x50, 0xd1, 0x32, 0x43, 0x29, 0x91, + 0xf4, 0xfc, 0xf4, 0x7c, 0xb0, 0xac, 0x3e, 0x88, 0x05, 0x51, 0xa8, 0x14, 0xc9, 0xc5, 0x16, 0x90, + 0x58, 0x94, 0x98, 0x5b, 0x2c, 0xa4, 0xcc, 0xc5, 0x9b, 0x9b, 0x99, 0x57, 0x12, 0x9f, 0x92, 0x59, + 0x9c, 0x98, 0x94, 0x93, 0x9a, 0x22, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x11, 0xc4, 0x03, 0x12, 0x74, + 0x81, 0x8a, 0x09, 0x69, 0x71, 0x09, 0x96, 0x54, 0xc4, 0xa7, 0xa5, 0xa6, 0xc6, 0xa7, 0x56, 0x24, + 0xa7, 0x16, 0x94, 0x64, 0xe6, 0xe7, 0x15, 0x4b, 0x30, 0x29, 0x30, 0x6b, 0x70, 0x06, 0xf1, 0x97, + 0x54, 0xb8, 0xa5, 0xa6, 0xba, 0xc2, 0x85, 0x9d, 0xdc, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, + 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, + 0x58, 0x8e, 0x21, 0x4a, 0x37, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, + 0xea, 0x50, 0xdd, 0x8c, 0xd2, 0x24, 0x18, 0x5b, 0xbf, 0x02, 0xe6, 0xad, 0x92, 0xca, 0x82, 0xd4, + 0xe2, 0x24, 0x36, 0xb0, 0x53, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x3f, 0x46, 0x7e, + 0xf5, 0x00, 0x00, 0x00, +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TxFeeExceptions) > 0 { + for iNdEx := len(m.TxFeeExceptions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.TxFeeExceptions[iNdEx]) + copy(dAtA[i:], m.TxFeeExceptions[iNdEx]) + i = encodeVarintPhoton(dAtA, i, uint64(len(m.TxFeeExceptions[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if m.MintDisabled { + i-- + if m.MintDisabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintPhoton(dAtA []byte, offset int, v uint64) int { + offset -= sovPhoton(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MintDisabled { + n += 2 + } + if len(m.TxFeeExceptions) > 0 { + for _, s := range m.TxFeeExceptions { + l = len(s) + n += 1 + l + sovPhoton(uint64(l)) + } + } + return n +} + +func sovPhoton(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozPhoton(x uint64) (n int) { + return sovPhoton(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPhoton + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MintDisabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPhoton + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.MintDisabled = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxFeeExceptions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPhoton + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPhoton + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPhoton + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxFeeExceptions = append(m.TxFeeExceptions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPhoton(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPhoton + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipPhoton(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPhoton + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPhoton + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPhoton + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthPhoton + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupPhoton + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthPhoton + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthPhoton = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPhoton = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupPhoton = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/photon/types/query.pb.go b/x/photon/types/query.pb.go new file mode 100644 index 00000000..8afef2f2 --- /dev/null +++ b/x/photon/types/query.pb.go @@ -0,0 +1,878 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: atomone/photon/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryParamsRequest is request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_4cb3d9462fe75129, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // params holds all the parameters of this module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4cb3d9462fe75129, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// QueryConversionRateRequest is request type for the Query/ConversionRate RPC method. +type QueryConversionRateRequest struct { +} + +func (m *QueryConversionRateRequest) Reset() { *m = QueryConversionRateRequest{} } +func (m *QueryConversionRateRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConversionRateRequest) ProtoMessage() {} +func (*QueryConversionRateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_4cb3d9462fe75129, []int{2} +} +func (m *QueryConversionRateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConversionRateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConversionRateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConversionRateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConversionRateRequest.Merge(m, src) +} +func (m *QueryConversionRateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConversionRateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConversionRateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConversionRateRequest proto.InternalMessageInfo + +// QueryConversionRateResponse is response type for the Query/ConversionRate RPC method. +type QueryConversionRateResponse struct { + // conversion_rate represents the factor used to convert atone to photon. + ConversionRate string `protobuf:"bytes,1,opt,name=conversion_rate,json=conversionRate,proto3" json:"conversion_rate,omitempty"` +} + +func (m *QueryConversionRateResponse) Reset() { *m = QueryConversionRateResponse{} } +func (m *QueryConversionRateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConversionRateResponse) ProtoMessage() {} +func (*QueryConversionRateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4cb3d9462fe75129, []int{3} +} +func (m *QueryConversionRateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConversionRateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConversionRateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryConversionRateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConversionRateResponse.Merge(m, src) +} +func (m *QueryConversionRateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConversionRateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConversionRateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConversionRateResponse proto.InternalMessageInfo + +func (m *QueryConversionRateResponse) GetConversionRate() string { + if m != nil { + return m.ConversionRate + } + return "" +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "atomone.photon.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "atomone.photon.v1.QueryParamsResponse") + proto.RegisterType((*QueryConversionRateRequest)(nil), "atomone.photon.v1.QueryConversionRateRequest") + proto.RegisterType((*QueryConversionRateResponse)(nil), "atomone.photon.v1.QueryConversionRateResponse") +} + +func init() { proto.RegisterFile("atomone/photon/v1/query.proto", fileDescriptor_4cb3d9462fe75129) } + +var fileDescriptor_4cb3d9462fe75129 = []byte{ + // 419 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0xaa, 0xd3, 0x40, + 0x18, 0x85, 0x93, 0xa2, 0x05, 0x47, 0xa8, 0x74, 0xec, 0xc2, 0xa6, 0x35, 0x6a, 0x50, 0x91, 0x42, + 0x32, 0xa4, 0x2e, 0xdc, 0x57, 0xc1, 0x9d, 0x68, 0x16, 0x2e, 0xdc, 0x94, 0x49, 0x18, 0xd2, 0x80, + 0x99, 0x3f, 0xcd, 0x4c, 0x82, 0x75, 0xe9, 0x13, 0x08, 0x2e, 0x5d, 0xf9, 0x0e, 0x3e, 0x44, 0x97, + 0x45, 0x37, 0xae, 0x44, 0x5a, 0x1f, 0xe4, 0xd2, 0x99, 0x49, 0xb9, 0xbd, 0xcd, 0xe5, 0xde, 0x4d, + 0x69, 0xfe, 0x73, 0xe6, 0x3b, 0x67, 0xfe, 0x04, 0xdd, 0xa7, 0x12, 0x72, 0xe0, 0x8c, 0x14, 0x0b, + 0x90, 0xc0, 0x49, 0x1d, 0x92, 0x65, 0xc5, 0xca, 0x55, 0x50, 0x94, 0x20, 0x01, 0xf7, 0x8d, 0x1c, + 0x68, 0x39, 0xa8, 0x43, 0x67, 0x90, 0x42, 0x0a, 0x4a, 0x25, 0xfb, 0x7f, 0xda, 0xe8, 0x8c, 0x53, + 0x80, 0xf4, 0x23, 0x23, 0xb4, 0xc8, 0x08, 0xe5, 0x1c, 0x24, 0x95, 0x19, 0x70, 0x61, 0xd4, 0x49, + 0x02, 0x22, 0x07, 0x41, 0x62, 0x2a, 0x98, 0xe6, 0x93, 0x3a, 0x8c, 0x99, 0xa4, 0x21, 0x29, 0x68, + 0x9a, 0x71, 0x65, 0x36, 0x5e, 0xf7, 0xb4, 0x91, 0x09, 0x37, 0xfa, 0x79, 0x56, 0x43, 0x49, 0x20, + 0x6b, 0xf4, 0x3e, 0xcd, 0x33, 0x0e, 0x44, 0xfd, 0x9a, 0xd1, 0x50, 0x1f, 0x99, 0xeb, 0xd6, 0xfa, + 0x41, 0x4b, 0xde, 0x00, 0xe1, 0x77, 0xfb, 0x3e, 0x6f, 0x69, 0x49, 0x73, 0x11, 0xb1, 0x65, 0xc5, + 0x84, 0xf4, 0xde, 0xa0, 0xbb, 0x47, 0x53, 0x51, 0x00, 0x17, 0x0c, 0xbf, 0x40, 0xdd, 0x42, 0x4d, + 0xee, 0xd9, 0x0f, 0xed, 0x67, 0xb7, 0xa7, 0xc3, 0xe0, 0x64, 0x3d, 0x81, 0x3e, 0x32, 0xbb, 0xb1, + 0xfe, 0xfb, 0xc0, 0x8a, 0x8c, 0xdd, 0x1b, 0x23, 0x47, 0xf1, 0x5e, 0x02, 0xaf, 0x59, 0x29, 0x32, + 0xe0, 0x11, 0x95, 0xac, 0x49, 0x7b, 0x8f, 0x46, 0xad, 0xea, 0x21, 0xf5, 0x4e, 0x72, 0x50, 0xe6, + 0x25, 0x95, 0x4c, 0xc5, 0xdf, 0x9a, 0xf5, 0x7e, 0xfd, 0xf4, 0x91, 0xb9, 0xcd, 0x2b, 0x96, 0x44, + 0xbd, 0xe4, 0x08, 0x30, 0xfd, 0xd1, 0x41, 0x37, 0x15, 0x18, 0x7f, 0x46, 0x5d, 0xdd, 0x0b, 0x3f, + 0x69, 0xa9, 0x7c, 0xba, 0x00, 0xe7, 0xe9, 0x55, 0x36, 0xdd, 0xcd, 0x7b, 0xf4, 0xe5, 0xf7, 0xff, + 0x6f, 0x9d, 0x11, 0x1e, 0x92, 0x96, 0xb7, 0xa6, 0x13, 0xbf, 0xdb, 0xa8, 0x77, 0x7c, 0x33, 0xec, + 0x5f, 0x46, 0x6f, 0xdd, 0x8f, 0x13, 0x5c, 0xd7, 0x6e, 0x4a, 0x4d, 0x54, 0xa9, 0xc7, 0xd8, 0x6b, + 0x29, 0x75, 0x61, 0x93, 0xb3, 0xd7, 0xeb, 0xad, 0x6b, 0x6f, 0xb6, 0xae, 0xfd, 0x6f, 0xeb, 0xda, + 0x5f, 0x77, 0xae, 0xb5, 0xd9, 0xb9, 0xd6, 0x9f, 0x9d, 0x6b, 0x7d, 0xf0, 0xd3, 0x4c, 0x2e, 0xaa, + 0x38, 0x48, 0x20, 0x6f, 0x38, 0xfe, 0xa2, 0x8a, 0x0f, 0xcc, 0x4f, 0x0d, 0x55, 0xae, 0x0a, 0x26, + 0xe2, 0xae, 0xfa, 0x9e, 0x9e, 0x9f, 0x05, 0x00, 0x00, 0xff, 0xff, 0x8d, 0xce, 0xc0, 0x76, 0x51, + 0x03, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // Parameters queries the parameters of the module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // ConversionRate queries the photon's conversion rate + ConversionRate(ctx context.Context, in *QueryConversionRateRequest, opts ...grpc.CallOption) (*QueryConversionRateResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/atomone.photon.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ConversionRate(ctx context.Context, in *QueryConversionRateRequest, opts ...grpc.CallOption) (*QueryConversionRateResponse, error) { + out := new(QueryConversionRateResponse) + err := c.cc.Invoke(ctx, "/atomone.photon.v1.Query/ConversionRate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Parameters queries the parameters of the module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // ConversionRate queries the photon's conversion rate + ConversionRate(context.Context, *QueryConversionRateRequest) (*QueryConversionRateResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) ConversionRate(ctx context.Context, req *QueryConversionRateRequest) (*QueryConversionRateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConversionRate not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/atomone.photon.v1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ConversionRate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConversionRateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ConversionRate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/atomone.photon.v1.Query/ConversionRate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ConversionRate(ctx, req.(*QueryConversionRateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "atomone.photon.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "ConversionRate", + Handler: _Query_ConversionRate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "atomone/photon/v1/query.proto", +} + +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryConversionRateRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConversionRateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConversionRateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryConversionRateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryConversionRateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConversionRateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConversionRate) > 0 { + i -= len(m.ConversionRate) + copy(dAtA[i:], m.ConversionRate) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConversionRate))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryConversionRateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryConversionRateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConversionRate) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConversionRateRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConversionRateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConversionRateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConversionRateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryConversionRateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConversionRateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConversionRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConversionRate = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/photon/types/query.pb.gw.go b/x/photon/types/query.pb.gw.go new file mode 100644 index 00000000..59a26639 --- /dev/null +++ b/x/photon/types/query.pb.gw.go @@ -0,0 +1,218 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: atomone/photon/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ConversionRate_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConversionRateRequest + var metadata runtime.ServerMetadata + + msg, err := client.ConversionRate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ConversionRate_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConversionRateRequest + var metadata runtime.ServerMetadata + + msg, err := server.ConversionRate(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConversionRate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_ConversionRate_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ConversionRate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConversionRate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_ConversionRate_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ConversionRate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"atomone", "photon", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ConversionRate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"atomone", "photon", "v1", "conversion_rate"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_ConversionRate_0 = runtime.ForwardResponseMessage +) diff --git a/x/photon/types/tx.pb.go b/x/photon/types/tx.pb.go new file mode 100644 index 00000000..7bf08602 --- /dev/null +++ b/x/photon/types/tx.pb.go @@ -0,0 +1,1083 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: atomone/photon/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgMintPhoton defines an sdk.Msg for burning atone and minting photons. +type MsgMintPhoton struct { + ToAddress string `protobuf:"bytes,1,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"` + Amount types.Coin `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount"` +} + +func (m *MsgMintPhoton) Reset() { *m = MsgMintPhoton{} } +func (m *MsgMintPhoton) String() string { return proto.CompactTextString(m) } +func (*MsgMintPhoton) ProtoMessage() {} +func (*MsgMintPhoton) Descriptor() ([]byte, []int) { + return fileDescriptor_7e60927c7c01862c, []int{0} +} +func (m *MsgMintPhoton) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMintPhoton) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMintPhoton.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMintPhoton) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMintPhoton.Merge(m, src) +} +func (m *MsgMintPhoton) XXX_Size() int { + return m.Size() +} +func (m *MsgMintPhoton) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMintPhoton.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMintPhoton proto.InternalMessageInfo + +type MsgMintPhotonResponse struct { + Minted types.Coin `protobuf:"bytes,1,opt,name=minted,proto3" json:"minted"` + // conversion_rate represents the factor used to convert atone to photon. + ConversionRate string `protobuf:"bytes,2,opt,name=conversion_rate,json=conversionRate,proto3" json:"conversion_rate,omitempty"` +} + +func (m *MsgMintPhotonResponse) Reset() { *m = MsgMintPhotonResponse{} } +func (m *MsgMintPhotonResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMintPhotonResponse) ProtoMessage() {} +func (*MsgMintPhotonResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_7e60927c7c01862c, []int{1} +} +func (m *MsgMintPhotonResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMintPhotonResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMintPhotonResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMintPhotonResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMintPhotonResponse.Merge(m, src) +} +func (m *MsgMintPhotonResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMintPhotonResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMintPhotonResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMintPhotonResponse proto.InternalMessageInfo + +func (m *MsgMintPhotonResponse) GetMinted() types.Coin { + if m != nil { + return m.Minted + } + return types.Coin{} +} + +func (m *MsgMintPhotonResponse) GetConversionRate() string { + if m != nil { + return m.ConversionRate + } + return "" +} + +// MsgUpdateParams is the Msg/UpdateParams request type. +type MsgUpdateParams struct { + // authority is the address that controls the module (defaults to x/gov unless + // overwritten). + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params defines the x/gov parameters to update. + // + // NOTE: All parameters must be supplied. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } +func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParams) ProtoMessage() {} +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return fileDescriptor_7e60927c7c01862c, []int{2} +} +func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParams.Merge(m, src) +} +func (m *MsgUpdateParams) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo + +func (m *MsgUpdateParams) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgUpdateParams) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +type MsgUpdateParamsResponse struct { +} + +func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} } +func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParamsResponse) ProtoMessage() {} +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_7e60927c7c01862c, []int{3} +} +func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src) +} +func (m *MsgUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgMintPhoton)(nil), "atomone.photon.v1.MsgMintPhoton") + proto.RegisterType((*MsgMintPhotonResponse)(nil), "atomone.photon.v1.MsgMintPhotonResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "atomone.photon.v1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "atomone.photon.v1.MsgUpdateParamsResponse") +} + +func init() { proto.RegisterFile("atomone/photon/v1/tx.proto", fileDescriptor_7e60927c7c01862c) } + +var fileDescriptor_7e60927c7c01862c = []byte{ + // 522 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x31, 0x6b, 0xdb, 0x40, + 0x14, 0xd6, 0xb5, 0x34, 0xa0, 0x6b, 0x9b, 0x10, 0x91, 0x12, 0x5b, 0x83, 0x6c, 0xd4, 0xc5, 0x18, + 0xac, 0xc3, 0x29, 0x24, 0x90, 0x76, 0xa9, 0x5b, 0xe8, 0x64, 0x08, 0x2a, 0x85, 0xd2, 0xa1, 0xe6, + 0x24, 0x1f, 0xb2, 0x06, 0xdd, 0x13, 0xba, 0xb3, 0x49, 0xb6, 0xd2, 0xa9, 0x74, 0x28, 0xfd, 0x09, + 0x19, 0x3b, 0x7a, 0xc8, 0xde, 0xa1, 0x50, 0x32, 0x86, 0x4c, 0x9d, 0x4a, 0xb1, 0x07, 0xf7, 0x67, + 0x14, 0xe9, 0xce, 0x8e, 0x55, 0x1b, 0x9c, 0x45, 0x48, 0xef, 0x7b, 0xdf, 0x7b, 0xdf, 0xf7, 0xdd, + 0x09, 0xdb, 0x54, 0x42, 0x02, 0x9c, 0x91, 0x74, 0x00, 0x12, 0x38, 0x19, 0xb5, 0x89, 0x3c, 0xf5, + 0xd2, 0x0c, 0x24, 0x58, 0xbb, 0x1a, 0xf3, 0x14, 0xe6, 0x8d, 0xda, 0xf6, 0x5e, 0x04, 0x11, 0x14, + 0x28, 0xc9, 0xdf, 0x54, 0xa3, 0xed, 0x84, 0x20, 0x12, 0x10, 0x24, 0xa0, 0x82, 0x91, 0x51, 0x3b, + 0x60, 0x92, 0xb6, 0x49, 0x08, 0x31, 0xd7, 0x78, 0x55, 0xe1, 0x3d, 0x45, 0x54, 0x1f, 0x1a, 0xda, + 0xd7, 0xd4, 0x44, 0x44, 0xf9, 0xee, 0x44, 0x44, 0x1a, 0xd8, 0xa5, 0x49, 0xcc, 0x81, 0x14, 0xcf, + 0xf9, 0x9a, 0x55, 0xad, 0x5a, 0x59, 0x81, 0xbb, 0x3f, 0x10, 0x7e, 0xd8, 0x15, 0x51, 0x37, 0xe6, + 0xf2, 0xa4, 0xa8, 0x5b, 0x47, 0x18, 0x4b, 0xe8, 0xd1, 0x7e, 0x3f, 0x63, 0x42, 0x54, 0x50, 0x1d, + 0x35, 0xcc, 0x4e, 0xe5, 0xfa, 0xa2, 0xb5, 0xa7, 0x35, 0x3c, 0x57, 0xc8, 0x6b, 0x99, 0xc5, 0x3c, + 0xf2, 0x4d, 0x09, 0xba, 0x60, 0x3d, 0xc3, 0x5b, 0x34, 0x81, 0x21, 0x97, 0x95, 0x3b, 0x75, 0xd4, + 0xb8, 0x7f, 0x50, 0xf5, 0x34, 0x23, 0xb7, 0xe8, 0x69, 0x8b, 0xde, 0x0b, 0x88, 0x79, 0xc7, 0xbc, + 0xfc, 0x5d, 0x33, 0xbe, 0xcd, 0xc6, 0x4d, 0xe4, 0x6b, 0xce, 0xf1, 0xd3, 0x4f, 0xe7, 0x35, 0xe3, + 0xef, 0x79, 0xcd, 0xf8, 0x38, 0x1b, 0x37, 0x97, 0x14, 0x7c, 0x9e, 0x8d, 0x9b, 0xb5, 0x55, 0x13, + 0x25, 0xcd, 0xee, 0x17, 0x84, 0x1f, 0x95, 0x2a, 0x3e, 0x13, 0x29, 0x70, 0xc1, 0x72, 0x51, 0x49, + 0xcc, 0x25, 0xeb, 0x17, 0x4e, 0x6e, 0x2d, 0x4a, 0x71, 0xac, 0x23, 0xbc, 0x13, 0x02, 0x1f, 0xb1, + 0x4c, 0xc4, 0xc0, 0x7b, 0x19, 0x95, 0xac, 0xf0, 0x66, 0x76, 0xb6, 0xaf, 0x2f, 0x5a, 0x58, 0x4f, + 0x7a, 0xc9, 0x42, 0x7f, 0xfb, 0xa6, 0xcd, 0xa7, 0x92, 0xb9, 0xdf, 0x11, 0xde, 0xe9, 0x8a, 0xe8, + 0x4d, 0xda, 0xa7, 0x92, 0x9d, 0xd0, 0x8c, 0x26, 0xc2, 0x3a, 0xc4, 0x26, 0x1d, 0xca, 0x01, 0x64, + 0xb1, 0x3c, 0xdb, 0x9c, 0xeb, 0xa2, 0x35, 0xb7, 0x90, 0x16, 0x13, 0x16, 0xb9, 0xae, 0xdc, 0x31, + 0x4f, 0xad, 0x28, 0x59, 0x50, 0x9c, 0xe3, 0xc3, 0x3c, 0xcf, 0x9b, 0x69, 0x79, 0x9c, 0x8f, 0xe7, + 0x71, 0x9e, 0x96, 0x03, 0x5d, 0x56, 0xeb, 0x56, 0xf1, 0xfe, 0x7f, 0xa5, 0x79, 0xa6, 0x07, 0x3f, + 0x11, 0xbe, 0xdb, 0x15, 0x91, 0xf5, 0x16, 0xe3, 0xa5, 0x7b, 0x53, 0x5f, 0x23, 0xab, 0x74, 0x26, + 0x76, 0x63, 0x53, 0xc7, 0xe2, 0xd4, 0xde, 0xe3, 0x07, 0xa5, 0xe8, 0xdc, 0xf5, 0xcc, 0xe5, 0x1e, + 0xbb, 0xb9, 0xb9, 0x67, 0x3e, 0xdf, 0xbe, 0xf7, 0x21, 0xcf, 0xa8, 0xf3, 0xea, 0x72, 0xe2, 0xa0, + 0xab, 0x89, 0x83, 0xfe, 0x4c, 0x1c, 0xf4, 0x75, 0xea, 0x18, 0x57, 0x53, 0xc7, 0xf8, 0x35, 0x75, + 0x8c, 0x77, 0xad, 0x28, 0x96, 0x83, 0x61, 0xe0, 0x85, 0x90, 0x10, 0x3d, 0xb6, 0x35, 0x18, 0x06, + 0x64, 0x25, 0x39, 0x79, 0x96, 0x32, 0x11, 0x6c, 0x15, 0x3f, 0xd3, 0x93, 0x7f, 0x01, 0x00, 0x00, + 0xff, 0xff, 0x73, 0x17, 0x27, 0x31, 0x1a, 0x04, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // MintPhoton defines a method to burn atone and mint photons. + MintPhoton(ctx context.Context, in *MsgMintPhoton, opts ...grpc.CallOption) (*MsgMintPhotonResponse, error) + // UpdateParams defines a governance operation for updating the x/photon + // module parameters. The authority is defined in the keeper. + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) MintPhoton(ctx context.Context, in *MsgMintPhoton, opts ...grpc.CallOption) (*MsgMintPhotonResponse, error) { + out := new(MsgMintPhotonResponse) + err := c.cc.Invoke(ctx, "/atomone.photon.v1.Msg/MintPhoton", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/atomone.photon.v1.Msg/UpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // MintPhoton defines a method to burn atone and mint photons. + MintPhoton(context.Context, *MsgMintPhoton) (*MsgMintPhotonResponse, error) + // UpdateParams defines a governance operation for updating the x/photon + // module parameters. The authority is defined in the keeper. + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) MintPhoton(ctx context.Context, req *MsgMintPhoton) (*MsgMintPhotonResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MintPhoton not implemented") +} +func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_MintPhoton_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMintPhoton) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MintPhoton(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/atomone.photon.v1.Msg/MintPhoton", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MintPhoton(ctx, req.(*MsgMintPhoton)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/atomone.photon.v1.Msg/UpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "atomone.photon.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "MintPhoton", + Handler: _Msg_MintPhoton_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "atomone/photon/v1/tx.proto", +} + +func (m *MsgMintPhoton) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMintPhoton) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMintPhoton) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ToAddress) > 0 { + i -= len(m.ToAddress) + copy(dAtA[i:], m.ToAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ToAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgMintPhotonResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMintPhotonResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMintPhotonResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConversionRate) > 0 { + i -= len(m.ConversionRate) + copy(dAtA[i:], m.ConversionRate) + i = encodeVarintTx(dAtA, i, uint64(len(m.ConversionRate))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Minted.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgMintPhoton) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ToAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgMintPhotonResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Minted.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.ConversionRate) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgUpdateParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUpdateParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgMintPhoton) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMintPhoton: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMintPhoton: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMintPhotonResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMintPhotonResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMintPhotonResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Minted", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Minted.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConversionRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConversionRate = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/photon/types/types.go b/x/photon/types/types.go new file mode 100644 index 00000000..ab1254f4 --- /dev/null +++ b/x/photon/types/types.go @@ -0,0 +1 @@ +package types