Skip to content
This repository has been archived by the owner on Nov 16, 2022. It is now read-only.

chain:Implement faucet service for testnet #2058

Merged
merged 10 commits into from
Jun 24, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion chain/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ COPY chain/ /chain

COPY chain/docker-config/run.sh .

RUN make install
RUN make install && make faucet

CMD bandd start --rpc.laddr tcp://0.0.0.0:26657
3 changes: 3 additions & 0 deletions chain/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ install: go.sum
go install -mod=readonly $(BUILD_FLAGS) ./cmd/bandcli
go install -mod=readonly $(BUILD_FLAGS) ./cmd/bandoracled2

faucet: go.sum
go install -mod=readonly $(BUILD_FLAGS) ./cmd/faucet

release: go.sum
env GOOS=linux GOARCH=amd64 \
go build -mod=readonly -o ./build/bandd_linux_amd64 $(BUILD_FLAGS) ./cmd/bandd
Expand Down
20 changes: 20 additions & 0 deletions chain/cmd/faucet/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func configCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "config [key] [value]",
Aliases: []string{"c"},
Short: "Set faucet configuration environment",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
viper.Set(args[0], args[1])
return viper.WriteConfig()
},
}
return cmd
}
14 changes: 14 additions & 0 deletions chain/cmd/faucet/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package main

import (
"github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
rpcclient "github.com/tendermint/tendermint/rpc/client"
)

type Context struct {
client rpcclient.Client
gasPrices sdk.DecCoins
keys chan keys.Info
amount sdk.Coins
}
82 changes: 82 additions & 0 deletions chain/cmd/faucet/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package main

import (
"fmt"
"net/http"

"github.com/bandprotocol/bandchain/chain/app"
sdkctx "github.com/cosmos/cosmos-sdk/client/context"
ckeys "github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/gin-gonic/gin"
)

type Request struct {
Address string `json:"address" binding:"required"`
}

type Response struct {
TxHash string `json:"txHash"`
}

var (
cdc = app.MakeCodec()
)

func handleRequest(gc *gin.Context, c *Context) {
key := <-c.keys
defer func() {
c.keys <- key
}()

var req Request
if err := gc.ShouldBindJSON(&req); err != nil {
gc.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
to, err := sdk.AccAddressFromBech32(req.Address)
if err != nil {
gc.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
msg := bank.NewMsgSend(key.GetAddress(), to, c.amount)
if err := msg.ValidateBasic(); err != nil {
gc.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

cliCtx := sdkctx.CLIContext{Client: c.client}
acc, err := auth.NewAccountRetriever(cliCtx).GetAccount(key.GetAddress())
if err != nil {
gc.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

txBldr := auth.NewTxBuilder(
auth.DefaultTxEncoder(cdc), acc.GetAccountNumber(), acc.GetSequence(),
200000, 1, false, cfg.ChainID, "", sdk.NewCoins(), c.gasPrices,
)
out, err := txBldr.WithKeybase(keybase).BuildAndSign(key.GetName(), ckeys.DefaultKeyPass, []sdk.Msg{msg})
if err != nil {
gc.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

res, err := cliCtx.BroadcastTxCommit(out)
if err != nil {
gc.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if res.Code != 0 {
gc.JSON(http.StatusInternalServerError, gin.H{
"error": fmt.Sprintf(":exploding_head: Tx returned nonzero code %d with log %s, tx hash: %s",
res.Code, res.RawLog, res.TxHash,
)})
return
}
gc.JSON(200, Response{
TxHash: res.TxHash,
})
}
165 changes: 165 additions & 0 deletions chain/cmd/faucet/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package main

import (
"bufio"
"fmt"

"github.com/cosmos/cosmos-sdk/client/input"
ckeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/go-bip39"
"github.com/spf13/cobra"
)

const (
flagAccount = "account"
flagIndex = "index"
flagCoinType = "coin-type"
flagRecover = "recover"
)

func keysCmd(c *Context) *cobra.Command {
cmd := &cobra.Command{
Use: "keys",
Aliases: []string{"k"},
Short: "Manage key held by the oracle process",
}
cmd.AddCommand(keysAddCmd(c))
cmd.AddCommand(keysDeleteCmd(c))
cmd.AddCommand(keysListCmd(c))
cmd.AddCommand(keysShowCmd(c))
return cmd
}

func keysAddCmd(c *Context) *cobra.Command {
cmd := &cobra.Command{
Use: "add [name]",
Aliases: []string{"a"},
Short: "Add a new key to the keychain",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
var mnemonic string
recover, err := cmd.Flags().GetBool(flagRecover)
if err != nil {
return err
}
if recover {
inBuf := bufio.NewReader(cmd.InOrStdin())
var err error
mnemonic, err = input.GetString("Enter your bip39 mnemonic", inBuf)
if err != nil {
return err
}
} else {
seed, err := bip39.NewEntropy(256)
if err != nil {
return err
}
mnemonic, err = bip39.NewMnemonic(seed)
if err != nil {
return err
}
fmt.Printf("Mnemonic: %s\n", mnemonic)
}

if err != nil {
return err
}
account, err := cmd.Flags().GetUint32(flagAccount)
if err != nil {
return err
}
index, err := cmd.Flags().GetUint32(flagIndex)
if err != nil {
return err
}
hdPath := keys.CreateHDPath(account, index)
info, err := keybase.CreateAccount(args[0], mnemonic, "", ckeys.DefaultKeyPass, hdPath.String(), keys.Secp256k1)
if err != nil {
return err
}
fmt.Printf("Address: %s\n", info.GetAddress().String())
return nil
},
}
cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating")
cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation")
cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation")
return cmd
}

func keysDeleteCmd(c *Context) *cobra.Command {
cmd := &cobra.Command{
Use: "delete [name]",
Aliases: []string{"d"},
Short: "Delete a key from the keychain",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]

_, err := keybase.Get(name)
if err != nil {
return err
}

inBuf := bufio.NewReader(cmd.InOrStdin())
confirmInput, err := input.GetString("Key will be deleted. Continue?[y/N]", inBuf)
if err != nil {
return err
}

if confirmInput != "y" {
fmt.Println("Cancel")
return nil
}

if err := keybase.Delete(name, "", true); err != nil {
return err
}

fmt.Printf("Deleted key: %s\n", name)
return nil
},
}
return cmd
}

func keysListCmd(c *Context) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Aliases: []string{"l"},
Short: "List all the keys in the keychain",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
keys, err := keybase.List()
if err != nil {
return err
}
for _, key := range keys {
fmt.Printf("%s => %s\n", key.GetName(), key.GetAddress().String())
}
return nil
},
}
return cmd
}

func keysShowCmd(c *Context) *cobra.Command {
cmd := &cobra.Command{
Use: "show [name]",
Aliases: []string{"s"},
Short: "Show address from name in the keychain",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]

key, err := keybase.Get(name)
if err != nil {
return err
}
fmt.Println(key.GetAddress().String())
return nil
},
}
return cmd
}
84 changes: 84 additions & 0 deletions chain/cmd/faucet/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package main

import (
"fmt"
"os"
"path"

"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/bandprotocol/bandchain/chain/app"
)

const (
flagValidator = "validator"
flagExecutor = "executor"
flagChainRestServerURI = "chain-rest-server"
flagPort = "port"
flagAmount = "amount"
)

// Config data structure for faucet server.
type Config struct {
ChainID string `mapstructure:"chain-id"` // ChainID of the target chain
NodeURI string `mapstructure:"node"` // Remote RPC URI of BandChain node to connect to
GasPrices string `mapstructure:"gas-prices"` // Gas prices of the transaction
Port string `mapstructure:"port"` // Port of faucet service
Amount int64 `mapstructure:"amount"` // Amount of BAND for each request
}

// Global instances.
var (
cfg Config
keybase keys.Keybase
)

func initConfig(cmd *cobra.Command) error {
home, err := cmd.PersistentFlags().GetString(flags.FlagHome)
if err != nil {
return err
}
viper.SetConfigFile(path.Join(home, "config.yaml"))
_ = viper.ReadInConfig() // If we fail to read config file, we'll just rely on cmd flags.
if err := viper.Unmarshal(&cfg); err != nil {
return err
}
return nil
}

func main() {
appConfig := sdk.GetConfig()
app.SetBech32AddressPrefixesAndBip44CoinType(appConfig)
appConfig.Seal()

ctx := &Context{}
rootCmd := &cobra.Command{
Use: "faucet",
Short: "Faucet server for devnet",
}

rootCmd.AddCommand(configCmd(), keysCmd(ctx), runCmd(ctx))
rootCmd.PersistentPreRunE = func(_ *cobra.Command, _ []string) error {
home, err := rootCmd.PersistentFlags().GetString(flags.FlagHome)
if err != nil {
return err
}
if err := os.MkdirAll(home, os.ModePerm); err != nil {
return err
}
keybase, err = keys.NewKeyring("band", "test", home, nil)
if err != nil {
return err
}
return initConfig(rootCmd)
}
rootCmd.PersistentFlags().String(flags.FlagHome, os.ExpandEnv("$HOME/.faucet"), "home directory")
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
Loading