From 6479820fb453d215879a69452e6724c2da991498 Mon Sep 17 00:00:00 2001 From: Mike Zak Date: Tue, 17 Dec 2019 15:09:07 +0200 Subject: [PATCH] [NOD-495] Remove faucet to separate repository --- faucet/config/config.go | 121 ------- faucet/database/database.go | 151 -------- faucet/database/log.go | 9 - faucet/docker/Dockerfile | 28 -- faucet/faucet.go | 332 ------------------ faucet/ip_usage.go | 66 ---- faucet/log.go | 11 - faucet/main.go | 88 ----- .../000001_create_ip_uses_table.down.sql | 1 - .../000001_create_ip_uses_table.up.sql | 6 - faucet/server.go | 81 ----- 11 files changed, 894 deletions(-) delete mode 100644 faucet/config/config.go delete mode 100644 faucet/database/database.go delete mode 100644 faucet/database/log.go delete mode 100644 faucet/docker/Dockerfile delete mode 100644 faucet/faucet.go delete mode 100644 faucet/ip_usage.go delete mode 100644 faucet/log.go delete mode 100644 faucet/main.go delete mode 100644 faucet/migrations/000001_create_ip_uses_table.down.sql delete mode 100644 faucet/migrations/000001_create_ip_uses_table.up.sql delete mode 100644 faucet/server.go diff --git a/faucet/config/config.go b/faucet/config/config.go deleted file mode 100644 index 024b06461..000000000 --- a/faucet/config/config.go +++ /dev/null @@ -1,121 +0,0 @@ -package config - -import ( - "github.com/jessevdk/go-flags" - "github.com/kaspanet/kaspad/dagconfig" - "github.com/kaspanet/kaspad/kasparov/logger" - "github.com/kaspanet/kaspad/util" - "github.com/pkg/errors" - "path/filepath" -) - -const ( - defaultLogFilename = "faucet.log" - defaultErrLogFilename = "faucet_err.log" -) - -var ( - // Default configuration options - defaultLogDir = util.AppDataDir("faucet", false) - defaultDBAddress = "localhost:3306" - defaultHTTPListen = "0.0.0.0:8081" - - // activeNetParams are the currently active net params - activeNetParams *dagconfig.Params -) - -// Config defines the configuration options for the API server. -type Config struct { - LogDir string `long:"logdir" description:"Directory to log output."` - HTTPListen string `long:"listen" description:"HTTP address to listen on (default: 0.0.0.0:8081)"` - KasparovdURL string `long:"kasparovd-url" description:"The API server url to connect to"` - PrivateKey string `long:"private-key" description:"Faucet Private key"` - DBAddress string `long:"dbaddress" description:"Database address"` - DBUser string `long:"dbuser" description:"Database user" required:"true"` - DBPassword string `long:"dbpass" description:"Database password" required:"true"` - DBName string `long:"dbname" description:"Database name" required:"true"` - Migrate bool `long:"migrate" description:"Migrate the database to the latest version. The server will not start when using this flag."` - FeeRate float64 `long:"fee-rate" description:"Coins per gram fee rate"` - TestNet bool `long:"testnet" description:"Connect to testnet"` - SimNet bool `long:"simnet" description:"Connect to the simulation test network"` - DevNet bool `long:"devnet" description:"Connect to the development test network"` -} - -var cfg *Config - -// Parse parses the CLI arguments and returns a config struct. -func Parse() error { - cfg = &Config{ - LogDir: defaultLogDir, - DBAddress: defaultDBAddress, - HTTPListen: defaultHTTPListen, - } - parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag) - _, err := parser.Parse() - if err != nil { - return err - } - - if !cfg.Migrate { - if cfg.KasparovdURL == "" { - return errors.New("api-server-url argument is required when --migrate flag is not raised") - } - if cfg.PrivateKey == "" { - return errors.New("private-key argument is required when --migrate flag is not raised") - } - } - - err = resolveNetwork(cfg) - if err != nil { - return err - } - - logFile := filepath.Join(cfg.LogDir, defaultLogFilename) - errLogFile := filepath.Join(cfg.LogDir, defaultErrLogFilename) - logger.InitLog(logFile, errLogFile) - - return nil -} - -func resolveNetwork(cfg *Config) error { - // Multiple networks can't be selected simultaneously. - numNets := 0 - if cfg.TestNet { - numNets++ - } - if cfg.SimNet { - numNets++ - } - if cfg.DevNet { - numNets++ - } - if numNets > 1 { - return errors.New("multiple net params (testnet, simnet, devnet, etc.) can't be used " + - "together -- choose one of them") - } - - activeNetParams = &dagconfig.MainNetParams - switch { - case cfg.TestNet: - activeNetParams = &dagconfig.TestNetParams - case cfg.SimNet: - activeNetParams = &dagconfig.SimNetParams - case cfg.DevNet: - activeNetParams = &dagconfig.DevNetParams - } - - return nil -} - -// MainConfig is a getter to the main config -func MainConfig() (*Config, error) { - if cfg == nil { - return nil, errors.New("No configuration was set for the faucet") - } - return cfg, nil -} - -// ActiveNetParams returns the currently active net params -func ActiveNetParams() *dagconfig.Params { - return activeNetParams -} diff --git a/faucet/database/database.go b/faucet/database/database.go deleted file mode 100644 index 5e6c0f4a3..000000000 --- a/faucet/database/database.go +++ /dev/null @@ -1,151 +0,0 @@ -package database - -import ( - nativeerrors "errors" - "fmt" - "github.com/pkg/errors" - "os" - - "github.com/golang-migrate/migrate/v4/source" - "github.com/jinzhu/gorm" - "github.com/kaspanet/kaspad/faucet/config" - - "github.com/golang-migrate/migrate/v4" -) - -// db is the API server database. -var db *gorm.DB - -// DB returns a reference to the database connection -func DB() (*gorm.DB, error) { - if db == nil { - return nil, errors.New("Database is not connected") - } - return db, nil -} - -type gormLogger struct{} - -func (l gormLogger) Print(v ...interface{}) { - str := fmt.Sprint(v...) - log.Errorf(str) -} - -// Connect connects to the database mentioned in -// config variable. -func Connect() error { - connectionString, err := buildConnectionString() - if err != nil { - return err - } - migrator, driver, err := openMigrator(connectionString) - if err != nil { - return err - } - isCurrent, version, err := isCurrent(migrator, driver) - if err != nil { - return errors.Errorf("Error checking whether the database is current: %s", err) - } - if !isCurrent { - return errors.Errorf("Database is not current (version %d). Please migrate"+ - " the database by running the faucet with --migrate flag and then run it again.", version) - } - - db, err = gorm.Open("mysql", connectionString) - if err != nil { - return err - } - - db.SetLogger(gormLogger{}) - return nil -} - -// Close closes the connection to the database -func Close() error { - if db == nil { - return nil - } - err := db.Close() - db = nil - return err -} - -func buildConnectionString() (string, error) { - cfg, err := config.MainConfig() - if err != nil { - return "", err - } - return fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True", - cfg.DBUser, cfg.DBPassword, cfg.DBAddress, cfg.DBName), nil -} - -// isCurrent resolves whether the database is on the latest -// version of the schema. -func isCurrent(migrator *migrate.Migrate, driver source.Driver) (bool, uint, error) { - // Get the current version - version, isDirty, err := migrator.Version() - if nativeerrors.Is(err, migrate.ErrNilVersion) { - return false, 0, nil - } - if err != nil { - return false, 0, err - } - if isDirty { - return false, 0, errors.Errorf("Database is dirty") - } - - // The database is current if Next returns ErrNotExist - _, err = driver.Next(version) - if pathErr, ok := err.(*os.PathError); ok { - if pathErr.Err == os.ErrNotExist { - return true, version, nil - } - } - return false, version, err -} - -func openMigrator(connectionString string) (*migrate.Migrate, source.Driver, error) { - driver, err := source.Open("file://migrations") - if err != nil { - return nil, nil, err - } - migrator, err := migrate.NewWithSourceInstance( - "migrations", driver, "mysql://"+connectionString) - if err != nil { - return nil, nil, err - } - return migrator, driver, nil -} - -// Migrate database to the latest version. -func Migrate() error { - connectionString, err := buildConnectionString() - if err != nil { - return err - } - migrator, driver, err := openMigrator(connectionString) - if err != nil { - return err - } - isCurrent, version, err := isCurrent(migrator, driver) - if err != nil { - return errors.Errorf("Error checking whether the database is current: %s", err) - } - if isCurrent { - log.Infof("Database is already up-to-date (version %d)", version) - return nil - } - err = migrator.Up() - if err != nil { - return err - } - version, isDirty, err := migrator.Version() - if err != nil { - return err - } - if isDirty { - return errors.Errorf("error migrating database: database is dirty") - } - log.Infof("Migrated database to the latest version (version %d)", version) - return nil -} diff --git a/faucet/database/log.go b/faucet/database/log.go deleted file mode 100644 index 5f406abc9..000000000 --- a/faucet/database/log.go +++ /dev/null @@ -1,9 +0,0 @@ -package database - -import "github.com/kaspanet/kaspad/util/panics" -import "github.com/kaspanet/kaspad/kasparov/logger" - -var ( - log = logger.BackendLog.Logger("DTBS") - spawn = panics.GoroutineWrapperFunc(log) -) diff --git a/faucet/docker/Dockerfile b/faucet/docker/Dockerfile deleted file mode 100644 index 7929fe300..000000000 --- a/faucet/docker/Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -# -- multistage docker build: stage #1: build stage -FROM golang:1.13-alpine AS build - -RUN mkdir -p /go/src/github.com/kaspanet/kaspad - -WORKDIR /go/src/github.com/kaspanet/kaspad - -RUN apk add --no-cache curl git - -COPY go.mod . -COPY go.sum . - -RUN go mod download - -COPY . . - -RUN cd faucet && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o faucet . - -# --- multistage docker build: stage #2: runtime image -FROM alpine -WORKDIR /app - -RUN apk add --no-cache tini - -COPY --from=build /go/src/github.com/kaspanet/kaspad/faucet /app/ - -ENTRYPOINT ["/sbin/tini", "--"] -CMD ["/app/faucet"] diff --git a/faucet/faucet.go b/faucet/faucet.go deleted file mode 100644 index 384b24c24..000000000 --- a/faucet/faucet.go +++ /dev/null @@ -1,332 +0,0 @@ -package main - -import ( - "bytes" - "encoding/hex" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "path" - - "github.com/kaspanet/kaspad/blockdag" - "github.com/kaspanet/kaspad/faucet/config" - "github.com/kaspanet/kaspad/httpserverutils" - "github.com/kaspanet/kaspad/kasparov/kasparovd/apimodels" - "github.com/kaspanet/kaspad/txscript" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/kaspanet/kaspad/wire" - "github.com/pkg/errors" -) - -const ( - sendAmount = 10000 - // Value 8 bytes + serialized varint size for the length of ScriptPubKey + - // ScriptPubKey bytes. - outputSize uint64 = 8 + 1 + 25 - minTxFee uint64 = 3000 - - requiredConfirmations = 10 -) - -type utxoSet map[wire.Outpoint]*blockdag.UTXOEntry - -// apiURL returns a full concatenated URL from the base -// API server URL and the given path. -func apiURL(requestPath string) (string, error) { - cfg, err := config.MainConfig() - if err != nil { - return "", err - } - u, err := url.Parse(cfg.KasparovdURL) - if err != nil { - return "", errors.WithStack(err) - } - u.Path = path.Join(u.Path, requestPath) - return u.String(), nil -} - -// getFromAPIServer makes an HTTP GET request to the API server -// to the given request path, and returns the response body. -func getFromAPIServer(requestPath string) ([]byte, error) { - getAPIURL, err := apiURL(requestPath) - if err != nil { - return nil, err - } - resp, err := http.Get(getAPIURL) - if err != nil { - return nil, errors.WithStack(err) - } - defer func() { - err := resp.Body.Close() - if err != nil { - panic(errors.WithStack(err)) - } - }() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, errors.WithStack(err) - } - if resp.StatusCode != http.StatusOK { - clientError := &httpserverutils.ClientError{} - err := json.Unmarshal(body, &clientError) - if err != nil { - return nil, errors.WithStack(err) - } - return nil, errors.WithStack(clientError) - } - return body, nil -} - -// getFromAPIServer makes an HTTP POST request to the API server -// to the given request path. It converts the given data to JSON, -// and post it as the POST data. -func postToAPIServer(requestPath string, data interface{}) error { - dataBytes, err := json.Marshal(data) - if err != nil { - return errors.WithStack(err) - } - r := bytes.NewReader(dataBytes) - postAPIURL, err := apiURL(requestPath) - if err != nil { - return err - } - resp, err := http.Post(postAPIURL, "application/json", r) - if err != nil { - return errors.WithStack(err) - } - defer func() { - err := resp.Body.Close() - if err != nil { - panic(errors.WithStack(err)) - } - }() - if resp.StatusCode != http.StatusOK { - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return errors.WithStack(err) - } - clientError := &httpserverutils.ClientError{} - err = json.Unmarshal(body, &clientError) - if err != nil { - return errors.WithStack(err) - } - return errors.WithStack(clientError) - } - return nil -} - -func isUTXOMatured(entry *blockdag.UTXOEntry, confirmations uint64) bool { - if entry.IsCoinbase() { - return confirmations >= config.ActiveNetParams().BlockCoinbaseMaturity - } - return confirmations >= requiredConfirmations -} - -func getWalletUTXOSet() (utxoSet, error) { - body, err := getFromAPIServer(fmt.Sprintf("utxos/address/%s", faucetAddress.EncodeAddress())) - if err != nil { - return nil, err - } - utxoResponses := []*apimodels.TransactionOutputResponse{} - err = json.Unmarshal(body, &utxoResponses) - if err != nil { - return nil, err - } - walletUTXOSet := make(utxoSet) - for _, utxoResponse := range utxoResponses { - scriptPubKey, err := hex.DecodeString(utxoResponse.ScriptPubKey) - if err != nil { - return nil, err - } - txOut := &wire.TxOut{ - Value: utxoResponse.Value, - ScriptPubKey: scriptPubKey, - } - txID, err := daghash.NewTxIDFromStr(utxoResponse.TransactionID) - if err != nil { - return nil, err - } - outpoint := wire.NewOutpoint(txID, utxoResponse.Index) - utxoEntry := blockdag.NewUTXOEntry(txOut, *utxoResponse.IsCoinbase, utxoResponse.AcceptingBlockBlueScore) - if !isUTXOMatured(utxoEntry, *utxoResponse.Confirmations) { - continue - } - walletUTXOSet[*outpoint] = utxoEntry - } - return walletUTXOSet, nil -} - -func sendToAddress(address util.Address) (*wire.MsgTx, error) { - tx, err := createTx(address) - if err != nil { - return nil, err - } - buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) - if err := tx.Serialize(buf); err != nil { - return nil, err - } - rawTx := &apimodels.RawTransaction{RawTransaction: hex.EncodeToString(buf.Bytes())} - return tx, postToAPIServer("transaction", rawTx) -} - -func createTx(address util.Address) (*wire.MsgTx, error) { - walletUTXOSet, err := getWalletUTXOSet() - if err != nil { - return nil, err - } - tx, err := createUnsignedTx(walletUTXOSet, address) - if err != nil { - return nil, err - } - err = signTx(walletUTXOSet, tx) - if err != nil { - return nil, err - } - return tx, nil -} - -func createUnsignedTx(walletUTXOSet utxoSet, address util.Address) (*wire.MsgTx, error) { - tx := wire.NewNativeMsgTx(wire.TxVersion, nil, nil) - netAmount, isChangeOutputRequired, err := fundTx(walletUTXOSet, tx, sendAmount) - if err != nil { - return nil, err - } - if isChangeOutputRequired { - tx.AddTxOut(&wire.TxOut{ - Value: sendAmount, - ScriptPubKey: address.ScriptAddress(), - }) - tx.AddTxOut(&wire.TxOut{ - Value: netAmount - sendAmount, - ScriptPubKey: faucetScriptPubKey, - }) - return tx, nil - } - tx.AddTxOut(&wire.TxOut{ - Value: netAmount, - ScriptPubKey: address.ScriptAddress(), - }) - return tx, nil -} - -// signTx signs a transaction -func signTx(walletUTXOSet utxoSet, tx *wire.MsgTx) error { - for i, txIn := range tx.TxIn { - outpoint := txIn.PreviousOutpoint - - sigScript, err := txscript.SignatureScript(tx, i, walletUTXOSet[outpoint].ScriptPubKey(), - txscript.SigHashAll, faucetPrivateKey, true) - if err != nil { - return errors.Errorf("Failed to sign transaction: %s", err) - } - txIn.SignatureScript = sigScript - } - - return nil -} - -func fundTx(walletUTXOSet utxoSet, tx *wire.MsgTx, amount uint64) (netAmount uint64, isChangeOutputRequired bool, err error) { - amountSelected := uint64(0) - isTxFunded := false - for outpoint, entry := range walletUTXOSet { - amountSelected += entry.Amount() - - // Add the selected output to the transaction - tx.AddTxIn(wire.NewTxIn(&outpoint, nil)) - - // Check if transaction has enough funds. If we don't have enough - // coins from the current amount selected to pay the fee continue - // to grab more coins. - isTxFunded, isChangeOutputRequired, netAmount, err = isFundedAndIsChangeOutputRequired(tx, amountSelected, amount, walletUTXOSet) - if err != nil { - return 0, false, err - } - if isTxFunded { - break - } - } - - if !isTxFunded { - return 0, false, errors.Errorf("not enough funds for coin selection") - } - - return netAmount, isChangeOutputRequired, nil -} - -// isFundedAndIsChangeOutputRequired returns three values and an error: -// * isTxFunded is whether the transaction inputs cover the target amount + the required fee. -// * isChangeOutputRequired is whether it is profitable to add an additional change -// output to the transaction. -// * netAmount is the amount of coins that will be eventually sent to the recipient. If no -// change output is needed, the netAmount will be usually a little bit higher than the -// targetAmount. Otherwise, it'll be the same as the targetAmount. -func isFundedAndIsChangeOutputRequired(tx *wire.MsgTx, amountSelected uint64, targetAmount uint64, walletUTXOSet utxoSet) (isTxFunded, isChangeOutputRequired bool, netAmount uint64, err error) { - // First check if it can be funded with one output and the required fee for it. - isFundedWithOneOutput, oneOutputFee, err := isFundedWithNumberOfOutputs(tx, 1, amountSelected, targetAmount, walletUTXOSet) - if err != nil { - return false, false, 0, err - } - if !isFundedWithOneOutput { - return false, false, 0, nil - } - - // Now check if it can be funded with two outputs and the required fee for it. - isFundedWithTwoOutputs, twoOutputsFee, err := isFundedWithNumberOfOutputs(tx, 2, amountSelected, targetAmount, walletUTXOSet) - if err != nil { - return false, false, 0, err - } - - // If it can be funded with two outputs, check if adding a change output worth it: i.e. check if - // the amount you save by not sending the recipient the whole inputs amount (minus fees) is greater - // than the additional fee that is required by adding a change output. If this is the case, return - // isChangeOutputRequired as true. - if isFundedWithTwoOutputs && twoOutputsFee-oneOutputFee < targetAmount-amountSelected { - return true, true, amountSelected - twoOutputsFee, nil - } - return true, false, amountSelected - oneOutputFee, nil -} - -// isFundedWithNumberOfOutputs returns whether the transaction inputs cover -// the target amount + the required fee with the assumed number of outputs. -func isFundedWithNumberOfOutputs(tx *wire.MsgTx, numberOfOutputs uint64, amountSelected uint64, targetAmount uint64, walletUTXOSet utxoSet) (isTxFunded bool, fee uint64, err error) { - reqFee, err := calcFee(tx, numberOfOutputs, walletUTXOSet) - if err != nil { - return false, 0, err - } - return amountSelected > reqFee && amountSelected-reqFee >= targetAmount, reqFee, nil -} - -func calcFee(msgTx *wire.MsgTx, numberOfOutputs uint64, walletUTXOSet utxoSet) (uint64, error) { - txMass := calcTxMass(msgTx, walletUTXOSet) - txMassWithOutputs := txMass + outputsTotalSize(numberOfOutputs)*blockdag.MassPerTxByte - cfg, err := config.MainConfig() - if err != nil { - return 0, err - } - reqFee := uint64(float64(txMassWithOutputs) * cfg.FeeRate) - if reqFee < minTxFee { - return minTxFee, nil - } - return reqFee, nil -} - -func outputsTotalSize(numberOfOutputs uint64) uint64 { - return numberOfOutputs*outputSize + uint64(wire.VarIntSerializeSize(numberOfOutputs)) -} - -func calcTxMass(msgTx *wire.MsgTx, walletUTXOSet utxoSet) uint64 { - previousScriptPubKeys := getPreviousScriptPubKeys(msgTx, walletUTXOSet) - return blockdag.CalcTxMass(util.NewTx(msgTx), previousScriptPubKeys) -} - -func getPreviousScriptPubKeys(msgTx *wire.MsgTx, walletUTXOSet utxoSet) [][]byte { - previousScriptPubKeys := make([][]byte, len(msgTx.TxIn)) - for i, txIn := range msgTx.TxIn { - outpoint := txIn.PreviousOutpoint - previousScriptPubKeys[i] = walletUTXOSet[outpoint].ScriptPubKey() - } - return previousScriptPubKeys -} diff --git a/faucet/ip_usage.go b/faucet/ip_usage.go deleted file mode 100644 index 5757ab821..000000000 --- a/faucet/ip_usage.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -import ( - "github.com/kaspanet/kaspad/faucet/database" - "github.com/kaspanet/kaspad/httpserverutils" - "github.com/pkg/errors" - "net" - "net/http" - "time" -) - -const minRequestInterval = time.Hour * 24 - -type ipUse struct { - IP string - LastUse time.Time -} - -func ipFromRequest(r *http.Request) (string, error) { - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - return "", err - } - return ip, nil -} - -func validateIPUsage(r *http.Request) error { - db, err := database.DB() - if err != nil { - return err - } - now := time.Now() - timeBeforeMinRequestInterval := now.Add(-minRequestInterval) - var count int - ip, err := ipFromRequest(r) - if err != nil { - return err - } - dbResult := db.Model(&ipUse{}).Where(&ipUse{IP: ip}).Where("last_use BETWEEN ? AND ?", timeBeforeMinRequestInterval, now).Count(&count) - dbErrors := dbResult.GetErrors() - if httpserverutils.HasDBError(dbErrors) { - return httpserverutils.NewErrorFromDBErrors("Some errors were encountered when checking the last use of an IP:", dbResult.GetErrors()) - } - if count != 0 { - return httpserverutils.NewHandlerError(http.StatusForbidden, errors.New("A user is allowed to to have one request from the faucet every 24 hours")) - } - return nil -} - -func updateIPUsage(r *http.Request) error { - db, err := database.DB() - if err != nil { - return err - } - - ip, err := ipFromRequest(r) - if err != nil { - return err - } - dbResult := db.Where(&ipUse{IP: ip}).Assign(&ipUse{LastUse: time.Now()}).FirstOrCreate(&ipUse{}) - dbErrors := dbResult.GetErrors() - if httpserverutils.HasDBError(dbErrors) { - return httpserverutils.NewErrorFromDBErrors("Some errors were encountered when upserting the IP to the new date:", dbResult.GetErrors()) - } - return nil -} diff --git a/faucet/log.go b/faucet/log.go deleted file mode 100644 index d3a8fb534..000000000 --- a/faucet/log.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "github.com/kaspanet/kaspad/logger" - "github.com/kaspanet/kaspad/util/panics" -) - -var ( - log = logger.BackendLog.Logger("FAUC") - spawn = panics.GoroutineWrapperFunc(log) -) diff --git a/faucet/main.go b/faucet/main.go deleted file mode 100644 index c4133a6d2..000000000 --- a/faucet/main.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "fmt" - "github.com/kaspanet/kaspad/dagconfig" - "github.com/kaspanet/kaspad/ecc" - "github.com/kaspanet/kaspad/faucet/config" - "github.com/kaspanet/kaspad/faucet/database" - "github.com/kaspanet/kaspad/txscript" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/base58" - "github.com/pkg/errors" - "os" - - _ "github.com/golang-migrate/migrate/v4/database/mysql" - _ "github.com/golang-migrate/migrate/v4/source/file" - _ "github.com/jinzhu/gorm/dialects/mysql" - "github.com/kaspanet/kaspad/signal" - "github.com/kaspanet/kaspad/util/panics" -) - -var ( - faucetAddress util.Address - faucetPrivateKey *ecc.PrivateKey - faucetScriptPubKey []byte -) - -func main() { - defer panics.HandlePanic(log, nil, nil) - - err := config.Parse() - if err != nil { - err := errors.Wrap(err, "Error parsing command-line arguments") - _, err = fmt.Fprintf(os.Stderr, err.Error()) - if err != nil { - panic(err) - } - return - } - - cfg, err := config.MainConfig() - if err != nil { - panic(err) - } - - if cfg.Migrate { - err := database.Migrate() - if err != nil { - panic(errors.Errorf("Error migrating database: %s", err)) - } - return - } - - err = database.Connect() - if err != nil { - panic(errors.Errorf("Error connecting to database: %s", err)) - } - defer func() { - err := database.Close() - if err != nil { - panic(errors.Errorf("Error closing the database: %s", err)) - } - }() - - privateKeyBytes := base58.Decode(cfg.PrivateKey) - faucetPrivateKey, _ = ecc.PrivKeyFromBytes(ecc.S256(), privateKeyBytes) - - faucetAddress, err = privateKeyToP2PKHAddress(faucetPrivateKey, config.ActiveNetParams()) - if err != nil { - panic(errors.Errorf("Failed to get P2PKH address from private key: %s", err)) - } - - faucetScriptPubKey, err = txscript.PayToAddrScript(faucetAddress) - if err != nil { - panic(errors.Errorf("failed to generate faucetScriptPubKey to address: %s", err)) - } - - shutdownServer := startHTTPServer(cfg.HTTPListen) - defer shutdownServer() - - interrupt := signal.InterruptListener() - <-interrupt -} - -// privateKeyToP2PKHAddress generates p2pkh address from private key. -func privateKeyToP2PKHAddress(key *ecc.PrivateKey, net *dagconfig.Params) (util.Address, error) { - return util.NewAddressPubKeyHashFromPublicKey(key.PubKey().SerializeCompressed(), net.Prefix) -} diff --git a/faucet/migrations/000001_create_ip_uses_table.down.sql b/faucet/migrations/000001_create_ip_uses_table.down.sql deleted file mode 100644 index 0ee7d0b52..000000000 --- a/faucet/migrations/000001_create_ip_uses_table.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE `ip_uses`; diff --git a/faucet/migrations/000001_create_ip_uses_table.up.sql b/faucet/migrations/000001_create_ip_uses_table.up.sql deleted file mode 100644 index 4dc324ce0..000000000 --- a/faucet/migrations/000001_create_ip_uses_table.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -CREATE TABLE `ip_uses` -( - `ip` VARCHAR(39) NOT NULL, - `last_use` DATETIME NOT NULL, - PRIMARY KEY (`ip`) -); \ No newline at end of file diff --git a/faucet/server.go b/faucet/server.go deleted file mode 100644 index 4d4236e52..000000000 --- a/faucet/server.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "github.com/kaspanet/kaspad/faucet/config" - "github.com/kaspanet/kaspad/httpserverutils" - "github.com/kaspanet/kaspad/util" - "github.com/pkg/errors" - "net/http" - "time" - - "github.com/gorilla/handlers" - "github.com/gorilla/mux" -) - -const gracefulShutdownTimeout = 30 * time.Second - -// startHTTPServer starts the HTTP REST server and returns a -// function to gracefully shutdown it. -func startHTTPServer(listenAddr string) func() { - router := mux.NewRouter() - router.Use(httpserverutils.AddRequestMetadataMiddleware) - router.Use(httpserverutils.RecoveryMiddleware) - router.Use(httpserverutils.LoggingMiddleware) - router.Use(httpserverutils.SetJSONMiddleware) - router.HandleFunc( - "/request_money", - httpserverutils.MakeHandler(requestMoneyHandler)). - Methods("POST") - httpServer := &http.Server{ - Addr: listenAddr, - Handler: handlers.CORS()(router), - } - spawn(func() { - log.Errorf("%s", httpServer.ListenAndServe()) - }) - - return func() { - ctx, cancel := context.WithTimeout(context.Background(), gracefulShutdownTimeout) - defer cancel() - err := httpServer.Shutdown(ctx) - if err != nil { - log.Errorf("Error shutting down HTTP server: %s", err) - } - } -} - -type requestMoneyData struct { - Address string `json:"address"` -} - -func requestMoneyHandler(_ *httpserverutils.ServerContext, r *http.Request, _ map[string]string, _ map[string]string, - requestBody []byte) (interface{}, error) { - hErr := validateIPUsage(r) - if hErr != nil { - return nil, hErr - } - requestData := &requestMoneyData{} - err := json.Unmarshal(requestBody, requestData) - if err != nil { - return nil, httpserverutils.NewHandlerErrorWithCustomClientMessage(http.StatusUnprocessableEntity, - errors.Wrap(err, "Error unmarshalling request body"), - "The request body is not json-formatted") - } - address, err := util.DecodeAddress(requestData.Address, config.ActiveNetParams().Prefix) - if err != nil { - return nil, httpserverutils.NewHandlerErrorWithCustomClientMessage(http.StatusUnprocessableEntity, - errors.Wrap(err, "Error decoding address"), - "Error decoding address") - } - tx, err := sendToAddress(address) - if err != nil { - return nil, err - } - hErr = updateIPUsage(r) - if hErr != nil { - return nil, hErr - } - return tx.TxID().String(), nil -}