├── .github └── workflows │ ├── build.yml │ └── golangcli-lint.yml ├── .gitignore ├── .golangci.yml ├── Makefile ├── app ├── ante.go ├── app.go ├── encoding.go ├── export.go ├── genesis.go ├── ica_sim_helper.go ├── keepers │ ├── keepers.go │ └── keys.go ├── modules.go ├── params │ ├── amino.go │ ├── doc.go │ ├── encoding.go │ ├── params.go │ ├── proto.go │ └── weights.go ├── simulation_test.go ├── test_helpers.go ├── upgrades │ ├── types.go │ ├── v2 │ │ ├── constants.go │ │ └── upgrades.go │ ├── v3 │ │ ├── constants.go │ │ └── upgrades.go │ ├── v301 │ │ ├── constants.go │ │ └── upgrades.go │ └── v303 │ │ ├── constants.go │ │ └── upgrades.go └── wasm_config.go ├── cmd ├── mund-manager │ ├── lib │ │ ├── args.go │ │ ├── process.go │ │ ├── scanner.go │ │ └── upgrade.go │ └── main.go └── mund │ ├── cmd │ ├── config.go │ ├── genaccounts.go │ └── root.go │ ├── genwasm.go │ └── main.go ├── config.yml ├── docs ├── docs.go ├── static │ └── openapi.yml └── template │ └── index.tpl ├── go.mod ├── go.sum ├── internal ├── ante │ └── min_commission.go ├── statesync │ └── snapshotter.go └── wasm │ ├── distribution.go │ ├── encoder.go │ └── registry.go ├── mupgrade.sh ├── proto ├── claim │ └── v1beta1 │ │ ├── claim_record.proto │ │ ├── genesis.proto │ │ ├── params.proto │ │ ├── query.proto │ │ └── tx.proto └── ibank │ ├── genesis.proto │ ├── params.proto │ ├── query.proto │ ├── transaction.proto │ └── tx.proto ├── readme.md ├── testutil └── nullify │ └── nullify.go ├── upgrade.md └── x ├── claim ├── abci.go ├── client │ └── cli │ │ ├── query.go │ │ ├── tx.go │ │ ├── tx_claim_for.go │ │ └── tx_update_merkle_root.go ├── handler.go ├── keeper │ ├── claim.go │ ├── genesis.go │ ├── grpc_query.go │ ├── grpc_query_merkle.go │ ├── hooks.go │ ├── keeper.go │ ├── merkle.go │ ├── msg_server.go │ ├── msg_server_claim_for.go │ ├── msg_server_update_merkle_root.go │ └── params.go ├── module.go ├── module_simulation.go ├── simulation │ ├── decoder.go │ ├── decoder_test.go │ ├── genesis.go │ ├── genesis_test.go │ ├── operations.go │ ├── params.go │ └── params_test.go └── types │ ├── claim_record.pb.go │ ├── codec.go │ ├── errors.go │ ├── events.go │ ├── expected_keepers.go │ ├── genesis.go │ ├── genesis.pb.go │ ├── genesis_test.go │ ├── keys.go │ ├── message_claim_for.go │ ├── message_update_merkle_root.go │ ├── params.go │ ├── params.pb.go │ ├── query.pb.go │ ├── query.pb.gw.go │ ├── tx.pb.go │ ├── tx_test.go │ └── types.go └── ibank ├── README.md ├── abci.go ├── client └── cli │ ├── query.go │ ├── query_params.go │ ├── query_show_incoming.go │ ├── query_show_outgoing.go │ ├── query_transaction.go │ ├── tx.go │ ├── tx_receive.go │ └── tx_send.go ├── genesis.go ├── keeper ├── grpc_query.go ├── grpc_query_params.go ├── grpc_query_show_incoming.go ├── grpc_query_show_outgoing.go ├── grpc_query_transaction.go ├── keeper.go ├── keeper_test.go ├── msg_server.go ├── msg_server_receive.go ├── msg_server_send.go ├── params.go └── transaction.go ├── module.go ├── module_simulation.go ├── simulation ├── decoder.go ├── decoder_test.go ├── genesis.go ├── genesis_test.go ├── helpers.go ├── operations.go └── params.go └── types ├── codec.go ├── errors.go ├── events.go ├── expected_keepers.go ├── genesis.go ├── genesis.pb.go ├── genesis_test.go ├── keys.go ├── message_receive.go ├── message_send.go ├── params.go ├── params.pb.go ├── query.pb.go ├── query.pb.gw.go ├── transaction.pb.go ├── tx.pb.go └── types.go /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Compile MUN 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | name: build 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Setup go 18 | uses: actions/setup-go@v3 19 | with: 20 | go-version: 1.19.0 21 | - run: make build 22 | 23 | test: 24 | runs-on: ubuntu-latest 25 | name: test 26 | steps: 27 | - name: Install Go 28 | uses: actions/setup-go@v3 29 | with: 30 | go-version: 1.19.0 31 | - name: Checkout code 32 | uses: actions/checkout@v3 33 | - name: Test 34 | run: go test ./... 35 | -------------------------------------------------------------------------------- /.github/workflows/golangcli-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: golangci-lint 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | branches: 8 | - master 9 | - main 10 | pull_request: 11 | paths: 12 | - '**.go' 13 | 14 | permissions: 15 | contents: read 16 | # Optional: allow read access to pull request. Use with `only-new-issues` option. 17 | # pull-requests: read 18 | 19 | concurrency: 20 | group: ${{ github.workflow }}-${{ github.ref }} 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | golangci: 25 | name: lint 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/setup-go@v3 29 | with: 30 | go-version: 1.19.0 31 | - uses: actions/checkout@v3 32 | 33 | - name: golangci-lint-mund 34 | uses: golangci/golangci-lint-action@v3 35 | with: 36 | version: latest 37 | args: --timeout 10m 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vue/ 2 | ts-client/ 3 | release/ 4 | build/ 5 | .idea/ 6 | .vscode/ 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | tests: false 3 | timeout: 5m 4 | 5 | linters: 6 | disable-all: true 7 | # Enable specific linter 8 | # https://golangci-lint.run/usage/linters/#enabled-by-default-linters 9 | enable: 10 | - asciicheck 11 | - bidichk 12 | # - depguard 13 | - durationcheck 14 | - errcheck 15 | - errname 16 | - exportloopref 17 | - forcetypeassert 18 | - goconst 19 | - gofmt 20 | - goimports 21 | - goheader 22 | - gomodguard 23 | - goprintffuncname 24 | - gosimple 25 | - govet 26 | - importas 27 | - ineffassign 28 | - makezero 29 | - misspell 30 | - nakedret 31 | - nilnil 32 | - paralleltest 33 | - promlinter 34 | - staticcheck 35 | - stylecheck 36 | - tenv 37 | - testpackage 38 | - typecheck 39 | - unconvert 40 | - unused 41 | - whitespace 42 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION := 3.0.3 2 | COMMIT := $(shell git log -1 --format='%H') 3 | GOPATH ?= $(shell go env GOPATH) 4 | BINDIR ?= $(GOPATH)/bin 5 | CURRENT_DIR = $(shell pwd) 6 | APP = ./app 7 | 8 | ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=mun \ 9 | -X github.com/cosmos/cosmos-sdk/version.ServerName=mund \ 10 | -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ 11 | -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) 12 | 13 | BUILD_FLAGS := -ldflags '$(ldflags)' 14 | DOCKER := $(shell which docker) 15 | DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf 16 | PROJECT_NAME = $(shell git remote get-url origin | xargs basename -s .git) 17 | 18 | all: install 19 | 20 | install: go.sum 21 | go install -mod=readonly $(BUILD_FLAGS) ./cmd/mund-manager 22 | go install -mod=readonly $(BUILD_FLAGS) ./cmd/mund 23 | 24 | build: go.sum clean 25 | go build -mod=mod $(BUILD_FLAGS) -o build/mund-manager ./cmd/mund-manager 26 | go build -mod=mod $(BUILD_FLAGS) -o build/mund ./cmd/mund 27 | 28 | build-linux: 29 | GOOS=linux GOARCH=amd64 $(MAKE) build 30 | 31 | go.sum: go.mod 32 | @echo "--> Ensure dependencies have not been modified" 33 | GO111MODULE=on go mod verify 34 | 35 | # look into .golangci.yml for enabling / disabling linters 36 | lint: 37 | @echo "--> Running linter" 38 | @golangci-lint run 39 | @go mod verify 40 | 41 | # devnet 42 | 43 | devnet: clean install devnet-prepare devnet-start 44 | 45 | devnet-prepare: 46 | ./scripts/prepare-devnet.sh 47 | 48 | devnet-start: 49 | DAEMON_NAME=diversifid DAEMON_HOME=~/.diversifid DAEMON_ALLOW_DOWNLOAD_BINARIES=true DAEMON_RESTART_AFTER_UPGRADE=true \ 50 | diversifid start --pruning="nothing" --inv-check-period 5 51 | 52 | # Clean up the build directory 53 | clean: 54 | rm -rf build/ 55 | 56 | 57 | # Localnet 58 | 59 | # Build nodes using Docker 60 | build-docker: 61 | $(MAKE) -C networks/local 62 | 63 | # Run a 4-node testnet locally 64 | localnet-start: build-linux localnet-stop 65 | @if ! [ -f build/node0/diversifid/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/diversifid:Z lottery/core testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test --chain-id test ; fi 66 | ./scripts/import-localnet-seeds.sh 67 | docker-compose up 68 | 69 | # Stop testnet 70 | localnet-stop: 71 | docker-compose down 72 | 73 | localnet: clean build-linux build-docker localnet-start 74 | 75 | ############################################################################### 76 | ### Tests & Simulation ### 77 | ############################################################################### 78 | 79 | runsim: 80 | go install github.com/cosmos/tools/cmd/runsim@latest 81 | 82 | PACKAGES_SIM=$(shell go list ./... | grep '/app') 83 | 84 | test-sim-suite: 85 | @VERSION=$(VERSION) go test -mod=readonly $(PACKAGES_SIM) 86 | 87 | test-sim-app: 88 | @VERSION=$(VERSION) go test -mod=readonly -run ^TestFullAppSimulation ./app -Enabled=true -v -NumBlocks=10 -BlockSize=200 -Commit=true -Period=0 89 | 90 | test-sim-full-app: runsim 91 | @echo "Running short multi-seed application simulation. This may take awhile!" 92 | @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(APP) -ExitOnFail 50 10 TestFullAppSimulation 93 | 94 | test-sim-multi-seed-long: runsim 95 | @echo "Running long multi-seed application simulation. This may take awhile!" 96 | @cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 500 50 TestFullAppSimulation 97 | 98 | test-sim-nondeterminism: 99 | @echo "Running non-determinism test..." 100 | go test -mod=readonly -run ^TestAppStateDeterminism ./app -Enabled=true -NumBlocks=10 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h 101 | 102 | test-sim-import-export: runsim 103 | @echo "Running application import/export simulation. This may take several minutes..." 104 | @cd ${CURRENT_DIR}/app && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 50 5 TestAppImportExport 105 | 106 | test-sim-after-import: runsim 107 | @echo "Running application simulation-after-import. This may take several minutes..." 108 | @cd ${CURRENT_DIR}/app && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 50 5 TestAppSimulationAfterImport 109 | 110 | test-sim-bench: 111 | @VERSION=$(VERSION) go test -benchmem -run ^BenchmarkFullAppSimulation -bench ^BenchmarkFullAppSimulation -cpuprofile cpu.out $(PACKAGES_SIM) 112 | 113 | 114 | ############################################################################### 115 | ### Protobuf ### 116 | ############################################################################### 117 | 118 | protoVer=v0.3 119 | protoImageName=tendermintdev/sdk-proto-gen:$(protoVer) 120 | containerProtoGen=$(PROJECT_NAME)-proto-gen-$(protoVer) 121 | containerProtoGenAny=$(PROJECT_NAME)-proto-gen-any-$(protoVer) 122 | containerProtoGenSwagger=$(PROJECT_NAME)-proto-gen-swagger-$(protoVer) 123 | containerProtoFmt=$(PROJECT_NAME)-proto-fmt-$(protoVer) 124 | 125 | proto-all: proto-format proto-lint proto-gen 126 | 127 | proto-gen: 128 | docker run --rm -v $(CURDIR):/workspace --workdir /workspace bharvest/liquidity-proto-gen sh ./scripts/protocgen.sh 129 | go mod tidy 130 | 131 | # This generates the SDK's custom wrapper for google.protobuf.Any. It should only be run manually when needed 132 | proto-gen-js: 133 | @echo "Generating Protobuf Typescript" 134 | bash ./scripts/protocgen-js.sh 135 | 136 | proto-swagger-gen: 137 | @echo "Generating Protobuf Swagger" 138 | @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoGenSwagger}$$"; then docker start -a $(containerProtoGenSwagger); else docker run --name $(containerProtoGenSwagger) -v $(CURDIR):/workspace --workdir /workspace $(protoImageName) \ 139 | sh ./scripts/protoc-swagger-gen.sh; fi 140 | 141 | proto-format: 142 | @echo "Formatting Protobuf files" 143 | @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoFmt}$$"; then docker start -a $(containerProtoFmt); else docker run --name $(containerProtoFmt) -v $(CURDIR):/workspace --workdir /workspace tendermintdev/docker-build-proto \ 144 | find ./ -not -path "./third_party/*" -name "*.proto" -exec clang-format -i {} \; ; fi 145 | 146 | proto-lint: 147 | @$(DOCKER_BUF) lint --error-format=json 148 | 149 | proto-check-breaking: 150 | @$(DOCKER_BUF) breaking --against $(HTTPS_GIT)#branch=master 151 | 152 | # Create log files 153 | log-files: 154 | sudo mkdir -p /var/log/mund && sudo touch /var/log/mund/mund.log && sudo touch /var/log/mund/mund_error.log 155 | -------------------------------------------------------------------------------- /app/ante.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 7 | "github.com/cosmos/cosmos-sdk/x/auth/ante" 8 | "github.com/cosmos/cosmos-sdk/x/authz" 9 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 10 | ibcante "github.com/cosmos/ibc-go/v4/modules/core/ante" 11 | ibckeeper "github.com/cosmos/ibc-go/v4/modules/core/keeper" 12 | 13 | wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" 14 | wasmTypes "github.com/CosmWasm/wasmd/x/wasm/types" 15 | ) 16 | 17 | // HandlerOptions extends the SDK's AnteHandler options by requiring the IBC 18 | // channel keeper. 19 | type HandlerOptions struct { 20 | ante.HandlerOptions 21 | 22 | IBCKeeper *ibckeeper.Keeper 23 | TxCounterStoreKey sdk.StoreKey 24 | WasmConfig wasmTypes.WasmConfig 25 | Cdc codec.BinaryCodec 26 | } 27 | 28 | type MinCommissionDecorator struct { 29 | cdc codec.BinaryCodec 30 | } 31 | 32 | func NewMinCommissionDecorator(cdc codec.BinaryCodec) MinCommissionDecorator { 33 | return MinCommissionDecorator{cdc} 34 | } 35 | 36 | func (min MinCommissionDecorator) AnteHandle( 37 | ctx sdk.Context, tx sdk.Tx, 38 | simulate bool, next sdk.AnteHandler, 39 | ) (newCtx sdk.Context, err error) { 40 | msgs := tx.GetMsgs() 41 | minCommissionRate := sdk.NewDecWithPrec(0, 2) 42 | 43 | validMsg := func(m sdk.Msg) error { 44 | switch msg := m.(type) { 45 | case *stakingtypes.MsgCreateValidator: 46 | // prevent new validators joining the set with 47 | // commission set below 5% 48 | c := msg.Commission 49 | if c.Rate.LT(minCommissionRate) { 50 | return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "commission can't be lower than 5%") 51 | } 52 | case *stakingtypes.MsgEditValidator: 53 | // if commission rate is nil, it means only 54 | // other fields are affected - skip 55 | if msg.CommissionRate == nil { 56 | break 57 | } 58 | if msg.CommissionRate.LT(minCommissionRate) { 59 | return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "commission can't be lower than 5%") 60 | } 61 | } 62 | 63 | return nil 64 | } 65 | 66 | validAuthz := func(execMsg *authz.MsgExec) error { 67 | for _, v := range execMsg.Msgs { 68 | var innerMsg sdk.Msg 69 | err := min.cdc.UnpackAny(v, &innerMsg) 70 | if err != nil { 71 | return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "cannot unmarshal authz exec msgs") 72 | } 73 | 74 | err = validMsg(innerMsg) 75 | if err != nil { 76 | return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid inner MSG!!!") 77 | } 78 | } 79 | 80 | return nil 81 | } 82 | 83 | for _, m := range msgs { 84 | if msg, ok := m.(*authz.MsgExec); ok { 85 | if err := validAuthz(msg); err != nil { 86 | // return ctx, err 87 | return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid Authz MSG!!!") 88 | } 89 | continue 90 | } 91 | 92 | // validate normal msgs 93 | err = validMsg(m) 94 | if err != nil { 95 | return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "invalid normal MSG!!!") 96 | } 97 | } 98 | 99 | return next(ctx, tx, simulate) 100 | } 101 | 102 | // NewAnteHandler returns an AnteHandler that checks and increments sequence 103 | // numbers, checks signatures & account numbers, and deducts fees from the first 104 | // signer. 105 | func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { 106 | if options.AccountKeeper == nil { 107 | return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for ante builder") 108 | } 109 | 110 | if options.BankKeeper == nil { 111 | return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for ante builder") 112 | } 113 | 114 | if options.SignModeHandler == nil { 115 | return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder") 116 | } 117 | 118 | sigGasConsumer := options.SigGasConsumer 119 | if sigGasConsumer == nil { 120 | sigGasConsumer = ante.DefaultSigVerificationGasConsumer 121 | } 122 | 123 | anteDecorators := []sdk.AnteDecorator{ 124 | ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first 125 | NewMinCommissionDecorator(options.Cdc), 126 | wasmkeeper.NewLimitSimulationGasDecorator(options.WasmConfig.SimulationGasLimit), 127 | wasmkeeper.NewCountTXDecorator(options.TxCounterStoreKey), 128 | ante.NewRejectExtensionOptionsDecorator(), 129 | ante.NewMempoolFeeDecorator(), 130 | ante.NewValidateBasicDecorator(), 131 | ante.NewTxTimeoutHeightDecorator(), 132 | ante.NewValidateMemoDecorator(options.AccountKeeper), 133 | ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), 134 | ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), 135 | // SetPubKeyDecorator must be called before all signature verification decorators 136 | ante.NewSetPubKeyDecorator(options.AccountKeeper), 137 | ante.NewValidateSigCountDecorator(options.AccountKeeper), 138 | ante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer), 139 | ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), 140 | ante.NewIncrementSequenceDecorator(options.AccountKeeper), 141 | ibcante.NewAnteDecorator(options.IBCKeeper), 142 | } 143 | 144 | return sdk.ChainAnteDecorators(anteDecorators...), nil 145 | } 146 | -------------------------------------------------------------------------------- /app/encoding.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "mun/app/params" 5 | 6 | "github.com/cosmos/cosmos-sdk/std" 7 | ) 8 | 9 | func MakeEncodingConfig() params.EncodingConfig { 10 | encodingConfig := params.MakeEncodingConfig() 11 | std.RegisterLegacyAminoCodec(encodingConfig.Amino) 12 | std.RegisterInterfaces(encodingConfig.InterfaceRegistry) 13 | ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) 14 | ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) 15 | return encodingConfig 16 | } 17 | -------------------------------------------------------------------------------- /app/export.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | 7 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 8 | 9 | servertypes "github.com/cosmos/cosmos-sdk/server/types" 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" 12 | "github.com/cosmos/cosmos-sdk/x/staking" 13 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 14 | ) 15 | 16 | // ExportAppStateAndValidators exports the state of the application for a genesis 17 | // file. 18 | func (app *App) ExportAppStateAndValidators( 19 | forZeroHeight bool, jailAllowedAddrs []string, 20 | ) (servertypes.ExportedApp, error) { 21 | // as if they could withdraw from the start of the next block 22 | ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) 23 | 24 | // We export at last height + 1, because that's the height at which 25 | // Tendermint will start InitChain. 26 | height := app.LastBlockHeight() + 1 27 | if forZeroHeight { 28 | height = 0 29 | app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) 30 | } 31 | 32 | genState := app.mm.ExportGenesis(ctx, app.appCodec) 33 | appState, err := json.MarshalIndent(genState, "", " ") 34 | if err != nil { 35 | return servertypes.ExportedApp{}, err 36 | } 37 | 38 | validators, err := staking.WriteValidators(ctx, app.StakingKeeper) 39 | if err != nil { 40 | return servertypes.ExportedApp{}, err 41 | } 42 | return servertypes.ExportedApp{ 43 | AppState: appState, 44 | Validators: validators, 45 | Height: height, 46 | ConsensusParams: app.BaseApp.GetConsensusParams(ctx), 47 | }, nil 48 | } 49 | 50 | // prepare for fresh start at zero height 51 | // NOTE zero height genesis is a temporary feature which will be deprecated 52 | // 53 | // in favour of export at a block height 54 | func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { 55 | applyAllowedAddrs := false 56 | 57 | // check if there is a allowed address list 58 | if len(jailAllowedAddrs) > 0 { 59 | applyAllowedAddrs = true 60 | } 61 | 62 | allowedAddrsMap := make(map[string]bool) 63 | 64 | for _, addr := range jailAllowedAddrs { 65 | _, err := sdk.ValAddressFromBech32(addr) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | allowedAddrsMap[addr] = true 70 | } 71 | 72 | /* Just to be safe, assert the invariants on current state. */ 73 | app.CrisisKeeper.AssertInvariants(ctx) 74 | 75 | /* Handle fee distribution state. */ 76 | 77 | // withdraw all validator commission 78 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { 79 | _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) 80 | return false 81 | }) 82 | 83 | // withdraw all delegator rewards 84 | dels := app.StakingKeeper.GetAllDelegations(ctx) 85 | for _, delegation := range dels { 86 | _, err := app.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.GetDelegatorAddr(), delegation.GetValidatorAddr()) 87 | if err != nil { 88 | panic(err) 89 | } 90 | } 91 | 92 | // clear validator slash events 93 | app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) 94 | 95 | // clear validator historical rewards 96 | app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) 97 | 98 | // set context height to zero 99 | height := ctx.BlockHeight() 100 | ctx = ctx.WithBlockHeight(0) 101 | 102 | // reinitialize all validators 103 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { 104 | // donate any unwithdrawn outstanding reward fraction tokens to the community pool 105 | scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) 106 | feePool := app.DistrKeeper.GetFeePool(ctx) 107 | feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) 108 | app.DistrKeeper.SetFeePool(ctx, feePool) 109 | 110 | app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) 111 | return false 112 | }) 113 | 114 | // reinitialize all delegations 115 | for _, del := range dels { 116 | app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.GetDelegatorAddr(), del.GetValidatorAddr()) 117 | app.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.GetDelegatorAddr(), del.GetValidatorAddr()) 118 | } 119 | 120 | // reset context height 121 | ctx = ctx.WithBlockHeight(height) 122 | 123 | /* Handle staking state. */ 124 | 125 | // iterate through redelegations, reset creation height 126 | app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { 127 | for i := range red.Entries { 128 | red.Entries[i].CreationHeight = 0 129 | } 130 | app.StakingKeeper.SetRedelegation(ctx, red) 131 | return false 132 | }) 133 | 134 | // iterate through unbonding delegations, reset creation height 135 | app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { 136 | for i := range ubd.Entries { 137 | ubd.Entries[i].CreationHeight = 0 138 | } 139 | app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) 140 | return false 141 | }) 142 | 143 | // Iterate through validators by power descending, reset bond heights, and 144 | // update bond intra-tx counters. 145 | store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey)) 146 | iter := sdk.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) 147 | counter := int16(0) 148 | 149 | for ; iter.Valid(); iter.Next() { 150 | addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) 151 | validator, found := app.StakingKeeper.GetValidator(ctx, addr) 152 | if !found { 153 | panic("expected validator, not found") 154 | } 155 | 156 | validator.UnbondingHeight = 0 157 | if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { 158 | validator.Jailed = true 159 | } 160 | 161 | app.StakingKeeper.SetValidator(ctx, validator) 162 | counter++ 163 | } 164 | 165 | iter.Close() 166 | 167 | if _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx); err != nil { 168 | panic(err) 169 | } 170 | 171 | /* Handle slashing state. */ 172 | 173 | // reset start height on signing infos 174 | app.SlashingKeeper.IterateValidatorSigningInfos( 175 | ctx, 176 | func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { 177 | info.StartHeight = 0 178 | app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info) 179 | return false 180 | }, 181 | ) 182 | } 183 | -------------------------------------------------------------------------------- /app/genesis.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | ) 8 | 9 | // The genesis state of the blockchain is represented here as a map of raw json 10 | // messages key'd by a identifier string. 11 | // The identifier is used to determine which module genesis information belongs 12 | // to so it may be appropriately routed during init chain. 13 | // Within this application default genesis information is retrieved from 14 | // the ModuleBasicManager which populates json from each BasicModule 15 | // object provided to it during init. 16 | type GenesisState map[string]json.RawMessage 17 | 18 | // NewDefaultGenesisState generates the default state for the application. 19 | func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState { 20 | return ModuleBasics.DefaultGenesis(cdc) 21 | } 22 | -------------------------------------------------------------------------------- /app/ica_sim_helper.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math/rand" 7 | 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | "github.com/cosmos/cosmos-sdk/types/module" 11 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 12 | ica "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts" 13 | icatypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/types" 14 | ) 15 | 16 | type ICAHostSimModule struct { 17 | ica.AppModule 18 | ica.AppModuleBasic 19 | cdc codec.Codec 20 | } 21 | 22 | var ( 23 | _ module.AppModule = ICAHostSimModule{} 24 | _ module.AppModuleBasic = ICAHostSimModule{} 25 | _ module.AppModuleSimulation = ICAHostSimModule{} 26 | ) 27 | 28 | func NewICAHostSimModule(baseModule ica.AppModule, cdc codec.Codec) ICAHostSimModule { 29 | return ICAHostSimModule{ 30 | cdc: cdc, 31 | AppModule: baseModule, 32 | AppModuleBasic: baseModule.AppModuleBasic, 33 | } 34 | } 35 | 36 | // ICAHostSimModuleSimulation functions 37 | // This thing does the bare minimum to avoid simulation panic-ing for missing state data. 38 | 39 | // GenerateGenesisState creates a randomized GenState of the ica module. 40 | func (i ICAHostSimModule) GenerateGenesisState(simState *module.SimulationState) { 41 | genesis := icatypes.DefaultGenesis() 42 | 43 | bz, err := json.MarshalIndent(&genesis, "", " ") 44 | if err != nil { 45 | panic(err) 46 | } 47 | fmt.Printf("Selected randomly generated %s parameters:\n%s\n", icatypes.ModuleName, bz) 48 | simState.GenState[icatypes.ModuleName] = simState.Cdc.MustMarshalJSON(genesis) 49 | } 50 | 51 | // ProposalContents returns all the ica content functions used to 52 | // simulate governance proposals. 53 | func (ICAHostSimModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { 54 | return nil 55 | } 56 | 57 | // RandomizedParams creates randomized ica param changes for the simulator. 58 | func (ICAHostSimModule) RandomizedParams(*rand.Rand) []simtypes.ParamChange { 59 | return nil 60 | } 61 | 62 | // RegisterStoreDecoder registers a decoder for ica module's types 63 | func (ICAHostSimModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { 64 | } 65 | 66 | // WeightedOperations returns the all the gov module operations with their respective weights. 67 | func (ICAHostSimModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /app/keepers/keys.go: -------------------------------------------------------------------------------- 1 | package keepers 2 | 3 | import ( 4 | "github.com/CosmWasm/wasmd/x/wasm" 5 | "github.com/cosmos/cosmos-sdk/x/authz" 6 | "github.com/cosmos/cosmos-sdk/x/feegrant" 7 | packetforwardtypes "github.com/strangelove-ventures/packet-forward-middleware/v4/router/types" 8 | 9 | storetypes "github.com/cosmos/cosmos-sdk/store/types" 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 12 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 13 | capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" 14 | distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" 15 | evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" 16 | govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" 17 | minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" 18 | paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" 19 | slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" 20 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 21 | upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" 22 | icacontrollertypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" 23 | icahosttypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/host/types" 24 | ibctransfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types" 25 | ibcfeetypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" 26 | ibchost "github.com/cosmos/ibc-go/v4/modules/core/24-host" 27 | 28 | claimmoduletypes "mun/x/claim/types" 29 | 30 | ibankmoduletypes "mun/x/ibank/types" 31 | ) 32 | 33 | func newKVStoreKeys() map[string]*storetypes.KVStoreKey { 34 | return sdk.NewKVStoreKeys( 35 | authtypes.StoreKey, authz.ModuleName, banktypes.StoreKey, stakingtypes.StoreKey, 36 | minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, 37 | govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, 38 | evidencetypes.StoreKey, ibctransfertypes.StoreKey, icahosttypes.StoreKey, capabilitytypes.StoreKey, 39 | ibcfeetypes.StoreKey, packetforwardtypes.StoreKey, icacontrollertypes.StoreKey, 40 | wasm.StoreKey, claimmoduletypes.StoreKey, ibankmoduletypes.StoreKey, 41 | // this line is used by starport scaffolding # stargate/app/storeKey 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /app/params/amino.go: -------------------------------------------------------------------------------- 1 | //go:build test_amino 2 | // +build test_amino 3 | 4 | package params 5 | 6 | import ( 7 | "github.com/cosmos/cosmos-sdk/codec" 8 | "github.com/cosmos/cosmos-sdk/codec/types" 9 | "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" 10 | ) 11 | 12 | // MakeTestEncodingConfig creates an EncodingConfig for an amino based test configuration. 13 | // This function should be used only internally (in the SDK). 14 | // App user shouldn't create new codecs - use the app.AppCodec instead. 15 | // [DEPRECATED] 16 | func MakeTestEncodingConfig() EncodingConfig { 17 | cdc := codec.NewLegacyAmino() 18 | interfaceRegistry := types.NewInterfaceRegistry() 19 | marshaler := codec.NewAminoCodec(cdc) 20 | 21 | return EncodingConfig{ 22 | InterfaceRegistry: interfaceRegistry, 23 | Marshaler: marshaler, 24 | TxConfig: legacytx.StdTxConfig{Cdc: cdc}, 25 | Amino: cdc, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/params/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package params defines the simulation parameters in the simapp. 3 | 4 | It contains the default weights used for each transaction used on the module's 5 | simulation. These weights define the chance for a transaction to be simulated at 6 | any gived operation. 7 | 8 | You can repace the default values for the weights by providing a params.json 9 | file with the weights defined for each of the transaction operations: 10 | 11 | { 12 | "op_weight_msg_send": 60, 13 | "op_weight_msg_delegate": 100, 14 | } 15 | 16 | In the example above, the `MsgSend` has 60% chance to be simulated, while the 17 | `MsgDelegate` will always be simulated. 18 | */ 19 | package params 20 | -------------------------------------------------------------------------------- /app/params/encoding.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/client" 5 | "github.com/cosmos/cosmos-sdk/codec" 6 | "github.com/cosmos/cosmos-sdk/codec/types" 7 | ) 8 | 9 | // EncodingConfig specifies the concrete encoding types to use for a given app. 10 | // This is provided for compatibility between protobuf and amino implementations. 11 | type EncodingConfig struct { 12 | InterfaceRegistry types.InterfaceRegistry 13 | // NOTE: this field will be renamed to Codec 14 | Marshaler codec.Codec 15 | TxConfig client.TxConfig 16 | Amino *codec.LegacyAmino 17 | } 18 | -------------------------------------------------------------------------------- /app/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Simulation parameter constants 4 | const ( 5 | StakePerAccount = "stake_per_account" 6 | InitiallyBondedValidators = "initially_bonded_validators" 7 | ) 8 | -------------------------------------------------------------------------------- /app/params/proto.go: -------------------------------------------------------------------------------- 1 | //go:build !test_amino 2 | // +build !test_amino 3 | 4 | package params 5 | 6 | import ( 7 | "github.com/cosmos/cosmos-sdk/codec" 8 | "github.com/cosmos/cosmos-sdk/codec/types" 9 | "github.com/cosmos/cosmos-sdk/x/auth/tx" 10 | ) 11 | 12 | func MakeEncodingConfig() EncodingConfig { 13 | cdc := codec.NewLegacyAmino() 14 | interfaceRegistry := types.NewInterfaceRegistry() 15 | marshaler := codec.NewProtoCodec(interfaceRegistry) 16 | 17 | return EncodingConfig{ 18 | InterfaceRegistry: interfaceRegistry, 19 | Marshaler: marshaler, 20 | TxConfig: tx.NewTxConfig(marshaler, tx.DefaultSignModes), 21 | Amino: cdc, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/params/weights.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Default simulation operation weights for messages and gov proposals. 4 | const ( 5 | DefaultWeightMsgCreateFixedAmountPlan int = 10 6 | DefaultWeightMsgCreateRatioPlan int = 10 7 | DefaultWeightMsgStake int = 85 8 | DefaultWeightMsgUnstake int = 20 9 | DefaultWeightMsgHarvest int = 30 10 | DefaultWeightMsgRemovePlan int = 10 11 | 12 | DefaultWeightMsgCreatePair int = 10 13 | DefaultWeightMsgCreatePool int = 15 14 | DefaultWeightMsgCreateRangedPool int = 20 15 | DefaultWeightMsgDeposit int = 20 16 | DefaultWeightMsgWithdraw int = 20 17 | DefaultWeightMsgLimitOrder int = 80 18 | DefaultWeightMsgMarketOrder int = 60 19 | DefaultWeightMsgCancelOrder int = 20 20 | DefaultWeightMsgCancelAllOrders int = 20 21 | 22 | DefaultWeightAddPublicPlanProposal int = 5 23 | DefaultWeightUpdatePublicPlanProposal int = 5 24 | DefaultWeightDeletePublicPlanProposal int = 5 25 | 26 | DefaultWeightMsgLiquidStake int = 80 27 | DefaultWeightMsgLiquidUnstake int = 30 28 | 29 | DefaultWeightAddWhitelistValidatorsProposal int = 50 30 | DefaultWeightUpdateWhitelistValidatorsProposal int = 5 31 | DefaultWeightDeleteWhitelistValidatorsProposal int = 5 32 | DefaultWeightCompleteRedelegationUnbonding int = 30 33 | DefaultWeightTallyWithLiquidStaking int = 30 34 | 35 | DefaultWeightMsgClaim int = 50 36 | ) 37 | -------------------------------------------------------------------------------- /app/test_helpers.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | "github.com/cosmos/cosmos-sdk/simapp" 8 | abci "github.com/tendermint/tendermint/abci/types" 9 | "github.com/tendermint/tendermint/libs/log" 10 | dbm "github.com/tendermint/tm-db" 11 | ) 12 | 13 | var defaultGenesisBz []byte 14 | 15 | func getDefaultGenesisStateBytes(cdc codec.JSONCodec) []byte { 16 | if len(defaultGenesisBz) == 0 { 17 | genesisState := NewDefaultGenesisState(cdc) 18 | stateBytes, err := json.MarshalIndent(genesisState, "", " ") 19 | if err != nil { 20 | panic(err) 21 | } 22 | defaultGenesisBz = stateBytes 23 | } 24 | return defaultGenesisBz 25 | } 26 | 27 | func Setup() *App { 28 | db := dbm.NewMemDB() 29 | encConfig := MakeEncodingConfig() 30 | app := NewMunApp( 31 | log.NewNopLogger(), 32 | db, 33 | nil, 34 | true, 35 | map[int64]bool{}, 36 | DefaultNodeHome, 37 | 0, 38 | encConfig, 39 | simapp.EmptyAppOptions{}, 40 | ) 41 | 42 | stateBytes := getDefaultGenesisStateBytes(encConfig.Marshaler) 43 | app.InitChain(abci.RequestInitChain{ 44 | Validators: []abci.ValidatorUpdate{}, 45 | ConsensusParams: simapp.DefaultConsensusParams, 46 | AppStateBytes: stateBytes, 47 | }) 48 | 49 | return app 50 | } 51 | -------------------------------------------------------------------------------- /app/upgrades/types.go: -------------------------------------------------------------------------------- 1 | package upgrade 2 | 3 | import ( 4 | "mun/app/keepers" 5 | 6 | store "github.com/cosmos/cosmos-sdk/store/types" 7 | "github.com/cosmos/cosmos-sdk/types/module" 8 | upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" 9 | ) 10 | 11 | // Upgrade defines a struct containing necessary fields that a SoftwareUpgradeProposal 12 | // must have written, in order for the state migration to go smoothly. 13 | // An upgrade must implement this struct, and then set it in the app.go. 14 | // The app.go will then define the handler. 15 | type UpgradeMun struct { 16 | // Upgrade version name, for the upgrade handler, e.g. `mun_v2` 17 | UpgradeName string 18 | 19 | // CreateUpgradeHandler defines the function that creates an upgrade handler 20 | CreateUpgradeHandler func(*module.Manager, module.Configurator, *keepers.AppKeepers) upgradetypes.UpgradeHandler 21 | 22 | // Store upgrades, should be used for any new modules introduced, new modules deleted, or store names renamed. 23 | StoreUpgrades store.StoreUpgrades 24 | } 25 | -------------------------------------------------------------------------------- /app/upgrades/v2/constants.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | // "mun/app/upgrades" 5 | upgrade "mun/app/upgrades" 6 | 7 | store "github.com/cosmos/cosmos-sdk/store/types" 8 | ) 9 | 10 | const ( 11 | // UpgradeName defines the on-chain upgrade name. 12 | UpgradeName = "mun-upgrade-v2" 13 | ) 14 | 15 | var Upgrade = upgrade.UpgradeMun{ 16 | UpgradeName: UpgradeName, 17 | CreateUpgradeHandler: CreateUpgradeHandler, 18 | StoreUpgrades: store.StoreUpgrades{ 19 | Added: []string{}, 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /app/upgrades/v2/upgrades.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "mun/app/keepers" 5 | "time" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "github.com/cosmos/cosmos-sdk/types/module" 9 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 10 | upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" 11 | ) 12 | 13 | func CreateUpgradeHandler( 14 | mm *module.Manager, 15 | configurator module.Configurator, 16 | keepers *keepers.AppKeepers, 17 | ) upgradetypes.UpgradeHandler { 18 | return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { 19 | ctx.Logger().Info("start to run module migrations...") 20 | 21 | unbondingTime, _ := time.ParseDuration("1814400s") 22 | stakingParams := stakingtypes.Params{ 23 | UnbondingTime: unbondingTime, 24 | MaxValidators: 150, 25 | MaxEntries: 7, 26 | HistoricalEntries: 10000, 27 | BondDenom: "utmun", 28 | } 29 | 30 | keepers.StakingKeeper.SetParams(ctx, stakingParams) 31 | return mm.RunMigrations(ctx, configurator, vm) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/upgrades/v3/constants.go: -------------------------------------------------------------------------------- 1 | package v3 2 | 3 | import ( 4 | // "mun/app/upgrades" 5 | upgrade "mun/app/upgrades" 6 | 7 | store "github.com/cosmos/cosmos-sdk/store/types" 8 | ) 9 | 10 | const ( 11 | // UpgradeName defines the on-chain upgrade name. 12 | UpgradeName = "mun-upgrade-v3" 13 | ) 14 | 15 | var Upgrade = upgrade.UpgradeMun{ 16 | UpgradeName: UpgradeName, 17 | CreateUpgradeHandler: CreateUpgradeHandler, 18 | StoreUpgrades: store.StoreUpgrades{ 19 | Added: []string{}, 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /app/upgrades/v3/upgrades.go: -------------------------------------------------------------------------------- 1 | package v3 2 | 3 | import ( 4 | "mun/app/keepers" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/cosmos/cosmos-sdk/types/module" 8 | upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" 9 | ) 10 | 11 | func CreateUpgradeHandler( 12 | mm *module.Manager, 13 | configurator module.Configurator, 14 | keepers *keepers.AppKeepers, 15 | ) upgradetypes.UpgradeHandler { 16 | return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { 17 | return mm.RunMigrations(ctx, configurator, vm) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/upgrades/v301/constants.go: -------------------------------------------------------------------------------- 1 | package v301 2 | 3 | import ( 4 | upgrade "mun/app/upgrades" 5 | ibankmoduletypes "mun/x/ibank/types" 6 | 7 | store "github.com/cosmos/cosmos-sdk/store/types" 8 | icacontrollertypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" 9 | ibcfeetypes "github.com/cosmos/ibc-go/v4/modules/core/05-port/types" 10 | packetforwardtypes "github.com/strangelove-ventures/packet-forward-middleware/v4/router/types" 11 | ) 12 | 13 | const ( 14 | // UpgradeName defines the on-chain upgrade name. 15 | UpgradeName = "mun-upgrade-v3.0.1" 16 | monitoringpStoreKey = "monitoringp" 17 | ) 18 | 19 | var Upgrade = upgrade.UpgradeMun{ 20 | UpgradeName: UpgradeName, 21 | CreateUpgradeHandler: CreateUpgradeHandler, 22 | StoreUpgrades: store.StoreUpgrades{ 23 | Added: []string{packetforwardtypes.ModuleName, icacontrollertypes.SubModuleName, ibcfeetypes.SubModuleName, ibankmoduletypes.ModuleName}, 24 | Deleted: []string{monitoringpStoreKey}, 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /app/upgrades/v301/upgrades.go: -------------------------------------------------------------------------------- 1 | package v301 2 | 3 | import ( 4 | "mun/app/keepers" 5 | 6 | "github.com/CosmWasm/wasmd/x/wasm" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "github.com/cosmos/cosmos-sdk/types/module" 9 | upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" 10 | icacontrollertypes "github.com/cosmos/ibc-go/v4/modules/apps/27-interchain-accounts/controller/types" 11 | ) 12 | 13 | func CreateUpgradeHandler( 14 | mm *module.Manager, 15 | configurator module.Configurator, 16 | keepers *keepers.AppKeepers, 17 | ) upgradetypes.UpgradeHandler { 18 | return func(ctx sdk.Context, plan upgradetypes.Plan, fromVm module.VersionMap) (module.VersionMap, error) { 19 | fromVm[wasm.ModuleName] = 1 20 | 21 | sb, ok := keepers.ParamsKeeper.GetSubspace(icacontrollertypes.SubModuleName) 22 | if !ok { 23 | panic("ica controller sub space does not exist") 24 | } 25 | 26 | sb.Set(ctx, icacontrollertypes.KeyControllerEnabled, true) 27 | defaultParams := icacontrollertypes.DefaultParams() 28 | sb.SetParamSet(ctx, &defaultParams) 29 | 30 | return mm.RunMigrations(ctx, configurator, fromVm) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/upgrades/v303/constants.go: -------------------------------------------------------------------------------- 1 | package v303 2 | 3 | import ( 4 | upgrade "mun/app/upgrades" 5 | 6 | store "github.com/cosmos/cosmos-sdk/store/types" 7 | ) 8 | 9 | const ( 10 | // UpgradeName defines the on-chain upgrade name. 11 | UpgradeName = "mun-upgrade-v3.0.3" 12 | allocStoreKey = "alloc" 13 | ) 14 | 15 | var Upgrade = upgrade.UpgradeMun{ 16 | UpgradeName: UpgradeName, 17 | CreateUpgradeHandler: CreateUpgradeHandler, 18 | StoreUpgrades: store.StoreUpgrades{ 19 | Added: []string{}, 20 | Deleted: []string{allocStoreKey}, 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /app/upgrades/v303/upgrades.go: -------------------------------------------------------------------------------- 1 | package v303 2 | 3 | import ( 4 | "mun/app/keepers" 5 | 6 | ibanktypes "mun/x/ibank/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "github.com/cosmos/cosmos-sdk/types/module" 10 | upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" 11 | ) 12 | 13 | const ( 14 | MerkleRoot = "4567efe1ffef2087b5ab30112753e7088bb509ce1955e79744ff44db4ab8790d" 15 | ) 16 | 17 | func CreateUpgradeHandler( 18 | mm *module.Manager, 19 | configurator module.Configurator, 20 | keepers *keepers.AppKeepers, 21 | ) upgradetypes.UpgradeHandler { 22 | return func(ctx sdk.Context, plan upgradetypes.Plan, fromVm module.VersionMap) (module.VersionMap, error) { 23 | keepers.ClaimKeeper.SetMerkleRoot(ctx, MerkleRoot) 24 | keepers.IbankKeeper.SetParams(ctx, ibanktypes.DefaultParams()) 25 | 26 | return mm.RunMigrations(ctx, configurator, fromVm) 27 | } 28 | } 29 | 30 | func ManualUpgrade(ctx sdk.Context, keepers *keepers.AppKeepers) { 31 | keepers.ClaimKeeper.SetMerkleRoot(ctx, MerkleRoot) 32 | keepers.IbankKeeper.SetParams(ctx, ibanktypes.DefaultParams()) 33 | } 34 | -------------------------------------------------------------------------------- /app/wasm_config.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" 5 | ) 6 | 7 | const ( 8 | // DefaultMunInstanceCost is initially set the same as in wasmd 9 | DefaultMunInstanceCost uint64 = 60_000 10 | // DefaultMunCompileCost set to a large number for testing 11 | DefaultMunCompileCost uint64 = 100 12 | ) 13 | 14 | // MunGasRegisterConfig is defaults plus a custom compile amount 15 | func MunGasRegisterConfig() wasmkeeper.WasmGasRegisterConfig { 16 | gasConfig := wasmkeeper.DefaultGasRegisterConfig() 17 | gasConfig.InstanceCost = DefaultMunInstanceCost 18 | gasConfig.CompileCost = DefaultMunCompileCost 19 | 20 | return gasConfig 21 | } 22 | 23 | func NewMunWasmGasRegister() wasmkeeper.WasmGasRegister { 24 | return wasmkeeper.NewWasmGasRegister(MunGasRegisterConfig()) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/mund-manager/lib/args.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "net/url" 8 | "os" 9 | "path/filepath" 10 | "strconv" 11 | ) 12 | 13 | const ( 14 | rootName = "upgrade_manager" 15 | genesisDir = "genesis" 16 | upgradesDir = "upgrades" 17 | currentLink = "current" 18 | ) 19 | 20 | // Config is the information passed in to control the daemon 21 | type Config struct { 22 | Home string 23 | Name string 24 | AllowDownloadBinaries bool 25 | RestartAfterUpgrade bool 26 | LogBufferSize int 27 | } 28 | 29 | // Root returns the root directory where all info lives 30 | func (cfg *Config) Root() string { 31 | return filepath.Join(cfg.Home, rootName) 32 | } 33 | 34 | // GenesisBin is the path to the genesis binary - must be in place to start manager 35 | func (cfg *Config) GenesisBin() string { 36 | return filepath.Join(cfg.Root(), genesisDir, "bin", cfg.Name) 37 | } 38 | 39 | // UpgradeBin is the path to the binary for the named upgrade 40 | func (cfg *Config) UpgradeBin(upgradeName string) string { 41 | return filepath.Join(cfg.UpgradeDir(upgradeName), "bin", cfg.Name) 42 | } 43 | 44 | // UpgradeDir is the directory named upgrade 45 | func (cfg *Config) UpgradeDir(upgradeName string) string { 46 | safeName := url.PathEscape(upgradeName) 47 | return filepath.Join(cfg.Root(), upgradesDir, safeName) 48 | } 49 | 50 | // Symlink to genesis 51 | func (cfg *Config) SymLinkToGenesis() (string, error) { 52 | genesis := filepath.Join(cfg.Root(), genesisDir) 53 | link := filepath.Join(cfg.Root(), currentLink) 54 | 55 | if err := os.Symlink(genesis, link); err != nil { 56 | return "", err 57 | } 58 | // and return the genesis binary 59 | return cfg.GenesisBin(), nil 60 | } 61 | 62 | // CurrentBin is the path to the currently selected binary (genesis if no link is set) 63 | // This will resolve the symlink to the underlying directory to make it easier to debug 64 | func (cfg *Config) CurrentBin() (string, error) { 65 | cur := filepath.Join(cfg.Root(), currentLink) 66 | // if nothing here, fallback to genesis 67 | info, err := os.Lstat(cur) 68 | if err != nil { 69 | //Create symlink to the genesis 70 | return cfg.SymLinkToGenesis() 71 | } 72 | // if it is there, ensure it is a symlink 73 | if info.Mode()&os.ModeSymlink == 0 { 74 | //Create symlink to the genesis 75 | return cfg.SymLinkToGenesis() 76 | } 77 | 78 | // resolve it 79 | dest, err := os.Readlink(cur) 80 | if err != nil { 81 | //Create symlink to the genesis 82 | return cfg.SymLinkToGenesis() 83 | } 84 | 85 | // and return the binary 86 | return filepath.Join(dest, "bin", cfg.Name), nil 87 | } 88 | 89 | // GetConfigFromEnv will read the environmental variables into a config 90 | // and then validate it is reasonable 91 | func GetConfigFromEnv() (*Config, error) { 92 | cfg := &Config{ 93 | Home: os.Getenv("DAEMON_HOME"), 94 | Name: os.Getenv("DAEMON_NAME"), 95 | } 96 | 97 | if os.Getenv("DAEMON_ALLOW_DOWNLOAD_BINARIES") == "true" { 98 | cfg.AllowDownloadBinaries = true 99 | } 100 | 101 | if os.Getenv("DAEMON_RESTART_AFTER_UPGRADE") == "true" { 102 | cfg.RestartAfterUpgrade = true 103 | } 104 | 105 | logBufferSizeStr := os.Getenv("DAEMON_LOG_BUFFER_SIZE") 106 | if logBufferSizeStr != "" { 107 | logBufferSize, err := strconv.Atoi(logBufferSizeStr) 108 | if err != nil { 109 | return nil, err 110 | } 111 | cfg.LogBufferSize = logBufferSize * 1024 112 | } else { 113 | cfg.LogBufferSize = bufio.MaxScanTokenSize 114 | } 115 | 116 | if err := cfg.validate(); err != nil { 117 | return nil, err 118 | } 119 | 120 | return cfg, nil 121 | } 122 | 123 | // validate returns an error if this config is invalid. 124 | // it enforces Home/cosmovisor is a valid directory and exists, 125 | // and that Name is set 126 | func (cfg *Config) validate() error { 127 | if cfg.Name == "" { 128 | return errors.New("DAEMON_NAME is not set") 129 | } 130 | 131 | if cfg.Home == "" { 132 | return errors.New("DAEMON_HOME is not set") 133 | } 134 | 135 | if !filepath.IsAbs(cfg.Home) { 136 | return errors.New("DAEMON_HOME must be an absolute path") 137 | } 138 | 139 | // ensure the root directory exists 140 | info, err := os.Stat(cfg.Root()) 141 | if err != nil { 142 | return fmt.Errorf("cannot stat home dir: %w", err) 143 | } 144 | 145 | if !info.IsDir() { 146 | return fmt.Errorf("%s is not a directory", info.Name()) 147 | } 148 | 149 | return nil 150 | } 151 | -------------------------------------------------------------------------------- /cmd/mund-manager/lib/process.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "os/signal" 11 | "strings" 12 | "sync" 13 | "syscall" 14 | ) 15 | 16 | // LaunchProcess runs a subprocess and returns when the subprocess exits, 17 | // either when it dies, or *after* a successful upgrade. 18 | func LaunchProcess(cfg *Config, args []string, stdout, stderr io.Writer) (bool, error) { 19 | bin, err := cfg.CurrentBin() 20 | if err != nil { 21 | return false, fmt.Errorf("error creating symlink to genesis: %w", err) 22 | } 23 | 24 | if err := EnsureBinary(bin); err != nil { 25 | return false, fmt.Errorf("current binary invalid: %w", err) 26 | } 27 | 28 | cmd := exec.Command(bin, args...) 29 | outpipe, err := cmd.StdoutPipe() 30 | if err != nil { 31 | return false, err 32 | } 33 | 34 | errpipe, err := cmd.StderrPipe() 35 | if err != nil { 36 | return false, err 37 | } 38 | 39 | scanOut := bufio.NewScanner(io.TeeReader(outpipe, stdout)) 40 | scanErr := bufio.NewScanner(io.TeeReader(errpipe, stderr)) 41 | // set scanner's buffer size to cfg.LogBufferSize, and ensure larger than bufio.MaxScanTokenSize otherwise fallback to bufio.MaxScanTokenSize 42 | var maxCapacity int 43 | if cfg.LogBufferSize < bufio.MaxScanTokenSize { 44 | maxCapacity = bufio.MaxScanTokenSize 45 | } else { 46 | maxCapacity = cfg.LogBufferSize 47 | } 48 | bufOut := make([]byte, maxCapacity) 49 | bufErr := make([]byte, maxCapacity) 50 | scanOut.Buffer(bufOut, maxCapacity) 51 | scanErr.Buffer(bufErr, maxCapacity) 52 | 53 | if err := cmd.Start(); err != nil { 54 | return false, fmt.Errorf("launching process %s %s: %w", bin, strings.Join(args, " "), err) 55 | } 56 | 57 | sigs := make(chan os.Signal, 1) 58 | signal.Notify(sigs, syscall.SIGQUIT, syscall.SIGTERM) 59 | go func() { 60 | sig := <-sigs 61 | if err := cmd.Process.Signal(sig); err != nil { 62 | log.Fatal(err) 63 | } 64 | }() 65 | 66 | // three ways to exit - command ends, find regexp in scanOut, find regexp in scanErr 67 | upgradeInfo, err := WaitForUpgradeOrExit(cmd, scanOut, scanErr) 68 | if err != nil { 69 | return false, err 70 | } 71 | 72 | if upgradeInfo != nil { 73 | return true, DoUpgrade(cfg, upgradeInfo) 74 | } 75 | 76 | return false, nil 77 | } 78 | 79 | // WaitResult is used to wrap feedback on cmd state with some mutex logic. 80 | // This is needed as multiple go-routines can affect this - two read pipes that can trigger upgrade 81 | // As well as the command, which can fail 82 | type WaitResult struct { 83 | // both err and info may be updated from several go-routines 84 | // access is wrapped by mutex and should only be done through methods 85 | err error 86 | info *UpgradeInfo 87 | mutex sync.Mutex 88 | } 89 | 90 | // AsResult reads the data protected by mutex to avoid race conditions 91 | func (u *WaitResult) AsResult() (*UpgradeInfo, error) { 92 | u.mutex.Lock() 93 | defer u.mutex.Unlock() 94 | return u.info, u.err 95 | } 96 | 97 | // SetError will set with the first error using a mutex 98 | // don't set it once info is set, that means we chose to kill the process 99 | func (u *WaitResult) SetError(myErr error) { 100 | u.mutex.Lock() 101 | defer u.mutex.Unlock() 102 | if u.info == nil && myErr != nil { 103 | u.err = myErr 104 | } 105 | } 106 | 107 | // SetUpgrade sets first non-nil upgrade info, ensure error is then nil 108 | // pass in a command to shutdown on successful upgrade 109 | func (u *WaitResult) SetUpgrade(up *UpgradeInfo) { 110 | u.mutex.Lock() 111 | defer u.mutex.Unlock() 112 | if u.info == nil && up != nil { 113 | u.info = up 114 | u.err = nil 115 | } 116 | } 117 | 118 | // WaitForUpgradeOrExit listens to both output streams of the process, as well as the process state itself 119 | // When it returns, the process is finished and all streams have closed. 120 | // 121 | // It returns (info, nil) if an upgrade should be initiated (and we killed the process) 122 | // It returns (nil, err) if the process died by itself, or there was an issue reading the pipes 123 | // It returns (nil, nil) if the process exited normally without triggering an upgrade. This is very unlikely 124 | // to happened with "start" but may happened with short-lived commands like `gaiad export ...` 125 | func WaitForUpgradeOrExit(cmd *exec.Cmd, scanOut, scanErr *bufio.Scanner) (*UpgradeInfo, error) { 126 | var res WaitResult 127 | 128 | waitScan := func(scan *bufio.Scanner) { 129 | upgrade, err := WaitForUpdate(scan) 130 | if err != nil { 131 | res.SetError(err) 132 | } else if upgrade != nil { 133 | res.SetUpgrade(upgrade) 134 | // now we need to kill the process 135 | _ = cmd.Process.Kill() 136 | } 137 | } 138 | 139 | // wait for the scanners, which can trigger upgrade and kill cmd 140 | go waitScan(scanOut) 141 | go waitScan(scanErr) 142 | 143 | // if the command exits normally (eg. short command like `gaiad version`), just return (nil, nil) 144 | // we often get broken read pipes if it runs too fast. 145 | // if we had upgrade info, we would have killed it, and thus got a non-nil error code 146 | err := cmd.Wait() 147 | if err == nil { 148 | return nil, nil // nolint: nilnil 149 | } 150 | // this will set the error code if it wasn't killed due to upgrade 151 | res.SetError(err) 152 | return res.AsResult() 153 | } 154 | -------------------------------------------------------------------------------- /cmd/mund-manager/lib/scanner.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bufio" 5 | "regexp" 6 | ) 7 | 8 | // Trim off whitespace around the info - match least greedy, grab as much space on both sides 9 | // Defined here: https://github.com/cosmos/cosmos-sdk/blob/release/v0.38.2/x/upgrade/abci.go#L38 10 | // 11 | // fmt.Sprintf("UPGRADE \"%s\" NEEDED at %s: %s", plan.Name, plan.DueAt(), plan.Info) 12 | // 13 | // DueAt defined here: https://github.com/cosmos/cosmos-sdk/blob/release/v0.38.2/x/upgrade/internal/types/plan.go#L73-L78 14 | // 15 | // if !p.Time.IsZero() { 16 | // return fmt.Sprintf("time: %s", p.Time.UTC().Format(time.RFC3339)) 17 | // } 18 | // return fmt.Sprintf("height: %d", p.Height) 19 | var upgradeRegex = regexp.MustCompile(`UPGRADE "(.*)" NEEDED at ((height): (\d+)|(time): (\S+)):\s+(\S*)`) 20 | 21 | // UpgradeInfo is the details from the regexp 22 | type UpgradeInfo struct { 23 | Name string 24 | Info string 25 | } 26 | 27 | // WaitForUpdate will listen to the scanner until a line matches upgradeRegexp. 28 | // It returns (info, nil) on a matching line 29 | // It returns (nil, err) if the input stream errored 30 | // It returns (nil, nil) if the input closed without ever matching the regexp 31 | func WaitForUpdate(scanner *bufio.Scanner) (*UpgradeInfo, error) { 32 | for scanner.Scan() { 33 | line := scanner.Text() 34 | if upgradeRegex.MatchString(line) { 35 | subs := upgradeRegex.FindStringSubmatch(line) 36 | info := UpgradeInfo{ 37 | Name: subs[1], 38 | Info: subs[7], 39 | } 40 | return &info, nil 41 | } 42 | } 43 | return nil, scanner.Err() 44 | } 45 | -------------------------------------------------------------------------------- /cmd/mund-manager/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "mun/cmd/mund-manager/lib" 8 | ) 9 | 10 | func main() { 11 | if err := Run(os.Args[1:]); err != nil { 12 | fmt.Fprintf(os.Stderr, "%+v\n", err) 13 | os.Exit(1) 14 | } 15 | } 16 | 17 | // Run is the main loop, but returns an error 18 | func Run(args []string) error { 19 | cfg, err := lib.GetConfigFromEnv() 20 | if err != nil { 21 | return err 22 | } 23 | 24 | doUpgrade, err := lib.LaunchProcess(cfg, args, os.Stdout, os.Stderr) 25 | // if RestartAfterUpgrade, we launch after a successful upgrade (only condition LaunchProcess returns nil) 26 | for cfg.RestartAfterUpgrade && err == nil && doUpgrade { 27 | doUpgrade, err = lib.LaunchProcess(cfg, args, os.Stdout, os.Stderr) 28 | } 29 | return err 30 | } 31 | -------------------------------------------------------------------------------- /cmd/mund/cmd/config.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "mun/app" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | func initSDKConfig() { 10 | // Set prefixes 11 | accountPubKeyPrefix := app.AccountAddressPrefix + "pub" 12 | validatorAddressPrefix := app.AccountAddressPrefix + "valoper" 13 | validatorPubKeyPrefix := app.AccountAddressPrefix + "valoperpub" 14 | consNodeAddressPrefix := app.AccountAddressPrefix + "valcons" 15 | consNodePubKeyPrefix := app.AccountAddressPrefix + "valconspub" 16 | 17 | // Set and seal config 18 | config := sdk.GetConfig() 19 | config.SetBech32PrefixForAccount(app.AccountAddressPrefix, accountPubKeyPrefix) 20 | config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix) 21 | config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix) 22 | config.Seal() 23 | } 24 | -------------------------------------------------------------------------------- /cmd/mund/genwasm.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/client" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func AddGenesisWasmMsgCmd(defaultNodeHome string) *cobra.Command { 9 | txCmd := &cobra.Command{ 10 | Use: "add-wasm-genesis-message", 11 | Short: "Wasm genesis subcommands", 12 | DisableFlagParsing: true, 13 | SuggestionsMinimumDistance: 2, 14 | RunE: client.ValidateCmd, 15 | } 16 | // genesisIO := wasmcli.NewDefaultGenesisIO() 17 | // txCmd.AddCommand( 18 | // wasmcli.GenesisStoreCodeCmd(defaultNodeHome, genesisIO), 19 | // wasmcli.GenesisInstantiateContractCmd(defaultNodeHome, genesisIO), 20 | // wasmcli.GenesisExecuteContractCmd(defaultNodeHome, genesisIO), 21 | // wasmcli.GenesisListContractsCmd(defaultNodeHome, genesisIO), 22 | // wasmcli.GenesisListCodesCmd(defaultNodeHome, genesisIO), 23 | // ) 24 | 25 | return txCmd 26 | } 27 | -------------------------------------------------------------------------------- /cmd/mund/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "mun/app" 7 | 8 | "mun/cmd/mund/cmd" 9 | 10 | "github.com/cosmos/cosmos-sdk/server" 11 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" 12 | ) 13 | 14 | func main() { 15 | // cmdOptions := GetWasmCmdOptions() 16 | rootCmd, _ := cmd.NewRootCmd() 17 | if err := svrcmd.Execute(rootCmd, app.DefaultNodeHome); err != nil { 18 | switch e := err.(type) { 19 | case server.ErrorCode: 20 | os.Exit(e.Code) 21 | 22 | default: 23 | os.Exit(1) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | build: 3 | main: ./cmd/mund 4 | proto: 5 | path: "" 6 | third_party_paths: [] 7 | accounts: 8 | - name: alice 9 | coins: 10 | - 2000000000utmun 11 | mnemonic: alarm robust diet dinosaur honey pencil dial wall scene strategy hint 12 | predict bleak physical ancient creek insane limb boring scan eager erode behave 13 | vehicle 14 | - name: bob 15 | coins: 16 | - 10000utmun 17 | mnemonic: economy ticket horror harbor draw educate vehicle fly hen crystal album 18 | exist mechanic creek ugly economy say category lunar much sound during inmate 19 | coyote 20 | - name: tom 21 | coins: 22 | - 1000000000000utmun 23 | mnemonic: orphan cradle clarify priority jeans light hold country bright flight 24 | tail position various bunker later scale nose edge suggest fit direct lock immense 25 | tumble 26 | - name: jerry 27 | coins: 28 | - 100000000000000utmun 29 | mnemonic: degree tissue code business hair agree fruit print bargain tissue fuel 30 | small neutral word prefer glimpse fiscal mention lawn royal balance tray jazz 31 | faculty 32 | faucet: 33 | name: bob 34 | coins: 35 | - 100000utmun 36 | host: 0.0.0.0:4500 37 | client: 38 | typescript: 39 | path: ts-client 40 | vuex: 41 | path: vue/src/store 42 | composables: 43 | path: vue/src/composables 44 | hooks: 45 | path: react/src/hooks 46 | openapi: 47 | path: docs/static/openapi.yml 48 | genesis: 49 | app_state: 50 | staking: 51 | params: 52 | bond_denom: utmun 53 | validators: 54 | - name: alice 55 | bonded: 100000000utmun 56 | - name: bob 57 | bonded: 1000000000utmun 58 | -------------------------------------------------------------------------------- /docs/docs.go: -------------------------------------------------------------------------------- 1 | package docs 2 | 3 | import ( 4 | "embed" 5 | httptemplate "html/template" 6 | "net/http" 7 | 8 | "github.com/gorilla/mux" 9 | ) 10 | 11 | const ( 12 | apiFile = "/static/openapi.yml" 13 | indexFile = "template/index.tpl" 14 | ) 15 | 16 | //go:embed static 17 | var Static embed.FS 18 | 19 | //go:embed template 20 | var template embed.FS 21 | 22 | func RegisterOpenAPIService(appName string, rtr *mux.Router) { 23 | rtr.Handle(apiFile, http.FileServer(http.FS(Static))) 24 | rtr.HandleFunc("/", handler(appName)) 25 | } 26 | 27 | // handler returns an http handler that servers OpenAPI console for an OpenAPI spec at specURL. 28 | func handler(title string) http.HandlerFunc { 29 | t, _ := httptemplate.ParseFS(template, indexFile) 30 | 31 | return func(w http.ResponseWriter, req *http.Request) { 32 | _ = t.Execute(w, struct { 33 | Title string 34 | URL string 35 | }{ 36 | title, 37 | apiFile, 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docs/template/index.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ .Title }} 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /internal/ante/min_commission.go: -------------------------------------------------------------------------------- 1 | package ante 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 7 | 8 | "github.com/cosmos/cosmos-sdk/x/authz" 9 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 10 | ) 11 | 12 | type MinCommissionDecorator struct { 13 | codec codec.BinaryCodec 14 | } 15 | 16 | func NewMinCommissionDecorator(codec codec.BinaryCodec) MinCommissionDecorator { 17 | return MinCommissionDecorator{ 18 | codec, 19 | } 20 | } 21 | 22 | func checkCommission(m sdk.Msg) error { 23 | switch msg := m.(type) { 24 | case *stakingtypes.MsgCreateValidator: 25 | c := msg.Commission 26 | if c.Rate.LT(sdk.NewDecWithPrec(5, 2)) { 27 | return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "commission can not be lower than 5%") 28 | } 29 | case *stakingtypes.MsgEditValidator: 30 | // if commission rate is nil, it means only other fields are being update and we must skip this validation 31 | if msg.CommissionRate == nil { 32 | return nil 33 | } 34 | if msg.CommissionRate.LT(sdk.NewDecWithPrec(5, 2)) { 35 | return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "commission can not be lower than 5%") 36 | } 37 | default: 38 | return nil 39 | } 40 | return nil 41 | } 42 | 43 | func (dec MinCommissionDecorator) Validate(m sdk.Msg) error { 44 | err := checkCommission(m) 45 | if err != nil { 46 | return err 47 | } 48 | if msg, ok := m.(*authz.MsgExec); ok { 49 | for _, v := range msg.Msgs { 50 | var wrappedMsg sdk.Msg 51 | err := dec.codec.UnpackAny(v, &wrappedMsg) 52 | if err != nil { 53 | return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "error decoding authz messages") 54 | } 55 | err = checkCommission(wrappedMsg) 56 | if err != nil { 57 | return err 58 | } 59 | } 60 | } 61 | return nil 62 | } 63 | 64 | func (dec MinCommissionDecorator) AnteHandle( 65 | ctx sdk.Context, tx sdk.Tx, 66 | simulate bool, next sdk.AnteHandler, 67 | ) (newCtx sdk.Context, err error) { 68 | msgs := tx.GetMsgs() 69 | for _, m := range msgs { 70 | err := dec.Validate(m) 71 | if err != nil { 72 | return ctx, err 73 | } 74 | } 75 | return next(ctx, tx, simulate) 76 | } 77 | -------------------------------------------------------------------------------- /internal/statesync/snapshotter.go: -------------------------------------------------------------------------------- 1 | package statesync 2 | 3 | import ( 4 | "io" 5 | 6 | snapshot "github.com/cosmos/cosmos-sdk/snapshots/types" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 9 | protoio "github.com/gogo/protobuf/io" 10 | abci "github.com/tendermint/tendermint/abci/types" 11 | "github.com/tendermint/tendermint/libs/log" 12 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 13 | ) 14 | 15 | const ( 16 | SnapshotName = "sg_version_snapshotter" 17 | SnapshotFormat = 0xFF 18 | ) 19 | 20 | var _ snapshot.ExtensionSnapshotter = &VersionSnapshotter{} 21 | 22 | type ConsensusParamsGetter interface { 23 | GetConsensusParams(ctx sdk.Context) *abci.ConsensusParams 24 | } 25 | 26 | type ProtocolVersionSetter interface { 27 | SetProtocolVersion(uint64) 28 | } 29 | 30 | type VersionSnapshotter struct { 31 | consensusParamGetter ConsensusParamsGetter 32 | versionSetter ProtocolVersionSetter 33 | ms sdk.MultiStore 34 | } 35 | 36 | func NewVersionSnapshotter(ms sdk.MultiStore, cpg ConsensusParamsGetter, vs ProtocolVersionSetter) *VersionSnapshotter { 37 | return &VersionSnapshotter{ 38 | consensusParamGetter: cpg, 39 | versionSetter: vs, 40 | ms: ms, 41 | } 42 | } 43 | 44 | func (vs *VersionSnapshotter) SnapshotName() string { 45 | return SnapshotName 46 | } 47 | 48 | // Snapshot writes snapshot items into the protobuf writer. 49 | func (vs *VersionSnapshotter) Snapshot(height uint64, protoWriter protoio.Writer) error { 50 | cms, err := vs.ms.CacheMultiStoreWithVersion(int64(height)) 51 | if err != nil { 52 | return err 53 | } 54 | ctx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) 55 | params := vs.consensusParamGetter.GetConsensusParams(ctx) 56 | // default to 1 for stargaze 57 | appVersion := uint64(1) 58 | if params != nil && params.Version != nil && params.Version.GetAppVersion() > 0 { 59 | appVersion = params.Version.GetAppVersion() 60 | } 61 | bz := sdk.Uint64ToBigEndian(appVersion) 62 | return snapshot.WriteExtensionItem(protoWriter, bz) 63 | } 64 | 65 | // Restore restores a state snapshot from the protobuf items read from the reader. 66 | func (vs *VersionSnapshotter) Restore(height uint64, format uint32, protoReader protoio.Reader) (snapshot.SnapshotItem, error) { 67 | if format == SnapshotFormat { 68 | var item snapshot.SnapshotItem 69 | for { 70 | item = snapshot.SnapshotItem{} 71 | err := protoReader.ReadMsg(&item) 72 | if err == io.EOF { 73 | break 74 | } else if err != nil { 75 | return snapshot.SnapshotItem{}, sdkerrors.Wrap(err, "invalid protobuf message") 76 | } 77 | payload := item.GetExtensionPayload() 78 | if payload == nil { 79 | break 80 | } 81 | appVersion := sdk.BigEndianToUint64(payload.Payload) 82 | vs.versionSetter.SetProtocolVersion(appVersion) 83 | } 84 | return item, nil 85 | } 86 | return snapshot.SnapshotItem{}, snapshot.ErrUnknownFormat 87 | } 88 | 89 | func (vs *VersionSnapshotter) SnapshotFormat() uint32 { 90 | return SnapshotFormat 91 | } 92 | 93 | func (vs *VersionSnapshotter) SupportedFormats() []uint32 { 94 | return []uint32{SnapshotFormat} 95 | } 96 | -------------------------------------------------------------------------------- /internal/wasm/distribution.go: -------------------------------------------------------------------------------- 1 | package wasm 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" 8 | wasmvmtypes "github.com/CosmWasm/wasmvm/types" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 11 | distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" 12 | ) 13 | 14 | const ( 15 | DistributionRoute = "distribution" 16 | ) 17 | 18 | var _ Encoder = CustomDistributionEncoder 19 | 20 | type FundCommunityPool struct { 21 | Amount wasmvmtypes.Coins `json:"amount"` 22 | } 23 | 24 | func (fcp FundCommunityPool) Encode(contract sdk.AccAddress) ([]sdk.Msg, error) { 25 | amount, err := wasmkeeper.ConvertWasmCoinsToSdkCoins(fcp.Amount) 26 | 27 | if err != nil { 28 | return nil, err 29 | } 30 | msg := distributiontypes.NewMsgFundCommunityPool(amount, contract) 31 | return []sdk.Msg{msg}, nil 32 | } 33 | 34 | type DistributionMsg struct { 35 | FundCommunityPool *FundCommunityPool `json:"fund_community_pool,omitempty"` 36 | } 37 | 38 | func CustomDistributionEncoder(contract sdk.AccAddress, data json.RawMessage, version string) ([]sdk.Msg, error) { 39 | msg := &DistributionMsg{} 40 | err := json.Unmarshal(data, msg) 41 | if err != nil { 42 | return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) 43 | } 44 | if msg.FundCommunityPool != nil { 45 | return msg.FundCommunityPool.Encode(contract) 46 | } 47 | return nil, fmt.Errorf("wasm: invalid custom distribution message") 48 | } 49 | -------------------------------------------------------------------------------- /internal/wasm/encoder.go: -------------------------------------------------------------------------------- 1 | package wasm 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/CosmWasm/wasmd/x/wasm" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 9 | ) 10 | 11 | // Encoder describes behavior for Stargaze smart contract message encoding. 12 | // The contract address must ALWAYS be set as the Msg signer. 13 | type Encoder func(contract sdk.AccAddress, data json.RawMessage, version string) ([]sdk.Msg, error) 14 | 15 | // MessageEncoders provides stargaze custom encoder for contracts 16 | func MessageEncoders(registry *EncoderRegistry) *wasm.MessageEncoders { 17 | return &wasm.MessageEncoders{ 18 | Custom: customEncoders(registry), 19 | } 20 | } 21 | 22 | type MessageEncodeRequest struct { 23 | Route string `json:"route"` 24 | MsgData json.RawMessage `json:"msg_data"` 25 | Version string `json:"version"` 26 | } 27 | 28 | func customEncoders(registry *EncoderRegistry) wasm.CustomEncoder { 29 | return func(sender sdk.AccAddress, m json.RawMessage) ([]sdk.Msg, error) { 30 | encodeRequest := &MessageEncodeRequest{} 31 | err := json.Unmarshal(m, encodeRequest) 32 | if err != nil { 33 | return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) 34 | } 35 | encode, exists := registry.encoders[encodeRequest.Route] 36 | if !exists { 37 | return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "encoder not found for route: %s", encodeRequest.Route) 38 | } 39 | 40 | msgs, err := encode(sender, encodeRequest.MsgData, encodeRequest.Version) 41 | if err != nil { 42 | return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) 43 | } 44 | for _, msg := range msgs { 45 | if err := msg.ValidateBasic(); err != nil { 46 | return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) 47 | } 48 | } 49 | return msgs, nil 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /internal/wasm/registry.go: -------------------------------------------------------------------------------- 1 | package wasm 2 | 3 | import "fmt" 4 | 5 | type EncoderRegistry struct { 6 | encoders map[string]Encoder 7 | } 8 | 9 | // NewEncoderRegistry creates a new registry for message encoders. 10 | func NewEncoderRegistry() *EncoderRegistry { 11 | return &EncoderRegistry{ 12 | encoders: make(map[string]Encoder), 13 | } 14 | } 15 | 16 | // RegisterEncoder adds a message encoder for the given route. 17 | func (qr *EncoderRegistry) RegisterEncoder(route string, encoder Encoder) { 18 | if _, exists := qr.encoders[route]; exists { 19 | panic(fmt.Sprintf("wasm: encoder already registered for route: %s", route)) 20 | } 21 | qr.encoders[route] = encoder 22 | } 23 | -------------------------------------------------------------------------------- /mupgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "==============================================================================" 4 | echo "=============Fetching latest version=============" 5 | sudo rm -rf $HOME/mun 6 | cd $HOME && git clone https://github.com/munblockchain/mun.git 7 | echo "=============Compiling binary=============" 8 | cd $HOME/mun && make install 9 | mund version 10 | sudo rm -rf /var/log/mund/ 11 | cd $HOME/mun && make log-files 12 | echo "=============Preparing binary upgrades=============" 13 | mkdir -p $HOME/.mun/upgrade_manager/upgrades/mun-upgrade-v3.0.3/bin 14 | cp $HOME/go/bin/mund $HOME/.mun/upgrade_manager/upgrades/mun-upgrade-v3.0.3/bin/ 15 | echo "=============Upgrading service configration=============" 16 | sudo sed -i 's/=on/=true/g' /etc/systemd/system/mund.service 17 | sudo sed -i 's/=true-/=on-/g' /etc/systemd/system/mund.service 18 | echo "=============Daemon reloading=============" 19 | sudo systemctl daemon-reload 20 | sudo systemctl restart mund.service 21 | echo "=============Done! Please wait until it reaches to upgrade block=============" 22 | -------------------------------------------------------------------------------- /proto/claim/v1beta1/claim_record.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.claim.v1beta1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "cosmos/base/v1beta1/coin.proto"; 6 | 7 | option go_package = "mun/x/claim/types"; 8 | 9 | enum Action { 10 | option (gogoproto.goproto_enum_prefix) = false; 11 | 12 | ActionInitialClaim = 0; 13 | ActionDelegateStake = 1; 14 | ActionVote = 2; 15 | ActionSwap = 3; 16 | } 17 | 18 | message ClaimRecord { 19 | // address of claim user 20 | string address = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ]; 21 | 22 | // total initial claimable amount for the user 23 | repeated cosmos.base.v1beta1.Coin initial_claimable_amount = 2 [ 24 | (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", 25 | (gogoproto.nullable) = false, 26 | (gogoproto.moretags) = "yaml:\"initial_claimable_amount\"" 27 | ]; 28 | 29 | // true if action is completed 30 | // index of bool in array refers to action enum # 31 | repeated bool action_completed = 3 [ 32 | (gogoproto.moretags) = "yaml:\"action_completed\"", 33 | (gogoproto.nullable) = false 34 | ]; 35 | 36 | // true if action is ready to claim 37 | // index of bool in array refers to action enum # 38 | repeated bool action_ready = 4 [ 39 | (gogoproto.moretags) = "yaml:\"action_ready\"", 40 | (gogoproto.nullable) = false 41 | ]; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /proto/claim/v1beta1/genesis.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.claim.v1beta1; 3 | 4 | // this line is used by starport scaffolding # genesis/proto/import 5 | import "gogoproto/gogo.proto"; 6 | import "cosmos/base/v1beta1/coin.proto"; 7 | import "claim/v1beta1/claim_record.proto"; 8 | import "claim/v1beta1/params.proto"; 9 | 10 | option go_package = "mun/x/claim/types"; 11 | 12 | // GenesisState defines the claim module's genesis state. 13 | message GenesisState { 14 | // this line is used by starport scaffolding # genesis/proto/state 15 | // balance of the claim module's account 16 | cosmos.base.v1beta1.Coin module_account_balance = 1 [ 17 | (gogoproto.moretags) = "yaml:\"module_account_balance\"", 18 | (gogoproto.nullable) = false 19 | ]; 20 | 21 | // params defines all the parameters of the module. 22 | Params params = 2 [ 23 | (gogoproto.moretags) = "yaml:\"params\"", 24 | (gogoproto.nullable) = false 25 | ]; 26 | 27 | // list of claim records, one for every airdrop recipient 28 | repeated ClaimRecord claim_records = 3 [ 29 | (gogoproto.moretags) = "yaml:\"claim_records\"", 30 | (gogoproto.nullable) = false 31 | ]; 32 | 33 | string merkle_root = 4; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /proto/claim/v1beta1/params.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.claim.v1beta1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "google/protobuf/duration.proto"; 6 | import "google/protobuf/timestamp.proto"; 7 | import "claim/v1beta1/claim_record.proto"; 8 | 9 | option go_package = "mun/x/claim/types"; 10 | 11 | message ClaimAuthorization { 12 | string contract_address = 1 13 | [ (gogoproto.moretags) = "yaml:\"contract_address\"" ]; 14 | Action action = 2 [ (gogoproto.moretags) = "yaml:\"action\"" ]; 15 | } 16 | 17 | // Params defines the claim module's parameters. 18 | message Params { 19 | option (gogoproto.goproto_stringer) = false; 20 | bool airdrop_enabled = 1; 21 | 22 | google.protobuf.Timestamp airdrop_start_time = 2 [ 23 | (gogoproto.stdtime) = true, 24 | (gogoproto.nullable) = false, 25 | (gogoproto.moretags) = "yaml:\"airdrop_start_time\"" 26 | ]; 27 | google.protobuf.Duration duration_until_decay = 3 [ 28 | (gogoproto.nullable) = false, 29 | (gogoproto.stdduration) = true, 30 | (gogoproto.jsontag) = "duration_until_decay,omitempty", 31 | (gogoproto.moretags) = "yaml:\"duration_until_decay\"" 32 | ]; 33 | google.protobuf.Duration duration_of_decay = 4 [ 34 | (gogoproto.nullable) = false, 35 | (gogoproto.stdduration) = true, 36 | (gogoproto.jsontag) = "duration_of_decay,omitempty", 37 | (gogoproto.moretags) = "yaml:\"duration_of_decay\"" 38 | ]; 39 | 40 | // denom of claimable asset 41 | string claim_denom = 5; 42 | 43 | // list of contracts and their allowed claim actions 44 | // repeated ClaimAuthorization allowed_claimers = 6 [ 45 | // (gogoproto.nullable) = false, 46 | // (gogoproto.jsontag) = "allowed_claimers", 47 | // (gogoproto.moretags) = "yaml:\"allowed_claimers\"" 48 | // ]; 49 | reserved 6; 50 | } 51 | -------------------------------------------------------------------------------- /proto/claim/v1beta1/query.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.claim.v1beta1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "google/api/annotations.proto"; 6 | import "cosmos/base/v1beta1/coin.proto"; 7 | import "claim/v1beta1/claim_record.proto"; 8 | import "claim/v1beta1/params.proto"; 9 | // this line is used by starport scaffolding # 1 10 | 11 | option go_package = "mun/x/claim/types"; 12 | 13 | // Query defines the gRPC querier service. 14 | service Query { 15 | // this line is used by starport scaffolding # 2 16 | rpc ModuleAccountBalance(QueryModuleAccountBalanceRequest) 17 | returns (QueryModuleAccountBalanceResponse) { 18 | option (google.api.http).get = 19 | "/mun/claim/v1beta1/module_account_balance"; 20 | } 21 | rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { 22 | option (google.api.http).get = "/mun/claim/v1beta1/params"; 23 | } 24 | rpc ClaimRecord(QueryClaimRecordRequest) returns (QueryClaimRecordResponse) { 25 | option (google.api.http).get = 26 | "/mun/claim/v1beta1/claim_record/{address}"; 27 | } 28 | rpc ClaimableForAction(QueryClaimableForActionRequest) returns (QueryClaimableForActionResponse) { 29 | option (google.api.http).get = "/mun/claim/v1beta1/claimable_for_action/{address}/{action}"; 30 | } 31 | rpc TotalClaimable(QueryTotalClaimableRequest) returns (QueryTotalClaimableResponse) { 32 | option (google.api.http).get = "/mun/claim/v1beta1/total_claimable/{address}"; 33 | } 34 | 35 | rpc MerkleRoot(QueryMerkleRootRequest) returns (QueryMerkleRootResponse) { 36 | option (google.api.http).get = "/mun/claim/v1beta1/merkle_root"; 37 | } 38 | 39 | rpc Whitelisted(QueryWhitelistedRequest) returns (QueryWhitelistedResponse) { 40 | option (google.api.http).get = "/mun/claim/v1beta1/verify/{address}/{proof}"; 41 | } 42 | } 43 | // this line is used by starport scaffolding # 3 44 | 45 | // QueryParamsRequest is the request type for the Query/Params RPC method. 46 | message QueryModuleAccountBalanceRequest {} 47 | 48 | // QueryParamsResponse is the response type for the Query/Params RPC method. 49 | message QueryModuleAccountBalanceResponse { 50 | // params defines the parameters of the module. 51 | repeated cosmos.base.v1beta1.Coin moduleAccountBalance = 1 [ 52 | (gogoproto.moretags) = "yaml:\"coins\"", 53 | (gogoproto.nullable) = false, 54 | (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" 55 | ]; 56 | } 57 | 58 | // QueryParamsRequest is the request type for the Query/Params RPC method. 59 | message QueryParamsRequest {} 60 | 61 | // QueryParamsResponse is the response type for the Query/Params RPC method. 62 | message QueryParamsResponse { 63 | // params defines the parameters of the module. 64 | Params params = 1 [ (gogoproto.nullable) = false ]; 65 | } 66 | 67 | message QueryClaimRecordRequest { 68 | string address = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; 69 | } 70 | 71 | message QueryClaimRecordResponse { 72 | ClaimRecord claim_record = 1 [ 73 | (gogoproto.moretags) = "yaml:\"claim_record\"", 74 | (gogoproto.nullable) = false 75 | ]; 76 | } 77 | 78 | message QueryClaimableForActionRequest { 79 | string address = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ]; 80 | Action action = 2 [ (gogoproto.moretags) = "yaml:\"action\"" ]; 81 | } 82 | 83 | message QueryClaimableForActionResponse { 84 | repeated cosmos.base.v1beta1.Coin coins = 1 [ 85 | (gogoproto.moretags) = "yaml:\"coins\"", 86 | (gogoproto.nullable) = false, 87 | (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" 88 | ]; 89 | } 90 | 91 | message QueryTotalClaimableRequest { 92 | string address = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ]; 93 | } 94 | 95 | message QueryTotalClaimableResponse { 96 | repeated cosmos.base.v1beta1.Coin coins = 1 [ 97 | (gogoproto.moretags) = "yaml:\"coins\"", 98 | (gogoproto.nullable) = false, 99 | (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" 100 | ]; 101 | } 102 | 103 | message QueryMerkleRootRequest { 104 | } 105 | 106 | message QueryMerkleRootResponse { 107 | string merkle_root = 1; 108 | } 109 | 110 | message QueryWhitelistedRequest { 111 | string address = 1; 112 | string proof = 2; 113 | } 114 | 115 | message QueryWhitelistedResponse { 116 | bool whitelisted = 1; 117 | } -------------------------------------------------------------------------------- /proto/claim/v1beta1/tx.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.claim.v1beta1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "cosmos/base/v1beta1/coin.proto"; 6 | import "claim/v1beta1/claim_record.proto"; 7 | 8 | // this line is used by starport scaffolding # proto/tx/import 9 | 10 | option go_package = "mun/x/claim/types"; 11 | 12 | // Msg defines the Msg service. 13 | service Msg { 14 | // rpc InitialClaim(MsgInitialClaim) returns (MsgInitialClaimResponse); 15 | rpc ClaimFor(MsgClaimFor) returns (MsgClaimForResponse); 16 | rpc UpdateMerkleRoot(MsgUpdateMerkleRoot) returns (MsgUpdateMerkleRootResponse); 17 | // this line is used by starport scaffolding # proto/tx/rpc 18 | } 19 | 20 | message MsgClaimFor { 21 | string sender = 1; 22 | Action action = 2; 23 | string proof = 3; 24 | } 25 | 26 | message MsgClaimForResponse { 27 | string address = 1; 28 | // total initial claimable amount for the user 29 | repeated cosmos.base.v1beta1.Coin claimed_amount = 2 [ 30 | (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", 31 | (gogoproto.nullable) = false, 32 | (gogoproto.moretags) = "yaml:\"claimed_amount\"" 33 | ]; 34 | } 35 | 36 | message MsgUpdateMerkleRoot { 37 | string sender = 1; 38 | string root_value = 2; 39 | } 40 | 41 | message MsgUpdateMerkleRootResponse {} -------------------------------------------------------------------------------- /proto/ibank/genesis.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.ibank; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "ibank/params.proto"; 6 | import "ibank/transaction.proto"; 7 | // this line is used by starport scaffolding # genesis/proto/import 8 | 9 | option go_package = "mun/x/ibank/types"; 10 | 11 | // GenesisState defines the ibank module's genesis state. 12 | message GenesisState { 13 | Params params = 1 [(gogoproto.nullable) = false]; 14 | repeated Transaction transactionList = 2 [(gogoproto.nullable) = false]; 15 | uint64 transactionCount = 3; 16 | uint64 transactionChaser = 4; 17 | // this line is used by starport scaffolding # genesis/proto/state 18 | } 19 | -------------------------------------------------------------------------------- /proto/ibank/params.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.ibank; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "google/protobuf/duration.proto"; 6 | 7 | option go_package = "mun/x/ibank/types"; 8 | 9 | // Params defines the parameters for the module. 10 | message Params { 11 | option (gogoproto.goproto_stringer) = false; 12 | 13 | google.protobuf.Duration duration_of_expiration = 1 [ 14 | (gogoproto.nullable) = false, 15 | (gogoproto.stdduration) = true, 16 | (gogoproto.jsontag) = "duration_of_expiration,omitempty", 17 | (gogoproto.moretags) = "yaml:\"duration_of_expiration\"" 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /proto/ibank/query.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.ibank; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "google/api/annotations.proto"; 6 | import "cosmos/base/query/v1beta1/pagination.proto"; 7 | import "ibank/params.proto"; 8 | import "ibank/transaction.proto"; 9 | // this line is used by starport scaffolding # 1 10 | 11 | option go_package = "mun/x/ibank/types"; 12 | 13 | // Query defines the gRPC querier service. 14 | service Query { 15 | // Parameters queries the parameters of the module. 16 | rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { 17 | option (google.api.http).get = "/mun/ibank/params"; 18 | } 19 | // Queries a Transaction by id. 20 | rpc Transaction(QueryGetTransactionRequest) returns (QueryGetTransactionResponse) { 21 | option (google.api.http).get = "/mun/ibank/transaction/{id}"; 22 | } 23 | 24 | // Queries a list of Transaction items. 25 | rpc TransactionAll(QueryAllTransactionRequest) returns (QueryAllTransactionResponse) { 26 | option (google.api.http).get = "/mun/ibank/transaction_all/{address}"; 27 | } 28 | 29 | // Queries a list of ShowIncoming items. 30 | rpc Incoming(QueryIncomingRequest) returns (QueryIncomingResponse) { 31 | option (google.api.http).get = "/mun/ibank/incoming/{receiver}/{pending}"; 32 | } 33 | 34 | // Queries a list of ShowOutgoing items. 35 | rpc Outgoing(QueryOutgoingRequest) returns (QueryOutgoingResponse) { 36 | option (google.api.http).get = "/mun/ibank/outgoing/{sender}/{pending}"; 37 | } 38 | 39 | // this line is used by starport scaffolding # 2 40 | } 41 | 42 | // QueryParamsRequest is request type for the Query/Params RPC method. 43 | message QueryParamsRequest {} 44 | 45 | // QueryParamsResponse is response type for the Query/Params RPC method. 46 | message QueryParamsResponse { 47 | // params holds all the parameters of this module. 48 | Params params = 1 [(gogoproto.nullable) = false]; 49 | } 50 | 51 | message QueryGetTransactionRequest { 52 | uint64 id = 1; 53 | } 54 | 55 | message QueryGetTransactionResponse { 56 | Transaction transaction = 1 [(gogoproto.nullable) = false]; 57 | } 58 | 59 | message QueryAllTransactionRequest { 60 | string address = 1; 61 | cosmos.base.query.v1beta1.PageRequest pagination = 2; 62 | } 63 | 64 | message QueryAllTransactionResponse { 65 | repeated TransactionWrapper transactions = 1 [(gogoproto.nullable) = false]; 66 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 67 | } 68 | 69 | message QueryIncomingRequest { 70 | string receiver = 1; 71 | bool pending = 2; 72 | cosmos.base.query.v1beta1.PageRequest pagination = 3; 73 | } 74 | 75 | message QueryIncomingResponse { 76 | repeated TransactionWrapper transactions = 1 [(gogoproto.nullable) = false]; 77 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 78 | } 79 | 80 | message TransactionWrapper { 81 | Transaction transaction = 1 [(gogoproto.nullable) = false]; 82 | uint32 time_left = 2; 83 | } 84 | 85 | message QueryOutgoingRequest { 86 | string sender = 1; 87 | bool pending = 2; 88 | cosmos.base.query.v1beta1.PageRequest pagination = 3; 89 | } 90 | 91 | message QueryOutgoingResponse { 92 | repeated TransactionWrapper transactions = 1 [(gogoproto.nullable) = false]; 93 | cosmos.base.query.v1beta1.PageResponse pagination = 2; 94 | } 95 | 96 | // this line is used by starport scaffolding # 3 97 | -------------------------------------------------------------------------------- /proto/ibank/transaction.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.ibank; 3 | 4 | option go_package = "mun/x/ibank/types"; 5 | import "gogoproto/gogo.proto"; 6 | import "cosmos/base/v1beta1/coin.proto"; 7 | import "google/protobuf/timestamp.proto"; 8 | 9 | enum TxnStatus { 10 | TXN_PENDING = 0; 11 | TXN_SENT = 1; 12 | TXN_EXPIRED = 2; 13 | TXN_DECLINED = 3; 14 | } 15 | 16 | message Transaction { 17 | uint64 id = 1; 18 | string sender = 2; 19 | google.protobuf.Timestamp sent_at = 3 [ 20 | (gogoproto.stdtime) = true, 21 | (gogoproto.nullable) = false 22 | ]; 23 | 24 | string receiver = 4; 25 | // If sent_at is equal to received_at, transaction have not been performed 26 | google.protobuf.Timestamp received_at = 5 [ 27 | (gogoproto.stdtime) = true, 28 | (gogoproto.nullable) = false 29 | ]; 30 | repeated cosmos.base.v1beta1.Coin coins = 6 [(gogoproto.nullable) = false]; 31 | TxnStatus status = 7; 32 | 33 | // hash value of 6 word password 34 | string password = 8; 35 | int32 retry = 9; 36 | } 37 | -------------------------------------------------------------------------------- /proto/ibank/tx.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mun.ibank; 3 | 4 | // this line is used by starport scaffolding # proto/tx/import 5 | import "cosmos/base/v1beta1/coin.proto"; 6 | import "gogoproto/gogo.proto"; 7 | 8 | option go_package = "mun/x/ibank/types"; 9 | 10 | // Msg defines the Msg service. 11 | service Msg { 12 | rpc Send(MsgSend) returns (MsgSendResponse); 13 | rpc Receive(MsgReceive) returns (MsgReceiveResponse); 14 | // this line is used by starport scaffolding # proto/tx/rpc 15 | } 16 | 17 | message MsgSend { 18 | string from_address = 1; 19 | string to_address = 2; 20 | cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false]; 21 | string password_hash = 4; 22 | } 23 | 24 | message MsgSendResponse { 25 | } 26 | 27 | message MsgReceive { 28 | string receiver = 1; 29 | int64 transaction_id = 2; 30 | string password = 3; 31 | } 32 | 33 | message MsgReceiveResponse { 34 | } 35 | 36 | // this line is used by starport scaffolding # proto/tx/message 37 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # How to join Munchain network 2 | ## Infrastructure 3 | ``` 4 | **Recommended configuration:** 5 | - Number of CPUs: 4 6 | - Memory: 16GB 7 | - OS: Ubuntu 22.04 LTS 8 | - Allow all incoming connections from TCP port 26656 and 26657 9 | - Static IP address 10 | - The recommended configuration from AWS is the equivalent of a t2.large machine with 500 GB HD 11 | ``` 12 | 13 | ## Installing prerequisites 14 | ``` 15 | sudo apt update 16 | sudo apt upgrade -y 17 | sudo apt install build-essential jq -y 18 | ``` 19 | 20 | ## Install Golang: 21 | 22 | ## Install go version 1.20 https://golang.org/doc/install 23 | ``` 24 | wget -q -O - https://raw.githubusercontent.com/canha/golang-tools-install-script/master/goinstall.sh | bash -s -- --version 1.20 25 | source ~/.profile 26 | ``` 27 | 28 | ## To verify that Golang installed 29 | ``` 30 | go version 31 | ``` 32 | // Should return go version go1.20 linux/amd64 33 | 34 | ## Clone repository 35 | ``` 36 | git clone https://github.com/munblockchain/mun 37 | cd mun 38 | ``` 39 | 40 | ## Install the executables 41 | 42 | ``` 43 | sudo rm -rf ~/.mun 44 | go mod tidy 45 | make install 46 | 47 | clear 48 | 49 | mkdir -p ~/.mun/upgrade_manager/upgrades 50 | mkdir -p ~/.mun/upgrade_manager/genesis/bin 51 | ``` 52 | 53 | ## Symlink genesis binary to upgrade 54 | ``` 55 | cp $(which mund) ~/.mun/upgrade_manager/genesis/bin 56 | sudo cp $(which mund-manager) /usr/bin 57 | ``` 58 | 59 | ## Initialize the validator with a moniker name(Example moniker: solid-moon-rock) 60 | ``` 61 | mund init [moniker_name] --chain-id testmun-3 62 | ``` 63 | 64 | ## Add a new wallet address, store seeds and buy TMUN to it. 65 | ``` 66 | mund keys add [wallet_name] --keyring-backend test 67 | ``` 68 | 69 | ## Fetch genesis.json from genesis node 70 | curl http://31.14.40.191/data/genesis_3093970.json --output ~/.mun/config/genesis.json 71 | 72 | ## Update seed in config.toml to make p2p connection 73 | ``` 74 | nano ~/.mun/config/config.toml 75 | seeds = "74bea3fc80aa128c9a757873f2348baa23e9921a@157.245.77.89:26656" 76 | ``` 77 | ## Join our Discord and ask for the validator role to get access to more seeds! 78 | 79 | ## Create the service file "/etc/systemd/system/mund.service" with the following content 80 | ``` 81 | sudo nano /etc/systemd/system/mund.service 82 | ``` 83 | 84 | ## Paste following content(*Please make sure to use correct name of user, group and DAEMON_HOME path at the below.) 85 | ``` 86 | [Unit] 87 | Description=mund 88 | Requires=network-online.target 89 | After=network-online.target 90 | 91 | [Service] 92 | Restart=on-failure 93 | RestartSec=3 94 | User=root 95 | Group=root 96 | Environment=DAEMON_NAME=mund 97 | Environment=DAEMON_HOME=/root/.mun 98 | Environment=DAEMON_ALLOW_DOWNLOAD_BINARIES=on 99 | Environment=DAEMON_RESTART_AFTER_UPGRADE=on 100 | PermissionsStartOnly=true 101 | ExecStart=/usr/bin/mund-manager start --pruning="nothing" --rpc.laddr "tcp://0.0.0.0:26657" 102 | StandardOutput=file:/var/log/mund/mund.log 103 | StandardError=file:/var/log/mund/mund_error.log 104 | ExecReload=/bin/kill -HUP $MAINPID 105 | KillSignal=SIGTERM 106 | LimitNOFILE=4096 107 | 108 | [Install] 109 | WantedBy=multi-user.target 110 | ``` 111 | **Tips** 112 | - How to get user and group name 113 | ``` 114 | whoami 115 | ``` 116 | - How to get DAEMON_HOME path 117 | ``` 118 | cd ~/.mun 119 | pwd 120 | ``` 121 | 122 | ## Create log files and starts running the node 123 | ``` 124 | make log-files 125 | 126 | sudo systemctl enable mund 127 | sudo systemctl start mund 128 | ``` 129 | 130 | ## Verify node is running properly 131 | ``` 132 | mund status 133 | ``` 134 | 135 | ## After buying TMUN, stake it to become a validator. 136 | **Tips** 137 | 138 | You should wait until the node gets fully synchronized with other nodes. You can cross check with the genesis node by visiting https://node1.mun.money/status and check the latest block height. You can also check your node status through this link http://[Your_Node_IP]:26657/status. 139 | 140 | 141 | **A transaction to become a validator by staking TMUN** 142 | 143 | ``` 144 | mund tx staking create-validator --from [wallet_name] --moniker [moniker_name] --pubkey $(mund tendermint show-validator) --chain-id testmun-3 --keyring-backend test --amount 2000000000000000utmun --commission-max-change-rate 0.01 --commission-max-rate 0.2 --commission-rate 0.1 --min-self-delegation 1 --fees 20000utmun -y 145 | ``` 146 | -------------------------------------------------------------------------------- /testutil/nullify/nullify.go: -------------------------------------------------------------------------------- 1 | // Package nullify provides methods to init nil values structs for test assertion. 2 | package nullify 3 | 4 | import ( 5 | "reflect" 6 | "unsafe" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | var ( 12 | coinType = reflect.TypeOf(sdk.Coin{}) 13 | coinsType = reflect.TypeOf(sdk.Coins{}) 14 | ) 15 | 16 | // Fill analyze all struct fields and slices with 17 | // reflection and initialize the nil and empty slices, 18 | // structs, and pointers. 19 | func Fill(x interface{}) interface{} { 20 | v := reflect.Indirect(reflect.ValueOf(x)) 21 | switch v.Kind() { 22 | case reflect.Slice: 23 | for i := 0; i < v.Len(); i++ { 24 | obj := v.Index(i) 25 | objPt := reflect.NewAt(obj.Type(), unsafe.Pointer(obj.UnsafeAddr())).Interface() 26 | objPt = Fill(objPt) 27 | obj.Set(reflect.ValueOf(objPt)) 28 | } 29 | case reflect.Struct: 30 | for i := 0; i < v.NumField(); i++ { 31 | f := reflect.Indirect(v.Field(i)) 32 | if !f.CanSet() { 33 | continue 34 | } 35 | switch f.Kind() { 36 | case reflect.Slice: 37 | f.Set(reflect.MakeSlice(f.Type(), 0, 0)) 38 | case reflect.Struct: 39 | switch f.Type() { 40 | case coinType: 41 | coin := reflect.New(coinType).Interface() 42 | s := reflect.ValueOf(coin).Elem() 43 | f.Set(s) 44 | case coinsType: 45 | coins := reflect.New(coinsType).Interface() 46 | s := reflect.ValueOf(coins).Elem() 47 | f.Set(s) 48 | default: 49 | objPt := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Interface() 50 | s := Fill(objPt) 51 | f.Set(reflect.ValueOf(s)) 52 | } 53 | } 54 | } 55 | } 56 | return reflect.Indirect(v).Interface() 57 | } 58 | -------------------------------------------------------------------------------- /upgrade.md: -------------------------------------------------------------------------------- 1 | # How to upgrade to mun-upgrade-v3 2 | ## 1. Vote 'yes' to upgrade proposal 3 | ``` 4 | mund tx gov vote [proposal_id] yes --from [wallet_name] --chain-id testmun --keyring-backend test -y 5 | ``` 6 | 7 | ## 2. Upgrade binary 8 | ``` 9 | wget -O update.sh https://raw.githubusercontent.com/munblockchain/mun/main/mupgrade.sh && chmod +x update.sh && sh update.sh 10 | ``` 11 | 12 | ## 3. Done, It will be upgraded when Munchain reaches to the upgrade block height. 13 | -------------------------------------------------------------------------------- /x/claim/abci.go: -------------------------------------------------------------------------------- 1 | package claim 2 | 3 | import ( 4 | "mun/x/claim/keeper" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // EndBlocker called every block, process inflation, update validator set. 10 | func EndBlocker(ctx sdk.Context, k keeper.Keeper) { 11 | params := k.GetParams(ctx) 12 | if !params.IsAirdropEnabled(ctx.BlockTime()) { 13 | return 14 | } 15 | // End Airdrop 16 | goneTime := ctx.BlockTime().Sub(params.AirdropStartTime) 17 | if goneTime > params.DurationUntilDecay+params.DurationOfDecay { 18 | // airdrop time passed 19 | err := k.EndAirdrop(ctx) 20 | if err != nil { 21 | panic(err) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /x/claim/client/cli/tx.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/cosmos/cosmos-sdk/client" 10 | // "github.com/cosmos/cosmos-sdk/client/flags" 11 | "mun/x/claim/types" 12 | ) 13 | 14 | var DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds()) 15 | 16 | // GetTxCmd returns the transaction commands for this module 17 | func GetTxCmd() *cobra.Command { 18 | cmd := &cobra.Command{ 19 | Use: types.ModuleName, 20 | Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), 21 | DisableFlagParsing: true, 22 | SuggestionsMinimumDistance: 2, 23 | RunE: client.ValidateCmd, 24 | } 25 | 26 | cmd.AddCommand(CmdClaimFor()) 27 | cmd.AddCommand(CmdUpdateMerkleRoot()) 28 | // this line is used by starport scaffolding # 1 29 | 30 | return cmd 31 | } 32 | -------------------------------------------------------------------------------- /x/claim/client/cli/tx_claim_for.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "mun/x/claim/types" 9 | 10 | "github.com/cosmos/cosmos-sdk/client" 11 | "github.com/cosmos/cosmos-sdk/client/flags" 12 | "github.com/cosmos/cosmos-sdk/client/tx" 13 | ) 14 | 15 | var _ = strconv.Itoa(0) 16 | 17 | func CmdClaimFor() *cobra.Command { 18 | cmd := &cobra.Command{ 19 | Use: "claim-for [action=0:InitialClaim,1:Staking,2:Voting]", 20 | Short: "Claim For Action", 21 | Args: cobra.ExactArgs(1), 22 | RunE: func(cmd *cobra.Command, args []string) (err error) { 23 | clientCtx, err := client.GetClientTxContext(cmd) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | action, err := strconv.ParseInt(args[0], 10, 64) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | var typeAction types.Action 34 | var proof string 35 | switch action { 36 | case 1: 37 | typeAction = types.ActionDelegateStake 38 | case 2: 39 | typeAction = types.ActionVote 40 | default: 41 | typeAction = types.ActionInitialClaim 42 | proof = cmd.Flags().Lookup("proof").Value.String() 43 | } 44 | 45 | msg := types.NewMsgClaimFor( 46 | clientCtx.GetFromAddress().String(), 47 | typeAction, 48 | proof, 49 | ) 50 | 51 | if err := msg.ValidateBasic(); err != nil { 52 | return err 53 | } 54 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 55 | }, 56 | } 57 | cmd.Flags().String("proof", "", "Proof for initial claim") 58 | 59 | flags.AddTxFlagsToCmd(cmd) 60 | 61 | return cmd 62 | } 63 | -------------------------------------------------------------------------------- /x/claim/client/cli/tx_update_merkle_root.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "mun/x/claim/types" 9 | 10 | "github.com/cosmos/cosmos-sdk/client" 11 | "github.com/cosmos/cosmos-sdk/client/flags" 12 | "github.com/cosmos/cosmos-sdk/client/tx" 13 | ) 14 | 15 | var _ = strconv.Itoa(0) 16 | 17 | func CmdUpdateMerkleRoot() *cobra.Command { 18 | cmd := &cobra.Command{ 19 | Use: "update-merkle-root [hash]", 20 | Short: "Update merkle root for initial airdrop", 21 | Args: cobra.ExactArgs(1), 22 | RunE: func(cmd *cobra.Command, args []string) (err error) { 23 | clientCtx, err := client.GetClientTxContext(cmd) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | msg := types.NewMsgUpdateMerkleRoot( 29 | clientCtx.GetFromAddress().String(), 30 | args[0], 31 | ) 32 | 33 | if err := msg.ValidateBasic(); err != nil { 34 | return err 35 | } 36 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 37 | }, 38 | } 39 | 40 | flags.AddTxFlagsToCmd(cmd) 41 | 42 | return cmd 43 | } 44 | -------------------------------------------------------------------------------- /x/claim/handler.go: -------------------------------------------------------------------------------- 1 | package claim 2 | 3 | import ( 4 | "fmt" 5 | 6 | "mun/x/claim/keeper" 7 | "mun/x/claim/types" 8 | 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 11 | ) 12 | 13 | // NewHandler ... 14 | func NewHandler(k keeper.Keeper) sdk.Handler { 15 | // this line is used by starport scaffolding # handler/msgServer 16 | msgServer := keeper.NewMsgServerImpl(k) 17 | // this line is used by starport scaffolding # handler/msgServer 18 | 19 | return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { 20 | ctx = ctx.WithEventManager(sdk.NewEventManager()) 21 | switch msg := msg.(type) { 22 | case *types.MsgUpdateMerkleRoot: 23 | res, err := msgServer.UpdateMerkleRoot(sdk.WrapSDKContext(ctx), msg) 24 | return sdk.WrapServiceResult(ctx, res, err) 25 | case *types.MsgClaimFor: 26 | res, err := msgServer.ClaimFor(sdk.WrapSDKContext(ctx), msg) 27 | return sdk.WrapServiceResult(ctx, res, err) 28 | // this line is used by starport scaffolding # 1 29 | default: 30 | errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg) 31 | return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /x/claim/keeper/genesis.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "time" 5 | 6 | "mun/x/claim/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | abci "github.com/tendermint/tendermint/abci/types" 10 | ) 11 | 12 | func (k Keeper) InitGenesis(ctx sdk.Context, data types.GenesisState) []abci.ValidatorUpdate { 13 | k.CreateModuleAccount(ctx, data.ModuleAccountBalance) 14 | if data.Params.AirdropEnabled && data.Params.AirdropStartTime.Equal(time.Time{}) { 15 | data.Params.AirdropStartTime = ctx.BlockTime() 16 | } 17 | err := k.SetClaimRecords(ctx, data.ClaimRecords) 18 | if err != nil { 19 | panic(err) 20 | } 21 | k.SetMerkleRoot(ctx, data.MerkleRoot) 22 | k.SetParams(ctx, data.Params) 23 | return nil 24 | } 25 | 26 | func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { 27 | genesis := types.DefaultGenesis() 28 | 29 | // this line is used by starport scaffolding # genesis/module/export 30 | params := k.GetParams(ctx) 31 | genesis.ModuleAccountBalance = k.GetModuleAccountBalance(ctx) 32 | genesis.Params = params 33 | genesis.ClaimRecords = k.ClaimRecords(ctx) 34 | genesis.MerkleRoot = k.GetMerkleRoot(ctx) 35 | return genesis 36 | } 37 | -------------------------------------------------------------------------------- /x/claim/keeper/grpc_query.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "mun/x/claim/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "google.golang.org/grpc/codes" 10 | "google.golang.org/grpc/status" 11 | ) 12 | 13 | var _ types.QueryServer = Keeper{} 14 | 15 | // Params returns params of the mint module. 16 | func (k Keeper) ModuleAccountBalance(c context.Context, _ *types.QueryModuleAccountBalanceRequest) (*types.QueryModuleAccountBalanceResponse, error) { 17 | ctx := sdk.UnwrapSDKContext(c) 18 | moduleAccBal := sdk.NewCoins(k.GetModuleAccountBalance(ctx)) 19 | 20 | return &types.QueryModuleAccountBalanceResponse{ModuleAccountBalance: moduleAccBal}, nil 21 | } 22 | 23 | // Params returns params of the mint module. 24 | func (k Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { 25 | ctx := sdk.UnwrapSDKContext(c) 26 | params := k.GetParams(ctx) 27 | 28 | return &types.QueryParamsResponse{Params: params}, nil 29 | } 30 | 31 | // Claimable returns claimable amount per user 32 | func (k Keeper) ClaimRecord( 33 | goCtx context.Context, 34 | req *types.QueryClaimRecordRequest, 35 | ) (*types.QueryClaimRecordResponse, error) { 36 | if req == nil { 37 | return nil, status.Error(codes.InvalidArgument, "empty request") 38 | } 39 | 40 | ctx := sdk.UnwrapSDKContext(goCtx) 41 | 42 | addr, err := sdk.AccAddressFromBech32(req.Address) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | claimRecord, err := k.GetClaimRecord(ctx, addr) 48 | return &types.QueryClaimRecordResponse{ClaimRecord: claimRecord}, err 49 | } 50 | 51 | // Activities returns activities 52 | func (k Keeper) ClaimableForAction( 53 | goCtx context.Context, 54 | req *types.QueryClaimableForActionRequest, 55 | ) (*types.QueryClaimableForActionResponse, error) { 56 | if req == nil { 57 | return nil, status.Error(codes.InvalidArgument, "empty request") 58 | } 59 | 60 | ctx := sdk.UnwrapSDKContext(goCtx) 61 | addr, err := sdk.AccAddressFromBech32(req.Address) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | coins, err := k.GetClaimableAmountForAction(ctx, addr, req.Action) 67 | 68 | return &types.QueryClaimableForActionResponse{ 69 | Coins: coins, 70 | }, err 71 | } 72 | 73 | // Activities returns activities 74 | func (k Keeper) TotalClaimable( 75 | goCtx context.Context, 76 | req *types.QueryTotalClaimableRequest, 77 | ) (*types.QueryTotalClaimableResponse, error) { 78 | if req == nil { 79 | return nil, status.Error(codes.InvalidArgument, "empty request") 80 | } 81 | 82 | ctx := sdk.UnwrapSDKContext(goCtx) 83 | addr, err := sdk.AccAddressFromBech32(req.Address) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | coins, err := k.GetUserTotalClaimable(ctx, addr) 89 | 90 | return &types.QueryTotalClaimableResponse{ 91 | Coins: coins, 92 | }, err 93 | } 94 | -------------------------------------------------------------------------------- /x/claim/keeper/grpc_query_merkle.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | "mun/x/claim/types" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "google.golang.org/grpc/codes" 9 | "google.golang.org/grpc/status" 10 | ) 11 | 12 | func (k Keeper) MerkleRoot( 13 | goCtx context.Context, 14 | req *types.QueryMerkleRootRequest, 15 | ) (*types.QueryMerkleRootResponse, error) { 16 | if req == nil { 17 | return nil, status.Error(codes.InvalidArgument, "empty request") 18 | } 19 | 20 | ctx := sdk.UnwrapSDKContext(goCtx) 21 | return &types.QueryMerkleRootResponse{ 22 | MerkleRoot: k.GetMerkleRoot(ctx), 23 | }, nil 24 | } 25 | 26 | func (k Keeper) Whitelisted(goCtx context.Context, 27 | req *types.QueryWhitelistedRequest, 28 | ) (*types.QueryWhitelistedResponse, error) { 29 | ctx := sdk.UnwrapSDKContext(goCtx) 30 | merkleRoot := k.GetMerkleRoot(ctx) 31 | 32 | return &types.QueryWhitelistedResponse{ 33 | Whitelisted: k.VerifyMerkleTree(merkleRoot, req.Address, req.Proof), 34 | }, nil 35 | } 36 | -------------------------------------------------------------------------------- /x/claim/keeper/hooks.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "mun/x/claim/types" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" 8 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 9 | ) 10 | 11 | func (k Keeper) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) { 12 | ret := k.SetClaimableActionReady(ctx, voterAddr, types.ActionVote) 13 | if !ret { 14 | panic("err.Error()") 15 | } 16 | } 17 | 18 | func (k Keeper) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { 19 | // must not run on genesis 20 | if ctx.BlockHeight() <= 1 { 21 | return 22 | } 23 | ret := k.SetClaimableActionReady(ctx, delAddr, types.ActionDelegateStake) 24 | if !ret { 25 | panic("err.Error()") 26 | } 27 | } 28 | 29 | // ________________________________________________________________________________________ 30 | 31 | // Hooks wrapper struct for slashing keeper 32 | type Hooks struct { 33 | k Keeper 34 | } 35 | 36 | var ( 37 | _ govtypes.GovHooks = Hooks{} 38 | _ stakingtypes.StakingHooks = Hooks{} 39 | ) 40 | 41 | // Return the wrapper struct 42 | func (k Keeper) Hooks() Hooks { 43 | return Hooks{k} 44 | } 45 | 46 | // governance hooks 47 | func (h Hooks) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) {} 48 | 49 | func (h Hooks) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) { 50 | } 51 | 52 | func (h Hooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) { 53 | h.k.AfterProposalVote(ctx, proposalID, voterAddr) 54 | } 55 | 56 | func (h Hooks) AfterProposalInactive(ctx sdk.Context, proposalID uint64) {} 57 | func (h Hooks) AfterProposalActive(ctx sdk.Context, proposalID uint64) {} 58 | 59 | func (h Hooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) {} 60 | func (h Hooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) {} 61 | 62 | // staking hooks 63 | func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {} 64 | func (h Hooks) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {} 65 | func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { 66 | } 67 | 68 | func (h Hooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { 69 | } 70 | 71 | func (h Hooks) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { 72 | } 73 | 74 | func (h Hooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { 75 | } 76 | 77 | func (h Hooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { 78 | } 79 | 80 | func (h Hooks) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { 81 | } 82 | 83 | func (h Hooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { 84 | h.k.AfterDelegationModified(ctx, delAddr, valAddr) 85 | } 86 | func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {} 87 | -------------------------------------------------------------------------------- /x/claim/keeper/keeper.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/tendermint/tendermint/libs/log" 7 | 8 | "mun/x/claim/types" 9 | 10 | "github.com/cosmos/cosmos-sdk/codec" 11 | sdk "github.com/cosmos/cosmos-sdk/types" 12 | paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" 13 | ) 14 | 15 | type ( 16 | Keeper struct { 17 | cdc codec.Codec 18 | storeKey sdk.StoreKey 19 | 20 | accountKeeper types.AccountKeeper 21 | bankKeeper types.BankKeeper 22 | stakingKeeper types.StakingKeeper 23 | distrKeeper types.DistrKeeper 24 | 25 | paramstore paramtypes.Subspace 26 | } 27 | ) 28 | 29 | func NewKeeper( 30 | cdc codec.Codec, 31 | storeKey sdk.StoreKey, 32 | accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper, 33 | stakingKeeper types.StakingKeeper, distrKeeper types.DistrKeeper, 34 | ps paramtypes.Subspace, 35 | ) *Keeper { 36 | // set KeyTable if it has not already been set 37 | if !ps.HasKeyTable() { 38 | ps = ps.WithKeyTable(types.ParamKeyTable()) 39 | } 40 | 41 | return &Keeper{ 42 | cdc: cdc, 43 | storeKey: storeKey, 44 | accountKeeper: accountKeeper, 45 | bankKeeper: bankKeeper, 46 | stakingKeeper: stakingKeeper, 47 | distrKeeper: distrKeeper, 48 | paramstore: ps, 49 | } 50 | } 51 | 52 | func (k Keeper) Logger(ctx sdk.Context) log.Logger { 53 | return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) 54 | } 55 | 56 | // GetModuleAccountBalance gets the airdrop coin balance of module account 57 | func (k Keeper) GetModuleAccountAddress(ctx sdk.Context) sdk.AccAddress { 58 | return k.accountKeeper.GetModuleAddress(types.ModuleName) 59 | } 60 | 61 | // GetModuleAccountBalance gets the airdrop coin balance of module account 62 | func (k Keeper) GetModuleAccountBalance(ctx sdk.Context) sdk.Coin { 63 | moduleAccAddr := k.GetModuleAccountAddress(ctx) 64 | params := k.GetParams(ctx) 65 | return k.bankKeeper.GetBalance(ctx, moduleAccAddr, params.ClaimDenom) 66 | } 67 | -------------------------------------------------------------------------------- /x/claim/keeper/merkle.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha256" 6 | "errors" 7 | ) 8 | 9 | func calcSha256(data []byte) []byte { 10 | s := sha256.Sum256(data) 11 | return s[:] 12 | } 13 | 14 | func VerifyProof(data []byte, hashes [][]byte, root []byte, dirs string) (bool, error) { 15 | dataHash := calcSha256(data) 16 | if len(hashes) != len(dirs) { 17 | return false, errors.New("invalid proof") 18 | } 19 | 20 | for i, hash := range hashes { 21 | if dirs[i] == 'R' { 22 | dataHash = calcSha256(append(dataHash, hash...)) 23 | } else { 24 | dataHash = calcSha256(append(hash, dataHash...)) 25 | } 26 | } 27 | return bytes.Equal(dataHash, root), nil 28 | } 29 | -------------------------------------------------------------------------------- /x/claim/keeper/msg_server.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "mun/x/claim/types" 5 | ) 6 | 7 | type msgServer struct { 8 | Keeper 9 | } 10 | 11 | // NewMsgServerImpl returns an implementation of the MsgServer interface 12 | // for the provided Keeper. 13 | func NewMsgServerImpl(keeper Keeper) types.MsgServer { 14 | return &msgServer{Keeper: keeper} 15 | } 16 | 17 | var _ types.MsgServer = msgServer{} 18 | -------------------------------------------------------------------------------- /x/claim/keeper/msg_server_claim_for.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "mun/x/claim/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | func (k msgServer) ClaimFor(goCtx context.Context, msg *types.MsgClaimFor) (*types.MsgClaimForResponse, error) { 12 | ctx := sdk.UnwrapSDKContext(goCtx) 13 | address, err := sdk.AccAddressFromBech32(msg.Sender) 14 | if err != nil { 15 | return nil, err 16 | } 17 | params := k.GetParams(ctx) 18 | if !params.IsAirdropEnabled(ctx.BlockTime()) { 19 | return nil, types.ErrAirdropNotEnabled 20 | } 21 | 22 | // Check if whitelisted for Initial Claim 23 | if msg.GetAction() == types.ActionInitialClaim { 24 | merkleRoot := k.Keeper.GetMerkleRoot(ctx) 25 | if !k.Keeper.VerifyMerkleTree(merkleRoot, msg.Sender, msg.GetProof()) { 26 | return nil, types.ErrUnauthorizedClaimer 27 | } 28 | } 29 | 30 | coins, err := k.Keeper.ClaimCoinsForAction(ctx, address, msg.GetAction()) 31 | if err != nil { 32 | return nil, err 33 | } 34 | ctx.EventManager().EmitEvents(sdk.Events{ 35 | sdk.NewEvent( 36 | sdk.EventTypeMessage, 37 | sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), 38 | sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), 39 | ), 40 | }) 41 | return &types.MsgClaimForResponse{ 42 | Address: msg.Sender, 43 | ClaimedAmount: coins, 44 | }, nil 45 | } 46 | -------------------------------------------------------------------------------- /x/claim/keeper/msg_server_update_merkle_root.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | "mun/x/claim/types" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | ) 9 | 10 | const ( 11 | AdminAddress = "mun1g5azxlyk6hnurr627xqd4n6efwjhmngqh3qkl5" 12 | ) 13 | 14 | func (k msgServer) UpdateMerkleRoot(goCtx context.Context, msg *types.MsgUpdateMerkleRoot) (*types.MsgUpdateMerkleRootResponse, error) { 15 | ctx := sdk.UnwrapSDKContext(goCtx) 16 | 17 | if AdminAddress != msg.Sender { 18 | return nil, types.ErrInvalidAdminAddress 19 | } 20 | 21 | k.Keeper.SetMerkleRoot(ctx, msg.RootValue) 22 | 23 | return &types.MsgUpdateMerkleRootResponse{}, nil 24 | } 25 | -------------------------------------------------------------------------------- /x/claim/keeper/params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "mun/x/claim/types" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // GetParams returns the total set of claim parameters. 10 | func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { 11 | k.paramstore.GetParamSet(ctx, ¶ms) 12 | return params 13 | } 14 | 15 | // SetParams sets claim parameters to the param space. 16 | func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { 17 | k.paramstore.SetParamSet(ctx, ¶ms) 18 | } 19 | -------------------------------------------------------------------------------- /x/claim/module.go: -------------------------------------------------------------------------------- 1 | package claim 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | 8 | // this line is used by starport scaffolding # 1 9 | 10 | "github.com/gorilla/mux" 11 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 12 | "github.com/spf13/cobra" 13 | 14 | abci "github.com/tendermint/tendermint/abci/types" 15 | 16 | "mun/x/claim/client/cli" 17 | "mun/x/claim/keeper" 18 | "mun/x/claim/types" 19 | 20 | "github.com/cosmos/cosmos-sdk/client" 21 | "github.com/cosmos/cosmos-sdk/codec" 22 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 23 | sdk "github.com/cosmos/cosmos-sdk/types" 24 | "github.com/cosmos/cosmos-sdk/types/module" 25 | ) 26 | 27 | var ( 28 | _ module.AppModule = AppModule{} 29 | _ module.AppModuleBasic = AppModuleBasic{} 30 | _ module.AppModuleSimulation = AppModule{} 31 | ) 32 | 33 | // ---------------------------------------------------------------------------- 34 | // AppModuleBasic 35 | // ---------------------------------------------------------------------------- 36 | 37 | // AppModuleBasic implements the AppModuleBasic interface for the claim module. 38 | type AppModuleBasic struct { 39 | cdc codec.BinaryCodec 40 | } 41 | 42 | func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic { 43 | return AppModuleBasic{cdc: cdc} 44 | } 45 | 46 | // Name returns the claim module's name. 47 | func (AppModuleBasic) Name() string { 48 | return types.ModuleName 49 | } 50 | 51 | func (AppModuleBasic) RegisterCodec(cdc *codec.LegacyAmino) { 52 | types.RegisterLegacyAminoCodec(cdc) 53 | } 54 | 55 | func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { 56 | types.RegisterLegacyAminoCodec(cdc) 57 | } 58 | 59 | // RegisterInterfaces registers the module's interface types 60 | func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) { 61 | types.RegisterInterfaces(reg) 62 | } 63 | 64 | // DefaultGenesis returns the claim module's default genesis state. 65 | func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { 66 | return cdc.MustMarshalJSON(types.DefaultGenesis()) 67 | } 68 | 69 | // ValidateGenesis performs genesis state validation for the claim module. 70 | func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { 71 | var genState types.GenesisState 72 | if err := cdc.UnmarshalJSON(bz, &genState); err != nil { 73 | return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) 74 | } 75 | return genState.Validate() 76 | } 77 | 78 | // RegisterRESTRoutes registers the claim module's REST service handlers. 79 | func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { 80 | } 81 | 82 | // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module. 83 | func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { 84 | err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) 85 | if err != nil { 86 | panic(err) 87 | } 88 | } 89 | 90 | // GetTxCmd returns the claim module's root tx command. 91 | func (a AppModuleBasic) GetTxCmd() *cobra.Command { 92 | return cli.GetTxCmd() 93 | } 94 | 95 | // GetQueryCmd returns the claim module's root query command. 96 | func (AppModuleBasic) GetQueryCmd() *cobra.Command { 97 | return cli.GetQueryCmd(types.StoreKey) 98 | } 99 | 100 | // ---------------------------------------------------------------------------- 101 | // AppModule 102 | // ---------------------------------------------------------------------------- 103 | 104 | // AppModule implements the AppModule interface for the claim module. 105 | type AppModule struct { 106 | AppModuleBasic 107 | 108 | accountKeeper types.AccountKeeper 109 | bankKeeper types.BankKeeper 110 | keeper keeper.Keeper 111 | } 112 | 113 | func NewAppModule(cdc codec.Codec, accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper, keeper keeper.Keeper) AppModule { 114 | return AppModule{ 115 | AppModuleBasic: NewAppModuleBasic(cdc), 116 | accountKeeper: accountKeeper, 117 | bankKeeper: bankKeeper, 118 | keeper: keeper, 119 | } 120 | } 121 | 122 | // Name returns the claim module's name. 123 | func (am AppModule) Name() string { 124 | return am.AppModuleBasic.Name() 125 | } 126 | 127 | // Route returns the claim module's message routing key. 128 | func (am AppModule) Route() sdk.Route { 129 | return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) 130 | } 131 | 132 | // QuerierRoute returns the claim module's query routing key. 133 | func (AppModule) QuerierRoute() string { return types.QuerierRoute } 134 | 135 | // LegacyQuerierHandler returns the claim module's Querier. 136 | func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { 137 | return nil 138 | } 139 | 140 | // RegisterServices registers a GRPC query service to respond to the 141 | // module-specific GRPC queries. 142 | func (am AppModule) RegisterServices(cfg module.Configurator) { 143 | types.RegisterQueryServer(cfg.QueryServer(), am.keeper) 144 | types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) 145 | err := cfg.RegisterMigration(types.ModuleName, 2, func(sdk.Context) error { return nil }) 146 | if err != nil { 147 | panic(err) 148 | } 149 | } 150 | 151 | // RegisterInvariants registers the claim module's invariants. 152 | func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} 153 | 154 | // InitGenesis performs the claim module's genesis initialization It returns 155 | // no validator updates. 156 | func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { 157 | var genState types.GenesisState 158 | // Initialize global index to index in genesis state 159 | cdc.MustUnmarshalJSON(gs, &genState) 160 | 161 | return am.keeper.InitGenesis(ctx, genState) 162 | } 163 | 164 | // ExportGenesis returns the claim module's exported genesis state as raw JSON bytes. 165 | func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { 166 | genState := am.keeper.ExportGenesis(ctx) 167 | return cdc.MustMarshalJSON(genState) 168 | } 169 | 170 | // ConsensusVersion implements ConsensusVersion. 171 | func (AppModule) ConsensusVersion() uint64 { return 2 } 172 | 173 | // BeginBlock executes all ABCI BeginBlock logic respective to the claim module. 174 | func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} 175 | 176 | // EndBlock executes all ABCI EndBlock logic respective to the claim module. It 177 | // returns no validator updates. 178 | func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { 179 | EndBlocker(ctx, am.keeper) 180 | return []abci.ValidatorUpdate{} 181 | } 182 | -------------------------------------------------------------------------------- /x/claim/module_simulation.go: -------------------------------------------------------------------------------- 1 | package claim 2 | 3 | import ( 4 | "math/rand" 5 | "mun/x/claim/simulation" 6 | "mun/x/claim/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "github.com/cosmos/cosmos-sdk/types/module" 10 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 11 | ) 12 | 13 | // GenerateGenesisState creates a randomized GenState of the ibank module. 14 | func (AppModule) GenerateGenesisState(simState *module.SimulationState) { 15 | simulation.RandomizedGenState(simState) 16 | } 17 | 18 | // ProposalContents returns nothing for governance proposals. 19 | func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { 20 | return nil 21 | } 22 | 23 | // RandomizedParams creates randomized cronos param changes for the simulator. 24 | func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { 25 | return simulation.ParamChanges(r) 26 | } 27 | 28 | // RegisterStoreDecoder registers a decoder for ibank module's types 29 | func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { 30 | sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) 31 | } 32 | 33 | // WeightedOperations returns the all the ibank module operations with their respective weights. 34 | func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { 35 | return simulation.WeightedOperations( 36 | simState.AppParams, simState.Cdc, am.accountKeeper, am.bankKeeper, &am.keeper, 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /x/claim/simulation/decoder.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "mun/x/claim/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | "github.com/cosmos/cosmos-sdk/types/kv" 10 | ) 11 | 12 | // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's 13 | // Value to the corresponding claim type. 14 | func NewDecodeStore(cdc codec.BinaryCodec) func(kvA, kvB kv.Pair) string { 15 | return func(kvA, kvB kv.Pair) string { 16 | switch { 17 | case bytes.Equal(kvA.Key, types.ClaimRecordsStorePrefix): 18 | var claimRecordA, claimRecordB types.ClaimRecord 19 | cdc.MustUnmarshal(kvA.Value, &claimRecordA) 20 | cdc.MustUnmarshal(kvB.Value, &claimRecordB) 21 | return fmt.Sprintf("%v\n%v", claimRecordA, claimRecordB) 22 | case bytes.Equal(kvA.Key, types.MerkleRootStorePrefix): 23 | return fmt.Sprintf("%v\n%v", string(kvA.Value), string(kvB.Value)) 24 | default: 25 | panic(fmt.Sprintf("invalid claim key prefix %v", kvA.Key)) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /x/claim/simulation/decoder_test.go: -------------------------------------------------------------------------------- 1 | package simulation_test 2 | 3 | import ( 4 | "fmt" 5 | "mun/app" 6 | "mun/x/claim/simulation" 7 | "mun/x/claim/types" 8 | "testing" 9 | 10 | "github.com/cosmos/cosmos-sdk/types/kv" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestDecodeStore(t *testing.T) { 15 | cdc := app.MakeEncodingConfig().Marshaler 16 | dec := simulation.NewDecodeStore(cdc) 17 | 18 | merkleRoot := "558ad18828f6da6d471cdb1a3443f039a770e03617f163896980d914d643e4bc" 19 | claimRecord := types.ClaimRecord{ 20 | Address: "mun1vk2zrdsg74vrztr5pe5qa0h6ljgmfy4sz2apec", 21 | ActionReady: make([]bool, 4), 22 | ActionCompleted: make([]bool, 4), 23 | } 24 | 25 | kvParis := kv.Pairs{ 26 | Pairs: []kv.Pair{ 27 | {Key: types.ClaimRecordsStorePrefix, Value: cdc.MustMarshal(&claimRecord)}, 28 | {Key: types.MerkleRootStorePrefix, Value: []byte(merkleRoot)}, 29 | }, 30 | } 31 | 32 | tests := []struct { 33 | name string 34 | expectedLog string 35 | }{ 36 | {"ClaimRecord", fmt.Sprintf("%v\n%v", claimRecord, claimRecord)}, 37 | {"MerkleRoot", fmt.Sprintf("%v\n%v", merkleRoot, merkleRoot)}, 38 | {"other", ""}, 39 | } 40 | 41 | for i, tt := range tests { 42 | i, tt := i, tt 43 | t.Run(tt.name, func(t *testing.T) { 44 | switch i { 45 | case len(tests) - 1: 46 | require.Panics(t, func() { dec(kvParis.Pairs[i], kvParis.Pairs[i]) }, tt.name) 47 | default: 48 | require.Equal(t, tt.expectedLog, dec(kvParis.Pairs[i], kvParis.Pairs[i]), tt.name) 49 | } 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /x/claim/simulation/genesis.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math/rand" 7 | "mun/x/claim/types" 8 | "time" 9 | 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | "github.com/cosmos/cosmos-sdk/types/module" 12 | ) 13 | 14 | const ( 15 | Enabled = "enabled" 16 | ClaimDenom = "claim_denom" 17 | StartTime = "start_time" 18 | DurationUntilDecay = "duration_until_decay" 19 | DurationOfDecay = "duratin_of_decay" 20 | ) 21 | 22 | func RandomEnabledFlag(r *rand.Rand) bool { 23 | return r.Int63n(101) <= 50 // 50% chance of airdrop enabled 24 | } 25 | 26 | func RandomStartTime(r *rand.Rand) time.Time { 27 | return time.Time{}.Add(time.Second * time.Duration(r.Int63n(10000))) 28 | } 29 | 30 | func RandomDurationUntilDecay(r *rand.Rand) time.Duration { 31 | // 1~100 seconds 32 | return time.Second * time.Duration(1+r.Int63n(100)) 33 | } 34 | 35 | func RandomDurationOfDecay(r *rand.Rand) time.Duration { 36 | // 1~5000 seconds 37 | return time.Second * time.Duration(1+r.Int63n(5000)) 38 | } 39 | 40 | func RandomGenesisModuleAccountBalance(r *rand.Rand) sdk.Coin { 41 | // 1000000 ~ 2000000 TMUN 42 | return sdk.NewCoin("utmun", sdk.NewInt(r.Int63n(1000000)+1000000).Mul(sdk.NewInt(1000000))) 43 | } 44 | 45 | func RandomGenesisMerkleRoot() string { 46 | return "04c287c1de1a9734959d8c04725fe56d7b70efefd4f13eb6885386cf55a8501b" // sha256("random genesis merkle root") 47 | } 48 | 49 | func RandomGenesisClaimRecords(simState *module.SimulationState) []types.ClaimRecord { 50 | records := make([]types.ClaimRecord, 0) 51 | 52 | for _, acc := range simState.Accounts { 53 | records = append(records, types.ClaimRecord{ 54 | Address: acc.Address.String(), 55 | ActionReady: make([]bool, 0), 56 | ActionCompleted: make([]bool, 0), 57 | }) 58 | } 59 | return records 60 | } 61 | 62 | // RandomizedGenState generates a random GenesisState for claim 63 | func RandomizedGenState(simState *module.SimulationState) { 64 | var enabled bool 65 | simState.AppParams.GetOrGenerate( 66 | simState.Cdc, Enabled, &enabled, simState.Rand, 67 | func(r *rand.Rand) { enabled = RandomEnabledFlag(r) }, 68 | ) 69 | 70 | claimDenom := "utmun" 71 | 72 | var startTime time.Time 73 | simState.AppParams.GetOrGenerate( 74 | simState.Cdc, StartTime, &startTime, simState.Rand, 75 | func(r *rand.Rand) { startTime = RandomStartTime(r) }, 76 | ) 77 | 78 | var durationUntilDecay time.Duration 79 | simState.AppParams.GetOrGenerate( 80 | simState.Cdc, DurationUntilDecay, &durationUntilDecay, simState.Rand, 81 | func(r *rand.Rand) { durationUntilDecay = RandomDurationUntilDecay(r) }, 82 | ) 83 | 84 | var durationOfDecay time.Duration 85 | simState.AppParams.GetOrGenerate( 86 | simState.Cdc, DurationOfDecay, &durationOfDecay, simState.Rand, 87 | func(r *rand.Rand) { durationOfDecay = RandomDurationOfDecay(r) }, 88 | ) 89 | 90 | params := types.NewParams( 91 | enabled, 92 | claimDenom, 93 | startTime, 94 | durationUntilDecay, 95 | durationOfDecay, 96 | ) 97 | 98 | claimGenesis := types.NewGenesisState( 99 | RandomGenesisModuleAccountBalance(simState.Rand), 100 | params, 101 | RandomGenesisClaimRecords(simState), 102 | RandomGenesisMerkleRoot(), 103 | ) 104 | 105 | bz, err := json.MarshalIndent(&claimGenesis.Params, "", " ") 106 | if err != nil { 107 | panic(err) 108 | } 109 | fmt.Printf("Selected randomly generated claim parameters:\n%s\n", bz) 110 | simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(claimGenesis) 111 | } 112 | -------------------------------------------------------------------------------- /x/claim/simulation/genesis_test.go: -------------------------------------------------------------------------------- 1 | package simulation_test 2 | 3 | import ( 4 | "encoding/json" 5 | "math/rand" 6 | "mun/x/claim/simulation" 7 | "mun/x/claim/types" 8 | "testing" 9 | 10 | "github.com/cosmos/cosmos-sdk/codec" 11 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 12 | "github.com/cosmos/cosmos-sdk/types/module" 13 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 14 | ) 15 | 16 | func TestRandomizedGenState(t *testing.T) { 17 | interfaceRegistry := codectypes.NewInterfaceRegistry() 18 | cdc := codec.NewProtoCodec(interfaceRegistry) 19 | s := rand.NewSource(1) 20 | r := rand.New(s) 21 | 22 | simState := module.SimulationState{ 23 | AppParams: make(simtypes.AppParams), 24 | Cdc: cdc, 25 | Rand: r, 26 | NumBonded: 3, 27 | Accounts: simtypes.RandomAccounts(r, 3), 28 | InitialStake: 1000, 29 | GenState: make(map[string]json.RawMessage), 30 | } 31 | 32 | simulation.RandomizedGenState(&simState) 33 | 34 | var claimGenesis types.GenesisState 35 | simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &claimGenesis) 36 | } 37 | -------------------------------------------------------------------------------- /x/claim/simulation/operations.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "math/rand" 7 | "mun/x/claim/keeper" 8 | "mun/x/claim/types" 9 | 10 | "github.com/cosmos/cosmos-sdk/baseapp" 11 | "github.com/cosmos/cosmos-sdk/codec" 12 | simappparams "github.com/cosmos/cosmos-sdk/simapp/params" 13 | sdk "github.com/cosmos/cosmos-sdk/types" 14 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 15 | "github.com/cosmos/cosmos-sdk/x/simulation" 16 | ) 17 | 18 | const ( 19 | DefaultWeightMsgClaimFor int = 100 20 | DefaultWeightMsgUpdateMerkleRoot int = 80 21 | 22 | OpWeightMsgClaimFor = "op_weight_msg_claim_for" 23 | OpWeightMsgUpdateMerkleRoot = "op_weight_msg_update_merkle_root" 24 | ) 25 | 26 | func WeightedOperations( 27 | appParams simtypes.AppParams, 28 | cdc codec.JSONCodec, 29 | ak types.AccountKeeper, 30 | bk types.BankKeeper, 31 | k *keeper.Keeper, 32 | ) []simtypes.WeightedOperation { 33 | var ( 34 | weightMsgClaimFor int 35 | weightMsgUpdateMerkleRoot int 36 | ) 37 | 38 | appParams.GetOrGenerate(cdc, OpWeightMsgClaimFor, &weightMsgClaimFor, nil, 39 | func(_ *rand.Rand) { 40 | weightMsgClaimFor = DefaultWeightMsgClaimFor 41 | }, 42 | ) 43 | 44 | appParams.GetOrGenerate(cdc, OpWeightMsgUpdateMerkleRoot, &weightMsgUpdateMerkleRoot, nil, 45 | func(_ *rand.Rand) { 46 | weightMsgUpdateMerkleRoot = DefaultWeightMsgUpdateMerkleRoot 47 | }, 48 | ) 49 | 50 | return simulation.WeightedOperations{ 51 | // simulation.NewWeightedOperation( 52 | // weightMsgClaimFor, 53 | // SimulateMsgClaimFor(ak, bk, k), 54 | // ), 55 | // simulation.NewWeightedOperation( 56 | // weightMsgUpdateMerkleRoot, 57 | // SimulateMsgUpdateMerkleRoot(ak), 58 | // ), 59 | } 60 | } 61 | 62 | func SimulateMsgClaimFor( 63 | ak types.AccountKeeper, 64 | bk types.BankKeeper, 65 | k *keeper.Keeper, 66 | ) simtypes.Operation { 67 | return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 68 | ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 69 | simAccount, _ := simtypes.RandomAcc(r, accs) 70 | 71 | sender := simAccount.Address.String() 72 | msg := types.NewMsgClaimFor(sender, types.Action(rand.Intn(3)), "") 73 | 74 | txCtx := simulation.OperationInput{ 75 | R: r, 76 | App: app, 77 | TxGen: simappparams.MakeTestEncodingConfig().TxConfig, 78 | Cdc: nil, 79 | Msg: msg, 80 | MsgType: msg.Type(), 81 | Context: ctx, 82 | SimAccount: simAccount, 83 | AccountKeeper: ak, 84 | Bankkeeper: bk, 85 | ModuleName: types.ModuleName, 86 | } 87 | 88 | return simulation.GenAndDeliverTxWithRandFees(txCtx) 89 | } 90 | } 91 | 92 | func SimulateMsgUpdateMerkleRoot(ak types.AccountKeeper) simtypes.Operation { 93 | return func( 94 | r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 95 | ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 96 | simAccount, _ := simtypes.RandomAcc(r, accs) 97 | 98 | // Generate random merkle root hash 99 | randomValue := fmt.Sprintf("%d", rand.Int()) 100 | merkleRoot := fmt.Sprintf("%x", sha256.Sum256([]byte(randomValue))) 101 | 102 | msg := types.NewMsgUpdateMerkleRoot(simAccount.Address.String(), merkleRoot) 103 | 104 | txCtx := simulation.OperationInput{ 105 | R: r, 106 | App: app, 107 | TxGen: simappparams.MakeTestEncodingConfig().TxConfig, 108 | Cdc: nil, 109 | Msg: msg, 110 | MsgType: msg.Type(), 111 | Context: ctx, 112 | SimAccount: simAccount, 113 | AccountKeeper: ak, 114 | ModuleName: types.ModuleName, 115 | } 116 | 117 | return simulation.GenAndDeliverTxWithRandFees(txCtx) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /x/claim/simulation/params.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "mun/x/claim/types" 7 | 8 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 9 | "github.com/cosmos/cosmos-sdk/x/simulation" 10 | ) 11 | 12 | // ParamChanges defines the parameters that can be modified by param change proposals 13 | // on the simulation 14 | func ParamChanges(r *rand.Rand) []simtypes.ParamChange { 15 | return []simtypes.ParamChange{ 16 | simulation.NewSimParamChange(types.ModuleName, string(types.KeyEnabled), 17 | func(r *rand.Rand) string { 18 | return fmt.Sprintf("%v", RandomEnabledFlag(r)) 19 | }, 20 | ), 21 | simulation.NewSimParamChange(types.ModuleName, string(types.KeyStartTime), 22 | func(r *rand.Rand) string { 23 | return fmt.Sprintf("\"%s\"", RandomStartTime(r).UTC().Format("2006-01-02T15:04:05Z07:00")) 24 | }, 25 | ), 26 | simulation.NewSimParamChange(types.ModuleName, string(types.KeyDurationUntilDecay), 27 | func(r *rand.Rand) string { 28 | return fmt.Sprintf("\"%d\"", RandomDurationUntilDecay(r)) 29 | }, 30 | ), 31 | simulation.NewSimParamChange(types.ModuleName, string(types.KeyDurationOfDecay), 32 | func(r *rand.Rand) string { 33 | return fmt.Sprintf("\"%d\"", RandomDurationOfDecay(r)) 34 | }, 35 | ), 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /x/claim/simulation/params_test.go: -------------------------------------------------------------------------------- 1 | package simulation_test 2 | 3 | import ( 4 | "math/rand" 5 | "mun/x/claim/simulation" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestParamChanges(t *testing.T) { 12 | s := rand.NewSource(1) 13 | r := rand.New(s) 14 | 15 | expected := []struct { 16 | composedKey string 17 | key string 18 | simValue string 19 | subspace string 20 | }{ 21 | {"claim/Enabled", "Enabled", "false", "claim"}, 22 | {"claim/StartTime", "StartTime", "\"0001-01-01T00:59:11Z\"", "claim"}, 23 | {"claim/DurationUntilDecay", "DurationUntilDecay", "\"22000000000\"", "claim"}, 24 | {"claim/DurationOfDecay", "DurationOfDecay", "\"52000000000\"", "claim"}, 25 | } 26 | 27 | paramChanges := simulation.ParamChanges(r) 28 | 29 | require.Len(t, paramChanges, 4) 30 | 31 | for i, p := range paramChanges { 32 | require.Equal(t, expected[i].composedKey, p.ComposedKey()) 33 | require.Equal(t, expected[i].key, p.Key()) 34 | require.Equal(t, expected[i].simValue, p.SimValue()(r)) 35 | require.Equal(t, expected[i].subspace, p.Subspace()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /x/claim/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 6 | cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "github.com/cosmos/cosmos-sdk/types/msgservice" 9 | ) 10 | 11 | func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { 12 | // cdc.RegisterConcrete(&MsgInitialClaim{}, "claim/InitialClaim", nil) 13 | cdc.RegisterConcrete(&MsgClaimFor{}, "claim/ClaimFor", nil) 14 | // this line is used by starport scaffolding # 2 15 | } 16 | 17 | func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { 18 | registry.RegisterImplementations((*sdk.Msg)(nil), 19 | // &MsgInitialClaim{}, 20 | &MsgClaimFor{}, 21 | ) 22 | 23 | // this line is used by starport scaffolding # 3 24 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) 25 | } 26 | 27 | var ( 28 | amino = codec.NewLegacyAmino() 29 | // ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) 30 | ModuleCdc = codec.NewAminoCodec(amino) 31 | ) 32 | 33 | func init() { 34 | RegisterLegacyAminoCodec(amino) 35 | cryptocodec.RegisterCrypto(amino) 36 | amino.Seal() 37 | } 38 | -------------------------------------------------------------------------------- /x/claim/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // DONTCOVER 4 | 5 | import ( 6 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 7 | ) 8 | 9 | // x/claim module sentinel errors 10 | var ( 11 | ErrAirdropNotEnabled = sdkerrors.Register(ModuleName, 2, "airdrop not enabled") 12 | ErrIncorrectModuleAccountBalance = sdkerrors.Register(ModuleName, 3, "claim module account balance != sum of all claim record InitialClaimableAmounts") 13 | ErrUnauthorizedClaimer = sdkerrors.Register(ModuleName, 4, "address is not allowed to claim") 14 | ErrInvalidAdminAddress = sdkerrors.Register(ModuleName, 5, "admin address is not valid") 15 | ErrInvalidBech32Address = sdkerrors.Register(ModuleName, 6, "invalid bech32 address") 16 | ) 17 | -------------------------------------------------------------------------------- /x/claim/types/events.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | EventTypeClaim = "claim" 5 | AttributeValueCategory = ModuleName 6 | ) 7 | -------------------------------------------------------------------------------- /x/claim/types/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 6 | ) 7 | 8 | type AccountKeeper interface { 9 | GetModuleAddress(name string) sdk.AccAddress 10 | SetModuleAccount(ctx sdk.Context, macc authtypes.ModuleAccountI) 11 | GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI // only used for simulation 12 | } 13 | 14 | type BankKeeper interface { 15 | SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error 16 | SendCoinsFromModuleToModule(ctx sdk.Context, senderPool, recipientPool string, amt sdk.Coins) error 17 | GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin 18 | MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error 19 | SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins 20 | } 21 | 22 | type StakingKeeper interface { 23 | BondDenom(sdk.Context) string 24 | } 25 | 26 | type DistrKeeper interface { 27 | FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error 28 | } 29 | -------------------------------------------------------------------------------- /x/claim/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | "github.com/cosmos/cosmos-sdk/codec" 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | // this line is used by starport scaffolding # genesis/types/import 12 | 13 | // DefaultIndex is the default capability global index 14 | const DefaultIndex uint64 = 1 15 | 16 | // DefaultGenesis returns the default Capability genesis state 17 | func DefaultGenesis() *GenesisState { 18 | return &GenesisState{ 19 | // this line is used by starport scaffolding # genesis/types/default 20 | ModuleAccountBalance: sdk.NewCoin(DefaultClaimDenom, sdk.ZeroInt()), 21 | Params: DefaultParams(), 22 | ClaimRecords: make([]ClaimRecord, 0), 23 | MerkleRoot: "", 24 | } 25 | } 26 | 27 | func DefaultParams() Params { 28 | return Params{ 29 | AirdropEnabled: true, 30 | AirdropStartTime: time.Time{}, 31 | DurationUntilDecay: DefaultDurationUntilDecay, 32 | DurationOfDecay: DefaultDurationOfDecay, 33 | ClaimDenom: DefaultClaimDenom, 34 | } 35 | } 36 | 37 | func NewGenesisState(moduleAccountBalance sdk.Coin, params Params, claimRecords []ClaimRecord, merkleRoot string) *GenesisState { 38 | return &GenesisState{ 39 | ModuleAccountBalance: moduleAccountBalance, 40 | Params: params, 41 | ClaimRecords: claimRecords, 42 | MerkleRoot: merkleRoot, 43 | } 44 | } 45 | 46 | // Validate performs basic genesis state validation returning an error upon any 47 | // failure. 48 | func (gs GenesisState) Validate() error { 49 | // this line is used by starport scaffolding # genesis/types/validate 50 | totalClaimable := sdk.Coins{} 51 | for _, claimRecord := range gs.ClaimRecords { 52 | totalClaimable = totalClaimable.Add(claimRecord.InitialClaimableAmount...) 53 | } 54 | 55 | if !totalClaimable.IsEqual(sdk.NewCoins(gs.ModuleAccountBalance)) { 56 | return ErrIncorrectModuleAccountBalance 57 | } 58 | 59 | return nil 60 | } 61 | 62 | // GetGenesisStateFromAppState return GenesisState 63 | func GetGenesisStateFromAppState(cdc codec.JSONCodec, appState map[string]json.RawMessage) *GenesisState { 64 | var genesisState GenesisState 65 | 66 | if appState[ModuleName] != nil { 67 | cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) 68 | } 69 | 70 | return &genesisState 71 | } 72 | -------------------------------------------------------------------------------- /x/claim/types/genesis_test.go: -------------------------------------------------------------------------------- 1 | package types_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "mun/x/claim/types" 8 | 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestGenesisState_Validate(t *testing.T) { 14 | for _, tc := range []struct { 15 | desc string 16 | genState *types.GenesisState 17 | valid bool 18 | }{ 19 | { 20 | desc: "default is valid", 21 | genState: types.DefaultGenesis(), 22 | valid: true, 23 | }, 24 | { 25 | desc: "valid genesis state", 26 | genState: &types.GenesisState{ 27 | // this line is used by starport scaffolding # types/genesis/validField 28 | ModuleAccountBalance: sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()), 29 | Params: types.Params{ 30 | AirdropEnabled: true, 31 | AirdropStartTime: time.Time{}, 32 | DurationUntilDecay: time.Hour * 24 * 60, 33 | DurationOfDecay: time.Hour * 24 * 30 * 4, 34 | ClaimDenom: sdk.DefaultBondDenom, 35 | }, 36 | ClaimRecords: []types.ClaimRecord{}, 37 | }, 38 | valid: true, 39 | }, 40 | // this line is used by starport scaffolding # types/genesis/testcase 41 | } { 42 | t.Run(tc.desc, func(t *testing.T) { 43 | err := tc.genState.Validate() 44 | if tc.valid { 45 | require.NoError(t, err) 46 | } else { 47 | require.Error(t, err) 48 | } 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /x/claim/types/keys.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName defines the module name 5 | ModuleName = "claim" 6 | 7 | // StoreKey defines the primary module store key 8 | StoreKey = ModuleName 9 | 10 | // RouterKey is the message route for slashing 11 | RouterKey = ModuleName 12 | 13 | // QuerierRoute defines the module's query routing key 14 | QuerierRoute = ModuleName 15 | 16 | // MemStoreKey defines the in-memory store key 17 | MemStoreKey = "mem_claim" 18 | 19 | // ParamsKey defines the store key for claim module parameters 20 | ParamsKey = "params" 21 | 22 | // ActionKey defines the store key to store user accomplished actions 23 | ActionKey = "action" 24 | 25 | InitialClaimAmount = "1000000000utmun" 26 | ) 27 | 28 | // KVStore keys 29 | var ( 30 | // ClaimRecordsStorePrefix defines the store prefix for the claim records 31 | ClaimRecordsStorePrefix = []byte{0x01} 32 | 33 | // MerkleRootStorePrefix defines the store prefix for the merkle root 34 | MerkleRootStorePrefix = []byte{0x02} 35 | ) 36 | -------------------------------------------------------------------------------- /x/claim/types/message_claim_for.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 6 | ) 7 | 8 | var _ sdk.Msg = &MsgClaimFor{} 9 | 10 | // msg types 11 | const ( 12 | TypeMsgClaimFor = "claim_for" 13 | ) 14 | 15 | func NewMsgClaimFor(sender string, action Action, proof string) *MsgClaimFor { 16 | return &MsgClaimFor{ 17 | Sender: sender, 18 | Action: action, 19 | Proof: proof, 20 | } 21 | } 22 | 23 | func (msg *MsgClaimFor) Route() string { 24 | return RouterKey 25 | } 26 | 27 | func (msg *MsgClaimFor) Type() string { 28 | return TypeMsgClaimFor 29 | } 30 | 31 | func (msg *MsgClaimFor) GetSigners() []sdk.AccAddress { 32 | sender, err := sdk.AccAddressFromBech32(msg.Sender) 33 | if err != nil { 34 | panic(err) 35 | } 36 | return []sdk.AccAddress{sender} 37 | } 38 | 39 | func (msg *MsgClaimFor) GetSignBytes() []byte { 40 | bz := ModuleCdc.MustMarshalJSON(msg) 41 | return sdk.MustSortJSON(bz) 42 | } 43 | 44 | func (msg *MsgClaimFor) ValidateBasic() error { 45 | _, err := sdk.AccAddressFromBech32(msg.Sender) 46 | if err != nil { 47 | return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sender address (%s)", err) 48 | } 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /x/claim/types/message_update_merkle_root.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 6 | ) 7 | 8 | var _ sdk.Msg = &MsgUpdateMerkleRoot{} 9 | 10 | // msg types 11 | const ( 12 | TypeMsgUpdateMerkleRoot = "update_merkle_root" 13 | ) 14 | 15 | func NewMsgUpdateMerkleRoot(sender string, value string) *MsgUpdateMerkleRoot { 16 | return &MsgUpdateMerkleRoot{ 17 | Sender: sender, 18 | RootValue: value, 19 | } 20 | } 21 | 22 | func (msg *MsgUpdateMerkleRoot) Route() string { 23 | return RouterKey 24 | } 25 | 26 | func (msg *MsgUpdateMerkleRoot) Type() string { 27 | return TypeMsgUpdateMerkleRoot 28 | } 29 | 30 | func (msg *MsgUpdateMerkleRoot) GetSigners() []sdk.AccAddress { 31 | sender, err := sdk.AccAddressFromBech32(msg.Sender) 32 | if err != nil { 33 | panic(err) 34 | } 35 | return []sdk.AccAddress{sender} 36 | } 37 | 38 | func (msg *MsgUpdateMerkleRoot) ValidateBasic() error { 39 | _, err := sdk.AccAddressFromBech32(msg.Sender) 40 | if err != nil { 41 | return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sender address (%s)", err) 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /x/claim/types/params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" 9 | yaml "gopkg.in/yaml.v2" 10 | ) 11 | 12 | var ( 13 | DefaultClaimDenom = "utmun" 14 | DefaultDurationUntilDecay = time.Hour 15 | DefaultDurationOfDecay = time.Hour * 5 16 | ) 17 | 18 | // Parameter store keys 19 | var ( 20 | KeyEnabled = []byte("Enabled") 21 | KeyStartTime = []byte("StartTime") 22 | KeyClaimDenom = []byte("ClaimDenom") 23 | KeyDurationUntilDecay = []byte("DurationUntilDecay") 24 | KeyDurationOfDecay = []byte("DurationOfDecay") 25 | ) 26 | 27 | func NewParams(enabled bool, claimDenom string, startTime time.Time, durationUntilDecay, durationOfDecay time.Duration) Params { 28 | return Params{ 29 | AirdropEnabled: enabled, 30 | ClaimDenom: claimDenom, 31 | AirdropStartTime: startTime, 32 | DurationUntilDecay: durationUntilDecay, 33 | DurationOfDecay: durationOfDecay, 34 | } 35 | } 36 | 37 | // String implements the stringer interface for Params 38 | func (p Params) String() string { 39 | out, err := yaml.Marshal(p) 40 | if err != nil { 41 | return "" 42 | } 43 | return string(out) 44 | } 45 | 46 | // ParamSetPairs - Implements params.ParamSet 47 | func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { 48 | return paramtypes.ParamSetPairs{ 49 | paramtypes.NewParamSetPair(KeyEnabled, &p.AirdropEnabled, validateEnabled), 50 | paramtypes.NewParamSetPair(KeyClaimDenom, &p.ClaimDenom, validateDenom), 51 | paramtypes.NewParamSetPair(KeyStartTime, &p.AirdropStartTime, validateTime), 52 | paramtypes.NewParamSetPair(KeyDurationUntilDecay, &p.DurationUntilDecay, validateDuration), 53 | paramtypes.NewParamSetPair(KeyDurationOfDecay, &p.DurationOfDecay, validateDuration), 54 | } 55 | } 56 | 57 | // Validate validates all params 58 | func (p Params) Validate() error { 59 | if err := validateEnabled(p.AirdropEnabled); err != nil { 60 | return err 61 | } 62 | err := validateDenom(p.ClaimDenom) 63 | return err 64 | } 65 | 66 | func (p Params) IsAirdropEnabled(t time.Time) bool { 67 | if !p.AirdropEnabled { 68 | return false 69 | } 70 | if p.AirdropStartTime.IsZero() { 71 | return false 72 | } 73 | if t.Before(p.AirdropStartTime) { 74 | return false 75 | } 76 | return true 77 | } 78 | 79 | // ParamKeyTable for staking module 80 | func ParamKeyTable() paramtypes.KeyTable { 81 | return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) 82 | } 83 | 84 | func validateEnabled(i interface{}) error { 85 | _, ok := i.(bool) 86 | if !ok { 87 | return fmt.Errorf("invalid parameter type: %T", i) 88 | } 89 | return nil 90 | } 91 | 92 | func validateDenom(i interface{}) error { 93 | v, ok := i.(string) 94 | if !ok { 95 | return fmt.Errorf("invalid parameter type: %T", i) 96 | } 97 | 98 | if strings.TrimSpace(v) == "" { 99 | return fmt.Errorf("invalid denom: %s", v) 100 | } 101 | 102 | return nil 103 | } 104 | 105 | func validateTime(i interface{}) error { 106 | _, ok := i.(time.Time) 107 | if !ok { 108 | return fmt.Errorf("invalid parameter type: %T", i) 109 | } 110 | return nil 111 | } 112 | 113 | func validateDuration(i interface{}) error { 114 | d, ok := i.(time.Duration) 115 | if !ok { 116 | return fmt.Errorf("invalid parameter type: %T", i) 117 | } 118 | if d < 1 { 119 | return fmt.Errorf("duration must be greater than or equal to 1: %d", d) 120 | } 121 | return nil 122 | } 123 | -------------------------------------------------------------------------------- /x/claim/types/tx_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "testing" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestMsgJsonSignBytes(t *testing.T) { 12 | goodAddress := sdk.AccAddress(make([]byte, 20)).String() 13 | specs := map[string]struct { 14 | src legacytx.LegacyMsg 15 | exp string 16 | }{ 17 | "MsgClaimFor": { 18 | src: &MsgClaimFor{Sender: goodAddress}, 19 | exp: ` 20 | { 21 | "type":"claim/ClaimFor", 22 | "value": {"sender": "cosmos1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnrql8a"} 23 | }`, 24 | }, 25 | } 26 | for name, spec := range specs { 27 | t.Run(name, func(t *testing.T) { 28 | bz := spec.src.GetSignBytes() 29 | assert.JSONEq(t, spec.exp, string(bz), "raw: %s", string(bz)) 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /x/claim/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | -------------------------------------------------------------------------------- /x/ibank/README.md: -------------------------------------------------------------------------------- 1 | # IBANK 2 | 3 | The ``IBANK`` module is a custom module that allows secure transfer of tokens between brokers. The module is designed for scenarios where the sender wishes to send tokens with a password that the receiver must know to receive the coins. `ibank` allows brokers to easily send funds from one to another, with added security and peace of mind. 4 | 5 | ## Features 6 | 7 | - Secure transfer of tokens between brokers 8 | - Encrypted password for the receiver to retrieve funds 9 | - Tokens are locked in the module throughout the transfer process 10 | - Funds will automatically return to the sender if the duration expires or max retry limit exceeds. -------------------------------------------------------------------------------- /x/ibank/abci.go: -------------------------------------------------------------------------------- 1 | package ibank 2 | 3 | import ( 4 | "fmt" 5 | "mun/x/ibank/keeper" 6 | "mun/x/ibank/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | func EndBlocker(ctx sdk.Context, k keeper.Keeper) { 12 | txCount := k.GetTransactionCount(ctx) 13 | 14 | var id = k.GetTransactionChaser(ctx) 15 | for ; id <= txCount; id++ { 16 | tx, found := k.GetTransaction(ctx, id) 17 | if !found { 18 | break 19 | } 20 | 21 | if !k.IsExpired(ctx, tx) { 22 | break 23 | } 24 | 25 | if tx.Status == types.TxPending { 26 | if err := k.Refund(ctx, tx, true); err != nil { 27 | ctx.Logger().Error(fmt.Sprintf("ibank: refund error on end blocker, id: %d", id)) 28 | } 29 | } 30 | } 31 | 32 | k.SetTransactionChaser(ctx, id) 33 | } 34 | -------------------------------------------------------------------------------- /x/ibank/client/cli/query.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | // "strings" 6 | 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/cosmos/cosmos-sdk/client" 10 | // "github.com/cosmos/cosmos-sdk/client/flags" 11 | // sdk "github.com/cosmos/cosmos-sdk/types" 12 | 13 | "mun/x/ibank/types" 14 | ) 15 | 16 | // GetQueryCmd returns the cli query commands for this module 17 | func GetQueryCmd(queryRoute string) *cobra.Command { 18 | // Group ibank queries under a subcommand 19 | cmd := &cobra.Command{ 20 | Use: types.ModuleName, 21 | Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), 22 | DisableFlagParsing: true, 23 | SuggestionsMinimumDistance: 2, 24 | RunE: client.ValidateCmd, 25 | } 26 | 27 | cmd.AddCommand(CmdQueryParams()) 28 | cmd.AddCommand(CmdListTransaction()) 29 | cmd.AddCommand(CmdShowTransaction()) 30 | cmd.AddCommand(CmdShowIncoming()) 31 | cmd.AddCommand(CmdShowOutgoing()) 32 | 33 | // this line is used by starport scaffolding # 1 34 | 35 | return cmd 36 | } 37 | -------------------------------------------------------------------------------- /x/ibank/client/cli/query_params.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "context" 5 | 6 | "mun/x/ibank/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func CmdQueryParams() *cobra.Command { 14 | cmd := &cobra.Command{ 15 | Use: "params", 16 | Short: "shows the parameters of the module", 17 | Args: cobra.NoArgs, 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | clientCtx := client.GetClientContextFromCmd(cmd) 20 | 21 | queryClient := types.NewQueryClient(clientCtx) 22 | 23 | res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | return clientCtx.PrintProto(res) 29 | }, 30 | } 31 | 32 | flags.AddQueryFlagsToCmd(cmd) 33 | 34 | return cmd 35 | } 36 | -------------------------------------------------------------------------------- /x/ibank/client/cli/query_show_incoming.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "strconv" 5 | 6 | "mun/x/ibank/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | "github.com/spf13/cast" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var _ = strconv.Itoa(0) 15 | 16 | func CmdShowIncoming() *cobra.Command { 17 | cmd := &cobra.Command{ 18 | Use: "incoming [receiver] [pending]", 19 | Short: "Query show-incoming", 20 | Args: cobra.ExactArgs(2), 21 | RunE: func(cmd *cobra.Command, args []string) (err error) { 22 | reqReceiver := args[0] 23 | reqPending, err := cast.ToBoolE(args[1]) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | clientCtx, err := client.GetClientQueryContext(cmd) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | queryClient := types.NewQueryClient(clientCtx) 34 | 35 | params := &types.QueryIncomingRequest{ 36 | 37 | Receiver: reqReceiver, 38 | Pending: reqPending, 39 | } 40 | 41 | res, err := queryClient.Incoming(cmd.Context(), params) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | return clientCtx.PrintProto(res) 47 | }, 48 | } 49 | 50 | flags.AddQueryFlagsToCmd(cmd) 51 | 52 | return cmd 53 | } 54 | -------------------------------------------------------------------------------- /x/ibank/client/cli/query_show_outgoing.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "strconv" 5 | 6 | "mun/x/ibank/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | "github.com/spf13/cast" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var _ = strconv.Itoa(0) 15 | 16 | func CmdShowOutgoing() *cobra.Command { 17 | cmd := &cobra.Command{ 18 | Use: "outgoing [sender] [pending]", 19 | Short: "Query show-outgoing", 20 | Args: cobra.ExactArgs(2), 21 | RunE: func(cmd *cobra.Command, args []string) (err error) { 22 | reqSender := args[0] 23 | reqPending, err := cast.ToBoolE(args[1]) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | clientCtx, err := client.GetClientQueryContext(cmd) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | queryClient := types.NewQueryClient(clientCtx) 34 | 35 | params := &types.QueryOutgoingRequest{ 36 | 37 | Sender: reqSender, 38 | Pending: reqPending, 39 | } 40 | 41 | res, err := queryClient.Outgoing(cmd.Context(), params) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | return clientCtx.PrintProto(res) 47 | }, 48 | } 49 | 50 | flags.AddQueryFlagsToCmd(cmd) 51 | 52 | return cmd 53 | } 54 | -------------------------------------------------------------------------------- /x/ibank/client/cli/query_transaction.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "mun/x/ibank/types" 8 | 9 | "github.com/cosmos/cosmos-sdk/client" 10 | "github.com/cosmos/cosmos-sdk/client/flags" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | func CmdListTransaction() *cobra.Command { 15 | cmd := &cobra.Command{ 16 | Use: "list-transaction [address]", 17 | Short: "list all transaction", 18 | Args: cobra.ExactArgs(1), 19 | RunE: func(cmd *cobra.Command, args []string) error { 20 | reqAddress := args[0] 21 | 22 | clientCtx, err := client.GetClientQueryContext(cmd) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | queryClient := types.NewQueryClient(clientCtx) 28 | 29 | params := &types.QueryAllTransactionRequest{ 30 | Address: reqAddress, 31 | } 32 | 33 | res, err := queryClient.TransactionAll(context.Background(), params) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | return clientCtx.PrintProto(res) 39 | }, 40 | } 41 | 42 | flags.AddPaginationFlagsToCmd(cmd, cmd.Use) 43 | flags.AddQueryFlagsToCmd(cmd) 44 | 45 | return cmd 46 | } 47 | 48 | func CmdShowTransaction() *cobra.Command { 49 | cmd := &cobra.Command{ 50 | Use: "show-transaction [id]", 51 | Short: "shows a transaction", 52 | Args: cobra.ExactArgs(1), 53 | RunE: func(cmd *cobra.Command, args []string) error { 54 | clientCtx := client.GetClientContextFromCmd(cmd) 55 | 56 | queryClient := types.NewQueryClient(clientCtx) 57 | 58 | id, err := strconv.ParseUint(args[0], 10, 64) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | params := &types.QueryGetTransactionRequest{ 64 | Id: id, 65 | } 66 | 67 | res, err := queryClient.Transaction(context.Background(), params) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | return clientCtx.PrintProto(res) 73 | }, 74 | } 75 | 76 | flags.AddQueryFlagsToCmd(cmd) 77 | 78 | return cmd 79 | } 80 | -------------------------------------------------------------------------------- /x/ibank/client/cli/tx.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/cosmos/cosmos-sdk/client" 10 | // "github.com/cosmos/cosmos-sdk/client/flags" 11 | "mun/x/ibank/types" 12 | ) 13 | 14 | var ( 15 | DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds()) 16 | ) 17 | 18 | // const ( 19 | // flagPacketTimeoutTimestamp = "packet-timeout-timestamp" 20 | // listSeparator = "," 21 | // ) 22 | 23 | // GetTxCmd returns the transaction commands for this module 24 | func GetTxCmd() *cobra.Command { 25 | cmd := &cobra.Command{ 26 | Use: types.ModuleName, 27 | Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), 28 | DisableFlagParsing: true, 29 | SuggestionsMinimumDistance: 2, 30 | RunE: client.ValidateCmd, 31 | } 32 | 33 | cmd.AddCommand(CmdSend()) 34 | cmd.AddCommand(CmdReceive()) 35 | // this line is used by starport scaffolding # 1 36 | 37 | return cmd 38 | } 39 | -------------------------------------------------------------------------------- /x/ibank/client/cli/tx_receive.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "strconv" 5 | 6 | "mun/x/ibank/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | "github.com/cosmos/cosmos-sdk/client/tx" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var _ = strconv.Itoa(0) 15 | 16 | func CmdReceive() *cobra.Command { 17 | cmd := &cobra.Command{ 18 | Use: "receive [transaction-id] [password]", 19 | Short: "Broadcast message receive", 20 | Args: cobra.ExactArgs(2), 21 | RunE: func(cmd *cobra.Command, args []string) (err error) { 22 | argTransactionID := args[0] 23 | argPassword := args[1] 24 | 25 | clientCtx, err := client.GetClientTxContext(cmd) 26 | if err != nil { 27 | return err 28 | } 29 | 30 | txnID, err := strconv.ParseInt(argTransactionID, 10, 64) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | msg := types.NewMsgReceive( 36 | clientCtx.GetFromAddress().String(), 37 | txnID, 38 | argPassword, 39 | ) 40 | if err := msg.ValidateBasic(); err != nil { 41 | return err 42 | } 43 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 44 | }, 45 | } 46 | 47 | flags.AddTxFlagsToCmd(cmd) 48 | 49 | return cmd 50 | } 51 | -------------------------------------------------------------------------------- /x/ibank/client/cli/tx_send.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "strconv" 5 | 6 | "mun/x/ibank/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | "github.com/cosmos/cosmos-sdk/client/tx" 11 | sdk "github.com/cosmos/cosmos-sdk/types" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | var _ = strconv.Itoa(0) 16 | 17 | func CmdSend() *cobra.Command { 18 | cmd := &cobra.Command{ 19 | Use: "send [to-address] [amount] [password-hash]", 20 | Short: "Broadcast message send", 21 | Args: cobra.ExactArgs(3), 22 | RunE: func(cmd *cobra.Command, args []string) (err error) { 23 | argToAddress := args[0] 24 | argPassword := args[2] 25 | argAmount, err := sdk.ParseCoinNormalized(args[1]) 26 | if err != nil { 27 | return err 28 | } 29 | 30 | clientCtx, err := client.GetClientTxContext(cmd) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | msg := types.NewMsgSend( 36 | clientCtx.GetFromAddress().String(), 37 | argToAddress, 38 | argAmount, 39 | argPassword, 40 | ) 41 | if err := msg.ValidateBasic(); err != nil { 42 | return err 43 | } 44 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 45 | }, 46 | } 47 | 48 | flags.AddTxFlagsToCmd(cmd) 49 | 50 | return cmd 51 | } 52 | -------------------------------------------------------------------------------- /x/ibank/genesis.go: -------------------------------------------------------------------------------- 1 | package ibank 2 | 3 | import ( 4 | "mun/x/ibank/keeper" 5 | "mun/x/ibank/types" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | ) 9 | 10 | // InitGenesis initializes the module's state from a provided genesis state. 11 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { 12 | // Set all the transaction 13 | for _, elem := range genState.TransactionList { 14 | k.SetTransaction(ctx, elem) 15 | } 16 | 17 | k.CreateModuleAccount(ctx) 18 | k.SetTransactionCount(ctx, genState.TransactionCount) 19 | k.SetTransactionChaser(ctx, genState.TransactionChaser) 20 | 21 | // this line is used by starport scaffolding # genesis/module/init 22 | k.SetParams(ctx, genState.Params) 23 | } 24 | 25 | // ExportGenesis returns the module's exported genesis 26 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { 27 | genesis := types.DefaultGenesis() 28 | genesis.Params = k.GetParams(ctx) 29 | 30 | genesis.TransactionList = k.GetAllTransaction(ctx) 31 | genesis.TransactionCount = k.GetTransactionCount(ctx) 32 | genesis.TransactionChaser = k.GetTransactionChaser(ctx) 33 | // this line is used by starport scaffolding # genesis/module/export 34 | 35 | return genesis 36 | } 37 | -------------------------------------------------------------------------------- /x/ibank/keeper/grpc_query.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "mun/x/ibank/types" 5 | "time" 6 | ) 7 | 8 | var _ types.QueryServer = Keeper{} 9 | 10 | func CalculateTimeLeftInSeconds(start, end time.Time, expirationDuration time.Duration) uint32 { 11 | if start.Add(expirationDuration).After(end) { 12 | return uint32(expirationDuration.Seconds() - end.Sub(start).Seconds()) 13 | } 14 | return 0 15 | } 16 | -------------------------------------------------------------------------------- /x/ibank/keeper/grpc_query_params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "mun/x/ibank/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "google.golang.org/grpc/codes" 10 | "google.golang.org/grpc/status" 11 | ) 12 | 13 | func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { 14 | if req == nil { 15 | return nil, status.Error(codes.InvalidArgument, "invalid request") 16 | } 17 | ctx := sdk.UnwrapSDKContext(c) 18 | 19 | return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil 20 | } 21 | -------------------------------------------------------------------------------- /x/ibank/keeper/grpc_query_show_incoming.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "mun/x/ibank/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/store/prefix" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | "google.golang.org/grpc/codes" 11 | "google.golang.org/grpc/status" 12 | ) 13 | 14 | func (k Keeper) Incoming(goCtx context.Context, req *types.QueryIncomingRequest) (*types.QueryIncomingResponse, error) { 15 | if req == nil { 16 | return nil, status.Error(codes.InvalidArgument, "invalid request") 17 | } 18 | 19 | var transactions []types.TransactionWrapper 20 | ctx := sdk.UnwrapSDKContext(goCtx) 21 | 22 | expirationDuration := k.GetParams(ctx).DurationOfExpiration 23 | 24 | store := ctx.KVStore(k.storeKey) 25 | transactionStore := prefix.NewStore(store, types.KeyPrefix(types.TransactionKey)) 26 | 27 | pageRes, err := paginate(transactionStore, req.Pagination, func(key []byte, value []byte, appendable bool) (error, bool) { 28 | var transaction types.Transaction 29 | if err := k.cdc.Unmarshal(value, &transaction); err != nil { 30 | return err, false 31 | } 32 | 33 | // check receiver 34 | if req.Receiver != transaction.Receiver { 35 | return nil, false 36 | } 37 | 38 | // only shows pending transactions 39 | if req.Pending && transaction.Status != types.TxPending { 40 | return nil, false 41 | } 42 | 43 | if appendable { 44 | wrapper := types.TransactionWrapper{ 45 | Transaction: transaction, 46 | TimeLeft: CalculateTimeLeftInSeconds(transaction.SentAt, ctx.BlockTime(), expirationDuration), 47 | } 48 | transactions = append(transactions, wrapper) 49 | } 50 | return nil, true 51 | }) 52 | 53 | if err != nil { 54 | return nil, status.Error(codes.Internal, err.Error()) 55 | } 56 | 57 | return &types.QueryIncomingResponse{Transactions: transactions, Pagination: pageRes}, nil 58 | } 59 | -------------------------------------------------------------------------------- /x/ibank/keeper/grpc_query_show_outgoing.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "mun/x/ibank/types" 8 | 9 | "github.com/cosmos/cosmos-sdk/store/prefix" 10 | storetypes "github.com/cosmos/cosmos-sdk/store/types" 11 | sdk "github.com/cosmos/cosmos-sdk/types" 12 | querytypes "github.com/cosmos/cosmos-sdk/types/query" 13 | db "github.com/tendermint/tm-db" 14 | "google.golang.org/grpc/codes" 15 | "google.golang.org/grpc/status" 16 | ) 17 | 18 | func (k Keeper) Outgoing(goCtx context.Context, req *types.QueryOutgoingRequest) (*types.QueryOutgoingResponse, error) { 19 | if req == nil { 20 | return nil, status.Error(codes.InvalidArgument, "invalid request") 21 | } 22 | 23 | var transactions []types.TransactionWrapper 24 | ctx := sdk.UnwrapSDKContext(goCtx) 25 | 26 | store := ctx.KVStore(k.storeKey) 27 | transactionStore := prefix.NewStore(store, types.KeyPrefix(types.TransactionKey)) 28 | 29 | expirationDuration := k.GetParams(ctx).DurationOfExpiration 30 | 31 | pageRes, err := paginate(transactionStore, req.Pagination, func(key []byte, value []byte, appendable bool) (error, bool) { 32 | var transaction types.Transaction 33 | if err := k.cdc.Unmarshal(value, &transaction); err != nil { 34 | return err, false 35 | } 36 | 37 | // check sender 38 | if req.Sender != transaction.Sender { 39 | return nil, false 40 | } 41 | 42 | // only shows pending transactions 43 | if req.Pending && transaction.Status != types.TxPending { 44 | return nil, false 45 | } 46 | 47 | if appendable { 48 | wrapper := types.TransactionWrapper{ 49 | Transaction: transaction, 50 | TimeLeft: CalculateTimeLeftInSeconds(transaction.SentAt, ctx.BlockTime(), expirationDuration), 51 | } 52 | transactions = append(transactions, wrapper) 53 | } 54 | return nil, true 55 | }) 56 | 57 | if err != nil { 58 | return nil, status.Error(codes.Internal, err.Error()) 59 | } 60 | 61 | return &types.QueryOutgoingResponse{Transactions: transactions, Pagination: pageRes}, nil 62 | } 63 | 64 | func paginate( 65 | prefixStore storetypes.KVStore, 66 | pageRequest *querytypes.PageRequest, 67 | onResult func([]byte, []byte, bool) (error, bool), 68 | ) (*querytypes.PageResponse, error) { 69 | if pageRequest == nil { 70 | pageRequest = &querytypes.PageRequest{} 71 | } 72 | 73 | offset := pageRequest.Offset 74 | key := pageRequest.Key 75 | limit := pageRequest.Limit 76 | countTotal := pageRequest.CountTotal 77 | reverse := pageRequest.Reverse 78 | 79 | if offset > 0 && key != nil { 80 | return nil, fmt.Errorf("invalid request, either offset or key is expected, got both") 81 | } 82 | 83 | if limit == 0 { 84 | limit = 100 //DefaultLimit 85 | 86 | // count total results when the limit is zero/not supplied 87 | countTotal = true 88 | } 89 | 90 | if len(key) != 0 { 91 | iterator := getIterator(prefixStore, key, reverse) 92 | defer iterator.Close() 93 | 94 | var count uint64 95 | var nextKey []byte 96 | 97 | for ; iterator.Valid(); iterator.Next() { 98 | if count == limit { 99 | nextKey = iterator.Key() 100 | break 101 | } 102 | if iterator.Error() != nil { 103 | return nil, iterator.Error() 104 | } 105 | err, eligible := onResult(iterator.Key(), iterator.Value(), true) 106 | if !eligible { 107 | continue 108 | } 109 | if err != nil { 110 | return nil, err 111 | } 112 | 113 | count++ 114 | } 115 | 116 | return &querytypes.PageResponse{ 117 | NextKey: nextKey, 118 | }, nil 119 | } 120 | 121 | iterator := getIterator(prefixStore, nil, reverse) 122 | defer iterator.Close() 123 | 124 | end := offset + limit 125 | 126 | var count uint64 127 | var nextKey []byte 128 | 129 | for ; iterator.Valid(); iterator.Next() { 130 | err, eligible := onResult(iterator.Key(), iterator.Value(), count >= offset) 131 | if err != nil { 132 | return nil, err 133 | } 134 | if !eligible { 135 | continue 136 | } 137 | 138 | if count == end { 139 | nextKey = iterator.Key() 140 | if !countTotal { 141 | break 142 | } 143 | } 144 | 145 | if iterator.Error() != nil { 146 | return nil, iterator.Error() 147 | } 148 | 149 | count++ 150 | } 151 | 152 | res := &querytypes.PageResponse{NextKey: nextKey} 153 | if countTotal { 154 | res.Total = count 155 | } 156 | 157 | return res, nil 158 | } 159 | 160 | func getIterator(prefixStore storetypes.KVStore, start []byte, reverse bool) db.Iterator { 161 | if reverse { 162 | var end []byte 163 | if start != nil { 164 | itr := prefixStore.Iterator(start, nil) 165 | defer itr.Close() 166 | if itr.Valid() { 167 | itr.Next() 168 | end = itr.Key() 169 | } 170 | } 171 | return prefixStore.ReverseIterator(nil, end) 172 | } 173 | return prefixStore.Iterator(start, nil) 174 | } 175 | -------------------------------------------------------------------------------- /x/ibank/keeper/grpc_query_transaction.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "mun/x/ibank/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/store/prefix" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 11 | "google.golang.org/grpc/codes" 12 | "google.golang.org/grpc/status" 13 | ) 14 | 15 | func (k Keeper) TransactionAll(c context.Context, req *types.QueryAllTransactionRequest) (*types.QueryAllTransactionResponse, error) { 16 | if req == nil { 17 | return nil, status.Error(codes.InvalidArgument, "invalid request") 18 | } 19 | 20 | var transactions []types.TransactionWrapper 21 | ctx := sdk.UnwrapSDKContext(c) 22 | 23 | store := ctx.KVStore(k.storeKey) 24 | transactionStore := prefix.NewStore(store, types.KeyPrefix(types.TransactionKey)) 25 | 26 | expirationDuration := k.GetParams(ctx).DurationOfExpiration 27 | 28 | pageRes, err := paginate(transactionStore, req.Pagination, func(key []byte, value []byte, appendable bool) (error, bool) { 29 | var transaction types.Transaction 30 | if err := k.cdc.Unmarshal(value, &transaction); err != nil { 31 | return err, false 32 | } 33 | 34 | if transaction.Sender != req.Address && transaction.Receiver != req.Address { 35 | return nil, false 36 | } 37 | 38 | wrapper := types.TransactionWrapper{ 39 | Transaction: transaction, 40 | TimeLeft: CalculateTimeLeftInSeconds(transaction.SentAt, ctx.BlockTime(), expirationDuration), 41 | } 42 | transactions = append(transactions, wrapper) 43 | return nil, true 44 | }) 45 | 46 | if err != nil { 47 | return nil, status.Error(codes.Internal, err.Error()) 48 | } 49 | 50 | return &types.QueryAllTransactionResponse{Transactions: transactions, Pagination: pageRes}, nil 51 | } 52 | 53 | func (k Keeper) Transaction(c context.Context, req *types.QueryGetTransactionRequest) (*types.QueryGetTransactionResponse, error) { 54 | if req == nil { 55 | return nil, status.Error(codes.InvalidArgument, "invalid request") 56 | } 57 | 58 | ctx := sdk.UnwrapSDKContext(c) 59 | transaction, found := k.GetTransaction(ctx, req.Id) 60 | if !found { 61 | return nil, sdkerrors.ErrKeyNotFound 62 | } 63 | 64 | return &types.QueryGetTransactionResponse{Transaction: transaction}, nil 65 | } 66 | -------------------------------------------------------------------------------- /x/ibank/keeper/keeper.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "crypto/sha256" 5 | "errors" 6 | "fmt" 7 | "strconv" 8 | 9 | "mun/x/ibank/types" 10 | 11 | "github.com/cosmos/cosmos-sdk/codec" 12 | storetypes "github.com/cosmos/cosmos-sdk/store/types" 13 | sdk "github.com/cosmos/cosmos-sdk/types" 14 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 15 | paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" 16 | "github.com/tendermint/tendermint/libs/log" 17 | ) 18 | 19 | type ( 20 | Keeper struct { 21 | cdc codec.BinaryCodec 22 | storeKey storetypes.StoreKey 23 | // memKey storetypes.StoreKey 24 | paramstore paramtypes.Subspace 25 | 26 | accountKeeper types.AccountKeeper 27 | bankKeeper types.BankKeeper 28 | } 29 | ) 30 | 31 | func NewKeeper( 32 | cdc codec.BinaryCodec, 33 | storeKey storetypes.StoreKey, 34 | ps paramtypes.Subspace, 35 | 36 | accountKeeper types.AccountKeeper, 37 | bankKeeper types.BankKeeper, 38 | ) *Keeper { 39 | // set KeyTable if it has not already been set 40 | if !ps.HasKeyTable() { 41 | ps = ps.WithKeyTable(types.ParamKeyTable()) 42 | } 43 | 44 | return &Keeper{ 45 | 46 | cdc: cdc, 47 | storeKey: storeKey, 48 | paramstore: ps, 49 | accountKeeper: accountKeeper, 50 | bankKeeper: bankKeeper, 51 | } 52 | } 53 | 54 | func (k Keeper) Logger(ctx sdk.Context) log.Logger { 55 | return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) 56 | } 57 | 58 | func (k Keeper) CreateModuleAccount(ctx sdk.Context) { 59 | macc := authtypes.NewEmptyModuleAccount(types.ModuleName, authtypes.Minter) 60 | k.accountKeeper.SetModuleAccount(ctx, macc) 61 | } 62 | 63 | // This function send amt Coin from `from` account to a module account 64 | func (k Keeper) SendCoin(ctx sdk.Context, from, to sdk.AccAddress, amt sdk.Coin, password string) error { 65 | if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, from, types.ModuleName, sdk.Coins{amt}); err != nil { 66 | return err 67 | } 68 | 69 | txnID := k.AppendTransaction(ctx, types.Transaction{ 70 | Sender: from.String(), 71 | Receiver: to.String(), 72 | Coins: sdk.Coins{amt}, 73 | SentAt: ctx.BlockTime(), 74 | ReceivedAt: ctx.BlockTime(), 75 | Status: types.TxPending, 76 | Password: password, 77 | Retry: 3, 78 | }) 79 | 80 | ctx.EventManager().EmitEvent(sdk.NewEvent( 81 | types.EventTypeIBank, 82 | sdk.NewAttribute(types.AttributeKeyAction, "send"), 83 | sdk.NewAttribute(types.AttributeKeySender, from.String()), 84 | sdk.NewAttribute(types.AttributeKeyRecipient, to.String()), 85 | sdk.NewAttribute(types.AttributeKeyAmount, amt.Amount.String()+amt.GetDenom()), 86 | sdk.NewAttribute(types.AttributeKeyRemittanceID, strconv.FormatUint(txnID, 10)), 87 | )) 88 | 89 | return nil 90 | } 91 | 92 | func (k Keeper) ReceiveCoin( 93 | ctx sdk.Context, 94 | receiver sdk.AccAddress, 95 | transactionID int64, 96 | words string, 97 | ) error { 98 | txn, found := k.GetTransaction(ctx, uint64(transactionID)) 99 | if !found { 100 | return types.ErrNoTransaction 101 | } 102 | 103 | // Check if transaction is performed 104 | if txn.Status != types.TxPending { 105 | if txn.Status == types.TxSent { 106 | return types.ErrAlreadyReceived 107 | } else if txn.Status == types.TxExpired { 108 | return types.ErrTxExpired 109 | } else { 110 | return types.ErrTxDeclined 111 | } 112 | } 113 | 114 | // Check if transaction is expired 115 | if k.IsExpired(ctx, txn) { 116 | return types.ErrTxExpired 117 | } 118 | 119 | to, err := sdk.AccAddressFromBech32(txn.Receiver) 120 | if err != nil || !to.Equals(receiver) { 121 | return types.ErrInvalidReceiver 122 | } 123 | 124 | // check password hash 125 | // hash := sha256.Sum256([]byte(password)) 126 | // formattedHash := fmt.Sprintf("%x", hash) 127 | if txn.Password != k.GetPasswordFromWords(ctx, words) { 128 | txn.Retry-- 129 | 130 | if txn.Retry == 0 { 131 | // maximum number of retries is exceeded, the funds will return to the sender 132 | ctx.EventManager().EmitEvent( 133 | sdk.NewEvent( 134 | types.EventTypeIBank, 135 | sdk.NewAttribute(types.AttributeKeyAction, "receive"), 136 | sdk.NewAttribute(types.AttributeKeyReceiveSuccess, "false"), 137 | sdk.NewAttribute(types.AttributeKeyRefunded, "true"), 138 | ), 139 | ) 140 | 141 | return k.Refund(ctx, txn, false) 142 | } else { 143 | // incorrect password; deduct retry and save 144 | k.SetTransaction(ctx, txn) 145 | ctx.EventManager().EmitEvent( 146 | sdk.NewEvent( 147 | types.EventTypeIBank, 148 | sdk.NewAttribute(types.AttributeKeyAction, "receive"), 149 | sdk.NewAttribute(types.AttributeKeyReceiveSuccess, "false"), 150 | ), 151 | ) 152 | return nil 153 | } 154 | } 155 | 156 | txn.ReceivedAt = ctx.BlockTime() 157 | txn.Status = types.TxSent 158 | k.SetTransaction(ctx, txn) 159 | 160 | err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiver, txn.Coins) 161 | 162 | if err == nil { 163 | ctx.EventManager().EmitEvent( 164 | sdk.NewEvent( 165 | types.EventTypeIBank, 166 | sdk.NewAttribute(types.AttributeKeyAction, "receive"), 167 | sdk.NewAttribute(types.AttributeKeyReceiveSuccess, "true"), 168 | ), 169 | ) 170 | } else { 171 | ctx.EventManager().EmitEvent( 172 | sdk.NewEvent( 173 | types.EventTypeIBank, 174 | sdk.NewAttribute(types.AttributeKeyAction, "receive"), 175 | sdk.NewAttribute(types.AttributeKeyReceiveSuccess, "false"), 176 | ), 177 | ) 178 | } 179 | 180 | return err 181 | } 182 | 183 | func (k Keeper) IsExpired(ctx sdk.Context, tranaction types.Transaction) bool { 184 | params := k.GetParams(ctx) 185 | 186 | // transaction.SentAt + ExpirationDuration < CurrentTime -> Expired 187 | return tranaction.SentAt.Add(params.GetDurationOfExpiration()).Before(ctx.BlockTime()) 188 | } 189 | 190 | func (k Keeper) Refund(ctx sdk.Context, transaction types.Transaction, expired bool) error { 191 | if transaction.Status != types.TxPending { 192 | return errors.New("only can refund pending txns") 193 | } 194 | 195 | if expired && !k.IsExpired(ctx, transaction) { 196 | return errors.New("tx is in progress") 197 | } 198 | 199 | sender, err := sdk.AccAddressFromBech32(transaction.Sender) 200 | if err != nil { 201 | return err 202 | } 203 | 204 | if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, transaction.Coins); err != nil { 205 | return err 206 | } 207 | 208 | if expired { 209 | transaction.Status = types.TxExpired 210 | } else { 211 | transaction.Status = types.TxDeclined 212 | transaction.Retry = 0 213 | } 214 | k.SetTransaction(ctx, transaction) 215 | 216 | return nil 217 | } 218 | 219 | func (k Keeper) GetPasswordFromWords(ctx sdk.Context, words string) string { 220 | hash := sha256.Sum256([]byte(words)) 221 | return fmt.Sprintf("%x", hash) 222 | } 223 | -------------------------------------------------------------------------------- /x/ibank/keeper/keeper_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "mun/app" 5 | "mun/x/ibank/types" 6 | "testing" 7 | 8 | "github.com/cosmos/cosmos-sdk/simapp" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | "github.com/stretchr/testify/suite" 11 | tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 12 | "github.com/tendermint/tendermint/types/time" 13 | ) 14 | 15 | const ( 16 | fooDenom = "foo" 17 | barDenom = "bar" 18 | ) 19 | 20 | func newFooCoin(amt int64) sdk.Coin { 21 | return sdk.NewInt64Coin(fooDenom, amt) 22 | } 23 | 24 | func newBarCoin(amt int64) sdk.Coin { 25 | return sdk.NewInt64Coin(barDenom, amt) 26 | } 27 | 28 | type IntegrationTestSuite struct { 29 | suite.Suite 30 | 31 | Ctx sdk.Context 32 | App *app.App 33 | } 34 | 35 | func (suite *IntegrationTestSuite) SetupTest() { 36 | isCheckTx := false 37 | 38 | suite.App = app.Setup() 39 | suite.Ctx = suite.App.GetBaseApp().NewContext(isCheckTx, tmproto.Header{ 40 | ChainID: "mun-test-1", 41 | Height: 1, 42 | Time: time.Now().UTC(), 43 | }) 44 | } 45 | 46 | func (suite *IntegrationTestSuite) TestCreateModuleAccount() { 47 | app := suite.App 48 | 49 | // remove module account 50 | ibankModuleAcc := app.AccountKeeper.GetAccount(suite.Ctx, app.AccountKeeper.GetModuleAddress(types.ModuleName)) 51 | app.AccountKeeper.RemoveAccount(suite.Ctx, ibankModuleAcc) 52 | 53 | // ensure module account was removed 54 | suite.Ctx = app.BaseApp.NewContext(false, tmproto.Header{}) 55 | ibankModuleAcc = app.AccountKeeper.GetAccount(suite.Ctx, app.AccountKeeper.GetModuleAddress(types.ModuleName)) 56 | suite.Require().Nil(ibankModuleAcc) 57 | 58 | // create module account 59 | app.IbankKeeper.CreateModuleAccount(suite.Ctx) 60 | 61 | // check module account is initialized 62 | ibankModuleAcc = app.AccountKeeper.GetAccount(suite.Ctx, app.AccountKeeper.GetModuleAddress(types.ModuleName)) 63 | suite.Require().NotNil(ibankModuleAcc) 64 | } 65 | 66 | func (suite *IntegrationTestSuite) TestSendCoin() { 67 | app, ctx := suite.App, suite.Ctx 68 | balances := sdk.NewCoins(newFooCoin(100), newBarCoin(50)) 69 | 70 | // Alice 71 | addrAlice := sdk.AccAddress([]byte("addr1_______________")) 72 | accAlice := app.AccountKeeper.NewAccountWithAddress(ctx, addrAlice) 73 | app.AccountKeeper.SetAccount(ctx, accAlice) 74 | suite.Require().NoError(simapp.FundAccount(app.BankKeeper, ctx, addrAlice, balances)) 75 | suite.Require().Equal(balances, app.BankKeeper.GetAllBalances(ctx, addrAlice)) 76 | 77 | // Bob 78 | addrBob := sdk.AccAddress([]byte("addr2_______________")) 79 | 80 | suite.Require().Nil(app.AccountKeeper.GetAccount(ctx, addrBob)) 81 | app.BankKeeper.GetAllBalances(ctx, addrBob) 82 | suite.Require().Empty(app.BankKeeper.GetAllBalances(ctx, addrBob)) 83 | 84 | // Module account 85 | addrIbank := app.AccountKeeper.GetModuleAddress(types.ModuleName) 86 | suite.Require().Empty(app.BankKeeper.GetAllBalances(ctx, addrIbank)) 87 | 88 | // Sending 89 | sendAmt := newFooCoin(50) 90 | password := "password" 91 | suite.Require().NoError(app.IbankKeeper.SendCoin(ctx, addrAlice, addrBob, sendAmt, password)) 92 | 93 | // Check balances 94 | expectedAliceBalances := sdk.NewCoins(newFooCoin(50), newBarCoin(50)) 95 | app.BankKeeper.GetAllBalances(ctx, addrAlice) 96 | suite.Require().Equal(expectedAliceBalances, app.BankKeeper.GetAllBalances(ctx, addrAlice)) 97 | suite.Require().Empty(app.BankKeeper.GetAllBalances(ctx, addrBob)) 98 | suite.Require().Equal(sdk.NewCoins(sendAmt), app.BankKeeper.GetAllBalances(ctx, addrIbank)) 99 | 100 | // Check pending transaction 101 | tx, found := app.IbankKeeper.GetTransaction(ctx, 0) 102 | suite.Require().Equal(true, found) 103 | suite.Require().Equal([]sdk.Coin{sendAmt}, tx.Coins) 104 | suite.Require().Equal(password, tx.Password) 105 | suite.Require().Equal(addrAlice.String(), tx.Sender) 106 | suite.Require().Equal(addrBob.String(), tx.Receiver) 107 | suite.Require().Equal(int32(3), tx.Retry) 108 | suite.Require().Equal(types.TxnStatus_TXN_PENDING, tx.Status) 109 | } 110 | 111 | func (suite *IntegrationTestSuite) TestReceiveCoin() { 112 | app, ctx := suite.App, suite.Ctx 113 | bankKeeper := app.BankKeeper 114 | 115 | // Test case 116 | aliceBalances := sdk.NewCoins(newFooCoin(100), newBarCoin(50)) 117 | bobBalances := sdk.NewCoins(newFooCoin(10)) 118 | sendAmt := newBarCoin(30) 119 | updatedAliceBal1 := aliceBalances.Sub(sdk.NewCoins(sendAmt)) // balance after send 120 | expectedAliceBal := aliceBalances.Sub(sdk.NewCoins(sendAmt)) //sdk.NewCoins(newFooCoin(90), newBarCoin(50)) 121 | expectedBobBal := bobBalances.Add(sendAmt) 122 | 123 | phrases := "word1 word2 word3 word4 word5 word6" 124 | password := app.IbankKeeper.GetPasswordFromWords(ctx, phrases) 125 | transactionId := 0 126 | 127 | // Alice 128 | addrAlice := sdk.AccAddress([]byte("addr1_______________")) 129 | accAlice := app.AccountKeeper.NewAccountWithAddress(ctx, addrAlice) 130 | app.AccountKeeper.SetAccount(ctx, accAlice) 131 | suite.Require().NoError(simapp.FundAccount(bankKeeper, ctx, addrAlice, aliceBalances)) 132 | suite.Require().Equal(aliceBalances, bankKeeper.GetAllBalances(ctx, addrAlice)) 133 | 134 | // Bob 135 | addrBob := sdk.AccAddress([]byte("addr2_______________")) 136 | accBob := app.AccountKeeper.NewAccountWithAddress(ctx, addrBob) 137 | app.AccountKeeper.SetAccount(ctx, accBob) 138 | suite.Require().NoError(simapp.FundAccount(bankKeeper, ctx, addrBob, bobBalances)) 139 | suite.Require().Equal(bobBalances, bankKeeper.GetAllBalances(ctx, addrBob)) 140 | 141 | // Send 142 | suite.Require().NoError(app.IbankKeeper.SendCoin(ctx, addrAlice, addrBob, sendAmt, password)) 143 | 144 | // Check balance after send 145 | suite.Require().Equal(updatedAliceBal1, bankKeeper.GetAllBalances(ctx, addrAlice)) 146 | 147 | // Receive 148 | suite.Require().NoError(app.IbankKeeper.ReceiveCoin(ctx, addrBob, int64(transactionId), phrases)) 149 | 150 | // check tx 151 | tx, found := app.IbankKeeper.GetTransaction(ctx, uint64(transactionId)) 152 | suite.Require().Equal(true, found) 153 | suite.Require().Equal(types.TxnStatus_TXN_SENT, tx.Status) 154 | 155 | // Check result after receive 156 | suite.Require().Equal(expectedAliceBal, bankKeeper.GetAllBalances(ctx, addrAlice)) 157 | suite.Require().Equal(expectedBobBal, bankKeeper.GetAllBalances(ctx, addrBob)) 158 | } 159 | 160 | func (suite *IntegrationTestSuite) TestRefund() { 161 | } 162 | 163 | func TestKeeperTestSuite(t *testing.T) { 164 | suite.Run(t, new(IntegrationTestSuite)) 165 | } 166 | -------------------------------------------------------------------------------- /x/ibank/keeper/msg_server.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "mun/x/ibank/types" 5 | ) 6 | 7 | type msgServer struct { 8 | Keeper 9 | } 10 | 11 | // NewMsgServerImpl returns an implementation of the MsgServer interface 12 | // for the provided Keeper. 13 | func NewMsgServerImpl(keeper Keeper) types.MsgServer { 14 | return &msgServer{Keeper: keeper} 15 | } 16 | 17 | var _ types.MsgServer = msgServer{} 18 | -------------------------------------------------------------------------------- /x/ibank/keeper/msg_server_receive.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "mun/x/ibank/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | func (k msgServer) Receive(goCtx context.Context, msg *types.MsgReceive) (*types.MsgReceiveResponse, error) { 12 | ctx := sdk.UnwrapSDKContext(goCtx) 13 | 14 | receiver, err := sdk.AccAddressFromBech32(msg.Receiver) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | if err := k.ReceiveCoin(ctx, receiver, msg.TransactionId, msg.Password); err != nil { 20 | return nil, err 21 | } 22 | 23 | return &types.MsgReceiveResponse{}, nil 24 | } 25 | -------------------------------------------------------------------------------- /x/ibank/keeper/msg_server_send.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "mun/x/ibank/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | func (k msgServer) Send(goCtx context.Context, msg *types.MsgSend) (*types.MsgSendResponse, error) { 12 | ctx := sdk.UnwrapSDKContext(goCtx) 13 | 14 | from, err := sdk.AccAddressFromBech32(msg.FromAddress) 15 | if err != nil { 16 | return nil, err 17 | } 18 | to, err := sdk.AccAddressFromBech32(msg.ToAddress) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | if err := k.SendCoin(ctx, from, to, msg.Amount, msg.PasswordHash); err != nil { 24 | return nil, err 25 | } 26 | 27 | return &types.MsgSendResponse{}, nil 28 | } 29 | -------------------------------------------------------------------------------- /x/ibank/keeper/params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "mun/x/ibank/types" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // GetParams get all parameters as types.Params 10 | func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { 11 | k.paramstore.GetParamSet(ctx, ¶ms) 12 | return 13 | } 14 | 15 | // SetParams set the params 16 | func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { 17 | k.paramstore.SetParamSet(ctx, ¶ms) 18 | } 19 | -------------------------------------------------------------------------------- /x/ibank/keeper/transaction.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "mun/x/ibank/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/store/prefix" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | ) 11 | 12 | // GetTransactionCount get the total number of transaction 13 | func (k Keeper) GetTransactionCount(ctx sdk.Context) uint64 { 14 | store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{}) 15 | byteKey := types.KeyPrefix(types.TransactionCountKey) 16 | bz := store.Get(byteKey) 17 | 18 | // Count doesn't exist: no element 19 | if bz == nil { 20 | return 0 21 | } 22 | 23 | // Parse bytes 24 | return binary.BigEndian.Uint64(bz) 25 | } 26 | 27 | // SetTransactionCount set the total number of transaction 28 | func (k Keeper) SetTransactionCount(ctx sdk.Context, count uint64) { 29 | store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{}) 30 | byteKey := types.KeyPrefix(types.TransactionCountKey) 31 | bz := make([]byte, 8) 32 | binary.BigEndian.PutUint64(bz, count) 33 | store.Set(byteKey, bz) 34 | } 35 | 36 | func (k Keeper) GetTransactionChaser(ctx sdk.Context) uint64 { 37 | store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{}) 38 | byteKey := types.KeyPrefix(types.TransactionChaserKey) 39 | bz := store.Get(byteKey) 40 | 41 | // Chaser doesn't exist: no element 42 | if bz == nil { 43 | return 0 44 | } 45 | 46 | // Parse bytes 47 | return binary.BigEndian.Uint64(bz) 48 | } 49 | 50 | func (k Keeper) SetTransactionChaser(ctx sdk.Context, chaser uint64) { 51 | store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{}) 52 | byteKey := types.KeyPrefix(types.TransactionChaserKey) 53 | bz := make([]byte, 8) 54 | binary.BigEndian.PutUint64(bz, chaser) 55 | store.Set(byteKey, bz) 56 | } 57 | 58 | // AppendTransaction appends a transaction in the store with a new id and update the count 59 | func (k Keeper) AppendTransaction( 60 | ctx sdk.Context, 61 | transaction types.Transaction, 62 | ) uint64 { 63 | // Create the transaction 64 | count := k.GetTransactionCount(ctx) 65 | 66 | // Set the ID of the appended value 67 | transaction.Id = count 68 | 69 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.TransactionKey)) 70 | appendedValue := k.cdc.MustMarshal(&transaction) 71 | store.Set(GetTransactionIDBytes(transaction.Id), appendedValue) 72 | 73 | // Update transaction count 74 | k.SetTransactionCount(ctx, count+1) 75 | 76 | return count 77 | } 78 | 79 | // SetTransaction set a specific transaction in the store 80 | func (k Keeper) SetTransaction(ctx sdk.Context, transaction types.Transaction) { 81 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.TransactionKey)) 82 | b := k.cdc.MustMarshal(&transaction) 83 | store.Set(GetTransactionIDBytes(transaction.Id), b) 84 | } 85 | 86 | // GetTransaction returns a transaction from its id 87 | func (k Keeper) GetTransaction(ctx sdk.Context, id uint64) (val types.Transaction, found bool) { 88 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.TransactionKey)) 89 | b := store.Get(GetTransactionIDBytes(id)) 90 | if b == nil { 91 | return val, false 92 | } 93 | k.cdc.MustUnmarshal(b, &val) 94 | return val, true 95 | } 96 | 97 | // RemoveTransaction removes a transaction from the store 98 | func (k Keeper) RemoveTransaction(ctx sdk.Context, id uint64) { 99 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.TransactionKey)) 100 | store.Delete(GetTransactionIDBytes(id)) 101 | } 102 | 103 | // GetAllTransaction returns all transaction 104 | func (k Keeper) GetAllTransaction(ctx sdk.Context) (list []types.Transaction) { 105 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.TransactionKey)) 106 | iterator := sdk.KVStorePrefixIterator(store, []byte{}) 107 | 108 | defer iterator.Close() 109 | 110 | for ; iterator.Valid(); iterator.Next() { 111 | var val types.Transaction 112 | k.cdc.MustUnmarshal(iterator.Value(), &val) 113 | list = append(list, val) 114 | } 115 | 116 | return 117 | } 118 | 119 | // GetTransactionIDBytes returns the byte representation of the ID 120 | func GetTransactionIDBytes(id uint64) []byte { 121 | bz := make([]byte, 8) 122 | binary.BigEndian.PutUint64(bz, id) 123 | return bz 124 | } 125 | 126 | // GetTransactionIDFromBytes returns ID in uint64 format from a byte array 127 | func GetTransactionIDFromBytes(bz []byte) uint64 { 128 | return binary.BigEndian.Uint64(bz) 129 | } 130 | -------------------------------------------------------------------------------- /x/ibank/module_simulation.go: -------------------------------------------------------------------------------- 1 | package ibank 2 | 3 | import ( 4 | "math/rand" 5 | "mun/x/ibank/simulation" 6 | "mun/x/ibank/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "github.com/cosmos/cosmos-sdk/types/module" 10 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 11 | ) 12 | 13 | // AppModuleSimulation functions 14 | 15 | // GenerateGenesisState creates a randomized GenState of the ibank module. 16 | func (AppModule) GenerateGenesisState(simState *module.SimulationState) { 17 | simulation.RandomizedGenState(simState) 18 | } 19 | 20 | // RandomizedParams creates randomized cronos param changes for the simulator. 21 | func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { 22 | return simulation.ParamChanges(r) 23 | } 24 | 25 | // ProposalContents returns nothing for governance proposals. 26 | func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { 27 | return nil 28 | } 29 | 30 | // RegisterStoreDecoder registers a decoder for ibank module's types 31 | func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { 32 | sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) 33 | } 34 | 35 | // WeightedOperations returns the all the ibank module operations with their respective weights. 36 | func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { 37 | return simulation.WeightedOperations( 38 | simState.AppParams, simState.Cdc, am.accountKeeper, am.bankKeeper, &am.keeper, 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /x/ibank/simulation/decoder.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | 7 | "mun/x/ibank/types" 8 | 9 | "github.com/cosmos/cosmos-sdk/codec" 10 | "github.com/cosmos/cosmos-sdk/types/kv" 11 | ) 12 | 13 | // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's 14 | // Value to the corresponding ibank type. 15 | func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { 16 | return func(kvA, kvB kv.Pair) string { 17 | switch { 18 | case string(kvA.Key) == types.TransactionKey: 19 | var txA, txB types.Transaction 20 | cdc.MustUnmarshal(kvA.Value, &txA) 21 | cdc.MustUnmarshal(kvB.Value, &txB) 22 | return fmt.Sprintf("%v\n%v", txA, txB) 23 | 24 | case string(kvA.Key) == types.TransactionCountKey: 25 | return fmt.Sprintf("%v\n%v", binary.BigEndian.Uint64(kvA.Value), binary.BigEndian.Uint64(kvB.Value)) 26 | 27 | case string(kvA.Key) == types.TransactionChaserKey: 28 | return fmt.Sprintf("%v\n%v", binary.BigEndian.Uint64(kvA.Value), binary.BigEndian.Uint64(kvB.Value)) 29 | 30 | default: 31 | panic(fmt.Sprintf("invalid ibank key prefix %v", kvA.Key)) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /x/ibank/simulation/decoder_test.go: -------------------------------------------------------------------------------- 1 | package simulation_test 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "testing" 7 | 8 | "mun/app" 9 | "mun/x/ibank/simulation" 10 | "mun/x/ibank/types" 11 | 12 | "github.com/cosmos/cosmos-sdk/types/kv" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestDecodeStore(t *testing.T) { 17 | cdc := app.MakeEncodingConfig().Marshaler 18 | dec := simulation.NewDecodeStore(cdc) 19 | 20 | tx := types.Transaction{ 21 | Id: 1, 22 | } 23 | txCount := 1 24 | txChaser := 1 25 | txCountInBytes := make([]byte, 8) 26 | txChaserInBytes := make([]byte, 8) 27 | binary.BigEndian.PutUint64(txCountInBytes, uint64(txCount)) 28 | binary.BigEndian.PutUint64(txChaserInBytes, uint64(txChaser)) 29 | 30 | kvParis := kv.Pairs{ 31 | Pairs: []kv.Pair{ 32 | {Key: []byte(types.TransactionKey), Value: cdc.MustMarshal(&tx)}, 33 | {Key: []byte(types.TransactionCountKey), Value: txCountInBytes}, 34 | {Key: []byte(types.TransactionChaserKey), Value: txChaserInBytes}, 35 | }, 36 | } 37 | 38 | tests := []struct { 39 | name string 40 | expectedLog string 41 | }{ 42 | {"TransactionKey", fmt.Sprintf("%v\n%v", tx, tx)}, 43 | {"TransactionCounterKey", fmt.Sprintf("%v\n%v", txCount, txCount)}, 44 | {"TransactionChaserKey", fmt.Sprintf("%v\n%v", txChaser, txChaser)}, 45 | {"other", ""}, 46 | } 47 | 48 | for i, tt := range tests { 49 | i, tt := i, tt 50 | t.Run(tt.name, func(t *testing.T) { 51 | switch i { 52 | case len(tests) - 1: 53 | require.Panics(t, func() { dec(kvParis.Pairs[i], kvParis.Pairs[i]) }, tt.name) 54 | default: 55 | require.Equal(t, tt.expectedLog, dec(kvParis.Pairs[i], kvParis.Pairs[i]), tt.name) 56 | } 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /x/ibank/simulation/genesis.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "math/rand" 7 | "time" 8 | 9 | "mun/x/ibank/types" 10 | 11 | "github.com/cosmos/cosmos-sdk/types/module" 12 | // "github.com/cosmos/cosmos-sdk/x/staking/types" 13 | ) 14 | 15 | // Simulation parameter constants 16 | const ( 17 | ExpirationTime = "expiration_time" 18 | ) 19 | 20 | func getExpirationTime(r *rand.Rand) time.Duration { 21 | dur, _ := time.ParseDuration(types.DefaultDurationOfExpiration) 22 | return dur 23 | } 24 | 25 | func RandomizedGenState(simState *module.SimulationState) { 26 | var ( 27 | expirationTime time.Duration 28 | ) 29 | 30 | simState.AppParams.GetOrGenerate( 31 | simState.Cdc, ExpirationTime, &expirationTime, simState.Rand, 32 | func(r *rand.Rand) { expirationTime = getExpirationTime(r) }, 33 | ) 34 | 35 | params := types.NewParams(expirationTime) 36 | 37 | ibankGenesis := types.NewGenesisState(params) 38 | 39 | bz, err := json.MarshalIndent(&ibankGenesis.Params, "", " ") 40 | if err != nil { 41 | panic(err) 42 | } 43 | fmt.Printf("Selected randomly generated ibank parameters:\n%s\n", bz) 44 | 45 | simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(ibankGenesis) 46 | } 47 | -------------------------------------------------------------------------------- /x/ibank/simulation/genesis_test.go: -------------------------------------------------------------------------------- 1 | package simulation_test 2 | 3 | import ( 4 | "encoding/json" 5 | "math/rand" 6 | "mun/x/ibank/simulation" 7 | "mun/x/ibank/types" 8 | "testing" 9 | "time" 10 | 11 | "github.com/cosmos/cosmos-sdk/codec" 12 | "github.com/cosmos/cosmos-sdk/types/module" 13 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 14 | "github.com/stretchr/testify/require" 15 | 16 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 17 | ) 18 | 19 | func TestRandomizedGenState(t *testing.T) { 20 | registry := codectypes.NewInterfaceRegistry() 21 | types.RegisterInterfaces(registry) 22 | cdc := codec.NewProtoCodec(registry) 23 | 24 | s := rand.NewSource(1) 25 | r := rand.New(s) 26 | 27 | simState := module.SimulationState{ 28 | AppParams: make(simtypes.AppParams), 29 | Cdc: cdc, 30 | Rand: r, 31 | NumBonded: 3, 32 | Accounts: simtypes.RandomAccounts(r, 3), 33 | InitialStake: 1000, 34 | GenState: make(map[string]json.RawMessage), 35 | } 36 | 37 | simulation.RandomizedGenState(&simState) 38 | 39 | var ibankGenesis types.GenesisState 40 | simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &ibankGenesis) 41 | 42 | require.Equal(t, time.Duration(172800000000000), ibankGenesis.Params.DurationOfExpiration) 43 | } 44 | -------------------------------------------------------------------------------- /x/ibank/simulation/helpers.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 6 | ) 7 | 8 | // FindAccount find a specific address from an account list 9 | func FindAccount(accs []simtypes.Account, address string) (simtypes.Account, bool) { 10 | creator, err := sdk.AccAddressFromBech32(address) 11 | if err != nil { 12 | panic(err) 13 | } 14 | return simtypes.FindAccount(accs, creator) 15 | } 16 | -------------------------------------------------------------------------------- /x/ibank/simulation/operations.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "math/rand" 5 | "mun/x/ibank/keeper" 6 | "mun/x/ibank/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/baseapp" 9 | "github.com/cosmos/cosmos-sdk/codec" 10 | simappparams "github.com/cosmos/cosmos-sdk/simapp/params" 11 | sdk "github.com/cosmos/cosmos-sdk/types" 12 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 13 | "github.com/cosmos/cosmos-sdk/x/simulation" 14 | ) 15 | 16 | const ( 17 | DefaultWeightMsgSend int = 100 18 | DefaultWeightMsgReceive int = 100 19 | 20 | OpWeightMsgSend = "op_weight_msg_send" 21 | OpWeightMsgReceive = "op_weight_msg_receive" 22 | ) 23 | 24 | func WeightedOperations( 25 | appParams simtypes.AppParams, cdc codec.JSONCodec, ak types.AccountKeeper, 26 | bk types.BankKeeper, k *keeper.Keeper, 27 | ) simulation.WeightedOperations { 28 | var ( 29 | weightMsgSend int 30 | weightMsgReceive int 31 | ) 32 | 33 | appParams.GetOrGenerate(cdc, OpWeightMsgSend, &weightMsgSend, nil, 34 | func(_ *rand.Rand) { 35 | weightMsgSend = DefaultWeightMsgSend 36 | }, 37 | ) 38 | 39 | appParams.GetOrGenerate(cdc, OpWeightMsgReceive, &weightMsgReceive, nil, 40 | func(_ *rand.Rand) { 41 | weightMsgReceive = DefaultWeightMsgReceive 42 | }, 43 | ) 44 | 45 | return simulation.WeightedOperations{ 46 | simulation.NewWeightedOperation( 47 | weightMsgSend, 48 | SimulateMsgSend(ak, bk, k), 49 | ), 50 | // simulation.NewWeightedOperation( 51 | // weightMsgReceive, 52 | // SimulateMsgReceive(ak, bk, k), 53 | // ), 54 | } 55 | } 56 | 57 | func SimulateMsgSend( 58 | ak types.AccountKeeper, 59 | bk types.BankKeeper, 60 | k *keeper.Keeper, 61 | ) simtypes.Operation { 62 | return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 63 | ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 64 | msg := &types.MsgSend{} 65 | 66 | if len(accs) < 2 { 67 | return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "Send simulation: not enough accounts"), nil, nil 68 | } 69 | 70 | fromAccount, _ := simtypes.RandomAcc(r, accs) 71 | toAccount, _ := simtypes.RandomAcc(r, accs) 72 | 73 | password := "557cf1df72ade86102a1d8f8e83cec2eab834133c2f7e6a1d64f13603d69fece" // sha256("word1 word2 word3 word4 word5 word6") 74 | spendable := bk.SpendableCoins(ctx, fromAccount.Address) 75 | if len(spendable) == 0 { 76 | return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "Send simulation: not enough funds to simulate"), nil, nil 77 | } 78 | 79 | msg = types.NewMsgSend( 80 | fromAccount.Address.String(), 81 | toAccount.Address.String(), 82 | spendable[0], 83 | password, 84 | ) 85 | 86 | txCtx := simulation.OperationInput{ 87 | R: r, 88 | App: app, 89 | TxGen: simappparams.MakeTestEncodingConfig().TxConfig, 90 | Cdc: nil, 91 | Msg: msg, 92 | MsgType: msg.Type(), 93 | Context: ctx, 94 | SimAccount: fromAccount, 95 | AccountKeeper: ak, 96 | Bankkeeper: bk, 97 | ModuleName: types.ModuleName, 98 | CoinsSpentInMsg: sdk.NewCoins(spendable[0]), 99 | } 100 | 101 | return simulation.GenAndDeliverTxWithRandFees(txCtx) 102 | } 103 | } 104 | 105 | func SimulateMsgReceive( 106 | ak types.AccountKeeper, 107 | bk types.BankKeeper, 108 | k *keeper.Keeper, 109 | ) simtypes.Operation { 110 | return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 111 | ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 112 | simAccount, _ := simtypes.RandomAcc(r, accs) 113 | msg := &types.MsgReceive{} 114 | 115 | txCount := k.GetTransactionCount(ctx) 116 | if txCount == 0 { 117 | return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "Send simulation: no ibank txs to simulate"), nil, nil 118 | } 119 | 120 | txID := rand.Int63n(int64(txCount)) 121 | password := "word1 word2 word3 word4 word5 word6" 122 | 123 | msg = types.NewMsgReceive( 124 | simAccount.Address.String(), 125 | txID, 126 | password, 127 | ) 128 | 129 | txCtx := simulation.OperationInput{ 130 | R: r, 131 | App: app, 132 | TxGen: simappparams.MakeTestEncodingConfig().TxConfig, 133 | Cdc: nil, 134 | Msg: msg, 135 | MsgType: msg.Type(), 136 | Context: ctx, 137 | SimAccount: simAccount, 138 | AccountKeeper: ak, 139 | Bankkeeper: bk, 140 | ModuleName: types.ModuleName, 141 | } 142 | 143 | return simulation.GenAndDeliverTxWithRandFees(txCtx) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /x/ibank/simulation/params.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "math/rand" 5 | 6 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 7 | // "github.com/cosmos/cosmos-sdk/x/staking/types" 8 | ) 9 | 10 | // ParamChanges defines the parameters that can be modified by param change proposals 11 | // on the simulation 12 | func ParamChanges(r *rand.Rand) []simtypes.ParamChange { 13 | return []simtypes.ParamChange{} 14 | } 15 | -------------------------------------------------------------------------------- /x/ibank/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/cosmos/cosmos-sdk/types/msgservice" 8 | ) 9 | 10 | func RegisterCodec(cdc *codec.LegacyAmino) { 11 | cdc.RegisterConcrete(&MsgSend{}, "ibank/Send", nil) 12 | cdc.RegisterConcrete(&MsgReceive{}, "ibank/Receive", nil) 13 | // this line is used by starport scaffolding # 2 14 | } 15 | 16 | func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { 17 | registry.RegisterImplementations((*sdk.Msg)(nil), 18 | &MsgSend{}, 19 | ) 20 | registry.RegisterImplementations((*sdk.Msg)(nil), 21 | &MsgReceive{}, 22 | ) 23 | // this line is used by starport scaffolding # 3 24 | 25 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) 26 | } 27 | 28 | var ( 29 | Amino = codec.NewLegacyAmino() 30 | ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) 31 | ) 32 | -------------------------------------------------------------------------------- /x/ibank/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // DONTCOVER 4 | 5 | import ( 6 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 7 | ) 8 | 9 | // x/ibank module sentinel errors 10 | var ( 11 | ErrNoTransaction = sdkerrors.Register(ModuleName, 2, "transaction not found") 12 | ErrAlreadyReceived = sdkerrors.Register(ModuleName, 3, "already received this fund") 13 | ErrTxExpired = sdkerrors.Register(ModuleName, 4, "transaction has expired") 14 | ErrTxDeclined = sdkerrors.Register(ModuleName, 5, "transaction is declined") 15 | ErrInvalidReceiver = sdkerrors.Register(ModuleName, 6, "invalid receiver") 16 | ) 17 | -------------------------------------------------------------------------------- /x/ibank/types/events.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | EventTypeIBank = "ibank" 5 | AttributeValueCategory = ModuleName 6 | 7 | AttributeKeyReceiveSuccess = "receive_success" 8 | AttributeKeyRefunded = "refund" 9 | AttributeKeySender = "sender" 10 | AttributeKeyRecipient = "recipient" 11 | AttributeKeyRemittanceID = "remittance_id" 12 | AttributeKeyAmount = "amount" 13 | AttributeKeyAction = "action" 14 | ) 15 | -------------------------------------------------------------------------------- /x/ibank/types/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | "github.com/cosmos/cosmos-sdk/x/auth/types" 6 | ) 7 | 8 | // AccountKeeper defines the expected account keeper used for simulations (noalias) 9 | type AccountKeeper interface { 10 | GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI 11 | GetModuleAddress(name string) sdk.AccAddress 12 | SetModuleAccount(ctx sdk.Context, macc types.ModuleAccountI) 13 | GetModuleAccount(ctx sdk.Context, moduleName string) types.ModuleAccountI 14 | NewAccount(ctx sdk.Context, acc types.AccountI) types.AccountI 15 | // Methods imported from account should be defined here 16 | } 17 | 18 | // BankKeeper defines the expected interface needed to retrieve account balances. 19 | type BankKeeper interface { 20 | SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins 21 | 22 | MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error 23 | 24 | SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error 25 | SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error 26 | // Methods imported from bank should be defined here 27 | } 28 | -------------------------------------------------------------------------------- /x/ibank/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // DefaultIndex is the default global index 8 | const DefaultIndex uint64 = 1 9 | 10 | // DefaultGenesis returns the default genesis state 11 | func DefaultGenesis() *GenesisState { 12 | return &GenesisState{ 13 | Params: DefaultParams(), 14 | TransactionList: []Transaction{}, 15 | TransactionCount: 0, 16 | TransactionChaser: 0, 17 | // this line is used by starport scaffolding # genesis/types/default 18 | } 19 | } 20 | 21 | func NewGenesisState(params Params) *GenesisState { 22 | return &GenesisState{ 23 | Params: params, 24 | TransactionList: nil, 25 | TransactionCount: 0, 26 | TransactionChaser: 0, 27 | } 28 | } 29 | 30 | // Validate performs basic genesis state validation returning an error upon any 31 | // failure. 32 | func (gs GenesisState) Validate() error { 33 | // Check for duplicated ID in transaction 34 | transactionIDMap := make(map[uint64]bool) 35 | transactionCount := gs.GetTransactionCount() 36 | for _, elem := range gs.TransactionList { 37 | if _, ok := transactionIDMap[elem.Id]; ok { 38 | return fmt.Errorf("duplicated id for transaction") 39 | } 40 | if elem.Id >= transactionCount { 41 | return fmt.Errorf("transaction id should be lower or equal than the last id") 42 | } 43 | transactionIDMap[elem.Id] = true 44 | } 45 | 46 | if gs.Params.DurationOfExpiration <= 0 { 47 | return fmt.Errorf("duration of expiration should be positive; %d", gs.Params.DurationOfExpiration) 48 | } 49 | // this line is used by starport scaffolding # genesis/types/validate 50 | 51 | return gs.Params.Validate() 52 | } 53 | -------------------------------------------------------------------------------- /x/ibank/types/genesis_test.go: -------------------------------------------------------------------------------- 1 | package types_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "mun/x/ibank/types" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestGenesisState_Validate(t *testing.T) { 12 | for _, tc := range []struct { 13 | desc string 14 | genState *types.GenesisState 15 | valid bool 16 | }{ 17 | { 18 | desc: "default is valid", 19 | genState: types.DefaultGenesis(), 20 | valid: true, 21 | }, 22 | { 23 | desc: "valid genesis state", 24 | genState: &types.GenesisState{ 25 | Params: types.DefaultParams(), 26 | TransactionList: []types.Transaction{ 27 | { 28 | Id: 0, 29 | }, 30 | { 31 | Id: 1, 32 | }, 33 | }, 34 | TransactionCount: 2, 35 | // this line is used by starport scaffolding # types/genesis/validField 36 | }, 37 | valid: true, 38 | }, 39 | { 40 | desc: "duplicated transaction", 41 | genState: &types.GenesisState{ 42 | TransactionList: []types.Transaction{ 43 | { 44 | Id: 0, 45 | }, 46 | { 47 | Id: 0, 48 | }, 49 | }, 50 | }, 51 | valid: false, 52 | }, 53 | { 54 | desc: "invalid transaction count", 55 | genState: &types.GenesisState{ 56 | TransactionList: []types.Transaction{ 57 | { 58 | Id: 1, 59 | }, 60 | }, 61 | TransactionCount: 0, 62 | }, 63 | valid: false, 64 | }, 65 | // this line is used by starport scaffolding # types/genesis/testcase 66 | } { 67 | t.Run(tc.desc, func(t *testing.T) { 68 | err := tc.genState.Validate() 69 | if tc.valid { 70 | require.NoError(t, err) 71 | } else { 72 | require.Error(t, err) 73 | } 74 | }) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /x/ibank/types/keys.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName defines the module name 5 | ModuleName = "ibank" 6 | 7 | // StoreKey defines the primary module store key 8 | StoreKey = ModuleName 9 | 10 | // RouterKey defines the module's message routing key 11 | RouterKey = ModuleName 12 | 13 | // MemStoreKey defines the in-memory store key 14 | MemStoreKey = "mem_ibank" 15 | ) 16 | 17 | func KeyPrefix(p string) []byte { 18 | return []byte(p) 19 | } 20 | 21 | const ( 22 | TransactionKey = "Transaction/value/" 23 | TransactionCountKey = "Transaction/count/" 24 | TransactionChaserKey = "Transaction/chaser/" 25 | ) 26 | -------------------------------------------------------------------------------- /x/ibank/types/message_receive.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 6 | ) 7 | 8 | const TypeMsgReceive = "receive" 9 | 10 | var _ sdk.Msg = &MsgReceive{} 11 | 12 | func NewMsgReceive(receiver string, transactionID int64, password string) *MsgReceive { 13 | return &MsgReceive{ 14 | Receiver: receiver, 15 | TransactionId: transactionID, 16 | Password: password, 17 | } 18 | } 19 | 20 | func (msg *MsgReceive) Route() string { 21 | return RouterKey 22 | } 23 | 24 | func (msg *MsgReceive) Type() string { 25 | return TypeMsgReceive 26 | } 27 | 28 | func (msg *MsgReceive) GetSigners() []sdk.AccAddress { 29 | receiver, err := sdk.AccAddressFromBech32(msg.Receiver) 30 | if err != nil { 31 | panic(err) 32 | } 33 | return []sdk.AccAddress{receiver} 34 | } 35 | 36 | func (msg *MsgReceive) GetSignBytes() []byte { 37 | bz := ModuleCdc.MustMarshalJSON(msg) 38 | return sdk.MustSortJSON(bz) 39 | } 40 | 41 | func (msg *MsgReceive) ValidateBasic() error { 42 | _, err := sdk.AccAddressFromBech32(msg.Receiver) 43 | if err != nil { 44 | return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid receiver address (%s)", err) 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /x/ibank/types/message_send.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 6 | ) 7 | 8 | const TypeMsgSend = "send" 9 | 10 | var _ sdk.Msg = &MsgSend{} 11 | 12 | func NewMsgSend(fromAddress string, toAddress string, amount sdk.Coin, password string) *MsgSend { 13 | return &MsgSend{ 14 | FromAddress: fromAddress, 15 | ToAddress: toAddress, 16 | Amount: amount, 17 | PasswordHash: password, 18 | } 19 | } 20 | 21 | func (msg *MsgSend) Route() string { 22 | return RouterKey 23 | } 24 | 25 | func (msg *MsgSend) Type() string { 26 | return TypeMsgSend 27 | } 28 | 29 | func (msg *MsgSend) GetSigners() []sdk.AccAddress { 30 | fromAddress, err := sdk.AccAddressFromBech32(msg.FromAddress) 31 | if err != nil { 32 | panic(err) 33 | } 34 | return []sdk.AccAddress{fromAddress} 35 | } 36 | 37 | func (msg *MsgSend) GetSignBytes() []byte { 38 | bz := ModuleCdc.MustMarshalJSON(msg) 39 | return sdk.MustSortJSON(bz) 40 | } 41 | 42 | func (msg *MsgSend) ValidateBasic() error { 43 | _, err := sdk.AccAddressFromBech32(msg.FromAddress) 44 | if err != nil { 45 | return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid fromAddress address (%s)", err) 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /x/ibank/types/params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" 8 | "gopkg.in/yaml.v2" 9 | ) 10 | 11 | const ( 12 | DefaultDurationOfExpiration = "48h" 13 | ) 14 | 15 | // Parameter store keys 16 | var ( 17 | KeyDurationOfExpiration = []byte("DurationOfExpiration") 18 | ) 19 | 20 | var _ paramtypes.ParamSet = (*Params)(nil) 21 | 22 | // ParamKeyTable the param key table for launch module 23 | func ParamKeyTable() paramtypes.KeyTable { 24 | return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) 25 | } 26 | 27 | // NewParams creates a new Params instance 28 | func NewParams(duration time.Duration) Params { 29 | return Params{ 30 | DurationOfExpiration: duration, 31 | } 32 | } 33 | 34 | // DefaultParams returns a default set of parameters 35 | func DefaultParams() Params { 36 | dur, _ := time.ParseDuration(DefaultDurationOfExpiration) 37 | return NewParams(dur) 38 | } 39 | 40 | // ParamSetPairs get the params.ParamSet 41 | func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { 42 | return paramtypes.ParamSetPairs{ 43 | paramtypes.NewParamSetPair(KeyDurationOfExpiration, &p.DurationOfExpiration, validateDuration), 44 | } 45 | } 46 | 47 | // Validate validates the set of params 48 | func (p Params) Validate() error { 49 | return nil 50 | } 51 | 52 | // String implements the Stringer interface. 53 | func (p Params) String() string { 54 | out, _ := yaml.Marshal(p) 55 | return string(out) 56 | } 57 | 58 | func validateDuration(i interface{}) error { 59 | d, ok := i.(time.Duration) 60 | if !ok { 61 | return fmt.Errorf("invalid parameter type: %T", i) 62 | } 63 | if d < 1 { 64 | return fmt.Errorf("duration must be greater than 1: %d", d) 65 | } 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /x/ibank/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | TxPending TxnStatus = 0 5 | TxSent TxnStatus = 1 6 | TxExpired TxnStatus = 2 7 | TxDeclined TxnStatus = 3 8 | ) 9 | --------------------------------------------------------------------------------