faucet: remove module (#678)

* changelog v0.4.0

* faucet: remove module

* changelog

* codecov
This commit is contained in:
Federico Kunze 2021-01-08 10:25:02 -03:00 committed by GitHub
parent 153478eb69
commit 4a619b1e1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 66 additions and 974 deletions

View File

@ -39,12 +39,13 @@ Ref: https://keepachangelog.com/en/1.0.0/
### API Breaking
* (faucet) [\#678](https://github.com/cosmos/ethermint/pull/678) Faucet module has been removed in favor of client libraries such as [`@cosmjs/faucet`](https://github.com/cosmos/cosmjs/tree/master/packages/faucet).
* (evm) [\#670](https://github.com/cosmos/ethermint/pull/670) Migrate types to the ones defined by the protobuf messages, which are required for the stargate release.
### Bug Fixes
* (evm) [\#672](https://github.com/cosmos/ethermint/issues/672) Fix panic of `wrong Block.Header.AppHash` when restart a node with snapshot
* (evm) [\#674](https://github.com/cosmos/ethermint/issues/674) Reset all cache after account data has been committed in `EndBlock` to make sure every node state consistent
* (evm) [\#674](https://github.com/cosmos/ethermint/issues/674) Reset all cache after account data has been committed in `EndBlock` to make sure every node state consistent.
* (evm) [\#672](https://github.com/cosmos/ethermint/issues/672) Fix panic of `wrong Block.Header.AppHash` when restart a node with snapshot.
## [v0.4.0] - 2020-12-15

View File

@ -30,7 +30,6 @@ import (
ethermintcodec "github.com/cosmos/ethermint/codec"
ethermint "github.com/cosmos/ethermint/types"
"github.com/cosmos/ethermint/x/evm"
"github.com/cosmos/ethermint/x/faucet"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
@ -74,7 +73,6 @@ var (
evidence.AppModuleBasic{},
upgrade.AppModuleBasic{},
evm.AppModuleBasic{},
faucet.AppModuleBasic{},
)
// module account permissions
@ -85,7 +83,6 @@ var (
staking.BondedPoolName: {supply.Burner, supply.Staking},
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
gov.ModuleName: {supply.Burner},
faucet.ModuleName: {supply.Minter},
}
// module accounts that are allowed to receive tokens
@ -126,7 +123,6 @@ type EthermintApp struct {
ParamsKeeper params.Keeper
EvidenceKeeper evidence.Keeper
EvmKeeper *evm.Keeper
FaucetKeeper faucet.Keeper
// the module manager
mm *module.Manager
@ -157,7 +153,7 @@ func NewEthermintApp(
bam.MainStoreKey, auth.StoreKey, staking.StoreKey,
supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey,
gov.StoreKey, params.StoreKey, upgrade.StoreKey, evidence.StoreKey,
evm.StoreKey, faucet.StoreKey,
evm.StoreKey,
)
tkeys := sdk.NewTransientStoreKeys(params.TStoreKey)
@ -215,9 +211,6 @@ func NewEthermintApp(
app.EvmKeeper = evm.NewKeeper(
app.cdc, keys[evm.StoreKey], app.subspaces[evm.ModuleName], app.AccountKeeper,
)
app.FaucetKeeper = faucet.NewKeeper(
app.cdc, keys[faucet.StoreKey], app.SupplyKeeper,
)
// create evidence keeper with router
evidenceKeeper := evidence.NewKeeper(
@ -261,7 +254,6 @@ func NewEthermintApp(
staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper),
evidence.NewAppModule(app.EvidenceKeeper),
evm.NewAppModule(app.EvmKeeper, app.AccountKeeper),
faucet.NewAppModule(app.FaucetKeeper),
)
// During begin block slashing happens after distr.BeginBlocker so that
@ -281,7 +273,6 @@ func NewEthermintApp(
auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName,
slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName,
evm.ModuleName, crisis.ModuleName, genutil.ModuleName, evidence.ModuleName,
faucet.ModuleName,
)
app.mm.RegisterInvariants(&app.CrisisKeeper)

View File

@ -58,7 +58,6 @@ ignore:
- "docs"
- "*.md"
- "cmd"
- "x/faucet"
- "**/*.pb.go"
- "types/*.pb.go"
- "tests/*"

View File

@ -2,67 +2,79 @@
order: 5
-->
# Joining Chainsafe's Public Testnet
This document outlines the steps to join the public testnet hosted by [Chainsafe](https://chainsafe.io).
This document outlines the steps to join the public testnet hosted by [Chainsafe](https://chainsafe.io).
## Steps:
1. Install the Ethermint binaries (ethermintd & ethermint cli):
```
git clone https://github.com/cosmos/ethermint
cd ethermint
make install
```
## Steps
2. Create an Ethermint account:
```
ethermintcli keys add <keyname>
```
1. Install the Ethermint binaries (ethermintd & ethermint cli)
3. Copy genesis file:
Follow this [link](https://gist.github.com/araskachoi/43f86f3edff23729b817e8b0bb86295a) and copy it over to the directory ~/.ethermintd/config/genesis.json
```bash
git clone https://github.com/cosmos/ethermint
cd ethermint
make install
```
4. Add peers:
Edit the file located in ~/.ethermintd/config/config.toml and edit line 350 (persistent_peers) to the following;
```
"7678d52de4a724e468e503a7743664d12a78b5b0@18.204.206.179:26656,c62fadc76b7fa1ab25669b64fdc00c8d8d422bd0@3.86.104.251:26656,5fa7d4550b57298b059d1dd8af01829482e7d097@54.210.246.165:26656"
```
2. Create an Ethermint account
5. Validate genesis and start the Ethermint network:
```
ethermintd validate-genesis
```
```
ethermintd start --pruning=nothing --rpc.unsafe --log_level "main:info,state:info,mempool:info" --trace
```
(we recommend running the command in the background for convenience)
```bash
ethermintcli keys add <keyname>
```
6. Start the RPC server:
```
ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key $KEY --chain-id etherminttestnet-777 --trace
```
where `$KEY` is the key name that was used in step 2.
(we recommend running the command in the background for convenience)
3. Copy genesis file
7. Request funds from the faucet:
You will need to know the Ethereum hex address, and it can be found with the following command:
Follow this [link](https://gist.github.com/araskachoi/43f86f3edff23729b817e8b0bb86295a) and copy it over to the directory ~/.ethermintd/config/genesis.json
```
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545
```
Using the output of the above command, you will then send the command with your valid Ethereum address:
```
curl --header "Content-Type: application/json" --request POST --data '{"address":"0xYourEthereumHexAddress"}' 3.95.21.91:3000
```
4. Add peers
Edit the file located in ~/.ethermintd/config/config.toml and edit line 350 (persistent_peers) to the following
```toml
"7678d52de4a724e468e503a7743664d12a78b5b0@18.204.206.179:26656,c62fadc76b7fa1ab25669b64fdc00c8d8d422bd0@3.86.104.251:26656,5fa7d4550b57298b059d1dd8af01829482e7d097@54.210.246.165:26656"
```
5. Validate genesis and start the Ethermint network
```bash
ethermintd validate-genesis
ethermintd start --pruning=nothing --rpc.unsafe --log_level "main:info,state:info,mempool:info" --trace
```
(we recommend running the command in the background for convenience)
6. Start the RPC server
```bash
ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key $KEY --chain-id etherminttestnet-777 --trace
```
where `$KEY` is the key name that was used in step 2.
(we recommend running the command in the background for convenience)
7. Request funds from the faucet
You will need to know the Ethereum hex address, and it can be found with the following command:
```bash
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545
```
Using the output of the above command, you will then send the command with your valid Ethereum address
```bash
curl --header "Content-Type: application/json" --request POST --data '{"address":"0xYourEthereumHexAddress"}' 3.95.21.91:3000
```
## Public Testnet Node RPC Endpoints
Node0: `54.210.246.165:8545`
Node1: `3.86.104.251:8545`
Node2: `18.204.206.179:8545`
- **Node0**: `54.210.246.165:8545`
- **Node1**: `3.86.104.251:8545`
- **Node2**: `18.204.206.179:8545`
example:
```
```bash
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' -H "Content-Type: application/json" 54.210.246.165:8545
```
```

View File

@ -12,7 +12,7 @@ Run a local node and start the REST and JSON-RPC clients {synopsis}
## Automated deployment
Run the local node with faucet enabled:
Run the local node
::: warning
The script below will remove any pre-existing binaries installed. Use the manual deploy if you want

View File

@ -311,31 +311,6 @@ For more information on seeds and peers, you can the Tendermint [P2P documentati
The final step is to [start the nodes](./run_node.md#start-node). Once enough voting power (+2/3) from the genesis validators is up-and-running, the testnet will start producing blocks.
## Testnet faucet
Once the ethermint daemon is up and running, you can request tokens to your address using the `faucet` module:
```bash
# query your initial balance
ethermintcli q bank balances $(ethermintcli keys show <mykey> -a)
# send a tx to request tokens to your account address
ethermintcli tx faucet request 100aphoton --from <mykey>
# query your balance after the request
ethermintcli q bank balances $(ethermintcli keys show <mykey> -a)
```
You can also check to total amount funded by the faucet and the total supply of the chain via:
```bash
# total amount funded by the faucet
ethermintcli q faucet funded
# total supply
ethermintcli q supply total
```
## Next {hide}
Learn about how to setup a [validator](./validator-setup.md) node on Ethermint {hide}

View File

@ -29,9 +29,6 @@ cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["crisis"]["constant_f
cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json
cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["mint"]["params"]["mint_denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json
# Enable faucet
cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["faucet"]["enable_faucet"]=true' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json
# increase block time (?)
cat $HOME/.ethermintd/config/genesis.json | jq '.consensus_params["block"]["time_iota_ms"]="30000"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json
@ -56,11 +53,6 @@ ethermintd gentx --name $KEY --amount=1000000000000000000aphoton --keyring-backe
# Collect genesis tx
ethermintd collect-gentxs
echo -e '\n\ntestnet faucet enabled'
echo -e 'to transfer tokens to your account address use:'
echo -e "ethermintcli tx faucet request 100aphoton --from $KEY\n"
# Run this to ensure everything worked and that the genesis file is setup correctly
ethermintd validate-genesis

View File

@ -31,9 +31,6 @@ cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["crisis"]["constant_f
cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json
cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["mint"]["params"]["mint_denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json
# Enable faucet
cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["faucet"]["enable_faucet"]=true' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json
# Allocate genesis accounts (cosmos formatted addresses)
$PWD/build/ethermintd add-genesis-account "$("$PWD"/build/ethermintcli keys show "$KEY$i" -a)" 100000000000000000000aphoton
@ -51,7 +48,7 @@ $PWD/build/ethermintd start --pruning=nothing --rpc.unsafe --log_level "main:inf
sleep 1
# Start the rest server with unlocked faucet key in background and log to file
# Start the rest server with unlocked key in background and log to file
$PWD/build/ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key $KEY --chain-id $CHAINID --trace > ethermintcli.log &
solcjs --abi $PWD/tests-solidity/suites/basic/contracts/Counter.sol --bin -o $PWD/tests-solidity/suites/basic/counter

View File

@ -42,14 +42,6 @@ ethermintd gentx --name $VAL_KEY --keyring-backend test
# Collect genesis tx
ethermintd collect-gentxs
# Enable faucet
cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["faucet"]["enable_faucet"]=true' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json
echo -e '\n\ntestnet faucet enabled'
echo -e 'to transfer tokens to your account address use:'
echo -e "ethermintcli tx faucet request 100aphoton --from $VAL_KEY\n"
# Run this to ensure everything worked and that the genesis file is setup correctly
ethermintd validate-genesis

View File

@ -1,24 +0,0 @@
package faucet
import (
"github.com/cosmos/ethermint/x/faucet/keeper"
"github.com/cosmos/ethermint/x/faucet/types"
)
const (
ModuleName = types.ModuleName
RouterKey = types.RouterKey
StoreKey = types.StoreKey
QuerierRoute = types.QuerierRoute
)
var (
NewKeeper = keeper.NewKeeper
NewQuerier = keeper.NewQuerier
ModuleCdc = types.ModuleCdc
RegisterCodec = types.RegisterCodec
)
type (
Keeper = keeper.Keeper
)

View File

@ -1,53 +0,0 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/ethermint/x/faucet/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GetQueryCmd defines evm module queries through the cli
func GetQueryCmd(cdc *codec.Codec) *cobra.Command {
faucetQueryCmd := &cobra.Command{
Use: types.ModuleName,
Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName),
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
faucetQueryCmd.AddCommand(flags.GetCommands(
GetCmdFunded(cdc),
)...)
return faucetQueryCmd
}
// GetCmdFunded queries the total amount funded by the faucet.
func GetCmdFunded(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "funded",
Short: "Gets storage for an account at a given key",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := context.NewCLIContext().WithCodec(cdc)
res, height, err := clientCtx.Query(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryFunded))
if err != nil {
return err
}
var out sdk.Coins
cdc.MustUnmarshalJSON(res, &out)
clientCtx = clientCtx.WithHeight(height)
return clientCtx.PrintOutput(out)
},
}
}

View File

@ -1,71 +0,0 @@
package cli
import (
"bufio"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
"github.com/cosmos/ethermint/x/faucet/types"
)
// GetTxCmd return faucet sub-command for tx
func GetTxCmd(cdc *codec.Codec) *cobra.Command {
faucetTxCmd := &cobra.Command{
Use: types.ModuleName,
Short: "faucet transaction subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
faucetTxCmd.AddCommand(flags.PostCommands(
GetCmdRequest(cdc),
)...)
return faucetTxCmd
}
// GetCmdRequest is the CLI command to fund an address with the requested coins
func GetCmdRequest(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "request [amount] [other-recipient (optional)]",
Short: "request an address with the requested coins",
Args: cobra.RangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
clientCtx := context.NewCLIContext().WithCodec(cdc)
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc))
amount, err := sdk.ParseCoins(args[0])
if err != nil {
return err
}
var recipient sdk.AccAddress
if len(args) == 1 {
recipient = clientCtx.GetFromAddress()
} else {
recipient, err = sdk.AccAddressFromBech32(args[1])
}
if err != nil {
return err
}
msg := types.NewMsgFund(amount, clientCtx.GetFromAddress(), recipient)
if err := msg.ValidateBasic(); err != nil {
return err
}
return authclient.GenerateOrBroadcastMsgs(clientCtx, txBldr, []sdk.Msg{msg})
},
}
}

View File

@ -1,83 +0,0 @@
package rest
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
"github.com/cosmos/ethermint/x/faucet/types"
)
// RegisterRoutes register REST endpoints for the faucet module
func RegisterRoutes(clientCtx context.CLIContext, r *mux.Router) {
r.HandleFunc(fmt.Sprintf("/%s/request", types.ModuleName), requestHandler(clientCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/%s/funded", types.ModuleName), fundedHandlerFn(clientCtx)).Methods("GET")
}
// PostRequestBody defines fund request's body.
type PostRequestBody struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Amount sdk.Coins `json:"amount" yaml:"amount"`
Recipient string `json:"receipient" yaml:"receipient"`
}
func requestHandler(clientCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req PostRequestBody
if !rest.ReadRESTReq(w, r, clientCtx.Codec, &req) {
rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
return
}
baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) {
return
}
sender, err := sdk.AccAddressFromBech32(baseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
var recipient sdk.AccAddress
if req.Recipient == "" {
recipient = sender
} else {
recipient, err = sdk.AccAddressFromBech32(req.Recipient)
}
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
msg := types.NewMsgFund(req.Amount, sender, recipient)
err = msg.ValidateBasic()
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
authclient.WriteGenerateStdTxResponse(w, clientCtx, baseReq, []sdk.Msg{msg})
}
}
func fundedHandlerFn(clientCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, _ *http.Request) {
res, height, err := clientCtx.Query(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryFunded))
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
clientCtx = clientCtx.WithHeight(height)
rest.PostProcessResponse(w, clientCtx, res)
}
}

View File

@ -1,35 +0,0 @@
package faucet
import (
"fmt"
"github.com/cosmos/ethermint/x/faucet/types"
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/tendermint/abci/types"
)
// InitGenesis initializes genesis state based on exported genesis
func InitGenesis(ctx sdk.Context, k Keeper, data types.GenesisState) []abci.ValidatorUpdate {
if acc := k.GetFaucetAccount(ctx); acc == nil {
panic(fmt.Sprintf("%s module account has not been set", ModuleName))
}
k.SetEnabled(ctx, data.EnableFaucet)
k.SetTimout(ctx, data.Timeout)
k.SetCap(ctx, data.FaucetCap)
k.SetMaxPerRequest(ctx, data.MaxAmountPerRequest)
return []abci.ValidatorUpdate{}
}
// ExportGenesis exports genesis state
func ExportGenesis(ctx sdk.Context, k Keeper) types.GenesisState {
return types.GenesisState{
EnableFaucet: k.IsEnabled(ctx),
Timeout: k.GetTimeout(ctx),
FaucetCap: k.GetCap(ctx),
MaxAmountPerRequest: k.GetMaxPerRequest(ctx),
}
}

View File

@ -1,43 +0,0 @@
package faucet
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/ethermint/x/faucet/types"
)
// NewHandler returns a handler for faucet messages.
func NewHandler(keeper Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())
switch msg := msg.(type) {
case types.MsgFund:
return handleMsgFund(ctx, keeper, msg)
default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg)
}
}
}
// handleMsgFund handles a message to fund an address
func handleMsgFund(ctx sdk.Context, keeper Keeper, msg types.MsgFund) (*sdk.Result, error) {
err := keeper.Fund(ctx, msg.Amount, msg.Recipient)
if err != nil {
return nil, err
}
ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
sdk.NewAttribute(types.AttributeRecipient, msg.Recipient.String()),
),
)
return &sdk.Result{
Events: ctx.EventManager().Events(),
}, nil
}

View File

@ -1,209 +0,0 @@
package keeper
import (
"errors"
"fmt"
"time"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
"github.com/cosmos/ethermint/x/faucet/types"
)
// Keeper defines the faucet Keeper.
type Keeper struct {
cdc *codec.Codec
storeKey sdk.StoreKey
supplyKeeper types.SupplyKeeper
// History of users and their funding timeouts. They are reset if the app is reinitialized.
timeouts map[string]time.Time
}
// NewKeeper creates a new faucet Keeper instance.
func NewKeeper(
cdc *codec.Codec, storeKey sdk.StoreKey, supplyKeeper types.SupplyKeeper,
) Keeper {
return Keeper{
cdc: cdc,
storeKey: storeKey,
supplyKeeper: supplyKeeper,
timeouts: make(map[string]time.Time),
}
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// GetFaucetAccount returns the faucet ModuleAccount
func (k Keeper) GetFaucetAccount(ctx sdk.Context) supplyexported.ModuleAccountI {
return k.supplyKeeper.GetModuleAccount(ctx, types.ModuleName)
}
// Fund checks for timeout and max thresholds and then mints coins and transfers
// coins to the recipient.
func (k Keeper) Fund(ctx sdk.Context, amount sdk.Coins, recipient sdk.AccAddress) error {
if !k.IsEnabled(ctx) {
return errors.New("faucet is not enabled. Restart the application and set faucet's 'enable_faucet' genesis field to true")
}
if err := k.rateLimit(ctx, recipient.String()); err != nil {
return err
}
totalRequested := sdk.ZeroInt()
for _, coin := range amount {
totalRequested = totalRequested.Add(coin.Amount)
}
maxPerReq := k.GetMaxPerRequest(ctx)
if totalRequested.GT(maxPerReq) {
return fmt.Errorf("canot fund more than %s per request. requested %s", maxPerReq, totalRequested)
}
funded := k.GetFunded(ctx)
totalFunded := sdk.ZeroInt()
for _, coin := range funded {
totalFunded = totalFunded.Add(coin.Amount)
}
cap := k.GetCap(ctx)
if totalFunded.Add(totalRequested).GT(cap) {
return fmt.Errorf("maximum cap of %s reached. Cannot continue funding", cap)
}
if err := k.supplyKeeper.MintCoins(ctx, types.ModuleName, amount); err != nil {
return err
}
if err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipient, amount); err != nil {
return err
}
k.SetFunded(ctx, funded.Add(amount...))
k.Logger(ctx).Info(fmt.Sprintf("funded %s to %s", amount, recipient))
return nil
}
func (k Keeper) GetTimeout(ctx sdk.Context) time.Duration {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.TimeoutKey)
if len(bz) == 0 {
return time.Duration(0)
}
var timeout time.Duration
k.cdc.MustUnmarshalBinaryBare(bz, &timeout)
return timeout
}
func (k Keeper) SetTimout(ctx sdk.Context, timeout time.Duration) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryBare(timeout)
store.Set(types.TimeoutKey, bz)
}
func (k Keeper) IsEnabled(ctx sdk.Context) bool {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.EnableFaucetKey)
if len(bz) == 0 {
return false
}
var enabled bool
k.cdc.MustUnmarshalBinaryBare(bz, &enabled)
return enabled
}
func (k Keeper) SetEnabled(ctx sdk.Context, enabled bool) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryBare(enabled)
store.Set(types.EnableFaucetKey, bz)
}
func (k Keeper) GetCap(ctx sdk.Context) sdk.Int {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.CapKey)
if len(bz) == 0 {
return sdk.ZeroInt()
}
var cap sdk.Int
k.cdc.MustUnmarshalBinaryBare(bz, &cap)
return cap
}
func (k Keeper) SetCap(ctx sdk.Context, cap sdk.Int) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryBare(cap)
store.Set(types.CapKey, bz)
}
func (k Keeper) GetMaxPerRequest(ctx sdk.Context) sdk.Int {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.MaxPerRequestKey)
if len(bz) == 0 {
return sdk.ZeroInt()
}
var maxPerReq sdk.Int
k.cdc.MustUnmarshalBinaryBare(bz, &maxPerReq)
return maxPerReq
}
func (k Keeper) SetMaxPerRequest(ctx sdk.Context, maxPerReq sdk.Int) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryBare(maxPerReq)
store.Set(types.MaxPerRequestKey, bz)
}
func (k Keeper) GetFunded(ctx sdk.Context) sdk.Coins {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.FundedKey)
if len(bz) == 0 {
return nil
}
var funded sdk.Coins
k.cdc.MustUnmarshalBinaryBare(bz, &funded)
return funded
}
func (k Keeper) SetFunded(ctx sdk.Context, funded sdk.Coins) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryBare(funded)
store.Set(types.FundedKey, bz)
}
func (k Keeper) rateLimit(ctx sdk.Context, address string) error {
// first time requester, can send request
lastRequest, ok := k.timeouts[address]
if !ok {
k.timeouts[address] = time.Now().UTC()
return nil
}
defaultTimeout := k.GetTimeout(ctx)
sinceLastRequest := time.Since(lastRequest)
if defaultTimeout > sinceLastRequest {
wait := defaultTimeout - sinceLastRequest
return fmt.Errorf("%s has requested funds within the last %s, wait %s before trying again", address, defaultTimeout.String(), wait.String())
}
// user able to send funds since they have waited for period
k.timeouts[address] = time.Now().UTC()
return nil
}

View File

@ -1,34 +0,0 @@
package keeper
import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/ethermint/x/faucet/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// NewQuerier is the module level router for state queries
func NewQuerier(k Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) {
switch path[0] {
case types.QueryFunded:
return queryFunded(ctx, req, k)
default:
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query endpoint")
}
}
}
func queryFunded(ctx sdk.Context, _ abci.RequestQuery, k Keeper) ([]byte, error) {
funded := k.GetFunded(ctx)
bz, err := codec.MarshalJSONIndent(k.cdc, funded)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}

View File

@ -1,137 +0,0 @@
package faucet
import (
"encoding/json"
"fmt"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/ethermint/x/faucet/client/cli"
"github.com/cosmos/ethermint/x/faucet/client/rest"
"github.com/cosmos/ethermint/x/faucet/types"
)
// type check to ensure the interface is properly implemented
var (
_ module.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
)
// AppModuleBasic defines the basic application module used by the faucet module.
type AppModuleBasic struct{}
// Name returns the faucet module's name.
func (AppModuleBasic) Name() string {
return types.ModuleName
}
// RegisterCodec registers the faucet module's types for the given codec.
func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {
RegisterCodec(cdc)
}
// DefaultGenesis returns default genesis state as raw bytes for the faucet
// module.
func (AppModuleBasic) DefaultGenesis() json.RawMessage {
return types.ModuleCdc.MustMarshalJSON(types.DefaultGenesisState())
}
// ValidateGenesis performs genesis state validation for the faucet module.
func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error {
var genesisState types.GenesisState
if err := types.ModuleCdc.UnmarshalJSON(bz, &genesisState); err != nil {
return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
}
return genesisState.Validate()
}
// RegisterRESTRoutes registers the REST routes for the faucet module.
func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) {
rest.RegisterRoutes(ctx, rtr)
}
// GetTxCmd returns the root tx command for the faucet module.
func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command {
return cli.GetTxCmd(cdc)
}
// GetQueryCmd returns no root query command for the faucet module.
func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
return cli.GetQueryCmd(cdc)
}
type AppModule struct {
AppModuleBasic
keeper Keeper
}
// NewAppModule creates a new AppModule Object
func NewAppModule(k Keeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
keeper: k,
}
}
// Name returns the faucet module's name.
func (AppModule) Name() string {
return ModuleName
}
// RegisterInvariants registers the faucet module invariants.
func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
// Route returns the message routing key for the faucet module.
func (AppModule) Route() string {
return RouterKey
}
// NewHandler returns an sdk.Handler for the faucet module.
func (am AppModule) NewHandler() sdk.Handler {
return NewHandler(am.keeper)
}
// QuerierRoute returns the faucet module's querier route name.
func (AppModule) QuerierRoute() string {
return QuerierRoute
}
// NewQuerierHandler returns the faucet module sdk.Querier.
func (am AppModule) NewQuerierHandler() sdk.Querier {
return NewQuerier(am.keeper)
}
// InitGenesis performs genesis initialization for the faucet module. It returns
// no validator updates.
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState types.GenesisState
types.ModuleCdc.MustUnmarshalJSON(data, &genesisState)
InitGenesis(ctx, am.keeper, genesisState)
return []abci.ValidatorUpdate{}
}
// ExportGenesis returns the exported genesis state as raw bytes for the faucet
// module.
func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
gs := ExportGenesis(ctx, am.keeper)
return types.ModuleCdc.MustMarshalJSON(gs)
}
// BeginBlock returns the begin blocker for the faucet module.
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
}
// EndBlock returns the end blocker for the faucet module. It returns no validator
// updates.
func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
return []abci.ValidatorUpdate{}
}

View File

@ -1,17 +0,0 @@
package types
import (
"github.com/cosmos/cosmos-sdk/codec"
)
// ModuleCdc is the codec for the module
var ModuleCdc = codec.New()
func init() {
RegisterCodec(ModuleCdc)
}
// RegisterCodec registers concrete types on the Amino codec
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(MsgFund{}, "ethermint/MsgFund", nil)
}

View File

@ -1,10 +0,0 @@
package types
import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
var (
// ErrWithdrawTooOften withdraw too often
ErrWithdrawTooOften = sdkerrors.Register(ModuleName, 2, "each address can withdraw only once")
)

View File

@ -1,11 +0,0 @@
package types
// Faucet module events
const (
AttributeRecipient string = "recipient"
)
// Supported endpoints
const (
QueryFunded = "funded"
)

View File

@ -1,20 +0,0 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// SupplyKeeper is required for mining coin
type SupplyKeeper interface {
MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
SendCoinsFromModuleToAccount(
ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins,
) error
GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI
}
// StakingKeeper is required for getting Denom
type StakingKeeper interface {
BondDenom(ctx sdk.Context) string
}

View File

@ -1,45 +0,0 @@
package types
import (
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenesisState defines the application's genesis state. It contains all the
// information required and accounts to initialize the blockchain.
type GenesisState struct {
// enable faucet funding
EnableFaucet bool `json:"enable_faucet" yaml:"enable_faucet"`
// addresses can send requests every <Timeout> duration
Timeout time.Duration `json:"timeout" yaml:"timeout"`
// max total amount to be funded by the faucet
FaucetCap sdk.Int `json:"faucet_cap" yaml:"faucet_cap"`
// max amount per request (i.e sum of all requested coin amounts).
MaxAmountPerRequest sdk.Int `json:"max_amount_per_request" yaml:"max_amount_per_request"`
}
// Validate performs a basic validation of the GenesisState fields.
func (gs GenesisState) Validate() error {
if gs.Timeout < 0 {
return fmt.Errorf("timeout cannot be negative: %s", gs.Timeout)
}
if gs.FaucetCap.IsNegative() {
return fmt.Errorf("faucet cap cannot be negative: %d", gs.FaucetCap)
}
if gs.MaxAmountPerRequest.IsNegative() {
return fmt.Errorf("max amount per request cannot be negative: %d", gs.MaxAmountPerRequest)
}
return nil
}
// DefaultGenesisState sets default evm genesis config
func DefaultGenesisState() GenesisState {
return GenesisState{
EnableFaucet: false,
Timeout: 20 * time.Minute,
FaucetCap: sdk.NewInt(1000000000),
MaxAmountPerRequest: sdk.NewInt(1000),
}
}

View File

@ -1,23 +0,0 @@
package types
const (
// ModuleName is the name of the module
ModuleName = "faucet"
// StoreKey to be used when creating the KVStore
StoreKey = ModuleName
// RouterKey uses module name for tx routing
RouterKey = ModuleName
// QuerierRoute uses module name for query routing
QuerierRoute = ModuleName
)
var (
EnableFaucetKey = []byte{0x01}
TimeoutKey = []byte{0x02}
CapKey = []byte{0x03}
MaxPerRequestKey = []byte{0x04}
FundedKey = []byte{0x05}
)

View File

@ -1,52 +0,0 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// MsgFund funds a recipient address
type MsgFund struct {
Amount sdk.Coins `json:"amount" yaml:"amount"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Recipient sdk.AccAddress `json:"receipient" yaml:"receipient"`
}
// NewMsgFund is a constructor function for NewMsgFund
func NewMsgFund(amount sdk.Coins, sender, recipient sdk.AccAddress) MsgFund {
return MsgFund{
Amount: amount,
Sender: sender,
Recipient: recipient,
}
}
// Route should return the name of the module
func (msg MsgFund) Route() string { return RouterKey }
// Type should return the action
func (msg MsgFund) Type() string { return "fund" }
// ValidateBasic runs stateless checks on the message
func (msg MsgFund) ValidateBasic() error {
if !msg.Amount.IsValid() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String())
}
if msg.Sender.Empty() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "sender %s", msg.Sender.String())
}
if msg.Recipient.Empty() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "recipient %s", msg.Recipient.String())
}
return nil
}
// GetSignBytes encodes the message for signing
func (msg MsgFund) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
}
// GetSigners defines whose signature is required
func (msg MsgFund) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Sender}
}