Skip to content

Commit

Permalink
Add base faucet logic
Browse files Browse the repository at this point in the history
  • Loading branch information
zivkovicmilos committed Sep 14, 2023
0 parents commit b4b8cef
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 0 deletions.
17 changes: 17 additions & 0 deletions cmd/faucet/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package faucet

import (
"context"
"fmt"
"os"

"github.com/gnolang/faucet/cmd/root"
)

func main() {
if err := root.New().ParseAndRun(context.Background(), os.Args[1:]); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)

os.Exit(1)
}
}
109 changes: 109 additions & 0 deletions cmd/root/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package root

import (
"context"
"flag"

"github.com/gnolang/faucet/config"
"github.com/peterbourgon/ff/v3"
"github.com/peterbourgon/ff/v3/ffcli"
"github.com/peterbourgon/ff/v3/fftoml"
)

const (
configFlagName = "config"
envPrefix = "GNO_FAUCET"
)

// faucetCfg wraps the faucet
// startup command configuration
type faucetCfg struct {
config.Config

corsConfigPath string
}

// New creates the root faucet command
func New() *ffcli.Command {
cfg := &faucetCfg{}

fs := flag.NewFlagSet("start", flag.ExitOnError)
registerFlags(cfg, fs)

return &ffcli.Command{
Name: "start",
ShortUsage: "start [flags]",
LongHelp: "Starts the Gno faucet service",
FlagSet: fs,
Exec: cfg.exec,
Options: []ff.Option{
// Allow using ENV variables
ff.WithEnvVars(),
ff.WithEnvVarPrefix(envPrefix),

// Allow using TOML config files
ff.WithConfigFileFlag(configFlagName),
ff.WithConfigFileParser(fftoml.Parser),
},
}
}

// registerFlags registers the faucet root command flags
func registerFlags(cfg *faucetCfg, fs *flag.FlagSet) {
// Config flag
fs.String(
configFlagName,
"",
"the path to the configuration file [TOML]",
)

// Top level flags
fs.StringVar(
&cfg.ListenAddress,
"listen-address",
"0.0.0.0:8545",
"the IP:PORT URL for the faucet server",
)

fs.StringVar(
&cfg.Remote,
"remote",
"http://127.0.0.1:26657",
"the JSON-RPC URL of the Gno chain",
)

fs.StringVar(
&cfg.ChainID,
"chain-id",
"dev",
"the chain ID associated with the remote Gno chain",
)

fs.StringVar(
&cfg.GasFee,
"gas-fee",
"1000000ugnot",
"the static gas fee for the transaction",
)

fs.StringVar(
&cfg.GasWanted,
"gas-wanted",
"100000",
"the static gas wanted for the transaction",
)

fs.StringVar(
&cfg.corsConfigPath,
"cors-config",
"",
"the path to the CORS TOML configuration, if any",
)
}

// exec executes the faucet start command
func (c *faucetCfg) exec(context.Context, []string) error {
// TODO

return nil
}
25 changes: 25 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package config

// Config defines the base-level Faucet configuration
type Config struct {
// The address at which the faucet will be served.
// Format should be: <IP>:<PORT>
ListenAddress string `toml:"listen_address"`

// The JSON-RPC URL of the Gno chain
Remote string `toml:"remote"`

// The chain ID associated with the remote Gno chain
ChainID string `toml:"chain_id"`

// The static gas fee for the transaction.
// Format should be: <AMOUNT>ugnot
GasFee string `toml:"gas_fee"`

// The static gas wanted for the transaction.
// Format should be: <AMOUNT>ugnot
GasWanted string `toml:"gas_wanted"`

// The associated CORS config, if any
CORSConfig *CORS `toml:"cors_config"`
}
16 changes: 16 additions & 0 deletions config/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package config

// CORS defines the Faucet CORS configuration
type CORS struct {
// A list of origins a cross-domain request can be executed from.
// If the special '*' value is present in the list, all origins will be allowed.
// An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com).
// Only one wildcard can be used per origin
AllowedOrigins []string `toml:"cors_allowed_origins"`

// A list of non-simple headers the client is allowed to use with cross-domain requests
AllowedHeaders []string `toml:"cors_allowed_headers"`

// A list of methods the client is allowed to use with cross-domain requests
AllowedMethods []string `toml:"cors_allowed_methods"`
}
84 changes: 84 additions & 0 deletions faucet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package faucet

import (
"context"
"errors"
"fmt"
"net/http"
"time"

"github.com/gnolang/faucet/config"
"github.com/go-chi/chi/v5"
"golang.org/x/sync/errgroup"
)

// Faucet is a standard Gno
// native currency faucet
type Faucet struct {
estimator Estimator // gas pricing estimations
logger Logger // log feedback

mux *chi.Mux // HTTP routing

config *config.Config // faucet configuration
middlewares []Middleware // request middlewares
}

// NewFaucet creates a new instance of the Gno faucet server
func NewFaucet(opts ...Option) *Faucet {
f := &Faucet{
estimator: nil, // TODO static estimator
logger: nil, // TODO nil logger
config: nil, // TODO default config
middlewares: nil, // TODO no middlewares

mux: chi.NewMux(),
}

// Apply the options
for _, opt := range opts {
opt(f)
}

return f
}

// Serve serves the Gno faucet [BLOCKING]
func (f *Faucet) Serve(ctx context.Context) error {
faucet := &http.Server{
Addr: f.config.ListenAddress,
Handler: f.mux,
ReadHeaderTimeout: 60 * time.Second,
}

group, gCtx := errgroup.WithContext(ctx)

group.Go(func() error {
f.logger.Info(
fmt.Sprintf(
"faucet started at %s",
f.config.ListenAddress,
),
)
defer f.logger.Info("faucet shut down")

if err := faucet.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
}

return nil
})

group.Go(func() error {
<-gCtx.Done()

f.logger.Info("faucet to be shutdown")

wsCtx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()

return faucet.Shutdown(wsCtx)
})

return group.Wait()
}
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/gnolang/faucet

go 1.21

require (
github.com/go-chi/chi/v5 v5.0.10
github.com/peterbourgon/ff/v3 v3.4.0
golang.org/x/sync v0.3.0
)

require github.com/pelletier/go-toml v1.9.5 // indirect
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc=
github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
31 changes: 31 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package faucet

import (
"github.com/gnolang/faucet/config"
)

type Option func(f *Faucet)

func WithLogger(l Logger) Option {
return func(f *Faucet) {
f.logger = l
}
}

func WithEstimator(e Estimator) Option {
return func(f *Faucet) {
f.estimator = e
}
}

func WithConfig(c *config.Config) Option {
return func(f *Faucet) {
f.config = c
}
}

func WithMiddlewares(middlewares []Middleware) Option {
return func(f *Faucet) {
f.middlewares = middlewares
}
}
15 changes: 15 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package faucet

import "net/http"

type Estimator interface {
// TODO define
}

type Logger interface {
Info(msg string, args ...interface{})
Debug(msg string, args ...interface{})
Error(msg string, args ...interface{})
}

type Middleware func(next http.Handler) http.Handler

0 comments on commit b4b8cef

Please sign in to comment.