From 3fe728d5c4d3ba2c1012fe8f3bb305c88a90d452 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Tue, 26 Nov 2024 16:52:12 +0100 Subject: [PATCH 01/10] docs: v2 upgrading instructions (#51) Relates to #44 Adapted version of https://github.com/cosmos/gaia/blob/v15.0.0/UPGRADING.md, with new section for the `minimum-gas-prices` change. Check `XXX` string for needed comments/review. [Rendered Version](https://github.com/tbruyelle/atomone/blob/tbruyelle/docs/v2-upgrading/UPGRADING.md) --- UPGRADING.md | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 266 insertions(+), 1 deletion(-) diff --git a/UPGRADING.md b/UPGRADING.md index 9e314479..27f1c56d 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,6 +1,15 @@ # 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]. + +## Release Binary + +Please use the correct release binary: `v2.0.0`. ## Go version bump @@ -18,3 +27,259 @@ 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) + - [`minimum-gas-prices` change](#minimum-gas-prices-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. + +### `minimum-gas-prices` 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" +``` + +### 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 From 8856cac2a7d5a84ce5ab8075dc0071312c43aa93 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Thu, 28 Nov 2024 16:14:20 +0100 Subject: [PATCH 02/10] docs(upgrade): add relayer config change section (#56) Relates to #44 I realized that the running relayers would also have to update their config to use PHOTON for the fees, or else all txs would be rejected. So added a section to cover that part. Also added a kind remind for validators that have `authz` transactions submitted periodically (for example validators who support services like restake) to update the tx fee denom as well. --------- Co-authored-by: Giuseppe Natale <12249307+giunatale@users.noreply.github.com> --- UPGRADING.md | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 27f1c56d..336c2644 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -7,6 +7,14 @@ 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`. @@ -43,7 +51,8 @@ $ GOROOT=$(go1.22.10 env GOROOT) PATH=$GOROOT/bin:$PATH make build - [Current runtime](#current-runtime) - [Target runtime](#target-runtime) - [Upgrade steps](#upgrade-steps) - - [`minimum-gas-prices` change](#minimum-gas-prices-change) + - [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) @@ -118,7 +127,7 @@ There are 2 major ways to upgrade a node: If you prefer to use Cosmovisor to upgrade, some preparation work is needed before upgrade. -### `minimum-gas-prices` change +### 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` @@ -135,6 +144,36 @@ Before upgrading, the setting should be changed to: 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 From a3829785f2f9d278e41e53d1df8fec3809ee626b Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Fri, 29 Nov 2024 11:23:02 +0100 Subject: [PATCH 03/10] feat(x/photon): mint photon message (#53) Relates to #44 This PR adds a new message `MsgMintPhoton` inside a new `x/photon` module. List of changes: - `x/photon` queries : `params` & `conversion_rate` - `x/photon` transaction: `MsgMintPhoton` - app plumbing with the new module - Some relative changes were made to the e2e tests to remove anything related to the `stake` denom (use `uatone` instead), which had some impact on other tests that had to be fixed. - Makefile: - target `start-localnet-ci` use `uatone` denom instead of default `stake` - new target `mockgen` to generates the mocks from gov and photon expected keepers --------- Co-authored-by: Giuseppe Natale <12249307+giunatale@users.noreply.github.com> --- Makefile | 18 +- 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 | 77 ++ x/photon/ante/ante_test.go | 293 +++++ 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 | 67 + 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 + 75 files changed, 5550 insertions(+), 180 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/Makefile b/Makefile index 4e549775..36091199 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"/' ~/.atomoned-liveness/config/app.toml ./build/atomoned start --home ~/.atomoned-liveness --x-crisis-skip-assert-invariants .PHONY: start-localnet-ci 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..a04c05d4 --- /dev/null +++ b/x/photon/ante/ante.go @@ -0,0 +1,77 @@ +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 is a gentx +// - tx mode is simulate +// - tx messages' type URLs match the `TxFeeExceptions` field of the +// [types.Params]. +// - tx has no fees or 0 fees. +func (vfd ValidateFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + if ctx.BlockHeight() == 0 || simulate { + // Skip if this is genesis height or simulate mode, because genesis and + // simulated transactions might have 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) + } + + 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 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..07fbf592 --- /dev/null +++ b/x/photon/ante/ante_test.go @@ -0,0 +1,293 @@ +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 + isGenTx bool + simulateMode bool + 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{}), + }, + }, + }, + isGenTx: false, + simulateMode: false, + }, + { + 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{}), + }, + }, + }, + isGenTx: false, + simulateMode: false, + }, + { + 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{}), + }, + }, + }, + isGenTx: false, + simulateMode: false, + }, + { + 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{}), + }, + }, + }, + isGenTx: false, + simulateMode: false, + 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{}), + }, + }, + }, + isGenTx: false, + simulateMode: false, + 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{}), + }, + }, + }, + isGenTx: false, + simulateMode: false, + expectedError: "too many fee coins, only accepts fees in one denom", + }, + { + name: "ok: MsgUpdateParams fee xxx with simulate", + 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{}), + }, + }, + }, + isGenTx: false, + simulateMode: true, + }, + { + name: "ok: MsgUpdateParams fee xxx with gentx", + 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{}), + }, + }, + }, + isGenTx: true, + simulateMode: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k, _, ctx := testutil.SetupPhotonKeeper(t) + if !tt.isGenTx { + // default block height is 0, if the tx is not a genTx, then it should + // be higher than 0. + ctx = ctx.WithBlockHeight(1) + } + 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, tt.simulateMode, 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..a65935ea --- /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/photo/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.NewProtoCodec(cdctypes.NewInterfaceRegistry()) +) + +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..4e73e040 --- /dev/null +++ b/x/photon/types/msgs_test.go @@ -0,0 +1,67 @@ +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) + }) + } +} 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 From c875ec8db3fc0626dcc46caf749df660c60e76b2 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Fri, 29 Nov 2024 11:34:58 +0100 Subject: [PATCH 04/10] docs(CL): add entry for photon --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) 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* From d9207a00efa600a173a58091c2115a6e9623a04e Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Fri, 29 Nov 2024 12:13:34 +0100 Subject: [PATCH 05/10] docs: fix typo --- UPGRADING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADING.md b/UPGRADING.md index 336c2644..d80e8ba7 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -111,7 +111,7 @@ appropriate `go` version. ### Target runtime -The AtomOne mainnet network, `atomone-1`, will run **[AtomOne v2.0.0][v2]. +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. From f242cb2757b9a242ee834e81c2ec6169adf655c7 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Fri, 29 Nov 2024 12:29:07 +0100 Subject: [PATCH 06/10] refactor(photon): remove exception from ante decorator Specifically, it's no longer needed to skip the ante decorator when the tx is a gentx or when the tx is executed in simulate mode, because the ante decorator is already skipped when the tx has no fees, which the case of gentxs and simulated tx. --- x/photon/ante/ante.go | 20 +++++--------- x/photon/ante/ante_test.go | 55 +------------------------------------- 2 files changed, 8 insertions(+), 67 deletions(-) diff --git a/x/photon/ante/ante.go b/x/photon/ante/ante.go index a04c05d4..e0d3d80f 100644 --- a/x/photon/ante/ante.go +++ b/x/photon/ante/ante.go @@ -20,22 +20,10 @@ func NewValidateFeeDecorator(k *keeper.Keeper) ValidateFeeDecorator { // AnteHandle implements the sdk.AnteDecorator interface. // It returns an error if the tx fee denom is not photon, with some exceptions: -// - tx is a gentx -// - tx mode is simulate +// - tx has no fees or 0 fees. // - tx messages' type URLs match the `TxFeeExceptions` field of the // [types.Params]. -// - tx has no fees or 0 fees. func (vfd ValidateFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - if ctx.BlockHeight() == 0 || simulate { - // Skip if this is genesis height or simulate mode, because genesis and - // simulated transactions might have 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) - } - feeTx, ok := tx.(sdk.FeeTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") //nolint:staticcheck @@ -45,6 +33,12 @@ func (vfd ValidateFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate // 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 } diff --git a/x/photon/ante/ante_test.go b/x/photon/ante/ante_test.go index 07fbf592..6f0d4c66 100644 --- a/x/photon/ante/ante_test.go +++ b/x/photon/ante/ante_test.go @@ -19,8 +19,6 @@ func TestValidateFeeDecorator(t *testing.T) { tests := []struct { name string tx sdk.Tx - isGenTx bool - simulateMode bool expectedError string }{ { @@ -35,8 +33,6 @@ func TestValidateFeeDecorator(t *testing.T) { }, }, }, - isGenTx: false, - simulateMode: false, }, { name: "ok: tx MsgMintPhoton accepts any fee denom bc declared in txFeeExceptions", @@ -55,8 +51,6 @@ func TestValidateFeeDecorator(t *testing.T) { }, }, }, - isGenTx: false, - simulateMode: false, }, { name: "ok: MsgUpdateParams fee uphoton", @@ -72,8 +66,6 @@ func TestValidateFeeDecorator(t *testing.T) { }, }, }, - isGenTx: false, - simulateMode: false, }, { name: "fail: MsgUpdateParams fee uatone", @@ -89,8 +81,6 @@ func TestValidateFeeDecorator(t *testing.T) { }, }, }, - isGenTx: false, - simulateMode: false, expectedError: "fee denom uatone not allowed: invalid fee token", }, { @@ -107,8 +97,6 @@ func TestValidateFeeDecorator(t *testing.T) { }, }, }, - isGenTx: false, - simulateMode: false, expectedError: "fee denom xxx not allowed: invalid fee token", }, { @@ -128,53 +116,12 @@ func TestValidateFeeDecorator(t *testing.T) { }, }, }, - isGenTx: false, - simulateMode: false, expectedError: "too many fee coins, only accepts fees in one denom", }, - { - name: "ok: MsgUpdateParams fee xxx with simulate", - 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{}), - }, - }, - }, - isGenTx: false, - simulateMode: true, - }, - { - name: "ok: MsgUpdateParams fee xxx with gentx", - 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{}), - }, - }, - }, - isGenTx: true, - simulateMode: false, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { k, _, ctx := testutil.SetupPhotonKeeper(t) - if !tt.isGenTx { - // default block height is 0, if the tx is not a genTx, then it should - // be higher than 0. - ctx = ctx.WithBlockHeight(1) - } k.SetParams(ctx, types.DefaultParams()) var ( nextInvoked bool @@ -185,7 +132,7 @@ func TestValidateFeeDecorator(t *testing.T) { vfd = NewValidateFeeDecorator(k) ) - _, err := vfd.AnteHandle(ctx, tt.tx, tt.simulateMode, next) + _, err := vfd.AnteHandle(ctx, tt.tx, false, next) if tt.expectedError != "" { require.EqualError(t, err, tt.expectedError) From 59e7e4fe7af42ccc8440c4bc75d281edfacfdc4d Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Mon, 16 Dec 2024 11:12:51 +0100 Subject: [PATCH 07/10] Update Makefile Co-authored-by: Albert Le Batteux --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 36091199..e687b5ca 100644 --- a/Makefile +++ b/Makefile @@ -276,7 +276,7 @@ start-localnet-ci: build ./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 = "0.001uatone,0.001uphoton"/' ~/.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 From db67e8d1bcf58651d2febd83f4212cb0a03e57a9 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Mon, 16 Dec 2024 11:24:44 +0100 Subject: [PATCH 08/10] fix makefile format --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e687b5ca..bb6901ce 100644 --- a/Makefile +++ b/Makefile @@ -276,7 +276,7 @@ start-localnet-ci: build ./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 = "0.001uatone,0.001uphoton"#g' ~/.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 From 4f3bcad74acbbc493222efe156984ae027bdf821 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Fri, 3 Jan 2025 17:13:21 +0100 Subject: [PATCH 09/10] fix(photon): amino concrete name typo --- x/photon/types/codec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/photon/types/codec.go b/x/photon/types/codec.go index a65935ea..2d2bfde8 100644 --- a/x/photon/types/codec.go +++ b/x/photon/types/codec.go @@ -12,7 +12,7 @@ import ( 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/photo/v1/Params", nil) + cdc.RegisterConcrete(&Params{}, "atomone/photon/v1/Params", nil) } func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { From 309ccbed2df1e5807cfa0d15ae3ac87f8ddef31b Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Fri, 10 Jan 2025 16:41:34 +0100 Subject: [PATCH 10/10] fix(x/photon): wrong codec for GetSignBytes --- x/photon/types/codec.go | 2 +- x/photon/types/msgs_test.go | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/x/photon/types/codec.go b/x/photon/types/codec.go index 2d2bfde8..9cd81f5f 100644 --- a/x/photon/types/codec.go +++ b/x/photon/types/codec.go @@ -24,7 +24,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { var ( amino = codec.NewLegacyAmino() - ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) + ModuleCdc = codec.NewAminoCodec(amino) ) func init() { diff --git a/x/photon/types/msgs_test.go b/x/photon/types/msgs_test.go index 4e73e040..28b7f83a 100644 --- a/x/photon/types/msgs_test.go +++ b/x/photon/types/msgs_test.go @@ -65,3 +65,48 @@ func TestMsgMintPhoton_ValidateBasic(t *testing.T) { }) } } + +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)) +}