├── .gitignore ├── HardForkContestInstruction.md ├── LICENSE ├── Mainnet-upgrade-instruction └── MainnetUpgradeInstructions.md ├── Makefile ├── Makefile.ledger ├── NetworkRehearsalInstructions └── NetworkRehearsalInstructions.md ├── README.md ├── SUMMARY.md ├── app ├── accountFetcher │ ├── AccountFetcher.go │ └── types │ │ └── types.go ├── ante │ ├── MempoolFeeDecorator.go │ ├── NewSetUpContextDecorator.go │ ├── NftChecksAnteHandler.go │ ├── TaxDecorator.go │ ├── ante.go │ └── gasmeter │ │ ├── ChargingGasMeter.go │ │ ├── ChargingGasMeterInterface.go │ │ ├── FreeGasMeter.go │ │ └── GasMeterKeeper.go ├── app.go └── test_common.go ├── cmd ├── blzcli │ └── main.go └── blzd │ ├── genaccounts.go │ └── main.go ├── curium.iml ├── docs ├── commands │ ├── qAndTX.md │ └── useful.md ├── nft │ ├── Bluzelle NFT Secure Storage API.md │ ├── auth-protocol.dia │ ├── auth-protocol.png │ ├── short protocol.dia │ └── short protocol.png ├── oracle │ ├── Diagram1.dia │ └── Diagram1.dia~ ├── public │ └── buildvalidatorsentry.md ├── rest │ └── buildrest.md └── setup │ ├── build.md │ ├── deploy.md │ ├── deployaddl.md │ ├── devenv.md │ └── os.md ├── go.mod ├── go.sum ├── home ├── .torrent.db ├── .torrent.db-shm ├── .torrent.db-wal ├── 4cbf988462cc3ba2e10e3aae9f5268546aa79016359fb45be7dd199c073125c0 └── nft │ ├── .torrent.db │ ├── .torrent.db-shm │ ├── .torrent.db-wal │ └── 4cbf988462cc3ba2e10e3aae9f5268546aa79016359fb45be7dd199c073125c0 ├── localsetup.sh ├── package.json ├── public-validator-+-sentry └── buildvalidatorsentry.md ├── rest-service └── buildrest.md ├── scripts ├── deploy.ts ├── package.json ├── setup.ts ├── tsconfig.json └── yarn.lock ├── test ├── integration │ ├── .gitignore │ ├── helpers │ │ ├── bluzelle-client.ts │ │ ├── blzcli.ts │ │ └── nft-helpers.ts │ ├── package.json │ ├── specs │ │ ├── ante.spec.ts │ │ ├── nft │ │ │ ├── MsgCreateNft.spec.ts │ │ │ ├── MsgPublishFile.spec.ts │ │ │ ├── nft-upload.spec.ts │ │ │ └── store-nft.spec.ts │ │ └── oracle │ │ │ ├── oracle-utils.ts │ │ │ ├── rest │ │ │ ├── add-source.spec.ts │ │ │ └── search-votes.spec.ts │ │ │ ├── shouldBeFreeHelper.ts │ │ │ └── sources.ts │ ├── tsconfig.json │ ├── yarn-error.log │ └── yarn.lock ├── load │ ├── package.json │ ├── src │ │ └── load.ts │ ├── tsconfig.json │ └── yarn.lock └── oracle-mock │ ├── node_modules │ ├── .yarn-integrity │ └── monet │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── dist │ │ ├── monet-pimp.js │ │ ├── monet-pimp.min.js │ │ ├── monet-pimp.min.js.map │ │ ├── monet.d.ts │ │ ├── monet.js │ │ ├── monet.min.js │ │ └── monet.min.js.map │ │ ├── package.json │ │ └── prepublish-test │ │ ├── browser-global.js │ │ ├── node-global.js │ │ └── node-regular.js │ ├── oracle-mock.ts │ ├── package.json │ └── yarn.lock ├── types ├── development.go └── types.go ├── x ├── aggregator │ ├── abci.go │ ├── alias.go │ ├── client │ │ ├── cli │ │ │ ├── query.go │ │ │ └── tx.go │ │ └── rest │ │ │ ├── query.go │ │ │ ├── rest.go │ │ │ └── tx.go │ ├── genesis.go │ ├── handler.go │ ├── keeper │ │ ├── Averager.go │ │ ├── Averager_test.go │ │ ├── keeper.go │ │ ├── keeper_test.go │ │ ├── params.go │ │ └── querier.go │ ├── module.go │ ├── spec │ │ └── README.md │ └── types │ │ ├── codec.go │ │ ├── errors.go │ │ ├── events.go │ │ ├── expected_keepers.go │ │ ├── genesis.go │ │ ├── keeper.go │ │ ├── key.go │ │ ├── key_test.go │ │ ├── msg.go │ │ ├── params.go │ │ └── querier.go ├── crud │ ├── alias.go │ ├── client │ │ ├── cli │ │ │ ├── query.go │ │ │ └── tx.go │ │ └── rest │ │ │ ├── query.go │ │ │ ├── rest.go │ │ │ └── tx.go │ ├── gas_calculator.go │ ├── gas_calculator_test.go │ ├── genesis.go │ ├── genesis_test.go │ ├── handler.go │ ├── handler_test.go │ ├── internal │ │ ├── keeper │ │ │ ├── keeper.go │ │ │ ├── keeper_test.go │ │ │ ├── querier.go │ │ │ └── querier_test.go │ │ └── types │ │ │ ├── codec.go │ │ │ ├── key.go │ │ │ ├── msgs.go │ │ │ ├── msgs_test.go │ │ │ ├── querier.go │ │ │ ├── querier_test.go │ │ │ ├── types.go │ │ │ └── types_test.go │ ├── mocks │ │ ├── mock_gas.go │ │ └── mock_keeper.go │ ├── module.go │ └── module_test.go ├── curium │ ├── alias.go │ ├── client │ │ ├── cli │ │ │ ├── query.go │ │ │ └── tx.go │ │ └── rest │ │ │ ├── query.go │ │ │ ├── rest.go │ │ │ └── tx.go │ ├── genesis.go │ ├── handler.go │ ├── keeper │ │ ├── AccountInfo.go │ │ ├── AccountInfo_test.go │ │ ├── GenesisReader.go │ │ ├── MsgBroadcastQueue.go │ │ ├── MsgBroadcastQueue_test.go │ │ ├── keeper.go │ │ └── querier.go │ ├── module.go │ └── types │ │ ├── codec.go │ │ ├── genesis.go │ │ ├── keys.go │ │ └── types.go ├── nft │ ├── alias.go │ ├── client │ │ ├── cli │ │ │ ├── query.go │ │ │ └── tx.go │ │ └── rest │ │ │ ├── getNftByVendorHandler.go │ │ │ ├── getNftHandler.go │ │ │ ├── query.go │ │ │ ├── rest.go │ │ │ ├── tx.go │ │ │ └── uploadNftHandler.go │ ├── demo │ │ ├── demo.ts │ │ ├── image.jpg │ │ ├── package.json │ │ └── yarn.lock │ ├── genesis.go │ ├── handler.go │ ├── keeper │ │ ├── TorrentClient.go │ │ ├── createNft.go │ │ ├── keeper.go │ │ ├── nft.go │ │ └── querier.go │ ├── module.go │ └── types │ │ ├── codec.go │ │ ├── genesis.go │ │ ├── keys.go │ │ ├── msgCreateNft.go │ │ ├── msgPublishFile.go │ │ ├── msgRegisterPeer.go │ │ ├── nft.go │ │ ├── peer.go │ │ ├── querier.go │ │ ├── uploadTokenManager.go │ │ └── uploadTokenManager_test.go ├── oracle │ ├── abci.go │ ├── alias.go │ ├── client │ │ ├── cli │ │ │ ├── query.go │ │ │ └── tx.go │ │ └── rest │ │ │ ├── query.go │ │ │ ├── rest.go │ │ │ └── tx.go │ ├── docs │ │ └── arch.md │ ├── feeder.go │ ├── genesis.go │ ├── handler.go │ ├── keeper │ │ ├── keeper.go │ │ ├── keeper_global_config.go │ │ ├── keeper_proof.go │ │ ├── keeper_source.go │ │ ├── keeper_test.go │ │ ├── keeper_value.go │ │ ├── keeper_vote.go │ │ ├── params.go │ │ └── querier.go │ ├── module.go │ ├── spec │ │ └── README.md │ └── types │ │ ├── codec.go │ │ ├── errors.go │ │ ├── events.go │ │ ├── expected_keepers.go │ │ ├── genesis.go │ │ ├── keeper.go │ │ ├── key.go │ │ ├── msg.go │ │ ├── params.go │ │ ├── querier.go │ │ └── types.go └── tax │ ├── README.md │ ├── alias.go │ ├── client │ ├── cli │ │ ├── query.go │ │ └── tx.go │ └── rest │ │ ├── query.go │ │ ├── rest.go │ │ └── tx.go │ ├── genesis.go │ ├── genesis_test.go │ ├── handler.go │ ├── handler_test.go │ ├── internal │ ├── keeper │ │ ├── keeper.go │ │ ├── keeper_test.go │ │ ├── querier.go │ │ └── querier_test.go │ └── types │ │ ├── codec.go │ │ ├── key.go │ │ ├── msg_collector.go │ │ ├── msg_collector_test.go │ │ ├── msg_percentage.go │ │ ├── types.go │ │ └── types_test.go │ ├── module.go │ └── module_test.go └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | 15 | # Goland project files 16 | .idea/ 17 | blzcli 18 | blzd 19 | 20 | # Garbage OS files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /Mainnet-upgrade-instruction/MainnetUpgradeInstructions.md: -------------------------------------------------------------------------------- 1 | # Mainnet Upgrade Instructions: from `Bluzelle Soft MainNet` to `Bluzelle Production Mainnet` 2 | 3 | Dear validators, you can now join the Bluzelle Production Mainnet by following [this guide](../public-validator-+-sentry/buildvalidatorsentry.md) 4 | 5 | **Please follow the instructions for the FORK path**. You will effectively be building new Mainnet nodes, but they will inherit the exact same state, accounts, etc as the existing Soft MainNet, as per a snapshot taken at a specific block height. Your new validator will in fact use the exact same consensus private key to sign blocks, as the old validator on the Soft MainNet. The new Mainnet will start all over again from block 0, but its starting state will be the state inherited from the snapshot taken from the Soft MainNet. 6 | 7 | :warning: Please complete the process as soon as possible. Please read carefully before you begin the fork from the Bluzelle Soft Mainnet. 8 | 9 | :warning: Just in case a revert could happen, please DO NOT delete your `Soft MainNet` validator and sentry machine(s) before you are instructed to do so. We expect the `Soft Mainnet` to be taken down on 4th February (tentatively). If for some reason we need to revert, your Soft MainNet validator WILL BE EXPECTED TO BE UP. If it is not up, you CAN BE SLASHED. 10 | 11 | :warning: As part of the process of forking the old network over to the new one, we **PRE-JAIL** all validators. This means that your validator, while already existing in the new network, is in a jailed state. You have NOT been slashed. The validator is effectively "frozen", safe from being slashed as it is not yet running. However, you should still join soon because unbonding has started. If you wait too long, you will risk losing all your delegations. 12 | 13 | :warning: If your Production Mainnet machines have a previous installation on them, you will need to remove those, before you continue. Please do all necessary cleanup on them to remove existing installation files, binaries, etc. It is recommended to conduct the rehearsal on a fresh machine, if possible. 14 | 15 | Please watch out for any communications on our [Discord server](https://discord.gg/KRhcKE6qS6). 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PACKAGES=$(shell go list ./... | grep -v '/simulation') 2 | 3 | VERSION := $(shell echo $(shell git describe --tags --dirty) | sed 's/^v//') 4 | COMMIT := $(shell git log -1 --format='%H') 5 | COSMOS_SDK := $(shell grep -i cosmos-sdk go.mod | awk '{print $$2}') 6 | 7 | include Makefile.ledger 8 | 9 | build_tags += $(BUILD_TAGS) 10 | build_tags := $(strip $(build_tags)) 11 | 12 | whitespace := 13 | whitespace += $(whitespace) 14 | comma := , 15 | build_tags_comma_sep := $(patsubst $(whitespace),$(comma),$(build_tags)) 16 | coverage := $(shell mktemp -u).coverage.out 17 | 18 | # process linker flags 19 | LDFLAGS = -X github.com/cosmos/cosmos-sdk/version.Name=BluzelleService \ 20 | -X github.com/cosmos/cosmos-sdk/version.ServerName=blzd \ 21 | -X github.com/cosmos/cosmos-sdk/version.ClientName=blzcli \ 22 | -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ 23 | -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) 24 | 25 | LDFLAGS_FAUCET = -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep),faucet,cosmos-sdk $(COSMOS_SDK)" 26 | LDFLAGS_NO_FAUCET = -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep),cosmos-sdk $(COSMOS_SDK)" 27 | 28 | BUILD_FLAGS := -tags "$(build_tags)" 29 | FAUCET_BUILD_FLAGS := -tags "$(build_tags),faucet" 30 | 31 | all: 32 | go build $(BUILD_FLAGS) -ldflags '$(LDFLAGS) $(LDFLAGS_NO_FAUCET)' ./cmd/blzd 33 | go build $(BUILD_FLAGS) -ldflags '$(LDFLAGS) $(LDFLAGS_NO_FAUCET)' ./cmd/blzcli 34 | 35 | clean: 36 | @rm -f blzd blzcli 37 | 38 | mainnet: go.sum 39 | go install -mod=readonly $(BUILD_FLAGS) -ldflags '$(LDFLAGS) $(LDFLAGS_NO_FAUCET)' ./cmd/blzd 40 | go install -mod=readonly $(BUILD_FLAGS) -ldflags '$(LDFLAGS) $(LDFLAGS_NO_FAUCET)' ./cmd/blzcli 41 | 42 | testnet: 43 | # only testnet has the faucet enabled... 44 | go install -mod=readonly $(FAUCET_BUILD_FLAGS) -ldflags '$(LDFLAGS) $(LDFLAGS_FAUCET)' ./cmd/blzd 45 | go install -mod=readonly $(FAUCET_BUILD_FLAGS) -ldflags '$(LDFLAGS) $(LDFLAGS_FAUCET)' ./cmd/blzcli 46 | 47 | go.sum: go.mod 48 | @echo "--> Ensure dependencies have not been modified" 49 | GO111MODULE=on go mod verify 50 | 51 | test: 52 | @go test -mod=readonly $(PACKAGES) 53 | 54 | test-tax: 55 | @go test -mod=readonly ./x/tax/... 56 | 57 | coverage: 58 | @go test -v -coverprofile=$(coverage) ./x/... 59 | @go tool cover -html=$(coverage) 60 | @rm $(coverage) 61 | -------------------------------------------------------------------------------- /Makefile.ledger: -------------------------------------------------------------------------------- 1 | LEDGER_ENABLED ?= true 2 | 3 | build_tags = 4 | ifeq ($(LEDGER_ENABLED),true) 5 | ifeq ($(OS),Windows_NT) 6 | GCCEXE = $(shell where gcc.exe 2> NUL) 7 | ifeq ($(GCCEXE),) 8 | $(error gcc.exe not installed for ledger support, please install or set LEDGER_ENABLED=false) 9 | else 10 | build_tags += ledger 11 | endif 12 | else 13 | UNAME_S = $(shell uname -s) 14 | ifeq ($(UNAME_S),OpenBSD) 15 | $(warning OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988)) 16 | else 17 | GCC = $(shell command -v gcc 2> /dev/null) 18 | ifeq ($(GCC),) 19 | $(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false) 20 | else 21 | build_tags += ledger 22 | endif 23 | endif 24 | endif 25 | endif 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CURIUM Overview 2 | 3 | [![Build Status](https://travis-ci.com/bluzelle/curium.svg?branch=devel)](https://travis-ci.com/bluzelle/curium) [![Coverage Status](https://coveralls.io/repos/github/bluzelle/curium/badge.svg?branch=devel)](https://coveralls.io/github/bluzelle/curium?branch=devel) 4 | 5 | ## The Bluzelle Curium Application 6 | 7 | The decentralized database for Web 3.0. Bluzelle uses blockchain and edge computing for greater performance and security. 8 | 9 | ### Bluzelle Curium REST Service 10 | 11 | If all you need is the Light-client Daemon \(LCD\) REST server: [Building the Curium REST Service](rest-service/buildrest.md) 12 | 13 | ### Bluzelle Curium Public Validator + Sentry Installation 14 | 15 | If you want to setup your own validator + sentry \(optional\) on our network: [Building a Public TestNet Validator + Sentry](public-validator-+-sentry/buildvalidatorsentry.md) 16 | 17 | ### Bluzelle Curium Private Multi-Validator Zone Installation 18 | 19 | These are steps involved in setting up the OS, Dev Environment, building and deploying a multi-node Curium Zone. 20 | 21 | 1. [OS Setup for Curium](setup/os.md) 22 | 2. [Development Environment Setup](setup/devenv.md) 23 | 3. [Build the Curium Project](setup/build.md) 24 | 4. [Deploy the Initial Node](setup/deploy.md) 25 | 5. [Deploy Additional Nodes](setup/deployaddl.md) 26 | 27 | ### Using a Bluzelle Curium Multi-Validator Zone 28 | 29 | * [Queries and Transactions](docs/commands/qAndTX.md) 30 | * [Useful Operations](docs/commands/useful.md) 31 | 32 | 33 | HELLO WORLD 2 34 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [CURIUM Overview](README.md) 4 | 5 | ## Setup 6 | 7 | * [OS Setup for Curium](setup/os.md) 8 | * [Development Environment Setup](setup/devenv.md) 9 | * [Build the Curium Project](setup/build.md) 10 | * [Deploy the Initial Node](setup/deploy.md) 11 | * [Deploy Additional Nodes](setup/deployaddl.md) 12 | 13 | ## Public Validator + Sentry 14 | 15 | * [Building a Public Testnet Validator + Sentry](public-validator-+-sentry/buildvalidatorsentry.md) 16 | 17 | ## REST Service 18 | 19 | * [Building the CURIUM REST Service](rest-service/buildrest.md) 20 | 21 | -------------------------------------------------------------------------------- /app/accountFetcher/AccountFetcher.go: -------------------------------------------------------------------------------- 1 | package accountFetcher 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/bluzelle/curium/app/accountFetcher/types" 6 | "github.com/bluzelle/curium/x/curium" 7 | "github.com/cosmos/cosmos-sdk/codec" 8 | "github.com/cosmos/cosmos-sdk/x/auth" 9 | abci "github.com/tendermint/tendermint/abci/types" 10 | ) 11 | 12 | type BaseAppWithQuery interface { 13 | Query(req abci.RequestQuery) abci.ResponseQuery 14 | 15 | } 16 | 17 | func AccountFetcher(app BaseAppWithQuery, cdc *codec.Codec, cliHome string) types.AccountFetcherFn { 18 | krReader := curium.NewKeyringReader(cliHome) 19 | return func(name string) (types.AcctInfo, error){ 20 | addr, err := krReader.GetAddress(name) 21 | 22 | if err != nil { 23 | return types.AcctInfo{}, err 24 | } 25 | 26 | d, _ := json.Marshal(map[string]string{ 27 | "Address": addr.String(), 28 | }) 29 | x := app.Query(abci.RequestQuery{ 30 | Data: d, 31 | Path: "custom/acc/account", 32 | }) 33 | 34 | var resp auth.BaseAccount 35 | cdc.MustUnmarshalJSON(x.Value, &resp) 36 | 37 | 38 | 39 | return types.AcctInfo{ 40 | Name: name, 41 | Address: addr.String(), 42 | AccNum: resp.GetAccountNumber(), 43 | Seq: resp.GetSequence(), 44 | }, nil 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/accountFetcher/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type AccountFetcherFn func(name string) (AcctInfo, error) 4 | 5 | type AcctInfo struct { 6 | Name string 7 | Address string 8 | AccNum uint64 9 | Seq uint64 10 | } 11 | 12 | -------------------------------------------------------------------------------- /app/ante/MempoolFeeDecorator.go: -------------------------------------------------------------------------------- 1 | package ante 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 6 | "github.com/cosmos/cosmos-sdk/x/auth/ante" 7 | ) 8 | 9 | type MempoolFeeDecorator struct{} 10 | 11 | func NewMempoolFeeDecorator() MempoolFeeDecorator { 12 | return MempoolFeeDecorator{} 13 | } 14 | 15 | func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 16 | feeTx, ok := tx.(ante.FeeTx) 17 | if !ok { 18 | return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") 19 | } 20 | 21 | feeCoins := feeTx.GetFee() 22 | gas := feeTx.GetGas() 23 | 24 | // Ensure that the provided fees meet a minimum threshold for the validator, 25 | // if this is a CheckTx. This is only for local mempool purposes, and thus 26 | // is only ran on check tx. 27 | if ctx.IsCheckTx() && !simulate { 28 | specifiedGasPrice := sdk.NewDecCoinsFromCoins(feeCoins[0]).QuoDec(sdk.NewDec(int64(gas)))[0] 29 | minGasPrice := ctx.MinGasPrices()[0] 30 | 31 | if specifiedGasPrice.IsLT(minGasPrice) { 32 | return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "gas price too low; got: %s required: %s", specifiedGasPrice, minGasPrice) 33 | } 34 | 35 | if gas == 0 { 36 | return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "max gas cannot be 0") 37 | } 38 | 39 | } 40 | 41 | return next(ctx, tx, simulate) 42 | } 43 | -------------------------------------------------------------------------------- /app/ante/NftChecksAnteHandler.go: -------------------------------------------------------------------------------- 1 | package ante 2 | 3 | import ( 4 | nft "github.com/bluzelle/curium/x/nft/keeper" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 7 | ) 8 | 9 | type NftChecksAnteHandler struct{ 10 | NftKeeper nft.Keeper 11 | } 12 | 13 | func NewNftChecksAnteHandler(nftKeeper nft.Keeper) NftChecksAnteHandler { 14 | return NftChecksAnteHandler{ 15 | NftKeeper: nftKeeper, 16 | } 17 | } 18 | 19 | func (mfd NftChecksAnteHandler) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 20 | signer := tx.GetMsgs()[0].GetSigners()[0] 21 | msgType := tx.GetMsgs()[0].Type() 22 | 23 | if msgType != "CreateNft" || mfd.NftKeeper.IsAuthorized(ctx, signer) { 24 | return next(ctx, tx, simulate) 25 | } 26 | return sdk.Context{}, sdkerrors.New("nft", 2, signer.String() + " is not on nft whitelist") 27 | } 28 | 29 | -------------------------------------------------------------------------------- /app/ante/TaxDecorator.go: -------------------------------------------------------------------------------- 1 | package ante 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/tax" 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/keeper" 8 | bank "github.com/cosmos/cosmos-sdk/x/bank" 9 | "github.com/cosmos/cosmos-sdk/x/gov/types" 10 | ) 11 | 12 | 13 | 14 | 15 | type TaxDecorator struct { 16 | ak keeper.AccountKeeper 17 | bk bank.Keeper 18 | tk tax.Keeper 19 | supplyKeeper types.SupplyKeeper 20 | } 21 | 22 | func NewTaxDecorator(ak keeper.AccountKeeper, sk types.SupplyKeeper, tk tax.Keeper, bk bank.Keeper) TaxDecorator { 23 | return TaxDecorator{ 24 | ak: ak, 25 | supplyKeeper: sk, 26 | tk: tk, 27 | bk: bk, 28 | } 29 | } 30 | 31 | // FeeTx defines the interface to be implemented by Tx to use the FeeDecorators 32 | type FeeTx interface { 33 | sdk.Tx 34 | GetGas() uint64 35 | GetFee() sdk.Coins 36 | FeePayer() sdk.AccAddress 37 | } 38 | 39 | func (td TaxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { 40 | 41 | if !simulate { 42 | 43 | feeTx, ok := tx.(FeeTx) 44 | if !ok { 45 | return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") 46 | } 47 | 48 | feePayer := feeTx.FeePayer() 49 | feePayerAcc := td.ak.GetAccount(ctx, feePayer) 50 | 51 | if feePayerAcc == nil { 52 | return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer) 53 | } 54 | 55 | if err := collectTransactionTax(ctx, td, tx, feeTx.GetFee(), feePayer); err != nil { 56 | return ctx, err 57 | } 58 | 59 | } 60 | return next(ctx, tx, simulate) 61 | } 62 | 63 | 64 | func collectTransactionTax(ctx sdk.Context, dfd TaxDecorator, tx sdk.Tx, fees sdk.Coins, feePayer sdk.AccAddress) error { 65 | 66 | // deduct the fees 67 | if !fees.IsZero() { 68 | taxInfo := dfd.tk.GetTaxInfo(ctx) 69 | 70 | // handle bank send tx fee 71 | msgs := tx.GetMsgs() 72 | for _, msg := range msgs { 73 | if msg.Type() == "send" { 74 | bankmsg := msg.(bank.MsgSend) 75 | trfFees := sdk.Coins{} 76 | for _, coin := range bankmsg.Amount { 77 | feeAmt := coin.Amount.Int64() * taxInfo.TransferBp / 10000 78 | if feeAmt > 0 { 79 | trfFee := sdk.NewInt64Coin(coin.Denom, feeAmt) 80 | trfFees = append(trfFees, trfFee) 81 | } 82 | } 83 | 84 | if len(trfFees) > 0 { 85 | if err := dfd.bk.SendCoins(ctx, feePayer, taxInfo.Collector, trfFees); err != nil { 86 | return err 87 | } 88 | } 89 | } 90 | } 91 | 92 | } 93 | return nil 94 | 95 | } 96 | 97 | 98 | -------------------------------------------------------------------------------- /app/ante/ante.go: -------------------------------------------------------------------------------- 1 | package ante 2 | 3 | import ( 4 | "github.com/bluzelle/curium/app/ante/gasmeter" 5 | "github.com/bluzelle/curium/x/crud" 6 | nft "github.com/bluzelle/curium/x/nft/keeper" 7 | "github.com/bluzelle/curium/x/tax" 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "github.com/cosmos/cosmos-sdk/x/auth/ante" 10 | "github.com/cosmos/cosmos-sdk/x/auth/keeper" 11 | "github.com/cosmos/cosmos-sdk/x/bank" 12 | "github.com/cosmos/cosmos-sdk/x/gov/types" 13 | ) 14 | 15 | func NewAnteHandler( 16 | accountKeeper keeper.AccountKeeper, 17 | supplyKeeper types.SupplyKeeper, 18 | taxKeeper tax.Keeper, 19 | bankKeeper bank.Keeper, 20 | crudKeeper crud.Keeper, 21 | nftKeeper *nft.Keeper, 22 | sigGasConsumer ante.SignatureVerificationGasConsumer, 23 | gasMeterKeeper *gasmeter.GasMeterKeeper, 24 | minGasPriceCoins sdk.DecCoins, 25 | ) sdk.AnteHandler { 26 | return sdk.ChainAnteDecorators( 27 | NewSetUpContextDecorator(gasMeterKeeper, supplyKeeper, accountKeeper, crudKeeper, minGasPriceCoins), // outermost AnteDecorator. SetUpContext must be called first 28 | NewMempoolFeeDecorator(), 29 | ante.NewValidateBasicDecorator(), 30 | ante.NewValidateMemoDecorator(accountKeeper), 31 | ante.NewConsumeGasForTxSizeDecorator(accountKeeper), 32 | ante.NewSetPubKeyDecorator(accountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators 33 | ante.NewValidateSigCountDecorator(accountKeeper), 34 | ante.NewSigGasConsumeDecorator(accountKeeper, sigGasConsumer), 35 | ante.NewSigVerificationDecorator(accountKeeper), 36 | NewTaxDecorator( 37 | accountKeeper, 38 | supplyKeeper, 39 | taxKeeper, 40 | bankKeeper, 41 | ), 42 | ante.NewIncrementSequenceDecorator(accountKeeper), // innermost AnteDecorator 43 | NewNftChecksAnteHandler(*nftKeeper), 44 | ) 45 | } -------------------------------------------------------------------------------- /app/ante/gasmeter/ChargingGasMeterInterface.go: -------------------------------------------------------------------------------- 1 | package gasmeter 2 | 3 | import sdk "github.com/cosmos/cosmos-sdk/types" 4 | 5 | type ChargingGasMeterInterface interface { 6 | Charge(ctx sdk.Context) error 7 | GetGasPrice() sdk.DecCoins 8 | } -------------------------------------------------------------------------------- /app/ante/gasmeter/FreeGasMeter.go: -------------------------------------------------------------------------------- 1 | package gasmeter 2 | 3 | import ( 4 | "fmt" 5 | storetypes "github.com/cosmos/cosmos-sdk/store/types" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "math" 8 | ) 9 | 10 | type freeGasMeter struct { 11 | limit storetypes.Gas 12 | consumed storetypes.Gas 13 | } 14 | 15 | func NewFreeGasMeter(limit storetypes.Gas) storetypes.GasMeter { 16 | return &freeGasMeter{ 17 | limit: limit, 18 | consumed: 0, 19 | } 20 | } 21 | 22 | func (g *freeGasMeter) GasConsumed() storetypes.Gas { 23 | return 0 24 | } 25 | 26 | func (g *freeGasMeter) Limit() storetypes.Gas { 27 | return g.limit 28 | } 29 | 30 | func (g *freeGasMeter) GasConsumedToLimit() storetypes.Gas { 31 | if g.IsPastLimit() { 32 | return 0 33 | } 34 | return 0 35 | } 36 | 37 | // addUint64Overflow performs the addition operation on two uint64 integers and 38 | // returns a boolean on whether or not the result overflows. 39 | func addUint64Overflow(a, b uint64) (uint64, bool) { 40 | if math.MaxUint64-a < b { 41 | return 0, true 42 | } 43 | 44 | return a + b, false 45 | } 46 | 47 | func (g *freeGasMeter) ConsumeGas(amount storetypes.Gas, descriptor string) { 48 | var overflow bool 49 | // TODO: Should we set the consumed field after overflow checking? 50 | g.consumed, overflow = addUint64Overflow(g.consumed, amount) 51 | if overflow && g.limit != 0 { 52 | panic(storetypes.ErrorGasOverflow{descriptor}) 53 | } 54 | 55 | if g.consumed > g.limit && g.limit != 0 { 56 | panic(storetypes.ErrorOutOfGas{descriptor}) 57 | } 58 | 59 | } 60 | 61 | func (g *freeGasMeter) IsPastLimit() bool { 62 | return g.consumed > g.limit && g.limit != 0 63 | } 64 | 65 | func (g *freeGasMeter) IsOutOfGas() bool { 66 | return g.consumed >= g.limit 67 | } 68 | 69 | func (g *freeGasMeter) String() string { 70 | return fmt.Sprintf("FreeGasMeter:\n limit: %d\n consumed: %d", g.limit, g.consumed) 71 | } 72 | 73 | func (g *freeGasMeter) Charge (ctx sdk.Context) error {return nil} 74 | 75 | func (g *freeGasMeter) GetGasPrice() sdk.DecCoins { 76 | return sdk.NewDecCoins() 77 | } -------------------------------------------------------------------------------- /app/ante/gasmeter/GasMeterKeeper.go: -------------------------------------------------------------------------------- 1 | package gasmeter 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | ) 6 | 7 | type GasMeterKeeper struct { 8 | gasMeters []*sdk.GasMeter 9 | } 10 | 11 | func NewGasMeterKeeper () *GasMeterKeeper { 12 | return &GasMeterKeeper{ 13 | gasMeters: make([]*sdk.GasMeter, 0), 14 | } 15 | } 16 | 17 | func (gk *GasMeterKeeper) ChargeAll (ctx sdk.Context) []error { 18 | errors := make([]error, 0) 19 | for _,gasMeter := range gk.gasMeters { 20 | gm := *gasMeter 21 | chargingGm := gm.(ChargingGasMeterInterface) 22 | err := chargingGm.Charge(ctx) 23 | if err != nil { 24 | errors = append(errors, err) 25 | } 26 | } 27 | return errors 28 | } 29 | 30 | func (gk *GasMeterKeeper) GetAllGasMeters () []*sdk.GasMeter { 31 | return gk.gasMeters 32 | } 33 | 34 | func (gk *GasMeterKeeper) AddGasMeter (gasMeter *sdk.GasMeter) { 35 | gk.gasMeters = append(gk.gasMeters, gasMeter) 36 | } 37 | 38 | func (gk *GasMeterKeeper) ClearAll () { 39 | gk.gasMeters = make([]*sdk.GasMeter, 0) 40 | } 41 | -------------------------------------------------------------------------------- /curium.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/nft/Bluzelle NFT Secure Storage API.md: -------------------------------------------------------------------------------- 1 | # Bluzelle NFT Secure Storage API 2 | 3 | The bluzelle NFT API is a two step process. 4 | 5 | * Upload the file (from the browser) 6 | * Notify the network that the file has been uploaded (from your application server) 7 | 8 | The second step is necessary so that your identity credentials can be kept secure while not requiring you to act as the middle man for the upload. The file upload happens directly between the users browser and the Bluzelle Net. 9 | 10 | The process looks like this: 11 | 12 | ![Protocol diagram](https://github.com/bluzelle/curium/raw/devel/docs/nft/short%20protocol.png) 13 | 14 | ### Step 1: Upload 15 | 16 | We have provided a helper function in our Bluzelle JS client library to aid with the upload. You can use this code in your browser application. 17 | 18 | ````typescript 19 | import {uploadNft} from 'bluzelle' 20 | uploadNft("https:...", data as Uint8Array, "your_org_id")) 21 | ```` 22 | 23 | 24 | 25 | **The url and org_id will be provided to you by Bluzelle.** 26 | 27 | ### Step 2: Notify the network 28 | 29 | In order for the network to start replicating the uploaded file, it must be notified that the file has been uploaded. We have created another helper function to aid you. 30 | 31 | ```typescript 32 | bz.createNft( 33 | id, // The id you want to assign to this file 34 | hash, // The hash of the file 35 | vendor, // Your organization id 36 | userId, // Your id for the user that uploaded the file 37 | mime, // The mime type for this file 38 | 'metadata', // Any arbritrary metadata you want to store for this file 39 | gasParams) // The gas parameters for the transaction (these will be provided to you) 40 | ``` 41 | 42 | ### Creating a bluzelle client instance 43 | 44 | Instructions on how to install and use the bluzelle JS client is at https://github.com/bluzelle/blzjs 45 | 46 | -------------------------------------------------------------------------------- /docs/nft/auth-protocol.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/docs/nft/auth-protocol.dia -------------------------------------------------------------------------------- /docs/nft/auth-protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/docs/nft/auth-protocol.png -------------------------------------------------------------------------------- /docs/nft/short protocol.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/docs/nft/short protocol.dia -------------------------------------------------------------------------------- /docs/nft/short protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/docs/nft/short protocol.png -------------------------------------------------------------------------------- /docs/oracle/Diagram1.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/docs/oracle/Diagram1.dia -------------------------------------------------------------------------------- /docs/oracle/Diagram1.dia~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/docs/oracle/Diagram1.dia~ -------------------------------------------------------------------------------- /docs/rest/buildrest.md: -------------------------------------------------------------------------------- 1 | [Back](../../README.md) 2 | *** 3 | 4 | Starting the Light-client Daemon (Local REST Server) 5 | ==================================================== 6 | 7 | >If required, please refer to the [OS Setup for Curium](../setup/os.md) and 8 | [Development Environment Setup](../setup/devenv.md) documents for 9 | instructions on how to set up your OS and Golang development environment. 10 | 11 | 12 | 1. In a terminal, change to the Curium project working directory 13 | 14 | cd ~/go/src/github.com/bluzelle/curium 15 | 16 | 2. Use make to build and install the blzd and blzcli executables 17 | 18 | make mainnet 19 | 20 | 3. Ensure Bluzelle CLI works by executing the binary, you should 21 | be able to execute the app from your home directory: 22 | 23 | cd 24 | blzcli 25 | 26 | The output of the Bluzelle CLI will be: 27 | 28 | Bluzelle CRUD Client 29 | 30 | Usage: 31 | blzcli [command] 32 | 33 | Available Commands: 34 | status Query remote node for status 35 | config Create or query an application CLI configuration file 36 | query Querying subcommands 37 | tx Transactions subcommands 38 | 39 | rest-server Start LCD (light-client daemon), a local REST server 40 | 41 | keys Add or view local private keys 42 | 43 | version Print the app version 44 | help Help about any command 45 | 46 | Flags: 47 | --chain-id string Chain ID of tendermint node 48 | -e, --encoding string Binary encoding (hex|b64|btc) (default "hex") 49 | -h, --help help for blzcli 50 | --home string directory for config and data (default "/Users/rnistuk/.blzcli") 51 | -o, --output string Output format (text|json) (default "text") 52 | --trace print out full stack trace on errors 53 | 54 | Use "blzcli [command] --help" for more information about a command. 55 | 56 | 4. Start the Light-client Daemon (LCD) on the command line using thes 57 | rest-server command with the "--node" argument to specify the IP address and RPC (TCP) port (typically 26657) of 58 | a Curium node in the zone to connect to 59 | 60 | blzcli rest-server --node : 61 | 62 | if the node is not specified the default value of "tcp://localhost:26657" 63 | will be used. 64 | 65 | 5. You will now be able to interact with the connected zone via the REST 66 | interface provided by the LCD. 67 | 68 | [Back](../../README.md) 69 | -------------------------------------------------------------------------------- /docs/setup/devenv.md: -------------------------------------------------------------------------------- 1 | # Development Environment Setup 2 | 3 | [prev](os.md) \| [next](build.md) 4 | 5 | ## Development Environment Setup 6 | 7 | 1. Curium uses Golang, in \*nix, run these commands in a terminal 8 | 9 | ```text 10 | wget https://golang.org/dl/go1.15.6.linux-amd64.tar.gz 11 | sudo tar -C /usr/local -xzf go1.15.6.linux-amd64.tar.gz 12 | ``` 13 | 14 | on macOS install the latest package from here: [https://golang.org/doc/install](https://golang.org/doc/install) 15 | 16 | 2. Make sure your Environment paths are set in your .profile file, for example, add the following to the end of your .profile, .\*rc file or equivalent 17 | 18 | ```text 19 | export GOPATH=$HOME/go 20 | export GOBIN=${GOPATH}/bin 21 | export PATH=$PATH:$GOBIN:/usr/local/go/bin 22 | ``` 23 | 24 | GOPATH is where the golang project source files, packages and binaries are kept. Make sure that the Golang compiler folder _and_ the GOBIN are in the PATH. 25 | 26 | BE SURE to source the file that you added these paths to. For example, if it was to your ".profile" file, do the following: 27 | 28 | ```text 29 | source ~/.profile 30 | ``` 31 | 32 | 3. Make the Golang working folders with the following commands in a terminal 33 | 34 | ```text 35 | mkdir -p ~/go/src/github.com/bluzelle 36 | mkdir ~/go/bin 37 | mkdir ~/go/pkg 38 | ``` 39 | 40 | 4. In a terminal change directory to the the bluzelle directory 41 | 42 | ```text 43 | cd ~/go/src/github.com/bluzelle 44 | ``` 45 | 46 | 5. Clone the Curium project from GitHub into the directory referenced above 47 | 48 | ```text 49 | git clone https://github.com/bluzelle/curium.git 50 | ``` 51 | 52 | 6. Create an nft directory in your home directory 53 | 54 | ```text 55 | mkdir ~/nft 56 | ``` 57 | 58 | CRITICAL: The "nft-base-dir" is where NFT files from the new Bluzelle NFT service will get stored to. It is vital that the volume this folder is in, be monitored and that it not fill up. NFT files can quickly overwhelm a small volume. If that volume happens to be the same one used by the blzd daemon, it can cause your node to crash completely, when full. In the case of a validator, your node could come down, and get slashed. It is recommended to keep this as a separate volume on a larger, monitored drive. 59 | 60 | [prev](os.md) \| [next](build.md) 61 | 62 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bluzelle/curium 2 | 3 | go 1.15 4 | 5 | replace github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 6 | 7 | require ( 8 | github.com/anacrolix/torrent v1.28.0 9 | github.com/cosmos/cosmos-sdk v0.39.2 10 | github.com/cosmos/modules/incubator/faucet v0.0.0-20200315124306-c86f71ae76a0 11 | github.com/ethereum/go-ethereum v1.10.4 12 | github.com/golang/mock v1.4.4 13 | github.com/gorilla/mux v1.7.4 14 | github.com/magiconair/properties v1.8.1 15 | github.com/robfig/cron/v3 v3.0.1 16 | github.com/spf13/cobra v1.0.0 17 | github.com/spf13/viper v1.6.3 18 | github.com/stretchr/testify v1.7.0 19 | github.com/tendermint/go-amino v0.15.1 20 | github.com/tendermint/tendermint v0.33.9 21 | github.com/tendermint/tm-db v0.5.1 22 | github.com/wenxiang/go-nestedjson v0.0.0-20150910062500-11a6c4340577 23 | github.com/zeebo/bencode v1.0.0 24 | golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba 25 | ) 26 | -------------------------------------------------------------------------------- /home/.torrent.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/home/.torrent.db -------------------------------------------------------------------------------- /home/.torrent.db-shm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/home/.torrent.db-shm -------------------------------------------------------------------------------- /home/.torrent.db-wal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/home/.torrent.db-wal -------------------------------------------------------------------------------- /home/4cbf988462cc3ba2e10e3aae9f5268546aa79016359fb45be7dd199c073125c0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/home/4cbf988462cc3ba2e10e3aae9f5268546aa79016359fb45be7dd199c073125c0 -------------------------------------------------------------------------------- /home/nft/.torrent.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/home/nft/.torrent.db -------------------------------------------------------------------------------- /home/nft/.torrent.db-shm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/home/nft/.torrent.db-shm -------------------------------------------------------------------------------- /home/nft/.torrent.db-wal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/home/nft/.torrent.db-wal -------------------------------------------------------------------------------- /home/nft/4cbf988462cc3ba2e10e3aae9f5268546aa79016359fb45be7dd199c073125c0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/home/nft/4cbf988462cc3ba2e10e3aae9f5268546aa79016359fb45be7dd199c073125c0 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "curium", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "private": true, 7 | "scripts": { 8 | "build": "make testnet", 9 | "deploy": "yarn --cwd=scripts zx deploy.ts", 10 | "setup": "./scripts/setup.ts" 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /rest-service/buildrest.md: -------------------------------------------------------------------------------- 1 | # Building the CURIUM REST Service 2 | 3 | [Back](../) 4 | 5 | ## Starting the Light-client Daemon \(Local REST Server\) 6 | 7 | > If required, please refer to the [OS Setup for Curium](../setup/os.md) and [Development Environment Setup](../setup/devenv.md) documents for instructions on how to set up your OS and Golang development environment. 8 | 9 | 1. In a terminal, change to the Curium project working directory 10 | 11 | ```text 12 | cd ~/go/src/github.com/bluzelle/curium 13 | ``` 14 | 15 | 2. Use make to build and install the blzd and blzcli executables 16 | 17 | ```text 18 | make mainnet 19 | ``` 20 | 21 | 3. Ensure Bluzelle CLI works by executing the binary, you should be able to execute the app from your home directory: 22 | 23 | ```text 24 | cd 25 | blzcli 26 | ``` 27 | 28 | The output of the Bluzelle CLI will be: 29 | 30 | ```text 31 | Bluzelle CRUD Client 32 | 33 | Usage: 34 | blzcli [command] 35 | 36 | Available Commands: 37 | status Query remote node for status 38 | config Create or query an application CLI configuration file 39 | query Querying subcommands 40 | tx Transactions subcommands 41 | 42 | rest-server Start LCD (light-client daemon), a local REST server 43 | 44 | keys Add or view local private keys 45 | 46 | version Print the app version 47 | help Help about any command 48 | 49 | Flags: 50 | --chain-id string Chain ID of tendermint node 51 | -e, --encoding string Binary encoding (hex|b64|btc) (default "hex") 52 | -h, --help help for blzcli 53 | --home string directory for config and data (default "/Users/rnistuk/.blzcli") 54 | -o, --output string Output format (text|json) (default "text") 55 | --trace print out full stack trace on errors 56 | 57 | Use "blzcli [command] --help" for more information about a command. 58 | ``` 59 | 60 | 4. Start the Light-client Daemon \(LCD\) on the command line using thes rest-server command with the "--node" argument to specify the IP address and RPC \(TCP\) port \(typically 26657\) of a Curium node in the zone to connect to 61 | 62 | ```text 63 | blzcli rest-server --node : 64 | ``` 65 | 66 | if the node is not specified the default value of "tcp://localhost:26657" will be used. 67 | 68 | 5. You will now be able to interact with the connected zone via the REST interface provided by the LCD. 69 | 70 | [Back](../) 71 | 72 | -------------------------------------------------------------------------------- /scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zx 2 | import {$, cd} from 'zx'; 3 | 4 | const deployCurium = () => { 5 | Promise.resolve() 6 | .then(() => $`git remote add temporary-remote-curium git@github.com:bluzelle/curium.git`) 7 | .then(() => $`git checkout -b deploy-curium`) 8 | .then(() => cd('../../')) 9 | .then(() => $`git filter-branch -f --tree-filter 'rm -rf curium/scripts/deploy.ts' HEAD`) 10 | .then(() => $`git filter-branch -f \ 11 | --subdirectory-filter curium/ \ 12 | --prune-empty \ 13 | --tag-name-filter cat -- --all`) 14 | .then(() => $`git push -f temporary-remote-curium devel`) 15 | .then(() => $`git fetch`) 16 | .then(() => $`git checkout origin/devel`) 17 | .then(() => $`git branch -f devel`) 18 | .then(() => $`git checkout devel`) 19 | .then(() => $`git branch -D deploy-curium`) 20 | .then(() => $`git remote remove temporary-remote-curium`) 21 | .catch(error => { 22 | console.log(`SOMETHING WENT WRONG: ${error.message}`) 23 | process.exit(1) 24 | }) 25 | } 26 | 27 | deployCurium() -------------------------------------------------------------------------------- /scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "curium-scripts", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "private": true, 7 | "dependencies": { 8 | "@types/lodash": "^4.14.172", 9 | "@types/node": "^16.6.2", 10 | "bip39": "^3.0.4", 11 | "delay": "^5.0.0", 12 | "lodash": "^4.17.21", 13 | "promise-passthrough": "^1.0.5", 14 | "ts-node": "^10.2.1", 15 | "typescript": "^4.3.5", 16 | "zx": "^3.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/integration/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /test/integration/helpers/bluzelle-client.ts: -------------------------------------------------------------------------------- 1 | import {bluzelle, BluzelleConfig} from "bluzelle"; 2 | import {memoize, times} from 'lodash' 3 | import {GasInfo} from "bluzelle"; 4 | import {getSwarm} from "@bluzelle/testing/lib/helpers/swarmHelpers"; 5 | import {SwarmConfig} from "daemon-manager/lib/SwarmConfig"; 6 | import {entropyToMnemonic} from "bip39"; 7 | import {Some} from "monet"; 8 | import {passThrough} from "promise-passthrough"; 9 | import {getSentryUrl} from "./nft-helpers"; 10 | 11 | export const getBzClient = memoize((config: Partial = {}) => 12 | bluzelle({ 13 | mnemonic: "vivid rack volume school expect tobacco hello paddle annual tobacco choice evoke consider fluid attract bind error setup depth wedding night shove note jazz", 14 | endpoint: 'https://localhost:1327', 15 | uuid: 'uuid', 16 | ...config 17 | }) 18 | ); 19 | 20 | export const getSwarmAndClient = () => 21 | getSwarm([withTestUsers]) 22 | .then(swarm => ({swarm})) 23 | .then(({swarm}) => swarm.getValidators()[0].getAuth().then(auth => ({swarm, auth}))) 24 | .then(({swarm, auth}) => 25 | ({ 26 | swarm, 27 | auth, 28 | bz: bluzelle({ 29 | mnemonic: auth.mnemonic, 30 | uuid: Date.now().toString(), 31 | endpoint: getSentryUrl(swarm) 32 | }) 33 | }) 34 | ) 35 | 36 | export const defaultGasParams = (gasInfo: GasInfo = {}): GasInfo => ({gas_price: 10, max_gas: 100000000, ...gasInfo}) 37 | 38 | function withTestUsers (config: SwarmConfig): SwarmConfig { 39 | return Some({ 40 | ...config, 41 | additionalUsers: times(4).map(n =>({ 42 | name: `test-${n}`, 43 | mnemonic: entropyToMnemonic((n + 1).toString().repeat(30) + 'aa') 44 | })) 45 | } as SwarmConfig) 46 | .map(passThrough((config: SwarmConfig) => console.log("******** test users ******\n", config.additionalUsers))) 47 | .join() 48 | } -------------------------------------------------------------------------------- /test/integration/helpers/blzcli.ts: -------------------------------------------------------------------------------- 1 | import * as util from "util"; 2 | 3 | const exec = util.promisify(require('child_process').exec); 4 | 5 | export const blzcli = (...args):Promise => 6 | exec(`blzcli ${args.join(' ')}`); 7 | 8 | export const STANDARD_GAS = '--gas auto --gas-adjustment 2 --gas-prices 0.002ubnt' 9 | export const BLOCK = '--broadcast-mode block' 10 | 11 | -------------------------------------------------------------------------------- /test/integration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "curium-tests", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "private": true, 7 | "dependencies": { 8 | "@bluzelle/oracle-js": "^1.0.8", 9 | "@bluzelle/testing": "^1.0.0", 10 | "@types/chai": "^4.2.21", 11 | "@types/chai-as-promised": "^7.1.4", 12 | "@types/create-torrent": "^5.0.0", 13 | "@types/lodash": "^4.14.172", 14 | "@types/mocha": "^9.0.0", 15 | "@types/node": "^14.14.20", 16 | "@types/node-fetch": "^2.5.12", 17 | "async-wait-until": "^2.0.7", 18 | "bip39": "^3.0.4", 19 | "bluzelle": "^3.0.7", 20 | "chai": "^4.2.0", 21 | "chai-as-promised": "^7.1.1", 22 | "cksum": "^1.3.0", 23 | "create-torrent": "^5.0.1", 24 | "daemon-manager": "^1.0.4", 25 | "delay": "^5.0.0", 26 | "js-sha256": "^0.9.0", 27 | "lodash": "^4.17.21", 28 | "mocha": "^8.2.1", 29 | "monet": "^0.9.1", 30 | "node-fetch": "^2.6.1", 31 | "promise-passthrough": "^1.0.5", 32 | "ts-node": "^9.1.1", 33 | "typescript": "^4.1.3" 34 | }, 35 | "scripts": { 36 | "test": "mocha -r ts-node/register specs/*/**/*.spec.ts" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/integration/specs/ante.spec.ts: -------------------------------------------------------------------------------- 1 | import {BLOCK, blzcli, STANDARD_GAS} from "../helpers/blzcli"; 2 | import {getBzClient} from "../helpers/bluzelle-client"; 3 | import {passThroughAwait} from "promise-passthrough"; 4 | import {expect} from 'chai' 5 | import {addOracleSource} from 'oracle-js' 6 | 7 | describe('Ante handler tests', () => { 8 | it("should charge for regular transactions", async () => { 9 | await shouldCharge( 10 | (bz) => bz.upsert('foo', 'bar', {gas_price: 0.002, max_gas: 10000000}) 11 | ) 12 | }); 13 | 14 | it('should not charge for oracle transactions', async () => { 15 | await shouldBeFree( 16 | (bz) => addOracleSource(bz, { 17 | Name: 'my-source', 18 | Url: 'my-url', 19 | Property: 'my-property' 20 | }, {gas_price: 0.002}) 21 | ) 22 | }); 23 | 24 | }) 25 | 26 | type CheckFn = (number) => number 27 | const shouldCharge = (fn: (API) => Promise, checkFn: CheckFn = (x) => x) => 28 | getBzClient().getBNT({ubnt: true}) 29 | .then(passThroughAwait(() => fn(getBzClient()))) 30 | .then(async (before) => 31 | expect(await getBzClient().getBNT({ubnt: true})) 32 | .to.be.lessThan(checkFn(before)) 33 | ) 34 | 35 | const shouldBeFree = (fn: (API) => Promise) => 36 | getBzClient().getBNT({ubnt: true}) 37 | .then(passThroughAwait(() => fn(getBzClient()))) 38 | .then(async (before) => 39 | expect(await getBzClient().getBNT({ubnt: true})) 40 | .to.equal(before) 41 | ) -------------------------------------------------------------------------------- /test/integration/specs/nft/MsgPublishFile.spec.ts: -------------------------------------------------------------------------------- 1 | import {defaultGasParams, getSwarmAndClient} from "../../helpers/bluzelle-client"; 2 | import {Swarm} from "daemon-manager/lib/Swarm"; 3 | import {API} from "bluzelle"; 4 | import chai from 'chai' 5 | import asPromised from 'chai-as-promised' 6 | import createTorrent from 'create-torrent' 7 | 8 | 9 | chai.use(asPromised); 10 | 11 | describe('MsgPublishFile', function () { 12 | this.timeout(400_000); 13 | let bz: API 14 | let swarm: Swarm 15 | 16 | beforeEach(() => { 17 | return getSwarmAndClient() 18 | .then(({bz: newBz, swarm: newSwarm}) => { 19 | bz = newBz 20 | swarm = newSwarm 21 | bz.uuid = 'bluzelle' 22 | }) 23 | .then(() => bz.upsert("nft-whitelist", JSON.stringify([bz.address]), defaultGasParams())) 24 | .then(() => Promise.all(swarm.getDaemons().map(daemon => 25 | daemon.exec(`rm -rf ${daemon.getNftBaseDir()}/nft*`) 26 | ))) 27 | }); 28 | 29 | it('should be rejected if the metainfo includes tracker info', (done) => { 30 | new Promise((resolve) => 31 | createTorrent(Buffer.from(''), { 32 | name: 'name', 33 | comment: 'comment', 34 | createdBy: 'createdBy', 35 | creationDate: Date.now(), 36 | private: true, 37 | pieceLength: 60, 38 | announceList: [['sometracker']], 39 | urlList: [], 40 | info: {} 41 | }, (err, torrent) => resolve(torrent)) 42 | ) 43 | .then(torrent => 44 | bz.sendMessage({ 45 | type: 'nft/PublishFile', 46 | value: { 47 | creator: bz.address, 48 | id: 'my-id', 49 | vendor: 'vendor', 50 | metainfo: Array.from(torrent as Uint8Array) 51 | } 52 | }, defaultGasParams()) 53 | ) 54 | .then(x => done("should have been rejected with error")) 55 | .catch(e => e.error === 'Invalid torrent metainfo in publish' ? done() : done(`wrong error: ${e.error}`)) 56 | }); 57 | 58 | 59 | }) -------------------------------------------------------------------------------- /test/integration/specs/nft/nft-upload.spec.ts: -------------------------------------------------------------------------------- 1 | import {getSwarmAndClient} from "../../helpers/bluzelle-client"; 2 | import {Swarm} from "daemon-manager/lib/Swarm"; 3 | import {API, uploadNft} from "bluzelle"; 4 | import {getSentryUrl} from "../../helpers/nft-helpers"; 5 | 6 | describe('nft upload', function() { 7 | this.timeout(100000); 8 | let bz: API 9 | let swarm: Swarm 10 | beforeEach(() => { 11 | return getSwarmAndClient() 12 | .then(({bz: newBz, swarm: newSwarm}) => { 13 | bz = newBz 14 | swarm = newSwarm 15 | }) 16 | }); 17 | 18 | it('should send a 403 if upload has not been authorized', (done) => { 19 | uploadNft(getSentryUrl(swarm), new TextEncoder().encode('data'), '1111', 'mintable') 20 | .then(x => done('should have thrown an error')) 21 | .catch(e => done(e.includes('auth invalid') ? undefined : `wrong error: ${e}`)) 22 | }) 23 | }) -------------------------------------------------------------------------------- /test/integration/specs/oracle/oracle-utils.ts: -------------------------------------------------------------------------------- 1 | import {API} from "../../../../../blzjs/client"; 2 | import {passThrough} from "promise-passthrough"; 3 | import {listOracleSources, Source} from '@bluzelle/oracle-js' 4 | 5 | const VALCONS = 'bluzellevalcons12345' 6 | 7 | export const deleteVotes = (bz: API) => 8 | bz.sendMessage({ 9 | type: 'oracle/MsgOracleDeleteVotes', 10 | value: { 11 | Prefix: '2', 12 | Owner: bz.address 13 | } 14 | }, {gas_price: 0.002}) 15 | 16 | export const deleteSources = (bz: API): Promise => 17 | listOracleSources(bz) 18 | .then((sources: Source[]): any => sources.length && bz.withTransaction(() => 19 | sources.map((source: Source) => deleteSource(bz, source.Name)) 20 | ) 21 | ) 22 | 23 | const deleteSource = (bz: API, name: string): Promise => 24 | bz.sendMessage({ 25 | type: 'oracle/MsgOracleDeleteSource', 26 | value: { 27 | Name: name, 28 | Owner: bz.address 29 | } 30 | }, {gas_price: 0.002}); 31 | 32 | const awaitContext = (prop: string, fn: Function) => (state: Object) => 33 | fn(state) 34 | .then((val: unknown) => ({...state, [prop]: val})) 35 | 36 | const getValcons = (bz: API) => bz.abciQuery('custom/oracle/getValcons', {}) 37 | .then(x => x.result) 38 | 39 | const calculateProofSig = (bz: API, value: string) => 40 | bz.abciQuery('custom/oracle/calculateVoteProofSig', { 41 | Value: value 42 | }) 43 | .then(x => x.result) 44 | 45 | 46 | export const addVote = (bz: API, vote: any) => 47 | Promise.resolve({bz: bz, vote: vote}) 48 | .then(awaitContext('valcons', (ctx: any) => getValcons(ctx.bz))) 49 | .then(awaitContext('sig', (ctx: any) => calculateProofSig(ctx.bz, ctx.vote.Value))) 50 | .then(passThrough((ctx: any) => 51 | bz.sendMessage({ 52 | type: 'oracle/MsgOracleVoteProof', 53 | value: { 54 | ValidatorAddr: ctx.valcons, 55 | VoteSig: ctx.sig, 56 | Owner: ctx.bz.address, 57 | SourceName: ctx.vote.SourceName 58 | } 59 | }, {gas_price: 0.002}) 60 | ) 61 | ) 62 | .then(ctx => 63 | ctx.bz.sendMessage({ 64 | type: 'oracle/MsgOracleVote', 65 | value: { 66 | ...vote, 67 | Valcons: ctx.valcons, 68 | Owner: ctx.bz.address, 69 | Batch: "" 70 | } 71 | }, {gas_price: 0.002}) 72 | ) 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /test/integration/specs/oracle/rest/add-source.spec.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai' 2 | import {API, bluzelle} from "bluzelle"; 3 | import {deleteSources} from "../oracle-utils"; 4 | import {getBzClient} from "../../../helpers/bluzelle-client"; 5 | import {feedSources} from "../sources"; 6 | import {listOracleSources, addOracleSource} from '@bluzelle/oracle-js' 7 | 8 | describe.skip('add-source functions', function() { 9 | this.timeout(10000); 10 | let bz: API 11 | 12 | beforeEach(() => bz = getBzClient()); 13 | beforeEach(() => deleteSources(bz)); 14 | 15 | it('should add sources', () => { 16 | return addOracleSource(bz, {Name: 'my-source', Url: 'my-url', Property: 'my-property', Weight: "100"}, {gas_price: 0.002}) 17 | .then(() => listOracleSources(bz)) 18 | .then(sources => { 19 | expect(sources).to.have.length(1); 20 | expect(sources[0].Name).to.equal('my-source'); 21 | }); 22 | }); 23 | 24 | it('should be able to add a source', async () => { 25 | await addOracleSource( 26 | bz, 27 | { 28 | Name: feedSources[0].name, 29 | Url: feedSources[0].url, 30 | Property: feedSources[0].property, 31 | Weight: '100' 32 | }, 33 | {gas_price: 0.002} 34 | ) 35 | 36 | await listOracleSources(bz) 37 | .then(sources => expect(sources).to.deep.equal([{ 38 | Name: "binance-eth-in-usdt", 39 | Url: "https://api.binance.com/api/v1/ticker/price?symbol=ETHUSDT", 40 | Property: "price", 41 | Owner: "bluzelle1ws42h2gjr6q8u5d2teexhrzz9xr9lqrxru50u2" 42 | }])); 43 | 44 | }); 45 | 46 | }); 47 | 48 | -------------------------------------------------------------------------------- /test/integration/specs/oracle/rest/search-votes.spec.ts: -------------------------------------------------------------------------------- 1 | import {API} from "../../../../../../blzjs/client"; 2 | import {getBzClient} from "../../../helpers/bluzelle-client"; 3 | import {addVote, deleteVotes} from "../oracle-utils"; 4 | import {expect} from "chai"; 5 | import {passThrough} from "promise-passthrough"; 6 | import {searchOracleVotes} from '@bluzelle/oracle-js' 7 | 8 | describe.skip('search-votes functions', function () { 9 | this.timeout(60000); 10 | let bz: API 11 | 12 | beforeEach(() => bz = getBzClient()); 13 | beforeEach(() => deleteVotes(bz)); 14 | 15 | it('should search votes', () => { 16 | return Promise.all([ 17 | addVote(bz, { 18 | Value: '10.5', 19 | SourceName: 'my-source' 20 | }), 21 | addVote(bz, { 22 | Value: '20.2', 23 | SourceName: 'my-source-2' 24 | }) 25 | ]) 26 | .then(() => searchOracleVotes(bz, {Prefix: '2021'})) 27 | .then(passThrough(votes => expect(votes).to.have.length(2))) 28 | .then((votes: any[]) => votes.map(v => parseFloat(v.Value))) 29 | .then(votes => expect(votes).to.deep.equal([20.2, 10.5])) 30 | }); 31 | 32 | it('should search vote keys', () => { 33 | return bz.abciQuery("/custom/oracle/searchvotekeys", { 34 | Prefix: '2021' 35 | }) 36 | .then(x => x) 37 | }) 38 | }); 39 | 40 | -------------------------------------------------------------------------------- /test/integration/specs/oracle/shouldBeFreeHelper.ts: -------------------------------------------------------------------------------- 1 | import {getBzClient} from "../../helpers/bluzelle-client"; 2 | import {passThroughAwait} from "promise-passthrough"; 3 | import {expect} from 'chai' 4 | 5 | export const testShouldBeFree = (msgName: string, fn: (API) => Promise) => 6 | it(`message ${msgName} should be free`, () => 7 | getBzClient().getBNT({ubnt: true}) 8 | .then(passThroughAwait(() => fn(getBzClient()))) 9 | .then(async (ubnt) => expect(await getBzClient().getBNT({ubnt: true})).to.equal(ubnt)) 10 | ) -------------------------------------------------------------------------------- /test/load/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "curium-load", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "@types/chai": "^4.2.21", 8 | "@types/lodash": "^4.14.172", 9 | "@types/node": "^16.9.1", 10 | "async-wait-until": "^2.0.7", 11 | "bluzelle": "^3.0.9", 12 | "chai": "^4.3.4", 13 | "cksum": "^1.3.0", 14 | "daemon-manager": "^1.0.4", 15 | "delay": "^5.0.0", 16 | "js-sha256": "^0.9.0", 17 | "lodash": "^4.17.21", 18 | "monet": "^0.9.2", 19 | "promise-passthrough": "^1.0.5", 20 | "ts-node": "^10.2.1", 21 | "typescript": "^4.4.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/oracle-mock/node_modules/.yarn-integrity: -------------------------------------------------------------------------------- 1 | { 2 | "systemParams": "darwin-x64-83", 3 | "modulesFolders": [ 4 | "node_modules" 5 | ], 6 | "flags": [], 7 | "linkedModules": [ 8 | "bluzelle", 9 | "daemon-manager", 10 | "oracle-js" 11 | ], 12 | "topLevelPatterns": [ 13 | "monet@^0.9.1" 14 | ], 15 | "lockfileEntries": { 16 | "monet@^0.9.1": "https://registry.yarnpkg.com/monet/-/monet-0.9.1.tgz#89686a22d9dd8a6228fcde5265fcd86aa63981a7" 17 | }, 18 | "files": [], 19 | "artifacts": {} 20 | } -------------------------------------------------------------------------------- /test/oracle-mock/node_modules/monet/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2018 Chris Myers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/oracle-mock/node_modules/monet/dist/monet-pimp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * monet-pimp.js 0.9.0-rc.1 3 | * 4 | * This file needs to be included after monet.js 5 | * 6 | * (c) 2012-2018 Chris Myers 7 | * @license Monet-pimp.js may be freely distributed under the MIT license. 8 | * For all details and documentation: 9 | * https://monet.github.io/monet.js/ 10 | */ 11 | (function(root, factory) { 12 | if (typeof define === "function" && define.amd) { 13 | define([ "monet" ], factory); 14 | } else if (typeof module === "object" && module.exports) { 15 | module.exports = factory(require("monet"), root); 16 | } else { 17 | factory(root.Monet, root); 18 | } 19 | })(typeof self !== "undefined" ? self : this, function(Monet, rootGlobalObject) { 20 | "use strict"; 21 | function wrapReader(fn, args) { 22 | var newArgs = args || []; 23 | return function() { 24 | var args1 = newArgs.concat(Array.prototype.slice.call(arguments)); 25 | return args1.length + 1 >= fn.length ? getStatic("Reader")(function(c) { 26 | return fn.apply(null, args1.concat(c)); 27 | }) : wrapReader(fn, args1); 28 | }; 29 | } 30 | function getStatic(name) { 31 | return rootGlobalObject && rootGlobalObject[name] || Monet[name]; 32 | } 33 | Object.prototype.cons = function(list) { 34 | return list.cons(this); 35 | }; 36 | Object.prototype.some = Object.prototype.just = function() { 37 | return getStatic("Some")(this); 38 | }; 39 | Object.prototype.success = function() { 40 | return getStatic("Validation").success(this); 41 | }; 42 | Object.prototype.fail = function() { 43 | return getStatic("Validation").fail(this); 44 | }; 45 | Object.prototype.right = function() { 46 | return getStatic("Either").Right(this); 47 | }; 48 | Object.prototype.left = function() { 49 | return getStatic("Either").Left(this); 50 | }; 51 | Array.prototype.list = function() { 52 | return getStatic("List").fromArray(this); 53 | }; 54 | Function.prototype.curry = function() { 55 | return Monet.curry(this); 56 | }; 57 | Function.prototype.compose = function(g) { 58 | return Monet.compose(this, g); 59 | }; 60 | Function.prototype.andThen = Function.prototype.map = function(g) { 61 | var f = this; 62 | return function(x) { 63 | return g(f(x)); 64 | }; 65 | }; 66 | Function.prototype.io = function() { 67 | return getStatic("IO")(this); 68 | }; 69 | Function.prototype.io1 = function() { 70 | var f = this; 71 | return function(x) { 72 | return getStatic("IO")(function() { 73 | return f(x); 74 | }); 75 | }; 76 | }; 77 | Function.prototype.reader = function() { 78 | return wrapReader(this); 79 | }; 80 | }); -------------------------------------------------------------------------------- /test/oracle-mock/node_modules/monet/dist/monet-pimp.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * monet-pimp.js 0.9.0-rc.1 3 | * 4 | * This file needs to be included after monet.js 5 | * 6 | * (c) 2012-2018 Chris Myers 7 | * @license Monet-pimp.js may be freely distributed under the MIT license. 8 | * For all details and documentation: 9 | * https://monet.github.io/monet.js/ 10 | */ 11 | !function(t,e){"function"==typeof define&&define.amd?define(["monet"],e):"object"==typeof module&&module.exports?module.exports=e(require("monet"),t):e(t.Monet,t)}("undefined"!=typeof self?self:this,function(e,n){"use strict";function getStatic(t){return n&&n[t]||e[t]}Object.prototype.cons=function(t){return t.cons(this)},Object.prototype.some=Object.prototype.just=function(){return getStatic("Some")(this)},Object.prototype.success=function(){return getStatic("Validation").success(this)},Object.prototype.fail=function(){return getStatic("Validation").fail(this)},Object.prototype.right=function(){return getStatic("Either").Right(this)},Object.prototype.left=function(){return getStatic("Either").Left(this)},Array.prototype.list=function(){return getStatic("List").fromArray(this)},Function.prototype.curry=function(){return e.curry(this)},Function.prototype.compose=function(t){return e.compose(this,t)},Function.prototype.andThen=Function.prototype.map=function(e){var n=this;return function(t){return e(n(t))}},Function.prototype.io=function(){return getStatic("IO")(this)},Function.prototype.io1=function(){var e=this;return function(t){return getStatic("IO")(function(){return e(t)})}},Function.prototype.reader=function(){return function wrapReader(n,t){var o=t||[];return function(){var e=o.concat(Array.prototype.slice.call(arguments));return e.length+1>=n.length?getStatic("Reader")(function(t){return n.apply(null,e.concat(t))}):wrapReader(n,e)}}(this)}}); -------------------------------------------------------------------------------- /test/oracle-mock/node_modules/monet/dist/monet-pimp.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["dist/monet-pimp.js"],"names":["root","factory","define","amd","module","exports","require","Monet","self","this","rootGlobalObject","getStatic","name","Object","prototype","cons","list","some","just","success","fail","right","Right","left","Left","Array","fromArray","Function","curry","compose","g","andThen","map","f","x","io","io1","reader","wrapReader","fn","args","newArgs","args1","concat","slice","call","arguments","length","c","apply"],"mappings":";;;;;;;;;;CAUA,SAAUA,EAAMC,GACU,mBAAXC,QAAyBA,OAAOC,IACvCD,OAAO,CAAE,SAAWD,GACK,iBAAXG,QAAuBA,OAAOC,QAC5CD,OAAOC,QAAUJ,EAAQK,QAAQ,SAAUN,GAE3CC,EAAQD,EAAKO,MAAOP,GAN5B,CAQmB,oBAATQ,KAAuBA,KAAOC,KAAM,SAASF,EAAOG,GAC1D,aAUA,SAASC,UAAUC,GACf,OAAOF,GAAoBA,EAAiBE,IAASL,EAAMK,GAE/DC,OAAOC,UAAUC,KAAO,SAASC,GAC7B,OAAOA,EAAKD,KAAKN,OAErBI,OAAOC,UAAUG,KAAOJ,OAAOC,UAAUI,KAAO,WAC5C,OAAOP,UAAU,OAAVA,CAAkBF,OAE7BI,OAAOC,UAAUK,QAAU,WACvB,OAAOR,UAAU,cAAcQ,QAAQV,OAE3CI,OAAOC,UAAUM,KAAO,WACpB,OAAOT,UAAU,cAAcS,KAAKX,OAExCI,OAAOC,UAAUO,MAAQ,WACrB,OAAOV,UAAU,UAAUW,MAAMb,OAErCI,OAAOC,UAAUS,KAAO,WACpB,OAAOZ,UAAU,UAAUa,KAAKf,OAEpCgB,MAAMX,UAAUE,KAAO,WACnB,OAAOL,UAAU,QAAQe,UAAUjB,OAEvCkB,SAASb,UAAUc,MAAQ,WACvB,OAAOrB,EAAMqB,MAAMnB,OAEvBkB,SAASb,UAAUe,QAAU,SAASC,GAClC,OAAOvB,EAAMsB,QAAQpB,KAAMqB,IAE/BH,SAASb,UAAUiB,QAAUJ,SAASb,UAAUkB,IAAM,SAASF,GAC3D,IAAIG,EAAIxB,KACR,OAAO,SAASyB,GACZ,OAAOJ,EAAEG,EAAEC,MAGnBP,SAASb,UAAUqB,GAAK,WACpB,OAAOxB,UAAU,KAAVA,CAAgBF,OAE3BkB,SAASb,UAAUsB,IAAM,WACrB,IAAIH,EAAIxB,KACR,OAAO,SAASyB,GACZ,OAAOvB,UAAU,KAAVA,CAAgB,WACnB,OAAOsB,EAAEC,OAIrBP,SAASb,UAAUuB,OAAS,WACxB,OAzDJ,SAASC,WAAWC,EAAIC,GACpB,IAAIC,EAAUD,GAAQ,GACtB,OAAO,WACH,IAAIE,EAAQD,EAAQE,OAAOlB,MAAMX,UAAU8B,MAAMC,KAAKC,YACtD,OAAOJ,EAAMK,OAAS,GAAKR,EAAGQ,OAASpC,UAAU,SAAVA,CAAoB,SAASqC,GAChE,OAAOT,EAAGU,MAAM,KAAMP,EAAMC,OAAOK,MAClCV,WAAWC,EAAIG,IAmDjBJ,CAAW7B","file":"dist/monet-pimp.min.js.map"} -------------------------------------------------------------------------------- /test/oracle-mock/node_modules/monet/prepublish-test/browser-global.js: -------------------------------------------------------------------------------- 1 | describe('Browser: global Monet object', function () { 2 | it('should be available', function () { 3 | expect(Monet).toBeDefined() 4 | expect(Monet.Maybe).toBeDefined() 5 | expect(Monet.Either).toBeDefined() 6 | expect(Monet.Validation.success).toBe(Monet.Success) 7 | 8 | expect(() => Maybe).toThrow() 9 | expect(() => Either).toThrow() 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /test/oracle-mock/node_modules/monet/prepublish-test/node-global.js: -------------------------------------------------------------------------------- 1 | global.Monet = require('../dist/monet.js') 2 | 3 | describe('Node: global Monet object', function () { 4 | it('should be available', function () { 5 | expect(Monet).toBeDefined() 6 | expect(Monet.Maybe).toBeDefined() 7 | expect(Monet.Either).toBeDefined() 8 | expect(Monet.Validation.success).toBe(Monet.Success) 9 | 10 | expect(() => Maybe).toThrow() 11 | expect(() => Either).toThrow() 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /test/oracle-mock/node_modules/monet/prepublish-test/node-regular.js: -------------------------------------------------------------------------------- 1 | const monet = require('../dist/monet.js') 2 | 3 | describe('Node: monet.js exports object', function () { 4 | it('should be available', function () { 5 | expect(monet).toBeDefined() 6 | expect(monet.Maybe).toBeDefined() 7 | expect(monet.Either).toBeDefined() 8 | expect(monet.Validation.success).toBe(monet.Success) 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /test/oracle-mock/oracle-mock.ts: -------------------------------------------------------------------------------- 1 | import { serve, ServerRequest } from "https://deno.land/std@0.86.0/http/server.ts"; 2 | 3 | const port = 8080; 4 | const server = serve({ hostname: "0.0.0.0", port }); 5 | console.log(`http://localhost:${port}`); 6 | 7 | const counter = function* (): Generator { 8 | let count = 1; 9 | while(1) { 10 | yield count += 1 11 | } 12 | }() 13 | 14 | for await (const request of server) { 15 | Promise.resolve(request.url) 16 | .then(url => endpoints()[url]) 17 | .then(fn => fn ? fn(request) : '') 18 | .then(result => JSON.stringify(result)) 19 | .then(body => request.respond({ status: 200, body})) 20 | } 21 | 22 | function endpoints() { 23 | return { 24 | '/number-whole': (req: ServerRequest) => ({ 25 | price: counter.next().value 26 | }), 27 | '/number-fractional': (req: ServerRequest) => ({ 28 | price: counter.next().value *.1 29 | }), 30 | '/string-whole': (req: ServerRequest) => ({ 31 | price: counter.next().value.toString() 32 | }), 33 | '/deep': (req: ServerRequest) => ({ 34 | deep: { 35 | deeper: { 36 | price: counter.next().value 37 | } 38 | } 39 | }) 40 | } as Record 41 | } 42 | -------------------------------------------------------------------------------- /test/oracle-mock/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "monet": "^0.9.1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/oracle-mock/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | monet@^0.9.1: 6 | version "0.9.1" 7 | resolved "https://registry.yarnpkg.com/monet/-/monet-0.9.1.tgz#89686a22d9dd8a6228fcde5265fcd86aa63981a7" 8 | integrity sha512-GaZw308g6t0WD+U2mGujwJckGp2b8AGGVuB6PiIwkXbq6rJeiqddre2mdbO4PxjyILPi+7GgtRkkfr6dBYXWtA== 9 | -------------------------------------------------------------------------------- /types/development.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | func IsDevelopment () (bool) { 9 | env, valid := os.LookupEnv("ENVIRONMENT") 10 | return valid && strings.Contains(env, "DEVEL") 11 | } -------------------------------------------------------------------------------- /types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // AppName is the name of the Cosmos app 5 | 6 | AppName = "CRUD" 7 | ) 8 | 9 | const ( 10 | // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address 11 | 12 | Bech32PrefixAccAddr = "bluzelle" 13 | 14 | // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key 15 | 16 | Bech32PrefixAccPub = "bluzellepub" 17 | 18 | // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address 19 | 20 | Bech32PrefixValAddr = "bluzellevaloper" 21 | 22 | // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key 23 | 24 | Bech32PrefixValPub = "bluzellevaloperpub" 25 | 26 | // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address 27 | 28 | Bech32PrefixConsAddr = "bluzellevalcons" 29 | 30 | // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key 31 | 32 | Bech32PrefixConsPub = "bluzellevalconspub" 33 | ) 34 | -------------------------------------------------------------------------------- /x/aggregator/abci.go: -------------------------------------------------------------------------------- 1 | package aggregator 2 | 3 | import ( 4 | "github.com/bluzelle/curium/app/ante/gasmeter" 5 | "github.com/bluzelle/curium/x/aggregator/keeper" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | 10 | func EndBlocker(ctx sdk.Context, k keeper.Keeper) { 11 | k.AggregateValues(ctx.WithGasMeter(gasmeter.NewFreeGasMeter(0))) 12 | } -------------------------------------------------------------------------------- /x/aggregator/alias.go: -------------------------------------------------------------------------------- 1 | package aggregator 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/aggregator/keeper" 5 | "github.com/bluzelle/curium/x/aggregator/types" 6 | ) 7 | 8 | var ( 9 | ModuleName = types.ModuleName 10 | StoreKey = types.StoreKey 11 | NewKeeper = keeper.NewKeeper 12 | NewQuerier = keeper.NewQuerier 13 | ModuleCdc = types.ModuleCdc 14 | RegisterCodec = types.RegisterCodec 15 | 16 | ) 17 | 18 | type ( 19 | Keeper = keeper.Keeper 20 | ) -------------------------------------------------------------------------------- /x/aggregator/client/cli/query.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cosmos/cosmos-sdk/client/context" 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | "github.com/cosmos/cosmos-sdk/codec" 11 | 12 | "github.com/bluzelle/curium/x/aggregator/types" 13 | ) 14 | 15 | // GetQueryCmd returns the cli query commands for this module 16 | func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { 17 | // Group aggregator queries under a subcommand 18 | aggregatorQueryCmd := &cobra.Command{ 19 | Use: types.ModuleName, 20 | Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), 21 | DisableFlagParsing: true, 22 | SuggestionsMinimumDistance: 2, 23 | RunE: client.ValidateCmd, 24 | } 25 | 26 | aggregatorQueryCmd.AddCommand( 27 | flags.GetCommands( 28 | GetCmdQSearchValues(queryRoute, cdc), 29 | // this line is used by starport scaffolding # 1 30 | )..., 31 | ) 32 | 33 | return aggregatorQueryCmd 34 | } 35 | 36 | func GetCmdQSearchValues(queryRoute string, cdc *codec.Codec) *cobra.Command { 37 | return &cobra.Command{ 38 | Use: "search-values", 39 | Short: "Search aggregator values", 40 | Args: cobra.ExactArgs(1), 41 | RunE: func(cmd *cobra.Command, args []string) error { 42 | cliCtx := context.NewCLIContext().WithCodec(cdc) 43 | data := types.QueryReqSearchValues{ 44 | Prefix: args[0], 45 | } 46 | json := cdc.MustMarshalJSON(data) 47 | res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QuerySearchValues), json) 48 | 49 | if err != nil { 50 | fmt.Printf("Error: %s", err) 51 | return nil 52 | } 53 | 54 | var out []types.AggregatorValue 55 | cdc.MustUnmarshalJSON(res, &out) 56 | 57 | return cliCtx.PrintOutput(out) 58 | }, 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /x/aggregator/client/cli/tx.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | _ "github.com/cosmos/cosmos-sdk/client/context" 11 | "github.com/cosmos/cosmos-sdk/codec" 12 | _ "github.com/cosmos/cosmos-sdk/types" 13 | _ "github.com/cosmos/cosmos-sdk/x/auth" 14 | _ "github.com/cosmos/cosmos-sdk/x/auth/client/utils" 15 | "github.com/bluzelle/curium/x/aggregator/types" 16 | ) 17 | 18 | // GetTxCmd returns the transaction commands for this module 19 | func GetTxCmd(cdc *codec.Codec) *cobra.Command { 20 | aggregatorTxCmd := &cobra.Command{ 21 | Use: types.ModuleName, 22 | Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), 23 | DisableFlagParsing: true, 24 | SuggestionsMinimumDistance: 2, 25 | RunE: client.ValidateCmd, 26 | } 27 | 28 | aggregatorTxCmd.AddCommand(flags.PostCommands( 29 | // this line is used by starport scaffolding # 1 30 | // TODO: Add tx based commands 31 | // GetCmd(cdc) 32 | )...) 33 | 34 | return aggregatorTxCmd 35 | } 36 | 37 | // Example: 38 | /* 39 | // GetCmd is the CLI command for doing 40 | func GetCmd(cdc *codec.Codec) *cobra.Command { 41 | return &cobra.Command{ 42 | Use: "Describe your action cmd", 43 | Short: "Provide a short description on the cmd", 44 | Args: cobra.ExactArgs(2), // Does your request require arguments 45 | RunE: func(cmd *cobra.Command, args []string) error { 46 | cliCtx := context.NewCLIContext().WithCodec(cdc) 47 | inBuf := bufio.NewReader(cmd.InOrStdin()) 48 | txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) 49 | 50 | msg := types.NewMsg(Action params) 51 | err = msg.ValidateBasic() 52 | if err != nil { 53 | return err 54 | } 55 | 56 | return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) 57 | }, 58 | } 59 | } 60 | */ 61 | -------------------------------------------------------------------------------- /x/aggregator/client/rest/query.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gorilla/mux" 8 | 9 | "github.com/bluzelle/curium/x/aggregator/types" 10 | "github.com/cosmos/cosmos-sdk/client/context" 11 | "github.com/cosmos/cosmos-sdk/types/rest" 12 | ) 13 | 14 | func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { 15 | r.HandleFunc("/aggregator/latestValues", queryLatestValuesFn(cliCtx)).Methods("GET") 16 | r.HandleFunc("/aggregator/latestPair/{symbol}/{inSymbol}", queryLatestPairFn(cliCtx)).Methods("GET") 17 | } 18 | 19 | func queryLatestValuesFn(cliCtx context.CLIContext) http.HandlerFunc { 20 | return func(w http.ResponseWriter, r *http.Request) { 21 | route := fmt.Sprintf("custom/%s/getLatestValues", types.QuerierRoute) 22 | res, _, err := cliCtx.QueryWithData(route, nil) 23 | 24 | if err != nil { 25 | rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) 26 | return 27 | } 28 | rest.PostProcessResponse(w, cliCtx, res) 29 | } 30 | } 31 | 32 | func queryLatestPairFn(cliCtx context.CLIContext) http.HandlerFunc { 33 | return func(w http.ResponseWriter, r *http.Request) { 34 | vars := mux.Vars(r) 35 | route := fmt.Sprintf("custom/%s/getLatestPair", types.QuerierRoute) 36 | args := types.RestLatestPairReq{Symbol: vars["symbol"], InSymbol: vars["inSymbol"]} 37 | data := types.ModuleCdc.MustMarshalBinaryBare(args) 38 | res, _, err := cliCtx.QueryWithData(route, data) 39 | 40 | if err != nil { 41 | rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) 42 | return 43 | } 44 | rest.PostProcessResponse(w, cliCtx, res) 45 | } 46 | } 47 | 48 | 49 | func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { 50 | return func(w http.ResponseWriter, r *http.Request) { 51 | cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) 52 | if !ok { 53 | return 54 | } 55 | 56 | route := fmt.Sprintf("custom/%s/parameters", types.QuerierRoute) 57 | 58 | res, height, err := cliCtx.QueryWithData(route, nil) 59 | if err != nil { 60 | rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) 61 | return 62 | } 63 | 64 | cliCtx = cliCtx.WithHeight(height) 65 | rest.PostProcessResponse(w, cliCtx, res) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /x/aggregator/client/rest/rest.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/gorilla/mux" 5 | 6 | "github.com/cosmos/cosmos-sdk/client/context" 7 | ) 8 | 9 | // RegisterRoutes registers aggregator-related REST handlers to a router 10 | func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { 11 | // this line is used by starport scaffolding # 1 12 | registerQueryRoutes(cliCtx, r) 13 | registerTxRoutes(cliCtx, r) 14 | } 15 | -------------------------------------------------------------------------------- /x/aggregator/client/rest/tx.go: -------------------------------------------------------------------------------- 1 | package rest 2 | // The packages below are commented out at first to prevent an error if this file isn't initially saved. 3 | import ( 4 | // "bytes" 5 | // "net/http" 6 | 7 | "github.com/gorilla/mux" 8 | 9 | "github.com/cosmos/cosmos-sdk/client/context" 10 | // sdk "github.com/cosmos/cosmos-sdk/types" 11 | // "github.com/cosmos/cosmos-sdk/types/rest" 12 | // "github.com/cosmos/cosmos-sdk/x/auth/client/utils" 13 | // "github.com/bluzelle/curium/x/aggregator/types" 14 | ) 15 | 16 | func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { 17 | // r.HandleFunc( 18 | // TODO: Define the Rest route , 19 | // Call the function which should be executed for this route), 20 | // ).Methods("POST") 21 | } 22 | 23 | /* 24 | // Action TX body 25 | type Req struct { 26 | BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` 27 | // TODO: Define more types if needed 28 | } 29 | 30 | func RequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { 31 | return func(w http.ResponseWriter, r *http.Request) { 32 | var req Req 33 | vars := mux.Vars(r) 34 | 35 | baseReq := req.BaseReq.Sanitize() 36 | if !baseReq.ValidateBasic(w) { 37 | return 38 | } 39 | 40 | // TODO: Define the module tx logic for this action 41 | 42 | utils.WriteGenerateStdTxResponse(w, cliCtx, BaseReq, []sdk.Msg{msg}) 43 | } 44 | } 45 | */ 46 | -------------------------------------------------------------------------------- /x/aggregator/genesis.go: -------------------------------------------------------------------------------- 1 | package aggregator 2 | 3 | import ( 4 | "fmt" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | "github.com/bluzelle/curium/x/aggregator/types" 7 | "github.com/bluzelle/curium/x/aggregator/keeper" 8 | ) 9 | 10 | // InitGenesis initialize default parameters 11 | // and the keeper's address to pubkey map 12 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, data types.GenesisState) { 13 | fmt.Println("testing") 14 | } 15 | 16 | // ExportGenesis writes the current store values 17 | // to a genesis file, which can be imported again 18 | // with InitGenesis 19 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) (data types.GenesisState) { 20 | return types.NewGenesisState( 21 | k.DumpAggValues(ctx), 22 | k.DumpQueueItems(ctx), 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /x/aggregator/handler.go: -------------------------------------------------------------------------------- 1 | package aggregator 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bluzelle/curium/app/ante/gasmeter" 6 | 7 | "github.com/bluzelle/curium/x/aggregator/keeper" 8 | "github.com/bluzelle/curium/x/aggregator/types" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 11 | ) 12 | 13 | // NewHandler creates an sdk.Handler for all the aggregator type messages 14 | func NewHandler(k keeper.Keeper) sdk.Handler { 15 | return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { 16 | ctx = ctx.WithEventManager(sdk.NewEventManager()).WithGasMeter(gasmeter.NewFreeGasMeter(0)) 17 | switch msg := msg.(type) { 18 | default: 19 | errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg) 20 | return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg) 21 | } 22 | } 23 | } 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /x/aggregator/keeper/Averager.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import sdk "github.com/cosmos/cosmos-sdk/types" 4 | 5 | type Averager struct { 6 | Sum sdk.Dec 7 | Count int64 8 | WeightSum int64 9 | } 10 | 11 | func NewAverager() Averager { 12 | return Averager{ 13 | Sum: sdk.NewDec(0), 14 | WeightSum: 0, 15 | Count: 0, 16 | } 17 | } 18 | 19 | func (a *Averager) Add(dec sdk.Dec, weight int64) Averager { 20 | if a.Sum.IsNil() { 21 | a.Sum = dec.MulInt64(weight) 22 | } else { 23 | a.Sum = a.Sum.Add(dec.MulInt64(weight)) 24 | } 25 | a.WeightSum = a.WeightSum + weight 26 | a.Count = a.Count + 1 27 | return *a 28 | } 29 | 30 | func (a Averager) CalculateAverage() sdk.Dec { 31 | if a.WeightSum == 0 { 32 | logger.Info("zero weight in average") 33 | return a.Sum.QuoInt64(a.Count) 34 | } 35 | return a.Sum.QuoInt64(a.WeightSum) 36 | } 37 | -------------------------------------------------------------------------------- /x/aggregator/keeper/Averager_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/aggregator/keeper" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | ) 9 | 10 | func TestAverager(t *testing.T) { 11 | t.Run("It should provide an average", func(t *testing.T) { 12 | x := keeper.NewAverager() 13 | x.Add(sdk.NewDec(10), 100) 14 | x.Add(sdk.NewDec(20), 100) 15 | x.Add(sdk.NewDec(30), 100) 16 | assert.True(t, x.CalculateAverage().Equal(sdk.NewDec(20))) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /x/aggregator/keeper/keeper_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | //import ( 4 | // bluzellechain "github.com/bluzelle/curium/types" 5 | // "github.com/bluzelle/curium/x/aggregator/keeper" 6 | // "github.com/bluzelle/curium/x/aggregator/types" 7 | // "github.com/bluzelle/curium/x/oracle" 8 | // "github.com/cosmos/cosmos-sdk/codec" 9 | // "github.com/cosmos/cosmos-sdk/server/mock" 10 | // storeTypes "github.com/cosmos/cosmos-sdk/store/types" 11 | // sdk "github.com/cosmos/cosmos-sdk/types" 12 | // abci "github.com/tendermint/tendermint/abci/types" 13 | // "testing" 14 | //) 15 | // 16 | //func getKeeper() (keeper.Keeper, sdk.AccAddress, sdk.Context) { 17 | // ms := mock.NewCommitMultiStore() 18 | // queueStoreKey := sdk.NewKVStoreKey(types.ValueQueueStoreKey) 19 | // valueStoreKey := sdk.NewKVStoreKey(types.AggValueStoreKey) 20 | // 21 | // ms.MountStoreWithDB(queueStoreKey, storeTypes.StoreTypeDB, nil) 22 | // ms.MountStoreWithDB(valueStoreKey, storeTypes.StoreTypeDB, nil) 23 | // 24 | // ctx := sdk.NewContext(ms, abci.Header{}, false, nil) 25 | // config := sdk.GetConfig() 26 | // config.SetBech32PrefixForAccount(bluzellechain.Bech32PrefixAccAddr, bluzellechain.Bech32PrefixAccPub) 27 | // config.SetBech32PrefixForValidator(bluzellechain.Bech32PrefixValAddr, bluzellechain.Bech32PrefixValPub) 28 | // config.SetBech32PrefixForConsensusNode(bluzellechain.Bech32PrefixConsAddr, bluzellechain.Bech32PrefixConsPub) 29 | // 30 | // addr, _ := sdk.AccAddressFromBech32("bluzelle1t0ywtmrduldf6h4wqrnnpyp9wr6law2u5jwa23") 31 | // keeper := keeper.NewKeeper(codec.New(), oracle.Keeper{}, queueStoreKey, valueStoreKey, nil) 32 | // 33 | // return keeper, addr, ctx 34 | //} 35 | // 36 | // 37 | //func Test_GetAggValueBatches(t *testing.T) { 38 | // t.Run("It should set a default limit of 50", func(t *testing.T) { 39 | // keeper, _, ctx := getKeeper() 40 | // store := keeper.GetAggValueStore(ctx) 41 | // 42 | // for i := 0; i < 1000; i++ { 43 | // store.Set([]byte(string(i % 100)+ ">testing"), make([]byte, 0)) 44 | // } 45 | // 46 | // // TODO: Finish here 47 | // //batches := keeper.GetAggValueBatches(ctx, "", 10, true) 48 | // //fmt.Println(batches) 49 | // 50 | // 51 | // }) 52 | //} -------------------------------------------------------------------------------- /x/aggregator/keeper/params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | // TODO: Define if your module needs Parameters, if not this can be deleted 4 | 5 | import ( 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/bluzelle/curium/x/aggregator/types" 8 | ) 9 | 10 | // GetParams returns the total set of aggregator parameters. 11 | func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { 12 | k.paramspace.GetParamSet(ctx, ¶ms) 13 | return params 14 | } 15 | 16 | // SetParams sets the aggregator parameters to the param space. 17 | func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { 18 | k.paramspace.SetParamSet(ctx, ¶ms) 19 | } 20 | 21 | -------------------------------------------------------------------------------- /x/aggregator/spec/README.md: -------------------------------------------------------------------------------- 1 | # aggregator module specification 2 | 3 | ## Abstract 4 | 5 | 6 | 7 | ## Contents 8 | 9 | // TODO: Create the below files if they are needed. 10 | 1. **[Concepts](01_concepts.md)** 11 | 2. **[State](02_state.md)** 12 | 3. **[Messages](03_messages.md)** 13 | 4. **[Begin-Block](04_begin_block.md)** 14 | 5. **[End-Block](06_end_bloc.md)** 15 | 6. **[05_hooks](06_hooks.md)** 16 | 7. **[Events](07_events.md)** 17 | 8. **[Parameters](08_params.md)** 18 | -------------------------------------------------------------------------------- /x/aggregator/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | ) 6 | 7 | // RegisterCodec registers concrete types on codec 8 | func RegisterCodec(cdc *codec.Codec) { 9 | // this line is used by starport scaffolding # 1 10 | // TODO: Register the modules msgs 11 | } 12 | 13 | // ModuleCdc defines the module codec 14 | var ModuleCdc *codec.Codec 15 | 16 | func init() { 17 | ModuleCdc = codec.New() 18 | RegisterCodec(ModuleCdc) 19 | codec.RegisterCrypto(ModuleCdc) 20 | ModuleCdc.Seal() 21 | } 22 | -------------------------------------------------------------------------------- /x/aggregator/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | _ "github.com/cosmos/cosmos-sdk/types/errors" 5 | ) 6 | 7 | // TODO: Fill out some custom errors for the module 8 | // You can see how they are constructed below: 9 | var ( 10 | // ErrInvalid = sdkerrors.Register(ModuleName, 1, "custom error message") 11 | ) 12 | -------------------------------------------------------------------------------- /x/aggregator/types/events.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // aggregator module event types 4 | const ( 5 | // TODO: Create your event types 6 | // EventType = "action" 7 | 8 | // TODO: Create keys fo your events, the values will be derivided from the msg 9 | // AttributeKeyAddress = "address" 10 | 11 | // TODO: Some events may not have values for that reason you want to emit that something happened. 12 | // AttributeValueDoubleSign = "double_sign" 13 | 14 | AttributeValueCategory = ModuleName 15 | ) 16 | -------------------------------------------------------------------------------- /x/aggregator/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/params" 6 | ) 7 | 8 | // ParamSubspace defines the expected Subspace interfcace 9 | type ParamSubspace interface { 10 | WithKeyTable(table params.KeyTable) params.Subspace 11 | Get(ctx sdk.Context, key []byte, ptr interface{}) 12 | GetParamSet(ctx sdk.Context, ps params.ParamSet) 13 | SetParamSet(ctx sdk.Context, ps params.ParamSet) 14 | } 15 | 16 | /* 17 | When a module wishes to interact with another module, it is good practice to define what it will use 18 | as an interface so the module cannot use things that are not permitted. 19 | TODO: Create interfaces of what you expect the other keepers to have to be able to use this module. 20 | type BankKeeper interface { 21 | SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error) 22 | SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error 23 | } 24 | */ 25 | -------------------------------------------------------------------------------- /x/aggregator/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // GenesisState - all aggregator state that must be provided at genesis 4 | type GenesisState struct { 5 | AggValues map[string]AggregatorValue 6 | QueueValues map[string]AggregatorQueueItem 7 | } 8 | 9 | // NewGenesisState creates a new GenesisState object 10 | func NewGenesisState(aggValues map[string]AggregatorValue, queueValues map[string]AggregatorQueueItem) GenesisState { 11 | return GenesisState{ 12 | AggValues: aggValues, 13 | QueueValues: queueValues, 14 | } 15 | } 16 | 17 | // DefaultGenesisState - default GenesisState used by Cosmos Hub 18 | func DefaultGenesisState() GenesisState { 19 | return GenesisState{ 20 | // TODO: Fill out according to your genesis state, these values will be initialized but empty 21 | } 22 | } 23 | 24 | // ValidateGenesis validates the aggregator genesis parameters 25 | func ValidateGenesis(data GenesisState) error { 26 | // TODO: Create a sanity check to make sure the state conforms to the modules needs 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /x/aggregator/types/keeper.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "github.com/cosmos/cosmos-sdk/types" 4 | 5 | type AggregatorValue struct { 6 | Batch string 7 | Symbol string 8 | InSymbol string 9 | Value types.Dec 10 | Count int64 11 | Height int64 12 | } 13 | 14 | type AggregatorQueueItem struct { 15 | SourceName string 16 | Batch string 17 | Symbol string 18 | InSymbol string 19 | Value types.Dec 20 | Height int64 21 | Weight int64 22 | } 23 | 24 | -------------------------------------------------------------------------------- /x/aggregator/types/key.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | // ModuleName is the name of the module 11 | ModuleName = "aggregator" 12 | 13 | // StoreKey to be used when creating the KVStore 14 | StoreKey = ModuleName 15 | 16 | // RouterKey to be used for routing msgs 17 | RouterKey = ModuleName 18 | 19 | // QuerierRoute to be used for querier msgs 20 | QuerierRoute = ModuleName 21 | 22 | // Store prefixes for various records 23 | AggValueStorePrefix = "V" 24 | QueueStorePrefix = "Q" 25 | 26 | 27 | ) 28 | 29 | type QueueItemKey struct { 30 | Height int64 31 | Batch string 32 | SourceName string 33 | } 34 | 35 | func (qik QueueItemKey) Bytes() []byte { 36 | blockStr := fmt.Sprintf("%020d", qik.Height) 37 | return []byte(QueueStorePrefix + blockStr + ">" + qik.Batch + ">" + qik.SourceName) 38 | } 39 | 40 | func QueueItemKeyFromBytes(bytes []byte) QueueItemKey { 41 | parts := strings.Split(string(bytes[1:]), ">") 42 | height, _ := strconv.ParseInt(parts[0], 10, 64) 43 | return QueueItemKey{ 44 | Height: height, 45 | Batch: parts[1], 46 | SourceName: parts[2], 47 | } 48 | } 49 | 50 | type AggStoreKey struct { 51 | Batch string 52 | Symbol string 53 | InSymbol string 54 | } 55 | 56 | func (ask AggStoreKey) Bytes() []byte { 57 | return []byte(AggValueStorePrefix + ask.Batch + ">" + ask.Symbol + "-" + ask.InSymbol) 58 | } 59 | 60 | func AggStoreKeyFromBytes(bytes []byte) AggStoreKey { 61 | parts := strings.Split(string(bytes[1:]), ">") 62 | symbolParts := strings.Split(parts[1], "-") 63 | return AggStoreKey{ 64 | Batch: parts[0], 65 | Symbol: symbolParts[0], 66 | InSymbol: symbolParts[1], 67 | } 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /x/aggregator/types/key_test.go: -------------------------------------------------------------------------------- 1 | package types_test 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/aggregator/types" 5 | "github.com/magiconair/properties/assert" 6 | "testing" 7 | ) 8 | 9 | func Test_QueueItemKey(t *testing.T) { 10 | t.Run("it should serialize and deserialize to bytes", func(t *testing.T) { 11 | bytes := types.QueueItemKey{ 12 | Height: 100, 13 | Batch: "2021-03-22-08:13", 14 | SourceName: "something-blz-in-usdt", 15 | }.Bytes() 16 | 17 | key := types.QueueItemKeyFromBytes(bytes) 18 | assert.Equal(t, key.Height, int64(100)) 19 | assert.Equal(t, key.Batch, "2021-03-22-08:13") 20 | assert.Equal(t, key.SourceName, "something-blz-in-usdt") 21 | }) 22 | } 23 | 24 | func Test_AggStoreKey(t *testing.T) { 25 | bytes := types.AggStoreKey{ 26 | Batch: "2021-03-22-08:13", 27 | Symbol: "blz", 28 | InSymbol: "btc", 29 | }.Bytes() 30 | 31 | key := types.AggStoreKeyFromBytes(bytes) 32 | assert.Equal(t, key.Batch, "2021-03-22-08:13") 33 | assert.Equal(t, key.Symbol, "blz") 34 | assert.Equal(t, key.InSymbol, "btc") 35 | } 36 | 37 | -------------------------------------------------------------------------------- /x/aggregator/types/msg.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | _ "github.com/cosmos/cosmos-sdk/types" 5 | _ "github.com/cosmos/cosmos-sdk/types/errors" 6 | ) 7 | 8 | // TODO: Describe your actions, these will implment the interface of `sdk.Msg` 9 | 10 | // verify interface at compile time 11 | //var _ sdk.Msg = &Msg{} 12 | 13 | // Msg - struct for unjailing jailed validator 14 | /* 15 | type Msg struct { 16 | ValidatorAddr sdk.ValAddress `json:"address" yaml:"address"` // address of the validator operator 17 | } 18 | */ 19 | 20 | // NewMsg creates a new Msg instance 21 | /* 22 | func NewMsg(validatorAddr sdk.ValAddress) Msg { 23 | return Msg{ 24 | ValidatorAddr: validatorAddr, 25 | } 26 | } 27 | */ 28 | 29 | // const Const = "" 30 | 31 | // nolint 32 | /* 33 | func (msg Msg) Route() string { return RouterKey } 34 | func (msg Msg) Type() string { return Const } 35 | func (msg Msg) GetSigners() []sdk.AccAddress { 36 | return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddr)} 37 | } 38 | 39 | // GetSignBytes gets the bytes for the message signer to sign on 40 | func (msg Msg) GetSignBytes() []byte { 41 | bz := ModuleCdc.MustMarshalJSON(msg) 42 | return sdk.MustSortJSON(bz) 43 | } 44 | 45 | // ValidateBasic validity check for the AnteHandler 46 | func (msg Msg) ValidateBasic() error { 47 | if msg.ValidatorAddr.Empty() { 48 | return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing validator address") 49 | } 50 | return nil 51 | } 52 | */ 53 | -------------------------------------------------------------------------------- /x/aggregator/types/params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cosmos/cosmos-sdk/x/params" 7 | ) 8 | 9 | // Default parameter namespace 10 | const ( 11 | DefaultParamspace = ModuleName 12 | // TODO: Define your default parameters 13 | ) 14 | 15 | // Parameter store keys 16 | var ( 17 | // TODO: Define your keys for the parameter store 18 | // KeyParamName = []byte("ParamName") 19 | ) 20 | 21 | // ParamKeyTable for aggregator module 22 | func ParamKeyTable() params.KeyTable { 23 | return params.NewKeyTable().RegisterParamSet(&Params{}) 24 | } 25 | 26 | // Params - used for initializing default parameter for aggregator at genesis 27 | type Params struct { 28 | // TODO: Add your Paramaters to the Paramter struct 29 | // KeyParamName string `json:"key_param_name"` 30 | } 31 | 32 | // NewParams creates a new Params object 33 | func NewParams(/* TODO: Pass in the paramters*/) Params { 34 | return Params{ 35 | // TODO: Create your Params Type 36 | } 37 | } 38 | 39 | // String implements the stringer interface for Params 40 | func (p Params) String() string { 41 | return fmt.Sprintf(` 42 | // TODO: Return all the params as a string 43 | `, ) 44 | } 45 | 46 | // ParamSetPairs - Implements params.ParamSet 47 | func (p *Params) ParamSetPairs() params.ParamSetPairs { 48 | return params.ParamSetPairs{ 49 | // TODO: Pair your key with the param 50 | // params.NewParamSetPair(KeyParamName, &p.ParamName), 51 | } 52 | } 53 | 54 | // DefaultParams defines the parameters for this module 55 | func DefaultParams() Params { 56 | return NewParams( /* TODO: Pass in your default Params */ ) 57 | } 58 | -------------------------------------------------------------------------------- /x/aggregator/types/querier.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Query endpoints supported by the aggregator querier 4 | const ( 5 | QueryParams = "queryParams" 6 | QuerySearchValues = "searchValues" 7 | QuerySearchBatchKeys = "searchBatchKeys" 8 | QueryGetAggregatedValue = "getAggregatedValue" 9 | QueryGetPairValuesHistory = "getPairValuesHistory" 10 | QueryGetLatestValues = "getLatestValues" 11 | QueryGetPairValues = "getLatestPair" 12 | ) 13 | 14 | type RestLatestPairReq struct { 15 | Symbol string 16 | InSymbol string 17 | } 18 | 19 | type QueryReqSearchValues struct { 20 | Prefix string 21 | Reverse bool 22 | Page uint 23 | Limit uint 24 | } 25 | 26 | type QueryReqGetAggregatorValue struct { 27 | Batch string 28 | Pair string 29 | } 30 | 31 | type QueryReqSearchBatches struct { 32 | PreviousBatch string 33 | Reverse bool 34 | Limit uint 35 | } 36 | 37 | type QueryReqGetPairValuesHistory struct { 38 | StartBatch string 39 | EndBatch string 40 | Step int64 41 | Symbol string 42 | InSymbol string 43 | } -------------------------------------------------------------------------------- /x/crud/alias.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package crud 16 | 17 | import ( 18 | "github.com/bluzelle/curium/x/crud/internal/keeper" 19 | "github.com/bluzelle/curium/x/crud/internal/types" 20 | ) 21 | 22 | const ( 23 | ModuleName = types.ModuleName 24 | RouterKey = types.RouterKey 25 | StoreKey = types.StoreKey 26 | LeaseKey = types.LeaseKey 27 | OwnerKey = types.OwnerKey 28 | ) 29 | 30 | var ( 31 | NewKeeper = keeper.NewKeeper 32 | NewQuerier = keeper.NewQuerier 33 | ModuleCdc = types.ModuleCdc 34 | RegisterCodec = types.RegisterCodec 35 | ) 36 | 37 | type ( 38 | Keeper = keeper.Keeper 39 | MaxKeeperSizes = keeper.MaxKeeperSizes 40 | MsgCreate = types.MsgCreate 41 | MsgRead = types.MsgRead 42 | MsgUpdate = types.MsgUpdate 43 | MsgDelete = types.MsgDelete 44 | QueryResultRead = types.QueryResultRead 45 | QueryResultHas = types.QueryResultHas 46 | ) 47 | -------------------------------------------------------------------------------- /x/crud/client/rest/rest.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package rest 16 | 17 | import ( 18 | "fmt" 19 | "github.com/cosmos/cosmos-sdk/client/context" 20 | "github.com/gorilla/mux" 21 | ) 22 | 23 | // RegisterRoutes - Central function to define routes that get registered by the main application 24 | func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) { 25 | r.HandleFunc("/abci-query", AbciQueryHandler(cliCtx)).Methods("POST") 26 | r.HandleFunc("/account-txs/{address}/{start}", AccountTxsHandler(cliCtx)).Methods("GET") 27 | r.HandleFunc(fmt.Sprintf("/%s/count/{UUID}", storeName), BlzQCountHandler(cliCtx, storeName)).Methods("GET") 28 | r.HandleFunc(fmt.Sprintf("/%s/getlease/{UUID}/{key}", storeName), BlzQGetLeaseHandler(cliCtx, storeName)).Methods("GET") 29 | r.HandleFunc(fmt.Sprintf("/%s/getnshortestleases/{UUID}/{N}", storeName), BlzQGetNShortestLeasesHandler(cliCtx, storeName)).Methods("GET") 30 | r.HandleFunc(fmt.Sprintf("/%s/has/{UUID}/{key}", storeName), BlzQHasHandler(cliCtx, storeName)).Methods("GET") 31 | r.HandleFunc(fmt.Sprintf("/%s/keys/{UUID}", storeName), BlzQKeysHandler(cliCtx, storeName)).Methods("GET") 32 | r.HandleFunc(fmt.Sprintf("/%s/search/{UUID}/{prefix}/{page}/{limit}/{direction}", storeName), BlzQSearchHandler(cliCtx, storeName)).Methods("GET") 33 | r.HandleFunc(fmt.Sprintf("/%s/keyvalues/{UUID}", storeName), BlzQKeyValuesHandler(cliCtx, storeName)).Methods("GET") 34 | r.HandleFunc(fmt.Sprintf("/%s/pread/{UUID}/{key}", storeName), BlzQProvenReadHandler(cliCtx, storeName)).Methods("GET") 35 | r.HandleFunc(fmt.Sprintf("/%s/read/{UUID}/{key}", storeName), BlzQReadHandler(cliCtx, storeName)).Methods("GET") 36 | r.HandleFunc(fmt.Sprintf("/%s/owner/{UUID}/{key}", storeName), BlzQOwnerHandler(cliCtx, storeName)).Methods("GET") 37 | r.HandleFunc(fmt.Sprintf("/%s/mykeys/{owner}/{UUID}", storeName), BlzQMyKeysHandler(cliCtx, storeName)).Methods("GET") 38 | } 39 | -------------------------------------------------------------------------------- /x/crud/client/rest/tx.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package rest 16 | 17 | 18 | -------------------------------------------------------------------------------- /x/crud/gas_calculator.go: -------------------------------------------------------------------------------- 1 | package crud 2 | 3 | import "math" 4 | 5 | const ( 6 | LeaseGasRateDefaultValue float64 = 1 7 | LeaseGasRateMaximumValue float64 = 3 8 | LeaseGasRateParamB float64 = 2.5 9 | LeaseGasRateParamC float64 = 75 10 | LeaseGasRateParamG float64 = 3 11 | ) 12 | 13 | func CalculateGasForLease(lease int64, bytes int) uint64 { 14 | leaseDays := LeaseInDays(lease) 15 | gasRate := leaseGasRatePerByte(leaseDays) 16 | return uint64(math.Round(gasRate * leaseDays * math.Max(float64(bytes), 200000))) 17 | } 18 | 19 | func LeaseInDays(lease int64) float64 { 20 | return (float64(lease) / 24 / 60 / 60) * 5.5 21 | } 22 | 23 | func leaseGasRatePerByte(days float64) float64 { 24 | return LeaseGasRateDefaultValue + (LeaseGasRateMaximumValue-LeaseGasRateDefaultValue)/ 25 | math.Pow(1.0+math.Pow(days/LeaseGasRateParamC, LeaseGasRateParamB), LeaseGasRateParamG) 26 | } 27 | -------------------------------------------------------------------------------- /x/crud/gas_calculator_test.go: -------------------------------------------------------------------------------- 1 | package crud 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "math" 6 | "testing" 7 | ) 8 | 9 | func daysToLease(days int64) int64 { 10 | return int64(math.Round(float64(days) * (24 * 60 * 60 / 5.5))) 11 | } 12 | 13 | 14 | func TestLeaseGasRatePerByte(t *testing.T) { 15 | assert.Equal(t, leaseGasRatePerByte(0), 3.0) 16 | assert.Equal(t, leaseGasRatePerByte(1), 2.999876836999191) 17 | assert.Equal(t, leaseGasRatePerByte(10), 2.961551101112777) 18 | assert.Equal(t, leaseGasRatePerByte(120), 1.0262720621151311) 19 | 20 | 21 | } 22 | 23 | func TestCalculateGasForLease(t *testing.T) { 24 | 25 | t.Run("Should work for partial day leases", func(t *testing.T) { 26 | assert.InDelta(t, uint64(5997), CalculateGasForLease(157, 20), 3) 27 | 28 | }) 29 | 30 | t.Run("Smaller number of bytes should default to 200000 bytes", func(t *testing.T) { 31 | assert.InDelta(t, uint64(599975), CalculateGasForLease(daysToLease(1), 20), 3) 32 | assert.InDelta(t, uint64(599975), CalculateGasForLease(daysToLease(1), 150000), 3) 33 | 34 | defaultBytesGas := CalculateGasForLease(daysToLease(100), 200000) 35 | assert.Equal(t, CalculateGasForLease(daysToLease(100), 100), defaultBytesGas) 36 | }) 37 | 38 | 39 | t.Run("Larger data should calculate the correct gas", func(t *testing.T) { 40 | assert.InDelta(t, uint64(2999876837), CalculateGasForLease( daysToLease(1), 1000 * 1000 * 1000), 17359) 41 | assert.Equal(t, uint64(94412671082), CalculateGasForLease(daysToLease(77), 1000 * 1000 * 1000)) 42 | }) 43 | 44 | t.Run("Larger number of days should calculate correct gas", func(t *testing.T) { 45 | assert.InDelta(t, uint64(17900418), CalculateGasForLease(daysToLease(50), 1), 2) 46 | assert.InDelta(t, uint64(89502092347), CalculateGasForLease(daysToLease(50), 1000000000), 7373) 47 | }) 48 | } 49 | 50 | -------------------------------------------------------------------------------- /x/crud/genesis_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package crud 16 | 17 | import ( 18 | "github.com/bluzelle/curium/x/crud/internal/types" 19 | "github.com/stretchr/testify/assert" 20 | "testing" 21 | ) 22 | 23 | 24 | func TestValidateGenesis(t *testing.T) { 25 | var blzValues []types.BLZValue 26 | 27 | genesisState := NewGenesisState(blzValues) 28 | 29 | assert.Nil(t, ValidateGenesis(genesisState)) 30 | 31 | blzValues = append(blzValues, types.BLZValue{Value: "test", Owner: []byte("notnilowner")}) 32 | 33 | assert.Nil(t, ValidateGenesis(genesisState)) 34 | 35 | blzValues = append(blzValues, types.BLZValue{Value: "test"}) 36 | 37 | assert.Nil(t, ValidateGenesis(genesisState)) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /x/crud/internal/types/codec.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package types 16 | 17 | import ( 18 | "github.com/cosmos/cosmos-sdk/codec" 19 | ) 20 | 21 | var ModuleCdc = codec.New() 22 | 23 | func init() { 24 | RegisterCodec(ModuleCdc) 25 | } 26 | 27 | func RegisterCodec(cdc *codec.Codec) { 28 | cdc.RegisterConcrete(MsgCount{}, "crud/count", nil) 29 | cdc.RegisterConcrete(MsgCreate{}, "crud/create", nil) 30 | cdc.RegisterConcrete(MsgDeleteAll{}, "crud/deleteall", nil) 31 | cdc.RegisterConcrete(MsgDelete{}, "crud/delete", nil) 32 | cdc.RegisterConcrete(MsgGetLease{}, "crud/getlease", nil) 33 | cdc.RegisterConcrete(MsgGetNShortestLeases{}, "crud/getnshortestleases", nil) 34 | cdc.RegisterConcrete(MsgHas{}, "crud/has", nil) 35 | cdc.RegisterConcrete(MsgKeyValues{}, "crud/keyvalues", nil) 36 | cdc.RegisterConcrete(MsgKeys{}, "crud/keys", nil) 37 | cdc.RegisterConcrete(MsgMultiUpdate{}, "crud/multiupdate", nil) 38 | cdc.RegisterConcrete(MsgRead{}, "crud/read", nil) 39 | cdc.RegisterConcrete(MsgRename{}, "crud/rename", nil) 40 | cdc.RegisterConcrete(MsgRenewLease{}, "crud/renewlease", nil) 41 | cdc.RegisterConcrete(MsgRenewLeaseAll{}, "crud/renewleaseall", nil) 42 | cdc.RegisterConcrete(MsgUpdate{}, "crud/update", nil) 43 | cdc.RegisterConcrete(MsgUpsert{}, "crud/upsert", nil) 44 | } 45 | -------------------------------------------------------------------------------- /x/crud/internal/types/key.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package types 16 | 17 | const ( 18 | // module name 19 | ModuleName = "crud" 20 | 21 | // StoreKey to be used when creating the KVStore 22 | StoreKey = ModuleName 23 | LeaseKey = "crudLease" 24 | OwnerKey = "crudOwner" 25 | ) 26 | -------------------------------------------------------------------------------- /x/crud/internal/types/querier.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package types 16 | 17 | type QueryResultRead struct { 18 | UUID string `json:"uuid"` 19 | Key string `json:"key"` 20 | Value string `json:"value"` 21 | } 22 | 23 | type QueryResultOwner struct { 24 | UUID string `json:"uuid"` 25 | Key string `json:"key"` 26 | Owner string `json:"owner"` 27 | } 28 | 29 | // for fmt.Stringer 30 | func (r QueryResultRead) String() string { 31 | return r.Value 32 | } 33 | 34 | type QueryResultHas struct { 35 | UUID string `json:"uuid"` 36 | Key string `json:"key"` 37 | Has bool `json:"has"` 38 | } 39 | 40 | // for fmt.Stringer 41 | func (r QueryResultHas) String() string { 42 | if r.Has { 43 | return "true" 44 | } else { 45 | return "false" 46 | } 47 | } 48 | 49 | type QueryResultKeys struct { 50 | UUID string `json:"uuid"` 51 | Keys []string `json:"keys"` 52 | } 53 | 54 | type QueryResultKeyValues struct { 55 | UUID string `json:"uuid"` 56 | KeyValues []KeyValue `json:"keyvalues"` 57 | } 58 | 59 | type QueryResultSearch struct { 60 | UUID string `json:"uuid"` 61 | KeyValues []KeyValue `json:"keyvalues"` 62 | } 63 | 64 | type QueryResultCount struct { 65 | UUID string `json:"uuid"` 66 | Count uint64 `json:"count,string"` 67 | } 68 | 69 | type QueryResultLease struct { 70 | UUID string `json:"uuid"` 71 | Key string `json:"key"` 72 | Lease int64 `json:"lease,string"` 73 | } 74 | 75 | type QueryResultNShortestLeaseKeys struct { 76 | UUID string `json:"uuid"` 77 | KeyLeases []KeyLease `json:"keyleases"` 78 | } 79 | 80 | type QueryResultMyKeys struct { 81 | UUID string `json:"uuid"` 82 | Keys []string `json:"keys"` 83 | } 84 | -------------------------------------------------------------------------------- /x/crud/internal/types/querier_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package types 16 | 17 | import ( 18 | "github.com/magiconair/properties/assert" 19 | "testing" 20 | ) 21 | 22 | func TestQueryResultRead_String(t *testing.T) { 23 | assert.Equal(t, QueryResultRead{UUID: "uuid", Key: "key", Value: "value"}.String(), "value") 24 | } 25 | 26 | func TestQueryResultHas_String(t *testing.T) { 27 | assert.Equal(t, QueryResultHas{UUID: "uuid", Key: "key", Has: false}.String(), "false") 28 | assert.Equal(t, QueryResultHas{UUID: "uuid", Key: "key", Has: true}.String(), "true") 29 | } 30 | -------------------------------------------------------------------------------- /x/crud/internal/types/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package types 16 | 17 | import ( 18 | "fmt" 19 | cc "github.com/cosmos/cosmos-sdk/codec" 20 | sdk "github.com/cosmos/cosmos-sdk/types" 21 | "strings" 22 | ) 23 | 24 | type BLZValue struct { 25 | Value string `json:"value"` 26 | Lease int64 `json:"lease"` 27 | Height int64 `json:"height"` 28 | Owner sdk.AccAddress `json:"owner"` 29 | } 30 | 31 | func (kv BLZValue) Unmarshal(b []byte) BLZValue { 32 | var cdc = cc.New() 33 | value := BLZValue{} 34 | _ = cdc.UnmarshalBinaryBare(b, &value) 35 | return value 36 | } 37 | 38 | func (kv BLZValue) String() string { 39 | if kv.Owner.Empty() { 40 | return strings.TrimSpace(fmt.Sprintf("Value: %s Owner: ", kv.Value)) 41 | } 42 | return strings.TrimSpace(fmt.Sprintf("Value: %s Owner: %s", kv.Value, kv.Owner.String())) 43 | } 44 | 45 | type KeyValue struct { 46 | Key string `json:"key"` 47 | Value string `json:"value"` 48 | } 49 | 50 | type KeyLease struct { 51 | Key string `json:"key"` 52 | Lease int64 `json:"lease,string"` 53 | } 54 | 55 | type KeyLeases []KeyLease 56 | 57 | func (a KeyLeases) Len() int { return len(a) } 58 | func (a KeyLeases) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 59 | func (a KeyLeases) Less(i, j int) bool { return a[i].Lease < a[j].Lease } 60 | -------------------------------------------------------------------------------- /x/crud/internal/types/types_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package types 16 | 17 | import ( 18 | cc "github.com/cosmos/cosmos-sdk/codec" 19 | "github.com/stretchr/testify/assert" 20 | "reflect" 21 | "sort" 22 | "testing" 23 | ) 24 | 25 | func TestBLZValue_Unmarshal(t *testing.T) { 26 | value := BLZValue{ 27 | Value: "value", 28 | Owner: []byte("bluzelle1t0ywtmrduldf6h4wqrnnpyp9wr6law2u5jwa23"), 29 | } 30 | 31 | cdc := cc.New() 32 | b, _ := cdc.MarshalBinaryBare(value) 33 | 34 | resultValue := BLZValue{} 35 | 36 | assert.True(t, reflect.DeepEqual(value, resultValue.Unmarshal(b))) 37 | } 38 | 39 | func TestBLZValue_String(t *testing.T) { 40 | value := BLZValue{ 41 | Value: "value", 42 | Owner: []byte("bluzelle1t0ywtmrduldf6h4wqrnnpyp9wr6law2u5jwa23"), 43 | } 44 | assert.Equal(t, value.String(), "Value: value Owner: cosmos1vfk827n9d3kx2vt5xpuhwardwfj82mryvcmxsdrhw9exumns09crjamjxekxzaejw56k5ampxgeslhg4h3") 45 | 46 | value = BLZValue{ 47 | Value: "value", 48 | } 49 | assert.Equal(t, value.String(), "Value: value Owner: ") 50 | } 51 | 52 | func TestKeyLeases_Sort(t *testing.T) { 53 | keyLeases := KeyLeases{} 54 | keyLeases = append(keyLeases, KeyLease{ 55 | Key: "three", 56 | Lease: 3, 57 | }) 58 | 59 | keyLeases = append(keyLeases, KeyLease{ 60 | Key: "one", 61 | Lease: 1, 62 | }) 63 | 64 | keyLeases = append(keyLeases, KeyLease{ 65 | Key: "zero", 66 | Lease: 0, 67 | }) 68 | 69 | keyLeases = append(keyLeases, KeyLease{ 70 | Key: "two", 71 | Lease: 2, 72 | }) 73 | 74 | sort.Sort(keyLeases) 75 | 76 | assert.Equal(t, int64(0), keyLeases[0].Lease) 77 | assert.Equal(t, int64(3), keyLeases[len(keyLeases)-1].Lease) 78 | } 79 | -------------------------------------------------------------------------------- /x/curium/alias.go: -------------------------------------------------------------------------------- 1 | package curium 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/curium/keeper" 5 | "github.com/bluzelle/curium/x/curium/types" 6 | ) 7 | 8 | var ( 9 | ModuleName = types.ModuleName 10 | StoreKey = types.StoreKey 11 | MemStoreKey = types.MemStoreKey 12 | NewKeeper = keeper.NewKeeper 13 | NewKeyringReader = keeper.NewKeyringReader 14 | ) 15 | type ( 16 | Keeper = keeper.Keeper 17 | MsgBroadcaster = keeper.MsgBroadcaster 18 | MsgBroadcasterResponse = keeper.MsgBroadcasterResponse 19 | KeyringReader = keeper.KeyringReader 20 | ) 21 | -------------------------------------------------------------------------------- /x/curium/client/cli/query.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/cosmos/cosmos-sdk/client" 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | 10 | "github.com/bluzelle/curium/x/curium/types" 11 | ) 12 | 13 | // GetQueryCmd returns the cli query commands for this module 14 | func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { 15 | // Group oracle queries under a subcommand 16 | curiumQueryCmd := &cobra.Command{ 17 | Use: types.ModuleName, 18 | Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), 19 | DisableFlagParsing: true, 20 | SuggestionsMinimumDistance: 2, 21 | RunE: client.ValidateCmd, 22 | } 23 | 24 | return curiumQueryCmd 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /x/curium/client/cli/tx.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/bluzelle/curium/x/curium/types" 8 | "github.com/cosmos/cosmos-sdk/client" 9 | _ "github.com/cosmos/cosmos-sdk/client/context" 10 | "github.com/cosmos/cosmos-sdk/codec" 11 | _ "github.com/cosmos/cosmos-sdk/types" 12 | _ "github.com/cosmos/cosmos-sdk/x/auth" 13 | _ "github.com/cosmos/cosmos-sdk/x/auth/client/utils" 14 | ) 15 | 16 | // GetTxCmd returns the transaction commands for this module 17 | func GetTxCmd(cdc *codec.Codec) *cobra.Command { 18 | curiumTxCmd := &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 | return curiumTxCmd 27 | } 28 | 29 | -------------------------------------------------------------------------------- /x/curium/client/rest/query.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package rest 16 | 17 | import ( 18 | "encoding/hex" 19 | "github.com/cosmos/cosmos-sdk/client/context" 20 | "github.com/cosmos/cosmos-sdk/types/rest" 21 | "io/ioutil" 22 | "net/http" 23 | ) 24 | 25 | 26 | func AbciQueryHandler(cliCtx context.CLIContext) http.HandlerFunc { 27 | 28 | type queryReq struct { 29 | Path string 30 | Data string 31 | } 32 | 33 | return func(w http.ResponseWriter, r *http.Request) { 34 | 35 | cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) 36 | if !ok { 37 | return 38 | } 39 | 40 | var req queryReq 41 | 42 | body, err := ioutil.ReadAll(r.Body) 43 | if err != nil { 44 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 45 | } 46 | 47 | err = cliCtx.Codec.UnmarshalJSON(body, &req) 48 | if err != nil { 49 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 50 | } 51 | 52 | bytes, err := hex.DecodeString(req.Data) 53 | if err != nil { 54 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 55 | } 56 | res, height, err := cliCtx.QueryWithData(req.Path, bytes) 57 | if err != nil { 58 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 59 | } 60 | 61 | cliCtx = cliCtx.WithHeight(height) 62 | rest.PostProcessResponse(w, cliCtx, res) 63 | } 64 | 65 | } 66 | 67 | -------------------------------------------------------------------------------- /x/curium/client/rest/rest.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package rest 16 | 17 | import ( 18 | "github.com/cosmos/cosmos-sdk/client/context" 19 | "github.com/gorilla/mux" 20 | ) 21 | 22 | // RegisterRoutes - Central function to define routes that get registered by the main application 23 | func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { 24 | r.HandleFunc("/abci-query", AbciQueryHandler(cliCtx)).Methods("POST") 25 | } 26 | -------------------------------------------------------------------------------- /x/curium/client/rest/tx.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package rest 16 | 17 | 18 | -------------------------------------------------------------------------------- /x/curium/genesis.go: -------------------------------------------------------------------------------- 1 | package curium 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/curium/keeper" 5 | "github.com/bluzelle/curium/x/curium/types" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // InitGenesis initializes the capability module's state from a provided genesis 10 | // state. 11 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { 12 | // this line is used by starport scaffolding # genesis/module/init 13 | 14 | } 15 | 16 | // ExportGenesis returns the capability module's exported genesis. 17 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { 18 | genesis := types.DefaultGenesisState() 19 | 20 | return genesis 21 | } 22 | -------------------------------------------------------------------------------- /x/curium/handler.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package curium 16 | 17 | import ( 18 | "fmt" 19 | "github.com/bluzelle/curium/x/curium/keeper" 20 | sdk "github.com/cosmos/cosmos-sdk/types" 21 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 22 | ) 23 | 24 | func NewHandler(keeper keeper.Keeper) sdk.Handler { 25 | return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { 26 | switch msg := msg.(type) { 27 | default: 28 | return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, fmt.Sprintf("Unrecognized nft msg type: %v", msg.Type())) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /x/curium/keeper/AccountInfo.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "github.com/bluzelle/curium/app/accountFetcher/types" 5 | ) 6 | 7 | type AccountInfoItem struct { 8 | AccNum uint64 9 | Seq uint64 10 | } 11 | 12 | type AccountInfo struct { 13 | items map[string]AccountInfoItem 14 | acctFetcher types.AccountFetcherFn 15 | } 16 | 17 | func NewAccountInfo(acctFetcher types.AccountFetcherFn) *AccountInfo { 18 | return &AccountInfo{ 19 | acctFetcher: acctFetcher, 20 | items: make(map[string]AccountInfoItem), 21 | } 22 | } 23 | 24 | func (ai *AccountInfo) SetAcctInfo(from string, item AccountInfoItem) *AccountInfo { 25 | ai.items[from] = item 26 | return ai 27 | } 28 | 29 | func (ai *AccountInfo) GetAcctInfo(from string) *AccountInfoItem { 30 | val, ok := ai.items[from] 31 | if ok { 32 | return &val 33 | } 34 | return nil 35 | } 36 | 37 | func (ai *AccountInfo) Next(from string) (*AccountInfoItem, error) { 38 | var err error 39 | if ai.Exists(from) { 40 | ai.IncrementSeq(from) 41 | } else { 42 | info, err := ai.acctFetcher(from) 43 | if err != nil { 44 | return nil, err 45 | } 46 | ai.SetAcctInfo(from, AccountInfoItem{ 47 | AccNum: info.AccNum, 48 | Seq: info.Seq, 49 | }) 50 | } 51 | 52 | return ai.GetAcctInfo(from), err 53 | } 54 | 55 | func (ai *AccountInfo) IncrementSeq(from string) uint64 { 56 | acct := ai.GetAcctInfo(from) 57 | ai.items[from] = AccountInfoItem{ 58 | Seq: acct.Seq + 1, 59 | AccNum: acct.AccNum, 60 | } 61 | return acct.Seq + 1 62 | } 63 | 64 | func (ai *AccountInfo) Exists(from string) bool { 65 | return ai.GetAcctInfo(from) != nil 66 | } 67 | -------------------------------------------------------------------------------- /x/curium/keeper/AccountInfo_test.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestAccountInfo(t *testing.T) { 9 | t.Run("GetAccountInfo()", func(t *testing.T) { 10 | t.Run("should return the account info for a given key", func(t *testing.T) { 11 | info := NewAccountInfo() 12 | info.SetAcctInfo("scott", AccountInfoItem{ 13 | Seq: 1, 14 | AccNum: 3, 15 | }) 16 | assert.Equal(t, uint64(1), info.GetAcctInfo("scott").Seq) 17 | assert.Equal(t, uint64(3), info.GetAcctInfo("scott").AccNum) 18 | }) 19 | 20 | t.Run("should return nil if no account exists", func(t *testing.T) { 21 | info := NewAccountInfo() 22 | assert.Nil(t, info.GetAcctInfo("fake")) 23 | }) 24 | }) 25 | 26 | t.Run("IncrementSeq()", func(t *testing.T) { 27 | t.Run("should increment the sequence number of an account", func(t *testing.T) { 28 | info := NewAccountInfo() 29 | info.SetAcctInfo("scott", AccountInfoItem{ 30 | AccNum: 3, 31 | Seq: 1, 32 | }) 33 | info.IncrementSeq("scott") 34 | assert.Equal(t, uint64(3), info.GetAcctInfo("scott").AccNum) 35 | assert.Equal(t, uint64(2), info.GetAcctInfo("scott").Seq) 36 | }) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /x/curium/keeper/GenesisReader.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/cosmos/cosmos-sdk/client/flags" 6 | "github.com/spf13/viper" 7 | "io/ioutil" 8 | ) 9 | 10 | type genesisData struct { 11 | ChainId string `json:"chain_id"` 12 | } 13 | 14 | func readGenesisJson() (*genesisData, error) { 15 | fileName := viper.GetString(flags.FlagHome) + "/config/genesis.json" 16 | bz, err := ioutil.ReadFile(fileName) 17 | if err != nil { 18 | return nil, err 19 | } 20 | var data = genesisData{} 21 | json.Unmarshal(bz, &data) 22 | return &data, nil 23 | 24 | } 25 | 26 | func readChainId() (string, error) { 27 | g, err := readGenesisJson() 28 | if (err != nil) { 29 | return "", err 30 | } 31 | return g.ChainId, nil 32 | } -------------------------------------------------------------------------------- /x/curium/keeper/MsgBroadcastQueue.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import sdk "github.com/cosmos/cosmos-sdk/types" 4 | 5 | type MsgBroadcastQueueItem struct { 6 | Msgs []sdk.Msg 7 | From string 8 | RetryCount int 9 | Resp chan *MsgBroadcasterResponse 10 | } 11 | 12 | func (mqi *MsgBroadcastQueueItem) IncrementRetry() { 13 | mqi.RetryCount = mqi.RetryCount + 1 14 | } 15 | 16 | type MsgBroadcastQueue struct { 17 | queue []*MsgBroadcastQueueItem 18 | } 19 | 20 | func NewMsgBroadcastQueue() *MsgBroadcastQueue { 21 | return &MsgBroadcastQueue{} 22 | } 23 | 24 | func (mq *MsgBroadcastQueue) Push(item *MsgBroadcastQueueItem) { 25 | mq.queue = append(mq.queue, item) 26 | } 27 | 28 | func (mq *MsgBroadcastQueue) Size() int { 29 | return len(mq.queue) 30 | } 31 | 32 | func (mq *MsgBroadcastQueue) Pop() *MsgBroadcastQueueItem { 33 | if !mq.IsEmpty() { 34 | item := mq.queue[0] 35 | mq.queue = mq.queue[1:] 36 | return item 37 | } 38 | return nil 39 | } 40 | 41 | func (mq *MsgBroadcastQueue) IsEmpty() bool { 42 | return len(mq.queue) == 0 43 | } -------------------------------------------------------------------------------- /x/curium/keeper/MsgBroadcastQueue_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/curium/keeper" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestMsgBroadcastQueue(t *testing.T) { 10 | t.Run("IsEmpty()", func(t *testing.T) { 11 | t.Run("should return true if queue is empty", func(t *testing.T) { 12 | assert.True(t, keeper.NewMsgBroadcastQueue().IsEmpty()) 13 | }) 14 | }) 15 | 16 | t.Run("Push()", func(t *testing.T) { 17 | t.Run("should add an item to the queue", func(t *testing.T) { 18 | queue := keeper.NewMsgBroadcastQueue() 19 | queue.Push(&keeper.MsgBroadcastQueueItem{}) 20 | assert.Equal(t, 1, queue.Size()) 21 | queue.Push(&keeper.MsgBroadcastQueueItem{}) 22 | assert.Equal(t, 2, queue.Size()) 23 | }) 24 | }) 25 | 26 | t.Run("Pop()", func(t *testing.T) { 27 | t.Run("should remove the first item from the queue", func(t *testing.T) { 28 | queue := keeper.NewMsgBroadcastQueue() 29 | queue.Push(&keeper.MsgBroadcastQueueItem{ 30 | From: "item1", 31 | }) 32 | queue.Push(&keeper.MsgBroadcastQueueItem{ 33 | From: "item2", 34 | }) 35 | queue.Push(&keeper.MsgBroadcastQueueItem{ 36 | From: "item3", 37 | }) 38 | assert.Equal(t, "item1", queue.Pop().From) 39 | assert.Equal(t, "item2", queue.Pop().From) 40 | assert.Equal(t, "item3", queue.Pop().From) 41 | assert.Nil(t, queue.Pop()) 42 | }) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /x/curium/keeper/querier.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | // this line is used by starport scaffolding # 1 5 | abci "github.com/tendermint/tendermint/abci/types" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 9 | ) 10 | 11 | // NewQuerier creates a new querier for curium clients. 12 | func NewQuerier(k Keeper) sdk.Querier { 13 | return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { 14 | switch path[0] { 15 | default: 16 | return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown curium query endpoint") 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /x/curium/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | ) 6 | 7 | // RegisterCodec registers concrete types on codec 8 | func RegisterCodec(cdc *codec.Codec) { 9 | 10 | } 11 | 12 | // ModuleCdc defines the module codec 13 | var ModuleCdc *codec.Codec 14 | 15 | func init() { 16 | ModuleCdc = codec.New() 17 | RegisterCodec(ModuleCdc) 18 | codec.RegisterCrypto(ModuleCdc) 19 | ModuleCdc.Seal() 20 | } 21 | -------------------------------------------------------------------------------- /x/curium/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type GenesisState struct { 4 | } 5 | 6 | // DefaultIndex is the default capability global index 7 | const DefaultIndex uint64 = 1 8 | 9 | // DefaultGenesisState returns the default Capability genesis state 10 | func DefaultGenesisState() *GenesisState { 11 | return &GenesisState{ 12 | } 13 | } 14 | 15 | // ValidateGenesis performs basic genesis state validation returning an error upon any 16 | // failure. 17 | func (gs GenesisState) ValidateGenesis() error { 18 | 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /x/curium/types/keys.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName defines the module name 5 | ModuleName = "curium" 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_capability" 18 | ) 19 | -------------------------------------------------------------------------------- /x/curium/types/types.go: -------------------------------------------------------------------------------- 1 | package types -------------------------------------------------------------------------------- /x/nft/alias.go: -------------------------------------------------------------------------------- 1 | package nft 2 | 3 | import ( 4 | keeper "github.com/bluzelle/curium/x/nft/keeper" 5 | types "github.com/bluzelle/curium/x/nft/types" 6 | ) 7 | 8 | var ( 9 | ModuleName = types.ModuleName 10 | StoreKey = types.StoreKey 11 | MemStoreKey = types.MemStoreKey 12 | NewKeeper = keeper.NewKeeper 13 | 14 | ) 15 | type ( 16 | Keeper = keeper.Keeper 17 | ) 18 | -------------------------------------------------------------------------------- /x/nft/client/cli/query.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/cosmos/cosmos-sdk/client" 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | 10 | "github.com/bluzelle/curium/x/oracle/types" 11 | ) 12 | 13 | // GetQueryCmd returns the cli query commands for this module 14 | func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { 15 | // Group oracle queries under a subcommand 16 | nftQueryCmd := &cobra.Command{ 17 | Use: types.ModuleName, 18 | Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), 19 | DisableFlagParsing: true, 20 | SuggestionsMinimumDistance: 2, 21 | RunE: client.ValidateCmd, 22 | } 23 | 24 | return nftQueryCmd 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /x/nft/client/cli/tx.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/bluzelle/curium/x/oracle/types" 8 | "github.com/cosmos/cosmos-sdk/client" 9 | _ "github.com/cosmos/cosmos-sdk/client/context" 10 | "github.com/cosmos/cosmos-sdk/codec" 11 | _ "github.com/cosmos/cosmos-sdk/types" 12 | _ "github.com/cosmos/cosmos-sdk/x/auth" 13 | _ "github.com/cosmos/cosmos-sdk/x/auth/client/utils" 14 | ) 15 | 16 | // GetTxCmd returns the transaction commands for this module 17 | func GetTxCmd(cdc *codec.Codec) *cobra.Command { 18 | nftTxCmd := &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 | return nftTxCmd 27 | } 28 | 29 | -------------------------------------------------------------------------------- /x/nft/client/rest/getNftByVendorHandler.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/cosmos/cosmos-sdk/client/context" 6 | "github.com/cosmos/cosmos-sdk/types/rest" 7 | "github.com/gorilla/mux" 8 | "io" 9 | "io/ioutil" 10 | "net/http" 11 | "os" 12 | 13 | ) 14 | 15 | func getNftByVendorHandler(clientCtx context.CLIContext) http.HandlerFunc { 16 | return func(w http.ResponseWriter, r *http.Request) { 17 | id := mux.Vars(r)["id"] 18 | vendor := mux.Vars(r)["vendor"] 19 | nftDirRes, _, err := clientCtx.Query("custom/nft/get-nft-dir") 20 | 21 | nftDir := string(nftDirRes) 22 | filename := vendor + "-" + id 23 | 24 | location := nftDir + "/" + filename 25 | infoLocation := nftDir + "/" + filename + ".info" 26 | if _, err := os.Stat(location); os.IsNotExist(err) { 27 | rest.WriteErrorResponse(w, http.StatusNotFound, "File does not exist") 28 | return 29 | } 30 | 31 | if _, err := os.Stat(infoLocation); os.IsNotExist(err) { 32 | rest.WriteErrorResponse(w, http.StatusNotFound, "File information does not exist") 33 | return 34 | } 35 | 36 | 37 | var info map[string]interface{} 38 | 39 | infoBytes, err := ioutil.ReadFile(infoLocation) 40 | if err != nil { 41 | rest.WriteErrorResponse(w, http.StatusNotFound, "Unable to read info file") 42 | } 43 | 44 | json.Unmarshal(infoBytes, &info) 45 | 46 | w.Header().Set("content-type", info["Mime"].(string)) 47 | 48 | reader, err := os.Open(location) 49 | if err != nil { 50 | rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) 51 | return 52 | } 53 | io.Copy(w, reader) 54 | } 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /x/nft/client/rest/getNftHandler.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/cosmos/cosmos-sdk/client/context" 6 | "github.com/cosmos/cosmos-sdk/types/rest" 7 | "github.com/gorilla/mux" 8 | "io" 9 | "io/ioutil" 10 | "net/http" 11 | "os" 12 | ) 13 | func getNftHandler(clientCtx context.CLIContext) http.HandlerFunc { 14 | return func(w http.ResponseWriter, r *http.Request) { 15 | hash := mux.Vars(r)["hash"] 16 | 17 | nftDirRes, _, err := clientCtx.Query("custom/nft/get-nft-dir") 18 | 19 | nftDir := string(nftDirRes) 20 | 21 | location := nftDir + "/" + hash 22 | infoLocation := nftDir + "/" + hash + ".info" 23 | if _, err := os.Stat(location); os.IsNotExist(err) { 24 | rest.WriteErrorResponse(w, http.StatusNotFound, hash) 25 | rest.WriteErrorResponse(w, http.StatusNotFound, "File does not exist") 26 | return 27 | } 28 | 29 | if _, err := os.Stat(infoLocation); os.IsNotExist(err) { 30 | rest.WriteErrorResponse(w, http.StatusNotFound, "File information does not exist") 31 | return 32 | } 33 | 34 | 35 | var info map[string]interface{} 36 | 37 | infoBytes, err := ioutil.ReadFile(infoLocation) 38 | if err != nil { 39 | rest.WriteErrorResponse(w, http.StatusNotFound, "Unable to read info file") 40 | } 41 | 42 | json.Unmarshal(infoBytes, &info) 43 | 44 | w.Header().Set("content-type", info["Mime"].(string)) 45 | 46 | reader, err := os.Open(location) 47 | if err != nil { 48 | rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) 49 | return 50 | } 51 | io.Copy(w, reader) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /x/nft/client/rest/query.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package rest 16 | 17 | import ( 18 | "encoding/hex" 19 | "github.com/cosmos/cosmos-sdk/client/context" 20 | "github.com/cosmos/cosmos-sdk/types/rest" 21 | "io/ioutil" 22 | "net/http" 23 | ) 24 | 25 | 26 | func AbciQueryHandler(cliCtx context.CLIContext) http.HandlerFunc { 27 | 28 | type queryReq struct { 29 | Path string 30 | Data string 31 | } 32 | 33 | return func(w http.ResponseWriter, r *http.Request) { 34 | 35 | cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) 36 | if !ok { 37 | return 38 | } 39 | 40 | var req queryReq 41 | 42 | body, err := ioutil.ReadAll(r.Body) 43 | if err != nil { 44 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 45 | } 46 | 47 | err = cliCtx.Codec.UnmarshalJSON(body, &req) 48 | if err != nil { 49 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 50 | } 51 | 52 | bytes, err := hex.DecodeString(req.Data) 53 | if err != nil { 54 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 55 | } 56 | res, height, err := cliCtx.QueryWithData(req.Path, bytes) 57 | if err != nil { 58 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 59 | } 60 | 61 | cliCtx = cliCtx.WithHeight(height) 62 | rest.PostProcessResponse(w, cliCtx, res) 63 | } 64 | 65 | } 66 | 67 | -------------------------------------------------------------------------------- /x/nft/client/rest/rest.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package rest 16 | 17 | import ( 18 | "github.com/cosmos/cosmos-sdk/client/context" 19 | "github.com/gorilla/mux" 20 | ) 21 | 22 | // RegisterRoutes - Central function to define routes that get registered by the main application 23 | func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { 24 | r.HandleFunc("/abci-query", AbciQueryHandler(cliCtx)).Methods("POST") 25 | r.HandleFunc("/nft/upload/{vendor}/{hash}/{chunkNum}", uploadNftHandler(cliCtx)).Methods("POST") 26 | r.HandleFunc("/nft/{hash}", getNftHandler(cliCtx)).Methods("GET") 27 | r.HandleFunc("/nft/{vendor}/{id}", getNftByVendorHandler(cliCtx)).Methods("GET") 28 | 29 | } 30 | -------------------------------------------------------------------------------- /x/nft/client/rest/tx.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Bluzelle 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License, version 3, 5 | // as published by the Free Software Foundation. 6 | // 7 | // This program is distributed in the hope that it will be useful, 8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | // GNU Affero General Public License for more details. 11 | // 12 | // You should have received a copy of the GNU Affero General Public License 13 | // along with this program. If not, see . 14 | 15 | package rest 16 | 17 | 18 | -------------------------------------------------------------------------------- /x/nft/client/rest/uploadNftHandler.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bluzelle/curium/x/nft/keeper" 6 | "github.com/bluzelle/curium/x/nft/types" 7 | "github.com/cosmos/cosmos-sdk/client/context" 8 | "github.com/cosmos/cosmos-sdk/types/rest" 9 | "github.com/ethereum/go-ethereum/common/math" 10 | "github.com/gorilla/mux" 11 | "io" 12 | "net/http" 13 | "os" 14 | "strings" 15 | ) 16 | func uploadNftHandler(clientCtx context.CLIContext) http.HandlerFunc { 17 | return func(w http.ResponseWriter, r *http.Request) { 18 | hash := mux.Vars(r)["hash"] 19 | chunkNum := mux.Vars(r)["chunkNum"] 20 | vendor := mux.Vars(r)["vendor"] 21 | auth := r.Header.Get("Authorization") 22 | 23 | token := strings.Split(auth, " ")[1] 24 | 25 | bz, _, err := clientCtx.QueryWithData("custom/nft/is-auth-valid", []byte(token)) 26 | var isValid bool 27 | clientCtx.Codec.MustUnmarshalJSON(bz, &isValid) 28 | 29 | if err != nil { 30 | rest.WriteErrorResponse(w, http.StatusBadRequest, "unable to read chunk number") 31 | return 32 | } 33 | 34 | if !isValid { 35 | rest.WriteErrorResponse(w, http.StatusForbidden, "auth invalid") 36 | return 37 | } 38 | 39 | 40 | uploadDirRes, _, err := clientCtx.Query("custom/nft/get-nft-upload-dir") 41 | 42 | uploadDir := string(uploadDirRes) 43 | 44 | uploadPath := uploadDir + "/" + vendor 45 | 46 | os.MkdirAll(uploadPath, os.ModePerm) 47 | 48 | chunkInt, ok := math.ParseUint64(chunkNum) 49 | if !ok { 50 | rest.WriteErrorResponse(w, http.StatusBadRequest, "unable to read chunk number") 51 | return 52 | } 53 | filename := fmt.Sprintf("%s/%s-%04d", uploadPath, hash, chunkInt) 54 | fileWriter, err := os.Create(filename) 55 | defer fileWriter.Close() 56 | if err != nil { 57 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 58 | return 59 | } 60 | io.Copy(fileWriter, r.Body) 61 | w.Write([]byte("ok")) 62 | 63 | checkUploadCompleteReq := types.QueryCheckUploadCompleteReq{ 64 | Hash: hash, 65 | Size: uint64(r.ContentLength), 66 | } 67 | 68 | bz, err = clientCtx.Codec.MarshalBinaryBare(&checkUploadCompleteReq) 69 | if err != nil { 70 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 71 | return 72 | } 73 | 74 | bz, _, err = clientCtx.QueryWithData("custom/nft/" + keeper.QueryCheckUploadComplete, bz) 75 | 76 | if err != nil { 77 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 78 | return 79 | } 80 | } 81 | } 82 | 83 | 84 | -------------------------------------------------------------------------------- /x/nft/demo/demo.ts: -------------------------------------------------------------------------------- 1 | import {bluzelle, uploadNft} from "bluzelle"; 2 | import {sha256} from "js-sha256"; 3 | import {readFile} from 'fs/promises' 4 | 5 | process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = '0'; 6 | 7 | readFile(`${__dirname}/image.jpg`) 8 | .then(data => ({ 9 | bz: bluzelle({ 10 | mnemonic: "mnemonic provided by bluzelle", 11 | endpoint: 'https://client.sentry.bluzellenet.bluzelle.com:1317', 12 | uuid: '' 13 | }), 14 | data, 15 | id: 'any-id-1' 16 | })) 17 | .then(ctx => ({...ctx, hash: sha256(ctx.data)})) 18 | .then(ctx => 19 | ctx.bz.createNft({ 20 | id: ctx.id, 21 | hash: ctx.hash, 22 | vendor: 'provided_by_bluzelle', // replace with your id provided to you by Bluzelle 23 | userId: 'user-id', 24 | mime: 'image/jpeg', 25 | meta: 'meta', 26 | size: ctx.data.byteLength, 27 | gasInfo: {max_gas: 100_000_000, gas_price: 0.002} 28 | }) 29 | .then(resp => ({...ctx, token: resp.token})) 30 | ) 31 | .then( 32 | ctx => uploadNft(ctx.bz.url, ctx.data, ctx.token, 'provided_by_bluzelle') 33 | .then(() => ctx) 34 | ).then(ctx => console.log('HASH:',ctx.hash)) 35 | .catch(e => console.log('ERROR:', e)) 36 | 37 | /************************************* 38 | * To use this code 39 | * 40 | * 1) obtain a mnemonic from Bluzelle and insert into the mnemonic field above 41 | * 3) run this code 42 | * 4) open https://client.sentry.testnet.public.bluzelle.com:1317/nft/customer/any-id-3 in a browser to see the result 43 | * 5) to verify replication go to https://[x].client.sentry.bluzellenet.bluzelle.com:1317/nft/[customer]/any-id-1 44 | * replacing x with 'a', 'b' or 'c' 45 | * 46 | * Feel free to change any of the fields above, especially the content of the file (the 'data' field), the id or the 'vendor'. 47 | * The max size of the data field is 100MB. 48 | */ -------------------------------------------------------------------------------- /x/nft/demo/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluzelle/curium/78dfebcb9eec9445edbf40af11c4239a374c2ce1/x/nft/demo/image.jpg -------------------------------------------------------------------------------- /x/nft/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "@types/node": "^16.7.6", 8 | "bluzelle": "^3.0.9", 9 | "js-sha256": "^0.9.0", 10 | "ts-node": "^10.2.1", 11 | "typescript": "^4.4.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /x/nft/genesis.go: -------------------------------------------------------------------------------- 1 | package nft 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/nft/keeper" 5 | "github.com/bluzelle/curium/x/nft/types" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // InitGenesis initializes the capability module's state from a provided genesis 10 | // state. 11 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { 12 | // this line is used by starport scaffolding # genesis/module/init 13 | // Set all the nft 14 | for _, elem := range genState.NftList { 15 | k.SetNft(ctx, *elem) 16 | } 17 | 18 | // this line is used by starport scaffolding # ibc/genesis/init 19 | } 20 | 21 | // ExportGenesis returns the capability module's exported genesis. 22 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { 23 | genesis := types.DefaultGenesisState() 24 | 25 | // this line is used by starport scaffolding # genesis/module/export 26 | // Get all nft 27 | nftList := k.GetAllNft(ctx) 28 | for _, elem := range nftList { 29 | elem := elem 30 | genesis.NftList = append(genesis.NftList, &elem) 31 | } 32 | 33 | // this line is used by starport scaffolding # ibc/genesis/export 34 | 35 | return genesis 36 | } 37 | -------------------------------------------------------------------------------- /x/nft/keeper/createNft.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "fmt" 5 | "github.com/anacrolix/torrent/metainfo" 6 | "github.com/bluzelle/curium/x/nft/types" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "github.com/zeebo/bencode" 9 | "io/ioutil" 10 | "os" 11 | "path/filepath" 12 | "regexp" 13 | ) 14 | 15 | 16 | 17 | func (k Keeper) SeedFile(metainfo *metainfo.MetaInfo) error { 18 | err := k.GetBtClient().SeedFile(metainfo) 19 | if err != nil { 20 | return err 21 | } 22 | return nil 23 | } 24 | 25 | func (k Keeper) BroadcastPublishFile(ctx sdk.Context, id string, vendor string, userId string, hash string, mime string, metainfo *metainfo.MetaInfo) error{ 26 | metaBytes, err := bencode.EncodeBytes(metainfo) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | addr, err := k.KeyringReader.GetAddress(k.NftUsername) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | publishMsg := types.MsgPublishFile{ 37 | Creator: addr.String(), 38 | Id: id, 39 | Vendor: vendor, 40 | Metainfo: metaBytes, 41 | } 42 | 43 | result := <- k.MsgBroadcaster([]sdk.Msg{&publishMsg}, k.NftUsername) 44 | if result.Error != nil { 45 | return result.Error 46 | } 47 | return nil 48 | 49 | } 50 | 51 | 52 | func (k Keeper) AssembleNftFile(uploadDir string, nftDir string, hash string) error { 53 | 54 | uploadRegEx, err := regexp.Compile(fmt.Sprintf("^%s-", hash)) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | // Delete upload file if already exists in nft dir and stop 60 | _, err = os.Stat(nftDir + "/" + hash) 61 | 62 | if err == nil { 63 | walkError := filepath.Walk(uploadDir, func(path string, info os.FileInfo, err error) error { 64 | if err == nil && uploadRegEx.MatchString(info.Name()) { 65 | err = os.Remove(path) 66 | if err != nil { 67 | return err 68 | } 69 | } 70 | return nil 71 | }) 72 | 73 | if walkError != nil { 74 | return walkError 75 | } 76 | 77 | return nil 78 | } 79 | 80 | err = filepath.Walk(uploadDir, func(path string, info os.FileInfo, err error) error { 81 | if err == nil && uploadRegEx.MatchString(info.Name()) { 82 | fmt.Println(path) 83 | if path != uploadDir { 84 | data, err := ioutil.ReadFile(path) 85 | if err != nil { 86 | return err 87 | } 88 | err = os.MkdirAll(nftDir, 0755) 89 | if err != nil { 90 | return err 91 | } 92 | f, err := os.OpenFile(nftDir+"/"+hash, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0744) 93 | if err != nil { 94 | return err 95 | } 96 | defer f.Close() 97 | _, err = f.Write(data) 98 | if err != nil { 99 | return err 100 | } 101 | err = os.Remove(path) 102 | if err != nil { 103 | return err 104 | } 105 | } 106 | } 107 | return nil 108 | }) 109 | if err != nil { 110 | return err 111 | } 112 | return nil 113 | } -------------------------------------------------------------------------------- /x/nft/keeper/nft.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/nft/types" 5 | "github.com/cosmos/cosmos-sdk/store/prefix" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // AppendNft appends a nft in the store with a new id and update the count 10 | func (k Keeper) AppendNft( 11 | ctx sdk.Context, 12 | creator string, 13 | id string, 14 | hash string, 15 | vendor string, 16 | userId string, 17 | meta string, 18 | mime string, 19 | size uint64, 20 | ) { 21 | // Create the nft 22 | var nft = types.Nft{ 23 | Creator: creator, 24 | Id: id, 25 | Vendor: vendor, 26 | UserId: userId, 27 | Hash: hash, 28 | Meta: meta, 29 | Mime: mime, 30 | Size: size, 31 | } 32 | 33 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.NftKey)) 34 | value := k.Cdc.MustMarshalBinaryBare(&nft) 35 | store.Set(GetNftHashBytes(nft.Hash), value) 36 | } 37 | 38 | // SetNft set a specific nft in the store 39 | func (k Keeper) SetNft(ctx sdk.Context, nft types.Nft) { 40 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.NftKey)) 41 | b := k.Cdc.MustMarshalBinaryBare(&nft) 42 | store.Set(GetNftHashBytes(nft.Hash), b) 43 | } 44 | 45 | func (k Keeper) GetNft(ctx sdk.Context, hash string) types.Nft { 46 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.NftKey)) 47 | var nft types.Nft 48 | k.Cdc.MustUnmarshalBinaryBare(store.Get(GetNftHashBytes(hash)), &nft) 49 | return nft 50 | } 51 | 52 | // HasNft checks if the nft exists in the store 53 | func (k Keeper) HasNft(ctx sdk.Context, hash string) bool { 54 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.NftKey)) 55 | return store.Has(GetNftHashBytes(hash)) 56 | } 57 | 58 | // GetNftOwner returns the creator of the nft 59 | func (k Keeper) GetNftOwner(ctx sdk.Context, hash string) string { 60 | return k.GetNft(ctx, hash).Creator 61 | } 62 | 63 | // RemoveNft removes a nft from the store 64 | func (k Keeper) RemoveNft(ctx sdk.Context, hash string) { 65 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.NftKey)) 66 | store.Delete(GetNftHashBytes(hash)) 67 | } 68 | 69 | // GetAllNft returns all nft 70 | func (k Keeper) GetAllNft(ctx sdk.Context) (list []types.Nft) { 71 | store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.NftKey)) 72 | iterator := sdk.KVStorePrefixIterator(store, []byte{}) 73 | 74 | defer iterator.Close() 75 | 76 | for ; iterator.Valid(); iterator.Next() { 77 | var val types.Nft 78 | k.Cdc.MustUnmarshalBinaryBare(iterator.Value(), &val) 79 | list = append(list, val) 80 | } 81 | 82 | return 83 | } 84 | 85 | func GetNftHashBytes(hash string) []byte { 86 | return []byte(hash) 87 | } 88 | 89 | -------------------------------------------------------------------------------- /x/nft/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | ) 6 | 7 | // RegisterCodec registers concrete types on codec 8 | func RegisterCodec(cdc *codec.Codec) { 9 | // this line is used by starport scaffolding # 1 10 | cdc.RegisterConcrete(&MsgCreateNft{}, "nft/CreateNft", nil) 11 | cdc.RegisterConcrete(&MsgPublishFile{}, "nft/PublishFile", nil) 12 | cdc.RegisterConcrete(&MsgRegisterPeer{}, "nft/RegisterPeer", nil) 13 | } 14 | 15 | // ModuleCdc defines the module codec 16 | var ModuleCdc *codec.Codec 17 | 18 | func init() { 19 | ModuleCdc = codec.New() 20 | RegisterCodec(ModuleCdc) 21 | codec.RegisterCrypto(ModuleCdc) 22 | ModuleCdc.Seal() 23 | } 24 | -------------------------------------------------------------------------------- /x/nft/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "fmt" 4 | 5 | type GenesisState struct { 6 | NftList []*Nft 7 | } 8 | 9 | // DefaultIndex is the default capability global index 10 | const DefaultIndex uint64 = 1 11 | 12 | // DefaultGenesisState returns the default Capability genesis state 13 | func DefaultGenesisState() *GenesisState { 14 | return &GenesisState{ 15 | // this line is used by starport scaffolding # ibc/genesistype/default 16 | // this line is used by starport scaffolding # genesis/types/default 17 | NftList: []*Nft{}, 18 | } 19 | } 20 | 21 | // ValidateGenesis performs basic genesis state validation returning an error upon any 22 | // failure. 23 | func (gs GenesisState) ValidateGenesis() error { 24 | 25 | // this line is used by starport scaffolding # genesis/types/validate 26 | // Check for duplicated ID in nft 27 | nftIdMap := make(map[string]bool) 28 | 29 | for _, elem := range gs.NftList { 30 | if _, ok := nftIdMap[elem.Id]; ok { 31 | return fmt.Errorf("duplicated id for nft") 32 | } 33 | nftIdMap[elem.Id] = true 34 | } 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /x/nft/types/keys.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName defines the module name 5 | ModuleName = "nft" 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_capability" 18 | 19 | // this line is used by starport scaffolding # ibc/keys/name 20 | ) 21 | 22 | // this line is used by starport scaffolding # ibc/keys/port 23 | 24 | func KeyPrefix(p string) []byte { 25 | return []byte(p) 26 | } 27 | 28 | const ( 29 | NftKey = "Nft-" 30 | FileKey = "file-" 31 | PeerKey = "peer-" 32 | ) 33 | -------------------------------------------------------------------------------- /x/nft/types/msgCreateNft.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 UPLOAD_MAX_SIZE = uint64(150) 9 | 10 | 11 | type MsgCreateNft struct { 12 | Id string 13 | Hash string 14 | Vendor string 15 | UserId string 16 | Creator string 17 | Mime string 18 | Meta string 19 | Size uint64 20 | } 21 | 22 | type MsgCreateNftResponse struct { 23 | Id string `json:"id"` 24 | Token string `json:"token"` 25 | } 26 | 27 | 28 | func (msg *MsgCreateNft) Route() string { 29 | return RouterKey 30 | } 31 | 32 | func (msg *MsgCreateNft) Type() string { 33 | return "CreateNft" 34 | } 35 | 36 | func (msg *MsgCreateNft) GetSigners() []sdk.AccAddress { 37 | creator, err := sdk.AccAddressFromBech32(msg.Creator) 38 | if err != nil { 39 | panic(err) 40 | } 41 | return []sdk.AccAddress{creator} 42 | } 43 | 44 | func (msg *MsgCreateNft) GetSignBytes() []byte { 45 | bz := ModuleCdc.MustMarshalJSON(msg) 46 | return sdk.MustSortJSON(bz) 47 | } 48 | 49 | func (msg *MsgCreateNft) ValidateBasic() error { 50 | _, err := sdk.AccAddressFromBech32(msg.Creator) 51 | if err != nil { 52 | return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) 53 | } 54 | 55 | if msg.Size > UPLOAD_MAX_SIZE * 1024 * 1024 { 56 | return sdkerrors.New("nft", 2, "upload too large") 57 | } 58 | 59 | return nil 60 | } -------------------------------------------------------------------------------- /x/nft/types/msgPublishFile.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/anacrolix/torrent/metainfo" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 7 | "github.com/zeebo/bencode" 8 | ) 9 | 10 | type MsgPublishFile struct { 11 | Creator string `json:"creator,omitempty"` 12 | Id string `json:"id,omitempty"` 13 | Vendor string `json:"vendor,omitempty"` 14 | Metainfo []byte `json:"metainfo,omitempty"` 15 | } 16 | 17 | type MsgPublishFileResponse struct { 18 | 19 | } 20 | 21 | func (msg *MsgPublishFile) Route() string { 22 | return RouterKey 23 | } 24 | 25 | func (msg *MsgPublishFile) Type() string { 26 | return "PublishFile" 27 | } 28 | 29 | func (msg *MsgPublishFile) GetSigners() []sdk.AccAddress { 30 | creator, err := sdk.AccAddressFromBech32(msg.Creator) 31 | if err != nil { 32 | panic(err) 33 | } 34 | return []sdk.AccAddress{creator} 35 | } 36 | 37 | func (msg *MsgPublishFile) GetSignBytes() []byte { 38 | bz := ModuleCdc.MustMarshalJSON(msg) 39 | return sdk.MustSortJSON(bz) 40 | } 41 | 42 | func (msg *MsgPublishFile) ValidateBasic() error { 43 | _, err := sdk.AccAddressFromBech32(msg.Creator) 44 | if err != nil { 45 | return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) 46 | } 47 | 48 | var metaInfo metainfo.MetaInfo 49 | err = bencode.DecodeBytes(msg.Metainfo, &metaInfo) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | if len(metaInfo.Announce) > 0 || 55 | metaInfo.AnnounceList != nil || 56 | metaInfo.Nodes != nil || 57 | metaInfo.UrlList != nil { 58 | return sdkerrors.New("nft", 2, "Invalid torrent metainfo in publish") 59 | } 60 | 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /x/nft/types/msgRegisterPeer.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 | type MsgRegisterPeer struct { 9 | Creator string `json:"creator,omitempty"` 10 | Id string `json:"id,omitempty"` 11 | Address string `json:"address,omitempty"` 12 | Port uint64 `json:"port,omitempty"` 13 | } 14 | 15 | type MsgRegisterPeerResponse struct { 16 | 17 | } 18 | 19 | func (msg *MsgRegisterPeer) Route() string { 20 | return RouterKey 21 | } 22 | 23 | func (msg *MsgRegisterPeer) Type() string { 24 | return "RegisterPeer" 25 | } 26 | 27 | func (msg *MsgRegisterPeer) GetSigners() []sdk.AccAddress { 28 | creator, err := sdk.AccAddressFromBech32(msg.Creator) 29 | if err != nil { 30 | panic(err) 31 | } 32 | return []sdk.AccAddress{creator} 33 | } 34 | 35 | func (msg *MsgRegisterPeer) GetSignBytes() []byte { 36 | bz := ModuleCdc.MustMarshalJSON(msg) 37 | return sdk.MustSortJSON(bz) 38 | } 39 | 40 | func (msg *MsgRegisterPeer) ValidateBasic() error { 41 | _, err := sdk.AccAddressFromBech32(msg.Creator) 42 | if err != nil { 43 | return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /x/nft/types/nft.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type Nft struct { 4 | Creator string 5 | Id string 6 | Vendor string 7 | UserId string 8 | Hash string 9 | Mime string 10 | Meta string 11 | Size uint64 12 | } 13 | 14 | type NftInfo struct { 15 | Id string 16 | Vendor string 17 | UserId string 18 | Mime string 19 | Size uint64 20 | } 21 | -------------------------------------------------------------------------------- /x/nft/types/peer.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type Peer struct { 4 | Id string `json:"id,omitempty"` 5 | Address string `json:"address,omitempty"` 6 | Port uint64 `json:"port,omitempty"` 7 | } 8 | -------------------------------------------------------------------------------- /x/nft/types/querier.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type QueryResultGetNft struct { 4 | Nft *Nft `json:"Nft,omitempty"` 5 | } 6 | 7 | type QueryCheckUploadCompleteReq struct { 8 | Hash string `json:"hash"` 9 | Size uint64 `json:"size"` 10 | } 11 | 12 | -------------------------------------------------------------------------------- /x/nft/types/uploadTokenManager.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/types" 5 | "sync" 6 | ) 7 | 8 | type UploadToken struct { 9 | Value string 10 | Hash string 11 | Size uint64 12 | Uploaded uint64 13 | Owner types.AccAddress 14 | ExpireBlock int64 15 | } 16 | 17 | type UploadTokenManager struct { 18 | Tokens sync.Map 19 | } 20 | 21 | 22 | 23 | func NewUploadTokenManager() *UploadTokenManager { 24 | return &UploadTokenManager{} 25 | } 26 | 27 | func (tt *UploadTokenManager) ExpireTokens(block int64) []*UploadToken { 28 | var expiredTokens []*UploadToken 29 | tt.Tokens.Range(func (hash interface{}, token interface{}) bool { 30 | if token.(*UploadToken).ExpireBlock <= block { 31 | tt.Tokens.Delete(hash) 32 | expiredTokens = append(expiredTokens, token.(*UploadToken)) 33 | } 34 | return true 35 | }) 36 | return expiredTokens 37 | } 38 | 39 | func (tt *UploadTokenManager) InvalidateToken(hash string) { 40 | tt.Tokens.Delete(hash) 41 | } 42 | 43 | func (tt *UploadTokenManager) ReportUpload(hash string, size uint64) { 44 | token := tt.GetToken(hash) 45 | if token != nil { 46 | token.Uploaded = token.Uploaded + size 47 | } 48 | } 49 | 50 | func (tt *UploadTokenManager) IsUploadComplete(hash string) bool { 51 | token := tt.GetToken(hash) 52 | if token == nil { 53 | return true 54 | } 55 | if token.Uploaded >= token.Size { 56 | tt.InvalidateToken(hash) 57 | return true 58 | } 59 | return false 60 | } 61 | 62 | func (tt *UploadTokenManager) GetToken(hash string) *UploadToken { 63 | token, ok := tt.Tokens.Load(hash) 64 | if ok { 65 | return token.(*UploadToken) 66 | } 67 | return nil 68 | } 69 | 70 | func (tt *UploadTokenManager) IsTokenValid(hash string) bool { 71 | return tt.GetToken(hash) != nil 72 | } 73 | 74 | func (tt *UploadTokenManager) NewUploadToken(hash string, size uint64, owner types.AccAddress, expire int64) *UploadToken { 75 | token := &UploadToken{ 76 | Value: hash, 77 | Hash: hash, 78 | Size: size, 79 | Owner: owner, 80 | ExpireBlock: expire, 81 | } 82 | tt.Tokens.Store(hash, token) 83 | return token 84 | } 85 | -------------------------------------------------------------------------------- /x/nft/types/uploadTokenManager_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func Test(t *testing.T) { 9 | t.Run("NewUploadToken()", func(t *testing.T) { 10 | t.Run("Should create a new token with a value", func(t *testing.T) { 11 | manager := NewUploadTokenManager() 12 | token := manager.NewUploadToken("my-hash", uint64(1000), nil, int64(1)) 13 | assert.Equal(t, 48, len(token.Value)) 14 | assert.Equal(t, "my-hash", token.Hash) 15 | assert.Equal(t, uint64(1000), token.Size) 16 | }) 17 | }) 18 | 19 | t.Run("Should expire tokens given a block greater than the expiry", func(t *testing.T) { 20 | manager := NewUploadTokenManager() 21 | manager.NewUploadToken("hash1", 1000, nil, int64(5)) 22 | manager.NewUploadToken("hash2", 1000, nil, int64(10)) 23 | 24 | manager.ExpireTokens(4) 25 | assert.True(t, manager.IsTokenValid("hash1")) 26 | assert.True(t, manager.IsTokenValid("hash2")) 27 | 28 | manager.ExpireTokens(5) 29 | assert.False(t, manager.IsTokenValid("hash1")) 30 | assert.True(t, manager.IsTokenValid("hash2")) 31 | 32 | manager.ExpireTokens(11) 33 | assert.False(t, manager.IsTokenValid("hash1")) 34 | assert.False(t, manager.IsTokenValid("hash2")) 35 | }) 36 | 37 | t.Run("InvalidateTokens()", func(t *testing.T) { 38 | t.Run("Should invalidate a token", func(t *testing.T) { 39 | manager := NewUploadTokenManager() 40 | manager.NewUploadToken("hash1", 1000, nil, int64(5)) 41 | manager.NewUploadToken("hash2", 1000, nil, int64(10)) 42 | manager.InvalidateToken("hash2") 43 | assert.True(t, manager.IsTokenValid("hash1")) 44 | assert.False(t, manager.IsTokenValid("hash2")) 45 | }) 46 | }) 47 | 48 | t.Run("GetToken()", func(t *testing.T) { 49 | t.Run("Should return a token from the store", func(t *testing.T) { 50 | manager := NewUploadTokenManager() 51 | token := manager.NewUploadToken("hash1", 1000, nil, int64(5)) 52 | assert.Equal(t, token, manager.GetToken("hash1")) 53 | }) 54 | 55 | t.Run("Should return nil if no token in store", func(t *testing.T) { 56 | manager := NewUploadTokenManager() 57 | assert.Nil(t, manager.GetToken("hash")) 58 | }) 59 | }) 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /x/oracle/abci.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | import ( 4 | "github.com/bluzelle/curium/app/ante/gasmeter" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | abci "github.com/tendermint/tendermint/abci/types" 7 | "sync" 8 | ) 9 | 10 | // BeginBlocker check for infraction evidence or downtime of validators 11 | // on every begin block 12 | func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) { 13 | ctx = ctx.WithGasMeter(gasmeter.NewFreeGasMeter(0)) 14 | currCtx = &ctx 15 | } 16 | 17 | var once sync.Once 18 | 19 | // EndBlocker called every block, process inflation, update validator set. 20 | func EndBlocker(ctx sdk.Context, am AppModule) { 21 | once.Do(func () { 22 | go StartFeeder(am.keeper) 23 | }) 24 | } -------------------------------------------------------------------------------- /x/oracle/alias.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/oracle/keeper" 5 | "github.com/bluzelle/curium/x/oracle/types" 6 | ) 7 | 8 | const ( 9 | ModuleName = types.ModuleName 10 | StoreKey = types.StoreKey 11 | ) 12 | 13 | 14 | var ( 15 | NewKeeper = keeper.NewKeeper 16 | NewQuerier = keeper.NewQuerier 17 | ModuleCdc = types.ModuleCdc 18 | RegisterCodec = types.RegisterCodec 19 | ) 20 | 21 | type ( 22 | Keeper = keeper.Keeper 23 | SourceValue = types.SourceValue 24 | ) 25 | -------------------------------------------------------------------------------- /x/oracle/client/rest/query.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/gorilla/mux" 5 | 6 | "github.com/cosmos/cosmos-sdk/client/context" 7 | ) 8 | 9 | func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { 10 | } 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /x/oracle/client/rest/rest.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "github.com/gorilla/mux" 5 | 6 | "github.com/cosmos/cosmos-sdk/client/context" 7 | ) 8 | 9 | // RegisterRoutes registers oracle-related REST handlers to a router 10 | func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { 11 | // this line is used by starport scaffolding # 1 12 | registerQueryRoutes(cliCtx, r) 13 | registerTxRoutes(cliCtx, r) 14 | } 15 | -------------------------------------------------------------------------------- /x/oracle/client/rest/tx.go: -------------------------------------------------------------------------------- 1 | package rest 2 | // The packages below are commented out at first to prevent an error if this file isn't initially saved. 3 | import ( 4 | "github.com/gorilla/mux" 5 | "github.com/cosmos/cosmos-sdk/client/context" 6 | ) 7 | 8 | func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { 9 | } 10 | 11 | -------------------------------------------------------------------------------- /x/oracle/docs/arch.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | The feeder and processor in the oracle runs as a Cosmos module that encompasses both functions. 4 | 5 | ### Feeder 6 | 7 | A feeder is responsible to fetch the data from remote sources and send the resulting data to the processor 8 | 9 | ### Processor 10 | 11 | A processor is responsible for combining and filtering votes to produce a single value that it writes to the DB. 12 | 13 | # Oracle flow 14 | 15 | ### Feeders 16 | 17 | * Feeders start and read their configuration from the CRUD module 18 | 19 | * Feeders then request information from the sources provided in the configuration 20 | 21 | * For each source a preflight vote proof is generated and put on the blockchain 22 | 23 | The preflight consists of the vote hashed with the address of the validator (to prevent free-riders). The preflight hash includes a signature, signed with the validator's consensus key. 24 | 25 | * For each source at 20 seconds past the minute a vote is cast on the blockchain that matches the value in the preflight proof. Each vote includes with it a signature, signed with the validator's consensus key. 26 | 27 | ### Processors 28 | 29 | * Listens for preflight proofs and votes on the blockchain 30 | * When a vote is received it is compared with the preflight proof - if the proof does not match, the vote is discarded. If the signatures (of both the preflight proof and the vote itself) does not match the signer, the vote is discarded. 31 | * Votes are then compared and combined using a combination of weights and elimination rules. 32 | * A single (blended) value is written to the DB for each source along with the votes and associated metadata that led to the single blended value. 33 | -------------------------------------------------------------------------------- /x/oracle/genesis.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | "github.com/bluzelle/curium/x/oracle/types" 6 | "github.com/bluzelle/curium/x/oracle/keeper" 7 | ) 8 | 9 | // InitGenesis initialize default parameters 10 | // and the keeper's address to pubkey map 11 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, data types.GenesisState) { 12 | k.SetAdminAddress(ctx, data.Config.AdminAddress) 13 | } 14 | 15 | // ExportGenesis writes the current store values 16 | // to a genesis file, which can be imported again 17 | // with InitGenesis 18 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) (data types.GenesisState) { 19 | return types.NewGenesisState( 20 | k.DumpGlobalConfig(ctx), 21 | k.DumpSources(ctx), 22 | k.DumpVotes(ctx), 23 | k.DumpSourceValues(ctx), 24 | ) 25 | } 26 | 27 | -------------------------------------------------------------------------------- /x/oracle/keeper/keeper.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "fmt" 5 | curium "github.com/bluzelle/curium/x/curium/keeper" 6 | "github.com/bluzelle/curium/x/oracle/types" 7 | "github.com/cosmos/cosmos-sdk/client/flags" 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | "github.com/cosmos/cosmos-sdk/x/staking" 11 | "github.com/spf13/viper" 12 | "github.com/tendermint/tendermint/libs/log" 13 | "github.com/tendermint/tendermint/privval" 14 | "os" 15 | ) 16 | 17 | var logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) 18 | 19 | var valueUpdateListeners []types.ValueUpdateListener = make([]types.ValueUpdateListener, 0) 20 | 21 | 22 | // Keeper of the oracle store 23 | type Keeper struct { 24 | storeKey sdk.StoreKey 25 | stakingKeeper staking.Keeper 26 | MsgBroadcaster curium.MsgBroadcaster 27 | KeyringReader *curium.KeyringReader 28 | cdc *codec.Codec 29 | paramspace types.ParamSubspace 30 | } 31 | 32 | // NewKeeper creates a oracle keeper 33 | func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, stakingKeeper staking.Keeper, msgBroadcaster curium.MsgBroadcaster, keyringReader *curium.KeyringReader, paramspace types.ParamSubspace) Keeper { 34 | keeper := Keeper{ 35 | storeKey: storeKey, 36 | stakingKeeper: stakingKeeper, 37 | MsgBroadcaster: msgBroadcaster, 38 | KeyringReader: keyringReader, 39 | cdc: cdc, 40 | // paramspace: paramspace.WithKeyTable(types.ParamKeyTable()), 41 | } 42 | return keeper 43 | } 44 | 45 | func (k Keeper) GetStore(ctx sdk.Context) sdk.KVStore { 46 | return ctx.KVStore(k.storeKey) 47 | } 48 | 49 | 50 | // Logger returns a module-specific logger. 51 | func (k Keeper) Logger(ctx sdk.Context) log.Logger { 52 | return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) 53 | } 54 | 55 | func (k Keeper) GetValidator(ctx sdk.Context, valcons string) (validator staking.Validator, found bool) { 56 | consAddr, _ := sdk.ConsAddressFromBech32(valcons) 57 | return k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr) 58 | } 59 | 60 | func (k Keeper) IsValidator(ctx sdk.Context) bool { 61 | valcons := GetValconsAddress() 62 | validator, _ := k.GetValidator(ctx, valcons) 63 | return !validator.DelegatorShares.IsNil() && !validator.DelegatorShares.IsZero() 64 | } 65 | 66 | func GetPrivateValidator() *privval.FilePV { 67 | homedir := viper.GetString(flags.FlagHome) 68 | return privval.LoadFilePV(homedir+"/config/priv_validator_key.json", homedir+"/data/priv_validator_state.json") 69 | } 70 | 71 | func GetValconsAddress() string { 72 | validator := GetPrivateValidator() 73 | address := validator.GetAddress() 74 | consAddress := (sdk.ConsAddress)(address) 75 | addressString := consAddress.String() 76 | return addressString 77 | } 78 | -------------------------------------------------------------------------------- /x/oracle/keeper/keeper_global_config.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/oracle/types" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | ) 7 | 8 | 9 | func (k Keeper) DumpGlobalConfig(ctx sdk.Context) types.GlobalOracleConfig { 10 | return types.GlobalOracleConfig{ 11 | AdminAddress: k.GetAdminAddress(ctx), 12 | } 13 | } 14 | 15 | func (k Keeper) SetAdminAddress(ctx sdk.Context, admin sdk.AccAddress) { 16 | store := k.GetStore(ctx) 17 | store.Set([]byte(types.ConfigStorePrefix + "admin-address"), admin) 18 | } 19 | 20 | func (k Keeper) GetAdminAddress(ctx sdk.Context) sdk.AccAddress{ 21 | store := k.GetStore(ctx) 22 | return store.Get([]byte(types.ConfigStorePrefix + "admin-address")) 23 | } 24 | -------------------------------------------------------------------------------- /x/oracle/keeper/keeper_proof.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "encoding/hex" 5 | "github.com/bluzelle/curium/x/oracle/types" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | var voteProofs map[string]types.MsgOracleVoteProof 10 | 11 | func (k Keeper) GetVoteProof(ctx sdk.Context, sourceName string, valcons string) types.MsgOracleVoteProof { 12 | var msg types.MsgOracleVoteProof 13 | proofStore := k.GetStore(ctx) 14 | proof := proofStore.Get([]byte(types.ProofStorePrefix + sourceName + valcons)) 15 | k.cdc.MustUnmarshalBinaryBare(proof, &msg) 16 | return msg 17 | } 18 | 19 | func CalculateProofSig(value string) string { 20 | v := GetPrivateValidator() 21 | s, _ := v.Key.PrivKey.Sign([]byte(value)) 22 | return hex.EncodeToString(s) 23 | } 24 | 25 | func (k Keeper) StoreVoteProof(ctx sdk.Context, msg types.MsgOracleVoteProof) { 26 | proofStore := k.GetStore(ctx) 27 | proofStore.Set([]byte(types.ProofStorePrefix + msg.SourceName + msg.ValidatorAddr), k.cdc.MustMarshalBinaryBare(msg)) 28 | } 29 | 30 | func (k Keeper) IsVoteValid(ctx sdk.Context, msg types.MsgOracleVote) bool { 31 | validator, found := k.GetValidator(ctx, msg.Valcons) 32 | 33 | if validator.Jailed { 34 | logger.Info("Oracle vote received from jailed validator", "name", msg.SourceName, "valcons", msg.Valcons) 35 | return false 36 | } 37 | 38 | if !found { 39 | logger.Info("Oracle vote received from unknown validator", "name", msg.SourceName, "valcons", msg.Valcons) 40 | } 41 | 42 | if found { 43 | proofSignatureString := k.GetVoteProof(ctx, msg.SourceName, msg.Valcons).VoteSig 44 | proofSignature, _ := hex.DecodeString(proofSignatureString) 45 | isGood := validator.ConsPubKey.VerifyBytes([]byte(msg.Value), proofSignature) 46 | 47 | if !isGood { 48 | logger.Info("Oracle vote/proof mismatch", "name", msg.SourceName, "valcons", msg.Valcons) 49 | } 50 | 51 | return isGood 52 | } 53 | return false 54 | } 55 | 56 | func (k Keeper) SearchVoteProofs(ctx sdk.Context, prefix string) []types.MsgOracleVoteProof { 57 | iterator := sdk.KVStorePrefixIterator(k.GetStore(ctx), []byte(types.ProofStorePrefix + prefix)) 58 | defer iterator.Close() 59 | proofs := make([]types.MsgOracleVoteProof, 0) 60 | 61 | for ;iterator.Valid(); iterator.Next() { 62 | if ctx.GasMeter().IsPastLimit() { 63 | break 64 | } 65 | 66 | var v types.MsgOracleVoteProof 67 | value := iterator.Value() 68 | k.cdc.MustUnmarshalBinaryBare(value, &v) 69 | proofs = append(proofs, v) 70 | } 71 | return proofs 72 | } 73 | 74 | -------------------------------------------------------------------------------- /x/oracle/keeper/keeper_source.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/oracle/types" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | ) 7 | 8 | func (k Keeper) AddSource(ctx sdk.Context, name string, source types.Source) { 9 | store := k.GetStore(ctx) 10 | store.Set([]byte(types.SourceStorePrefix + name), k.cdc.MustMarshalBinaryBare(source)) 11 | } 12 | 13 | func (k Keeper) GetSource(ctx sdk.Context, name string) (types.Source, error) { 14 | store := k.GetStore(ctx) 15 | var source types.Source 16 | err := k.cdc.UnmarshalBinaryBare(store.Get([]byte(types.SourceStorePrefix + name)), &source) 17 | return source, err 18 | } 19 | 20 | func (k Keeper) DeleteSource(ctx sdk.Context, name string) error { 21 | store := k.GetStore(ctx) 22 | store.Delete([]byte(types.SourceStorePrefix + name)) 23 | return nil 24 | } 25 | 26 | func (k Keeper) ListSources(ctx sdk.Context) ([]types.Source, error) { 27 | store := k.GetStore(ctx) 28 | iterator := sdk.KVStorePrefixIterator(store, []byte(types.SourceStorePrefix)) 29 | defer iterator.Close() 30 | var sources = make([]types.Source, 0) 31 | for ; iterator.Valid(); iterator.Next() { 32 | var source types.Source 33 | value := iterator.Value() 34 | k.cdc.UnmarshalBinaryBare(value, &source) 35 | sources = append(sources, source) 36 | } 37 | return sources, nil 38 | } 39 | 40 | func (k Keeper) DumpSources(ctx sdk.Context) map[string] types.Source { 41 | store := k.GetStore(ctx) 42 | var results = make(map[string]types.Source) 43 | iterator := sdk.KVStorePrefixIterator(store, []byte(types.SourceStorePrefix)) 44 | for ; iterator.Valid(); iterator.Next() { 45 | var source types.Source 46 | k.cdc.UnmarshalBinaryBare(iterator.Value(), &source) 47 | results[string(iterator.Key())] = source 48 | } 49 | return results 50 | } 51 | -------------------------------------------------------------------------------- /x/oracle/keeper/keeper_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | 4 | -------------------------------------------------------------------------------- /x/oracle/keeper/keeper_vote.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "fmt" 5 | types "github.com/bluzelle/curium/x/oracle/types" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | 10 | type VoteKey struct { 11 | Batch string 12 | SourceName string 13 | Valcons string 14 | } 15 | 16 | func voteToVoteKey(vote types.Vote) VoteKey { 17 | return VoteKey{ 18 | Batch: vote.Batch, 19 | SourceName: vote.SourceName, 20 | Valcons: vote.Valcons, 21 | } 22 | } 23 | 24 | func (vk VoteKey) Bytes() []byte { 25 | return []byte(fmt.Sprintf("%s%s>%s>%s", types.VoteStorePrefix, vk.Batch, vk.SourceName, vk.Valcons)) 26 | } 27 | 28 | func (k Keeper) StoreVote(ctx sdk.Context, vote types.Vote) { 29 | key := voteToVoteKey(vote).Bytes() 30 | store := k.GetStore(ctx) 31 | store.Set(key, k.cdc.MustMarshalBinaryBare(vote)) 32 | } 33 | 34 | func (k Keeper) DeleteVotes(ctx sdk.Context, prefix string) int { 35 | keys := k.SearchVoteKeys(ctx, prefix) 36 | store := k.GetStore(ctx) 37 | for _, key := range keys { 38 | store.Delete([]byte(key)) 39 | } 40 | return len(keys) 41 | } 42 | 43 | func (k Keeper) SearchVoteKeys(ctx sdk.Context, prefix string) []string { 44 | iterator := sdk.KVStorePrefixIterator(k.GetStore(ctx), []byte(types.VoteStorePrefix + prefix)) 45 | defer iterator.Close() 46 | keys := make([]string, 0) 47 | 48 | for ;iterator.Valid(); iterator.Next() { 49 | if ctx.GasMeter().IsPastLimit() { 50 | break 51 | } 52 | 53 | key := iterator.Key() 54 | keys = append(keys, string(key)) 55 | } 56 | return keys 57 | 58 | } 59 | 60 | func makeSearchVotePrefix(batch string, sourceName string) string { 61 | return fmt.Sprintf("%s>%s", batch, sourceName) 62 | } 63 | 64 | func (k Keeper) SearchVotes(ctx sdk.Context, prefix string) []types.Vote { 65 | iterator := sdk.KVStorePrefixIterator(k.GetStore(ctx), []byte(types.VoteStorePrefix + prefix)) 66 | defer iterator.Close() 67 | votes := make([]types.Vote, 0) 68 | 69 | for ;iterator.Valid(); iterator.Next() { 70 | if ctx.GasMeter().IsPastLimit() { 71 | break 72 | } 73 | 74 | var v types.Vote 75 | value := iterator.Value() 76 | k.cdc.MustUnmarshalBinaryBare(value, &v) 77 | votes = append(votes, v) 78 | } 79 | return votes 80 | } 81 | 82 | func (k Keeper) DumpVotes(ctx sdk.Context) map[string] types.Vote { 83 | store := k.GetStore(ctx) 84 | var results = make(map[string]types.Vote) 85 | iterator := sdk.KVStorePrefixIterator(store, []byte(types.VoteStorePrefix)) 86 | for ; iterator.Valid(); iterator.Next() { 87 | var vote types.Vote 88 | k.cdc.UnmarshalBinaryBare(iterator.Value(), &vote) 89 | results[string(iterator.Key())] = vote 90 | } 91 | return results 92 | } 93 | 94 | 95 | -------------------------------------------------------------------------------- /x/oracle/keeper/params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | // TODO: Define if your module needs Parameters, if not this can be deleted 4 | 5 | import ( 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/bluzelle/curium/x/oracle/types" 8 | ) 9 | 10 | // GetParams returns the total set of oracle parameters. 11 | func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { 12 | k.paramspace.GetParamSet(ctx, ¶ms) 13 | return params 14 | } 15 | 16 | // SetParams sets the oracle parameters to the param space. 17 | func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { 18 | k.paramspace.SetParamSet(ctx, ¶ms) 19 | } 20 | 21 | -------------------------------------------------------------------------------- /x/oracle/spec/README.md: -------------------------------------------------------------------------------- 1 | # oracle module specification 2 | 3 | ## Abstract 4 | 5 | 6 | 7 | ## Contents 8 | 9 | // TODO: Create the below files if they are needed. 10 | 1. **[Concepts](01_concepts.md)** 11 | 2. **[State](02_state.md)** 12 | 3. **[Messages](03_messages.md)** 13 | 4. **[Begin-Block](04_begin_block.md)** 14 | 5. **[End-Block](06_end_bloc.md)** 15 | 6. **[05_hooks](06_hooks.md)** 16 | 7. **[Events](07_events.md)** 17 | 8. **[Parameters](08_params.md)** 18 | -------------------------------------------------------------------------------- /x/oracle/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | ) 6 | 7 | // RegisterCodec registers concrete types on codec 8 | func RegisterCodec(cdc *codec.Codec) { 9 | // this line is used by starport scaffolding # 1 10 | cdc.RegisterConcrete(MsgOracleAddSource{}, "oracle/MsgAddSource", nil) 11 | cdc.RegisterConcrete(MsgOracleDeleteSource{}, "oracle/MsgDeleteSource", nil) 12 | cdc.RegisterConcrete(MsgOracleVoteProof{}, "oracle/MsgVoteProof", nil) 13 | cdc.RegisterConcrete(MsgOracleVote{}, "oracle/MsgVote", nil) 14 | cdc.RegisterConcrete(MsgOracleDeleteVotes{}, "oracle/MsgDeleteVotes", nil) 15 | cdc.RegisterConcrete(MsgOracleSetAdmin{}, "oracle/MsgSetAdmin", nil) 16 | } 17 | 18 | // ModuleCdc defines the module codec 19 | var ModuleCdc *codec.Codec 20 | 21 | func init() { 22 | ModuleCdc = codec.New() 23 | RegisterCodec(ModuleCdc) 24 | codec.RegisterCrypto(ModuleCdc) 25 | ModuleCdc.Seal() 26 | } 27 | -------------------------------------------------------------------------------- /x/oracle/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | _ "github.com/cosmos/cosmos-sdk/types/errors" 5 | ) 6 | 7 | // TODO: Fill out some custom errors for the module 8 | // You can see how they are constructed below: 9 | var ( 10 | // ErrInvalid = sdkerrors.Register(ModuleName, 1, "custom error message") 11 | ) 12 | -------------------------------------------------------------------------------- /x/oracle/types/events.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // oracle module event types 4 | const ( 5 | // TODO: Create your event types 6 | // EventType = "action" 7 | 8 | // TODO: Create keys fo your events, the values will be derivided from the msg 9 | // AttributeKeyAddress = "address" 10 | 11 | // TODO: Some events may not have values for that reason you want to emit that something happened. 12 | // AttributeValueDoubleSign = "double_sign" 13 | 14 | AttributeValueCategory = ModuleName 15 | ) 16 | -------------------------------------------------------------------------------- /x/oracle/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/params" 6 | ) 7 | 8 | // ParamSubspace defines the expected Subspace interfcace 9 | type ParamSubspace interface { 10 | WithKeyTable(table params.KeyTable) params.Subspace 11 | Get(ctx sdk.Context, key []byte, ptr interface{}) 12 | GetParamSet(ctx sdk.Context, ps params.ParamSet) 13 | SetParamSet(ctx sdk.Context, ps params.ParamSet) 14 | } 15 | 16 | /* 17 | When a module wishes to interact with another module, it is good practice to define what it will use 18 | as an interface so the module cannot use things that are not permitted. 19 | TODO: Create interfaces of what you expect the other keepers to have to be able to use this module. 20 | type BankKeeper interface { 21 | SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error) 22 | SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error 23 | } 24 | */ 25 | -------------------------------------------------------------------------------- /x/oracle/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/types/errors" 5 | ) 6 | 7 | // GenesisState - all oracle state that must be provided at genesis 8 | type GenesisState struct { 9 | Config GlobalOracleConfig 10 | Sources map[string] Source 11 | Votes map[string] Vote 12 | SourceValues map[string] SourceValue 13 | } 14 | 15 | // NewGenesisState creates a new GenesisState object 16 | func NewGenesisState(config GlobalOracleConfig, sources map[string] Source, votes map[string] Vote, values map[string] SourceValue) GenesisState { 17 | return GenesisState{ 18 | Config: config, 19 | Sources: sources, 20 | Votes: votes, 21 | SourceValues: values, 22 | } 23 | } 24 | 25 | // DefaultGenesisState - default GenesisState used by Cosmos Hub 26 | func DefaultGenesisState() GenesisState { 27 | return GenesisState{ 28 | Config: GlobalOracleConfig{}, 29 | Sources: map[string] Source{}, 30 | } 31 | } 32 | 33 | // ValidateGenesis validates the oracle genesis parameters 34 | func ValidateGenesis(data GenesisState) error { 35 | if data.Config.AdminAddress == nil { 36 | return errors.New("oracle", 1, "Genesis missing oracle admin address") 37 | } 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /x/oracle/types/keeper.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "github.com/cosmos/cosmos-sdk/types" 4 | 5 | type GlobalOracleConfig struct { 6 | AdminAddress types.AccAddress 7 | } 8 | 9 | -------------------------------------------------------------------------------- /x/oracle/types/key.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | 4 | 5 | 6 | const ( 7 | // ModuleName is the name of the module 8 | ModuleName = "oracle" 9 | 10 | // StoreKey to be used when creating the KVStore 11 | StoreKey = ModuleName 12 | 13 | // RouterKey to be used for routing msgs 14 | RouterKey = ModuleName 15 | 16 | // QuerierRoute to be used for querier msgs 17 | QuerierRoute = ModuleName 18 | 19 | // Store prefixes 20 | SourceStorePrefix = "SO" 21 | ProofStorePrefix = "PR" 22 | VoteStorePrefix = "VO" 23 | ValueStorePrefix = "VA" 24 | ConfigStorePrefix = "CO" 25 | ) 26 | -------------------------------------------------------------------------------- /x/oracle/types/params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cosmos/cosmos-sdk/x/params" 7 | ) 8 | 9 | // Default parameter namespace 10 | const ( 11 | DefaultParamspace = ModuleName 12 | // TODO: Define your default parameters 13 | ) 14 | 15 | // Parameter store keys 16 | var ( 17 | // TODO: Define your keys for the parameter store 18 | // KeyParamName = []byte("ParamName") 19 | ) 20 | 21 | // ParamKeyTable for oracle module 22 | func ParamKeyTable() params.KeyTable { 23 | return params.NewKeyTable().RegisterParamSet(&Params{}) 24 | } 25 | 26 | // Params - used for initializing default parameter for oracle at genesis 27 | type Params struct { 28 | // TODO: Add your Paramaters to the Paramter struct 29 | // KeyParamName string `json:"key_param_name"` 30 | } 31 | 32 | // NewParams creates a new Params object 33 | func NewParams(/* TODO: Pass in the paramters*/) Params { 34 | return Params{ 35 | // TODO: Create your Params Type 36 | } 37 | } 38 | 39 | // String implements the stringer interface for Params 40 | func (p Params) String() string { 41 | return fmt.Sprintf(` 42 | // TODO: Return all the params as a string 43 | `, ) 44 | } 45 | 46 | // ParamSetPairs - Implements params.ParamSet 47 | func (p *Params) ParamSetPairs() params.ParamSetPairs { 48 | return params.ParamSetPairs{ 49 | // TODO: Pair your key with the param 50 | // params.NewParamSetPair(KeyParamName, &p.ParamName), 51 | } 52 | } 53 | 54 | // DefaultParams defines the parameters for this module 55 | func DefaultParams() Params { 56 | return NewParams( /* TODO: Pass in your default Params */ ) 57 | } 58 | -------------------------------------------------------------------------------- /x/oracle/types/querier.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import sdk "github.com/cosmos/cosmos-sdk/types" 4 | 5 | const ( 6 | QueryListSources = "listsources" 7 | QuerySearchVotes = "searchvotes" 8 | QuerySearchVoteProofs = "searchProofs" 9 | QuerySearchVoteKeys = "searchvotekeys" 10 | QuerySearchSourceValues = "searchSourceValues" 11 | QueryConfig = "getConfig" 12 | QueryValidatorByValcons = "validatorByValcons" 13 | ) 14 | 15 | type QueryResultListSources = []struct{ 16 | Name string 17 | Url string 18 | Property string 19 | Owner sdk.AccAddress 20 | } 21 | 22 | type QueryResultConfig = struct{ 23 | AdminAddress sdk.AccAddress 24 | } 25 | 26 | type ValidatorByValconsQueryRequest = struct { 27 | Valcons string 28 | } 29 | 30 | type SearchVotesQueryRequest = struct{ 31 | Prefix string 32 | } 33 | 34 | type SearchVoteProofsQueryRequest = struct { 35 | Prefix string 36 | } 37 | 38 | type SearchSourceValuesQueryRequest = struct{ 39 | Prefix string 40 | Reverse bool 41 | Page uint 42 | Limit uint 43 | } 44 | 45 | 46 | type CalculateProofSigQueryRequest = struct { 47 | Valcons string 48 | Value string 49 | } -------------------------------------------------------------------------------- /x/oracle/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/types" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | ) 7 | 8 | 9 | type Source struct { 10 | Name string 11 | Url string 12 | Property string 13 | Owner types.AccAddress 14 | Weight int64 15 | } 16 | 17 | type Vote struct { 18 | SourceName string 19 | Batch string 20 | Value types.Dec 21 | Valcons string 22 | Owner types.AccAddress 23 | Weight types.Dec 24 | } 25 | 26 | type SourceValue struct { 27 | SourceName string 28 | Batch string 29 | Value types.Dec 30 | Owner types.AccAddress 31 | Height int64 32 | Count int64 33 | Weight int64 34 | } 35 | 36 | type LocalOracleConfig struct { 37 | UserAddress types.AccAddress 38 | UserMnemonic string 39 | } 40 | 41 | type ValueUpdateListener func(sdk.Context, SourceValue) 42 | -------------------------------------------------------------------------------- /x/tax/README.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | 3 | Tax module is for customization of fees flow. 4 | 5 | DeductFee ante handler is sending specified bp(basis point, unit of `0.0001`) of fee to the address specified by tax module. 6 | And rest of them are sent to fee collector module as it was doing. 7 | 8 | # Queries 9 | 10 | Query command query tax collector (owner) and fee tax bp and transfer tax bp. 11 | ```sh 12 | blzcli query tax info 13 | ``` 14 | 15 | # Transactions 16 | 17 | Set tax fee bp and transfer tax bp. 18 | ```sh 19 | blzcli tx tax set-bp 30 40 --from tax_owner --keyring-backend=test 20 | blzcli tx tax set-collector $(blzcli keys show -a user1 --keyring-backend=test) --from tax_owner --keyring-backend=test 21 | ``` 22 | # Command to run test 23 | 24 | Go to root directory of project and run `make test-tax`. -------------------------------------------------------------------------------- /x/tax/alias.go: -------------------------------------------------------------------------------- 1 | package tax 2 | 3 | import ( 4 | "github.com/bluzelle/curium/x/tax/internal/keeper" 5 | "github.com/bluzelle/curium/x/tax/internal/types" 6 | ) 7 | 8 | // constants 9 | var ( 10 | ModuleName = types.ModuleName 11 | RouterKey = types.RouterKey 12 | StoreKey = types.StoreKey 13 | ) 14 | 15 | // modules 16 | var ( 17 | NewKeeper = keeper.NewKeeper 18 | NewQuerier = keeper.NewQuerier 19 | ModuleCdc = types.ModuleCdc 20 | RegisterCodec = types.RegisterCodec 21 | ) 22 | 23 | // Keeper alias types 24 | type ( 25 | Keeper = keeper.Keeper 26 | ) 27 | -------------------------------------------------------------------------------- /x/tax/client/cli/query.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bluzelle/curium/x/tax/internal/types" 7 | "github.com/cosmos/cosmos-sdk/client" 8 | "github.com/cosmos/cosmos-sdk/client/context" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | "github.com/cosmos/cosmos-sdk/codec" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // GetQueryCmd returns module query commands 15 | func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { 16 | taxQueryCmd := &cobra.Command{ 17 | Use: types.ModuleName, 18 | Short: "Querying commands for the tax module", 19 | DisableFlagParsing: true, 20 | SuggestionsMinimumDistance: 0, 21 | RunE: client.ValidateCmd, 22 | } 23 | 24 | taxQueryCmd.AddCommand(flags.GetCommands( 25 | GetCmdQTaxInfo(storeKey, cdc), 26 | )...) 27 | 28 | return taxQueryCmd 29 | } 30 | 31 | // GetCmdQTaxInfo returns tax info by query 32 | func GetCmdQTaxInfo(queryRoute string, cdc *codec.Codec) *cobra.Command { 33 | return &cobra.Command{ 34 | Use: "info", 35 | Short: "show tax info", 36 | Args: cobra.ExactArgs(0), 37 | RunE: func(cmd *cobra.Command, args []string) error { 38 | cliCtx := context.NewCLIContext().WithCodec(cdc) 39 | res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/info", queryRoute), nil) 40 | 41 | if err != nil { 42 | fmt.Println("could get tax info ", err) 43 | return nil 44 | } 45 | 46 | var out types.TaxInfo 47 | cdc.MustUnmarshalJSON(res, &out) 48 | return cliCtx.PrintOutput(out) 49 | }, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /x/tax/client/cli/tx.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "bufio" 5 | "strconv" 6 | 7 | "github.com/bluzelle/curium/x/tax/internal/types" 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/context" 10 | "github.com/cosmos/cosmos-sdk/client/flags" 11 | "github.com/cosmos/cosmos-sdk/codec" 12 | sdk "github.com/cosmos/cosmos-sdk/types" 13 | "github.com/cosmos/cosmos-sdk/x/auth" 14 | "github.com/cosmos/cosmos-sdk/x/auth/client/utils" 15 | "github.com/spf13/cobra" 16 | ) 17 | 18 | func GetTxCmd(_ string, cdc *codec.Codec) *cobra.Command { 19 | taxTxCmd := &cobra.Command{ 20 | Use: types.ModuleName, 21 | Short: "tax transaction subcommands", 22 | DisableFlagParsing: true, 23 | SuggestionsMinimumDistance: 2, 24 | RunE: client.ValidateCmd, 25 | } 26 | taxTxCmd.AddCommand(flags.PostCommands( 27 | GetCmdSetCollector(cdc), 28 | GetCmdSetBp(cdc), 29 | )...) 30 | 31 | return taxTxCmd 32 | } 33 | 34 | func GetCmdSetCollector(cdc *codec.Codec) *cobra.Command { 35 | cc := cobra.Command{ 36 | Use: "set-collector [address]", 37 | Short: "set collector of tax module", 38 | Args: cobra.ExactArgs(1), 39 | RunE: func(cmd *cobra.Command, args []string) error { 40 | cliCtx := context.NewCLIContext().WithCodec(cdc) 41 | inBuf := bufio.NewReader(cmd.InOrStdin()) 42 | txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) 43 | 44 | collector, err := sdk.AccAddressFromBech32(args[0]) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | msg := types.NewMsgSetCollector(collector, cliCtx.GetFromAddress()) 50 | 51 | err = msg.ValidateBasic() 52 | if err != nil { 53 | return err 54 | } 55 | return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) 56 | }, 57 | } 58 | 59 | return &cc 60 | } 61 | 62 | func GetCmdSetBp(cdc *codec.Codec) *cobra.Command { 63 | return &cobra.Command{ 64 | Use: "set-bp [fee_bp] [transfer_bp]", 65 | Short: "set basis point (0.0001 unit) of tax", 66 | Args: cobra.ExactArgs(2), 67 | RunE: func(cmd *cobra.Command, args []string) error { 68 | cliCtx := context.NewCLIContext().WithCodec(cdc) 69 | inBuf := bufio.NewReader(cmd.InOrStdin()) 70 | txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) 71 | 72 | feebp, err := strconv.Atoi(args[0]) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | trfbp, err := strconv.Atoi(args[1]) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | msg := types.NewMsgSetBp(int64(feebp), int64(trfbp), cliCtx.GetFromAddress()) 83 | 84 | err = msg.ValidateBasic() 85 | if err != nil { 86 | return err 87 | } 88 | 89 | return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) 90 | }, 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /x/tax/client/rest/query.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/cosmos/cosmos-sdk/client/context" 8 | "github.com/cosmos/cosmos-sdk/types/rest" 9 | ) 10 | 11 | func QueryTaxInfoHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { 12 | return func(w http.ResponseWriter, r *http.Request) { 13 | 14 | res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/info", storeName), nil) 15 | if err != nil { 16 | rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) 17 | return 18 | } 19 | rest.PostProcessResponse(w, cliCtx, res) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /x/tax/client/rest/rest.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cosmos/cosmos-sdk/client/context" 7 | "github.com/gorilla/mux" 8 | ) 9 | 10 | // RegisterRoutes - Central function to define routes that get registered by the main application 11 | func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) { 12 | r.HandleFunc(fmt.Sprintf("/%s/info", storeName), QueryTaxInfoHandler(cliCtx, storeName)).Methods("GET") 13 | r.HandleFunc(fmt.Sprintf("/%s/bp", storeName), SetBpHandler(cliCtx)).Methods("POST") 14 | r.HandleFunc(fmt.Sprintf("/%s/collector", storeName), SetCollectorHandler(cliCtx)).Methods("POST") 15 | } 16 | -------------------------------------------------------------------------------- /x/tax/client/rest/tx.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/bluzelle/curium/x/tax/internal/types" 7 | "github.com/cosmos/cosmos-sdk/client/context" 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "github.com/cosmos/cosmos-sdk/types/rest" 10 | "github.com/cosmos/cosmos-sdk/x/auth/client/utils" 11 | ) 12 | 13 | type setCollectorReq struct { 14 | BaseReq rest.BaseReq 15 | Collector string 16 | Proposer string 17 | } 18 | 19 | func SetCollectorHandler(cliCtx context.CLIContext) http.HandlerFunc { 20 | return func(w http.ResponseWriter, r *http.Request) { 21 | var req setCollectorReq 22 | 23 | if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { 24 | rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") 25 | return 26 | } 27 | 28 | baseReq := req.BaseReq.Sanitize() 29 | if !baseReq.ValidateBasic(w) { 30 | return 31 | } 32 | 33 | proposer, err := sdk.AccAddressFromBech32(req.Proposer) 34 | if err != nil { 35 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 36 | return 37 | } 38 | 39 | collector, err := sdk.AccAddressFromBech32(req.Collector) 40 | if err != nil { 41 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 42 | return 43 | } 44 | 45 | msg := types.NewMsgSetCollector(collector, proposer) 46 | err = msg.ValidateBasic() 47 | if err != nil { 48 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 49 | return 50 | } 51 | 52 | utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) 53 | } 54 | } 55 | 56 | type setBpReq struct { 57 | BaseReq rest.BaseReq 58 | FeeBp int 59 | TransferBp int 60 | Proposer string 61 | } 62 | 63 | func SetBpHandler(cliCtx context.CLIContext) http.HandlerFunc { 64 | return func(w http.ResponseWriter, r *http.Request) { 65 | var req setBpReq 66 | 67 | if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { 68 | rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") 69 | return 70 | } 71 | 72 | baseReq := req.BaseReq.Sanitize() 73 | if !baseReq.ValidateBasic(w) { 74 | return 75 | } 76 | 77 | proposer, err := sdk.AccAddressFromBech32(req.Proposer) 78 | if err != nil { 79 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 80 | return 81 | } 82 | 83 | // create the message 84 | msg := types.NewMsgSetBp(int64(req.FeeBp), int64(req.TransferBp), proposer) 85 | err = msg.ValidateBasic() 86 | if err != nil { 87 | rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) 88 | return 89 | } 90 | 91 | utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /x/tax/genesis.go: -------------------------------------------------------------------------------- 1 | package tax 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/bluzelle/curium/x/tax/internal/keeper" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | abci "github.com/tendermint/tendermint/abci/types" 9 | ) 10 | 11 | type GenesisState struct { 12 | Collector sdk.AccAddress 13 | FeeBp int64 14 | TransferBp int64 15 | } 16 | 17 | func NewGenesisState() GenesisState { 18 | return GenesisState{} 19 | } 20 | 21 | func ValidateGenesis(data GenesisState) error { 22 | if data.Collector.Empty() && data.FeeBp > 0 { 23 | return errors.New("tax collector is empty but fee tax bp is not zero") 24 | } 25 | if data.Collector.Empty() && data.TransferBp > 0 { 26 | return errors.New("tax collector is empty but transfer tax bp is not zero") 27 | } 28 | return nil 29 | } 30 | 31 | func DefaultGenesisState() GenesisState { 32 | // $ blzcli keys add tax_owner --recover --keyring-backend=test 33 | // address: bluzelle1wjkdcz4hl4gcarnqtupu7vkftal6h34qxjh6rw 34 | // pubkey: bluzellepub1addwnpepq0r59990s6ljrucwnsf085p2lkugecf87gljr45cgalkfk623f88sr7re7n 35 | // mnemonic: day rabbit mom clown bleak brown large lobster reduce accuse violin where address click dynamic myself buyer daughter situate today wheel thumb sudden drill 36 | collector, err := sdk.AccAddressFromBech32("bluzelle1wjkdcz4hl4gcarnqtupu7vkftal6h34qxjh6rw") 37 | if err != nil { 38 | panic(err) 39 | } 40 | return GenesisState{ 41 | Collector: collector, 42 | FeeBp: 100, 43 | TransferBp: 1, 44 | } 45 | } 46 | 47 | func InitGenesis(ctx sdk.Context, k keeper.IKeeper, data GenesisState) []abci.ValidatorUpdate { 48 | k.SetCollector(ctx, data.Collector) 49 | k.SetFeeBp(ctx, data.FeeBp) 50 | k.SetTransferBp(ctx, data.TransferBp) 51 | return []abci.ValidatorUpdate{} 52 | } 53 | 54 | func ExportGenesis(ctx sdk.Context, k keeper.IKeeper) GenesisState { 55 | collector := k.GetCollector(ctx) 56 | feebp := k.GetFeeBp(ctx) 57 | trfbp := k.GetTransferBp(ctx) 58 | return GenesisState{ 59 | Collector: collector, 60 | FeeBp: feebp, 61 | TransferBp: trfbp, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /x/tax/genesis_test.go: -------------------------------------------------------------------------------- 1 | package tax 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestNewGenesisState(t *testing.T) { 10 | newGenesis := NewGenesisState() 11 | require.True(t, newGenesis.Collector.Empty()) 12 | require.True(t, newGenesis.FeeBp == 0) 13 | require.True(t, newGenesis.TransferBp == 0) 14 | } 15 | 16 | func TestValidateGenesis(t *testing.T) { 17 | genesis := GenesisState{ 18 | FeeBp: 100, // 1.00% 19 | } 20 | require.Error(t, ValidateGenesis(genesis)) 21 | } 22 | 23 | func TestInitGenesis(t *testing.T) { 24 | } 25 | -------------------------------------------------------------------------------- /x/tax/handler.go: -------------------------------------------------------------------------------- 1 | package tax 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/bluzelle/curium/x/tax/internal/keeper" 8 | "github.com/bluzelle/curium/x/tax/internal/types" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 11 | ) 12 | 13 | func NewHandler(keeper keeper.IKeeper) sdk.Handler { 14 | return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { 15 | switch msg := msg.(type) { 16 | case types.MsgSetCollector: 17 | return HandleMsgSetCollector(ctx, keeper, msg) 18 | case types.MsgSetBp: 19 | return HandleMsgSetBp(ctx, keeper, msg) 20 | default: 21 | return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, fmt.Sprintf("Unrecognized tax msg type: %v", msg.Type())) 22 | } 23 | } 24 | } 25 | 26 | func HandleMsgSetCollector(ctx sdk.Context, keeper keeper.IKeeper, msg types.MsgSetCollector) (*sdk.Result, error) { 27 | if err := msg.ValidateBasic(); err != nil { 28 | return &sdk.Result{}, err 29 | } 30 | oldCollector := keeper.GetCollector(ctx) 31 | if !bytes.Equal(msg.Proposer, oldCollector) { 32 | return &sdk.Result{}, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "proposer should be equal to original tax collector") 33 | } 34 | keeper.SetCollector(ctx, msg.NewCollector) 35 | return &sdk.Result{}, nil 36 | } 37 | 38 | func HandleMsgSetBp(ctx sdk.Context, keeper keeper.IKeeper, msg types.MsgSetBp) (*sdk.Result, error) { 39 | if err := msg.ValidateBasic(); err != nil { 40 | return &sdk.Result{}, err 41 | } 42 | oldCollector := keeper.GetCollector(ctx) 43 | if !bytes.Equal(msg.Proposer, oldCollector) { 44 | return &sdk.Result{}, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "proposer should be equal to original tax collector") 45 | } 46 | keeper.SetFeeBp(ctx, msg.NewFeeBp) 47 | keeper.SetTransferBp(ctx, msg.NewTransferBp) 48 | return &sdk.Result{}, nil 49 | } 50 | -------------------------------------------------------------------------------- /x/tax/handler_test.go: -------------------------------------------------------------------------------- 1 | package tax_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/bluzelle/curium/app" 8 | "github.com/bluzelle/curium/x/tax" 9 | "github.com/bluzelle/curium/x/tax/internal/types" 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | "github.com/stretchr/testify/require" 12 | abci "github.com/tendermint/tendermint/abci/types" 13 | ) 14 | 15 | func Test_handleMsgSetBp(t *testing.T) { 16 | tApp := app.NewTestApp() 17 | ctx := tApp.NewContext(true, abci.Header{}) 18 | tax.InitGenesis(ctx, tApp.GetTaxKeeper(), tax.DefaultGenesisState()) 19 | 20 | collector, err := sdk.AccAddressFromBech32("bluzelle1wjkdcz4hl4gcarnqtupu7vkftal6h34qxjh6rw") 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | addr1 := sdk.AccAddress([]byte("my----------address1")) 26 | 27 | // try setting Bp with correct owner 28 | msg1 := types.NewMsgSetBp(11, 11, collector) 29 | _, err = tax.HandleMsgSetBp(ctx, tApp.GetTaxKeeper(), msg1) 30 | require.NoError(t, err) 31 | feebp1 := tApp.GetTaxKeeper().GetFeeBp(ctx) 32 | require.True(t, feebp1 == 11) 33 | trfbp1 := tApp.GetTaxKeeper().GetTransferBp(ctx) 34 | require.True(t, trfbp1 == 11) 35 | 36 | // try setting Bp with incorrect owner 37 | msg2 := types.NewMsgSetBp(12, 12, addr1) 38 | _, err = tax.HandleMsgSetBp(ctx, tApp.GetTaxKeeper(), msg2) 39 | require.Error(t, err) 40 | feebp2 := tApp.GetTaxKeeper().GetFeeBp(ctx) 41 | require.True(t, feebp2 == 11) // not changed 42 | trfbp2 := tApp.GetTaxKeeper().GetTransferBp(ctx) 43 | require.True(t, trfbp2 == 11) // not changed 44 | } 45 | 46 | func Test_handleMsgSetCollector(t *testing.T) { 47 | tApp := app.NewTestApp() 48 | ctx := tApp.NewContext(true, abci.Header{}) 49 | tax.InitGenesis(ctx, tApp.GetTaxKeeper(), tax.DefaultGenesisState()) 50 | 51 | collector, err := sdk.AccAddressFromBech32("bluzelle1wjkdcz4hl4gcarnqtupu7vkftal6h34qxjh6rw") 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | addr1 := sdk.AccAddress([]byte("my----------address1")) 57 | 58 | // try setting collector with incorrect owner 59 | msg1 := types.NewMsgSetCollector(addr1, addr1) 60 | _, err = tax.HandleMsgSetCollector(ctx, tApp.GetTaxKeeper(), msg1) 61 | require.Error(t, err) 62 | owner1 := tApp.GetTaxKeeper().GetCollector(ctx) 63 | require.True(t, !bytes.Equal(owner1, addr1)) 64 | 65 | // try setting collector with correct owner 66 | msg2 := types.NewMsgSetCollector(addr1, collector) 67 | _, err = tax.HandleMsgSetCollector(ctx, tApp.GetTaxKeeper(), msg2) 68 | require.NoError(t, err) 69 | owner2 := tApp.GetTaxKeeper().GetCollector(ctx) 70 | require.True(t, bytes.Equal(owner2, addr1)) 71 | } 72 | -------------------------------------------------------------------------------- /x/tax/internal/keeper/keeper_test.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestKeeper_Collector(t *testing.T) { 8 | 9 | } 10 | 11 | func TestKeeper_Bp(t *testing.T) { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /x/tax/internal/keeper/querier.go: -------------------------------------------------------------------------------- 1 | package keeper 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 | abci "github.com/tendermint/tendermint/abci/types" 8 | ) 9 | 10 | // constants 11 | const ( 12 | QueryTaxInfo = "info" 13 | ) 14 | 15 | // NewQuerier helps querying 16 | func NewQuerier(keeper IKeeper) sdk.Querier { 17 | return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) { 18 | switch path[0] { 19 | case QueryTaxInfo: 20 | return queryTaxInfo(ctx, keeper) 21 | default: 22 | return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown tax query endpoint") 23 | } 24 | } 25 | } 26 | 27 | func queryTaxInfo(ctx sdk.Context, keeper IKeeper) ([]byte, error) { 28 | taxInfo := keeper.GetTaxInfo(ctx) 29 | 30 | res, err := codec.MarshalJSONIndent(keeper.GetCodec(), taxInfo) 31 | return res, err 32 | } 33 | -------------------------------------------------------------------------------- /x/tax/internal/keeper/querier_test.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_queryTaxInfo(t *testing.T) { 8 | } 9 | -------------------------------------------------------------------------------- /x/tax/internal/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | ) 6 | 7 | // ModuleCdc is codec for this module 8 | var ModuleCdc = codec.New() 9 | 10 | func init() { 11 | RegisterCodec(ModuleCdc) 12 | } 13 | 14 | // RegisterCodec register codecs for this module 15 | func RegisterCodec(cdc *codec.Codec) { 16 | cdc.RegisterConcrete(MsgSetCollector{}, "tax/collector", nil) 17 | cdc.RegisterConcrete(MsgSetBp{}, "tax/bp", nil) 18 | } 19 | -------------------------------------------------------------------------------- /x/tax/internal/types/key.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // keys for this module 4 | const ( 5 | // module name 6 | ModuleName = "tax" 7 | 8 | // StoreKey to be used when creating the KVStore 9 | StoreKey = ModuleName 10 | RouterKey = ModuleName 11 | ) 12 | -------------------------------------------------------------------------------- /x/tax/internal/types/msg_collector.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 | // MsgSetCollector defines a message to modify collector 9 | type MsgSetCollector struct { 10 | NewCollector sdk.AccAddress 11 | Proposer sdk.AccAddress 12 | } 13 | 14 | // NewMsgSetCollector returns a new instance of MsgSetCollector 15 | func NewMsgSetCollector(newCollector, proposer sdk.AccAddress) MsgSetCollector { 16 | 17 | return MsgSetCollector{ 18 | NewCollector: newCollector, 19 | Proposer: proposer, 20 | } 21 | } 22 | 23 | // Route returns MsgSetCollector message route 24 | func (msg MsgSetCollector) Route() string { return RouterKey } 25 | 26 | // Type returns MsgSetCollector message type 27 | func (msg MsgSetCollector) Type() string { return "set_collector" } 28 | 29 | // ValidateBasic do basic validation for MsgSetCollector 30 | func (msg MsgSetCollector) ValidateBasic() error { 31 | if msg.Proposer.Empty() { 32 | return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "proposer should not be empty address") 33 | } 34 | if msg.NewCollector.Empty() { 35 | return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "tax collector should not be empty address") 36 | } 37 | 38 | return nil 39 | } 40 | 41 | // GetSignBytes collect sign bytes from message 42 | func (msg MsgSetCollector) GetSignBytes() []byte { 43 | return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) 44 | } 45 | 46 | // GetSigners return signers of this message 47 | func (msg MsgSetCollector) GetSigners() []sdk.AccAddress { 48 | return []sdk.AccAddress{msg.Proposer} 49 | } 50 | -------------------------------------------------------------------------------- /x/tax/internal/types/msg_collector_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | bluzellechain "github.com/bluzelle/curium/types" 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | . "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func GetTestAddresses(t *testing.T) []sdk.AccAddress { 13 | addr1 := sdk.AccAddress([]byte("my----------address1")) 14 | addr2 := sdk.AccAddress([]byte("my----------address2")) 15 | return []sdk.AccAddress{addr1, addr2} 16 | } 17 | 18 | func TestNewMsgSetCollector(t *testing.T) { 19 | addrs := GetTestAddresses(t) 20 | proposer, newCollector := addrs[0], addrs[1] 21 | 22 | msg := NewMsgSetCollector(newCollector, proposer) 23 | True(t, bytes.Equal(msg.NewCollector.Bytes(), newCollector.Bytes())) 24 | True(t, bytes.Equal(msg.Proposer.Bytes(), proposer.Bytes())) 25 | } 26 | 27 | func TestMsgSetCollector_Route(t *testing.T) { 28 | Equal(t, MsgSetCollector{}.Route(), RouterKey) 29 | } 30 | 31 | func TestMsgSetCollector_Type(t *testing.T) { 32 | Equal(t, MsgSetCollector{}.Type(), "set_collector") 33 | } 34 | 35 | func TestMsgSetCollector_ValidateBasic(t *testing.T) { 36 | addrs := GetTestAddresses(t) 37 | proposer, newCollector := addrs[0], addrs[1] 38 | 39 | msg := NewMsgSetCollector(newCollector, proposer) 40 | err := msg.ValidateBasic() 41 | True(t, err == nil) 42 | 43 | msg = NewMsgSetCollector(sdk.AccAddress{}, proposer) 44 | err = msg.ValidateBasic() 45 | True(t, err != nil) 46 | Equal(t, err.Error(), "invalid address: tax collector should not be empty address") 47 | 48 | msg = NewMsgSetCollector(newCollector, sdk.AccAddress{}) 49 | err = msg.ValidateBasic() 50 | True(t, err != nil) 51 | Equal(t, err.Error(), "invalid address: proposer should not be empty address") 52 | } 53 | 54 | func TestMsgSetCollector_GetSignBytes(t *testing.T) { 55 | config := sdk.GetConfig() 56 | config.SetBech32PrefixForAccount(bluzellechain.Bech32PrefixAccAddr, bluzellechain.Bech32PrefixAccPub) 57 | 58 | addrs := GetTestAddresses(t) 59 | proposer, newCollector := addrs[0], addrs[1] 60 | msg := NewMsgSetCollector(newCollector, proposer) 61 | Equal(t, string(msg.GetSignBytes()), `{"type":"tax/collector","value":{"NewCollector":"bluzelle1d4uj6tfd95kj6tfd95kkzerywfjhxuejgs5ltx","Proposer":"bluzelle1d4uj6tfd95kj6tfd95kkzerywfjhxue3xrpf9e"}}`) 62 | } 63 | 64 | func TestMsgSetCollector_GetSigners(t *testing.T) { 65 | addrs := GetTestAddresses(t) 66 | proposer, newCollector := addrs[0], addrs[1] 67 | msg := NewMsgSetCollector(newCollector, proposer) 68 | Equal(t, msg.GetSigners(), []sdk.AccAddress{proposer}) 69 | } 70 | -------------------------------------------------------------------------------- /x/tax/internal/types/msg_percentage.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 | // MsgSetBp defines a message to modify collector 9 | type MsgSetBp struct { 10 | NewFeeBp int64 11 | NewTransferBp int64 12 | Proposer sdk.AccAddress 13 | } 14 | 15 | // NewMsgSetBp returns a new instance of MsgSetBp 16 | func NewMsgSetBp(newFeeBp int64, newTransferBp int64, proposer sdk.AccAddress) MsgSetBp { 17 | return MsgSetBp{ 18 | NewTransferBp: newTransferBp, 19 | NewFeeBp: newFeeBp, 20 | Proposer: proposer, 21 | } 22 | } 23 | 24 | // Route returns MsgSetBp message route 25 | func (msg MsgSetBp) Route() string { return RouterKey } 26 | 27 | // Type returns MsgSetBp message type 28 | func (msg MsgSetBp) Type() string { return "set_bp" } 29 | 30 | // ValidateBasic do basic validation for MsgSetBp 31 | func (msg MsgSetBp) ValidateBasic() error { 32 | if msg.Proposer.Empty() { 33 | return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "proposer should not be empty address") 34 | } 35 | if msg.NewFeeBp < 0 { 36 | return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "tax should not be negative") 37 | } 38 | if msg.NewFeeBp > 10000 { 39 | return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "tax should not exceed 10000 bp") 40 | } 41 | if msg.NewTransferBp < 0 { 42 | return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "tax should not be negative") 43 | } 44 | if msg.NewTransferBp > 10000 { 45 | return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "tax should not exceed 10000 bp") 46 | } 47 | 48 | return nil 49 | } 50 | 51 | // GetSignBytes collect sign bytes from message 52 | func (msg MsgSetBp) GetSignBytes() []byte { 53 | return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) 54 | } 55 | 56 | // GetSigners return signers of this message 57 | func (msg MsgSetBp) GetSigners() []sdk.AccAddress { 58 | return []sdk.AccAddress{msg.Proposer} 59 | } 60 | -------------------------------------------------------------------------------- /x/tax/internal/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import sdk "github.com/cosmos/cosmos-sdk/types" 4 | 5 | // TaxInfo is struct for tax querying 6 | type TaxInfo struct { 7 | Collector sdk.AccAddress 8 | FeeBp int64 9 | TransferBp int64 10 | } 11 | -------------------------------------------------------------------------------- /x/tax/internal/types/types_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | delay@^5.0.0: 6 | version "5.0.0" 7 | resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" 8 | integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== 9 | --------------------------------------------------------------------------------