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).
## Steps:
1. Install the Ethermint binaries (ethermintd & ethermint cli):
```
## Steps
1. Install the Ethermint binaries (ethermintd & ethermint cli)
```bash
git clone https://github.com/cosmos/ethermint
cd ethermint
make install
```
2. Create an Ethermint account:
```
2. Create an Ethermint account
```bash
ethermintcli keys add <keyname>
```
3. Copy genesis file:
3. Copy genesis file
Follow this [link](https://gist.github.com/araskachoi/43f86f3edff23729b817e8b0bb86295a) and copy it over to the directory ~/.ethermintd/config/genesis.json
4. Add peers:
Edit the file located in ~/.ethermintd/config/config.toml and edit line 350 (persistent_peers) to the following;
```
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:
```
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:
```
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:
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:
```
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}
}