diff options
author | Péter Szilágyi <peterke@gmail.com> | 2017-05-02 18:52:51 +0800 |
---|---|---|
committer | Péter Szilágyi <peterke@gmail.com> | 2017-05-04 16:42:43 +0800 |
commit | 8a28408616cab76bf756170a4f39025c821e4d21 (patch) | |
tree | 3211df8c26ac55f4743827edf7af461fb233a3e1 /cmd/faucet/faucet.go | |
parent | e1dc7ece624a2f539b71f1dfb5901047f7a9f214 (diff) | |
download | go-tangerine-8a28408616cab76bf756170a4f39025c821e4d21.tar go-tangerine-8a28408616cab76bf756170a4f39025c821e4d21.tar.gz go-tangerine-8a28408616cab76bf756170a4f39025c821e4d21.tar.bz2 go-tangerine-8a28408616cab76bf756170a4f39025c821e4d21.tar.lz go-tangerine-8a28408616cab76bf756170a4f39025c821e4d21.tar.xz go-tangerine-8a28408616cab76bf756170a4f39025c821e4d21.tar.zst go-tangerine-8a28408616cab76bf756170a4f39025c821e4d21.zip |
cmd/faucet, cmd/puppeth: support multi-tiered faucet
Diffstat (limited to 'cmd/faucet/faucet.go')
-rw-r--r-- | cmd/faucet/faucet.go | 85 |
1 files changed, 64 insertions, 21 deletions
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index 1c5c43edc..2a9a74593 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -27,11 +27,13 @@ import ( "fmt" "html/template" "io/ioutil" + "math" "math/big" "net/http" "net/url" "os" "path/filepath" + "strconv" "strings" "sync" "time" @@ -67,6 +69,7 @@ var ( netnameFlag = flag.String("faucet.name", "", "Network name to assign to the faucet") payoutFlag = flag.Int("faucet.amount", 1, "Number of Ethers to pay out per user request") minutesFlag = flag.Int("faucet.minutes", 1440, "Number of minutes to wait between funding rounds") + tiersFlag = flag.Int("faucet.tiers", 3, "Number of funding tiers to enable (x3 time, x2.5 funds)") accJSONFlag = flag.String("account.json", "", "Key json file to fund user requests with") accPassFlag = flag.String("account.pass", "", "Decryption password to access faucet funds") @@ -89,22 +92,46 @@ func main() { flag.Parse() log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + // Construct the payout tiers + amounts := make([]string, *tiersFlag) + periods := make([]string, *tiersFlag) + for i := 0; i < *tiersFlag; i++ { + // Calculate the amount for the next tier and format it + amount := float64(*payoutFlag) * math.Pow(2.5, float64(i)) + amounts[i] = fmt.Sprintf("%s Ethers", strconv.FormatFloat(amount, 'f', -1, 64)) + if amount == 1 { + amounts[i] = strings.TrimSuffix(amounts[i], "s") + } + // Calcualte the period for th enext tier and format it + period := *minutesFlag * int(math.Pow(3, float64(i))) + periods[i] = fmt.Sprintf("%d mins", period) + if period%60 == 0 { + period /= 60 + periods[i] = fmt.Sprintf("%d hours", period) + } + if period%24 == 0 { + period /= 24 + periods[i] = fmt.Sprintf("%d days", period) + } + if period == 1 { + periods[i] = strings.TrimSuffix(periods[i], "s") + } + } // Load up and render the faucet website tmpl, err := Asset("faucet.html") if err != nil { log.Crit("Failed to load the faucet template", "err", err) } - period := fmt.Sprintf("%d minute(s)", *minutesFlag) - if *minutesFlag%60 == 0 { - period = fmt.Sprintf("%d hour(s)", *minutesFlag/60) - } website := new(bytes.Buffer) - template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{ + err = template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{ "Network": *netnameFlag, - "Amount": *payoutFlag, - "Period": period, + "Amounts": amounts, + "Periods": periods, "Recaptcha": *captchaToken, }) + if err != nil { + log.Crit("Failed to render the faucet template", "err", err) + } // Load and parse the genesis block requested by the user blob, err := ioutil.ReadFile(*genesisFlag) if err != nil { @@ -171,10 +198,10 @@ type faucet struct { nonce uint64 // Current pending nonce of the faucet price *big.Int // Current gas price to issue funds with - conns []*websocket.Conn // Currently live websocket connections - history map[string]time.Time // History of users and their funding requests - reqs []*request // Currently pending funding requests - update chan struct{} // Channel to signal request updates + conns []*websocket.Conn // Currently live websocket connections + timeouts map[string]time.Time // History of users and their funding timeouts + reqs []*request // Currently pending funding requests + update chan struct{} // Channel to signal request updates lock sync.RWMutex // Lock protecting the faucet's internals } @@ -241,7 +268,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u index: index, keystore: ks, account: ks.Accounts()[0], - history: make(map[string]time.Time), + timeouts: make(map[string]time.Time), update: make(chan struct{}, 1), }, nil } @@ -295,14 +322,22 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { "peers": f.stack.Server().PeerCount(), "requests": f.reqs, }) - header, _ := f.client.HeaderByNumber(context.Background(), nil) - websocket.JSON.Send(conn, header) + // Send the initial block to the client + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + header, err := f.client.HeaderByNumber(ctx, nil) + cancel() + if err != nil { + log.Error("Failed to retrieve latest header", "err", err) + } else { + websocket.JSON.Send(conn, header) + } // Keep reading requests from the websocket until the connection breaks for { // Fetch the next funding request and validate against github var msg struct { URL string `json:"url"` + Tier uint `json:"tier"` Captcha string `json:"captcha"` } if err := websocket.JSON.Receive(conn, &msg); err != nil { @@ -312,7 +347,11 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { websocket.JSON.Send(conn, map[string]string{"error": "URL doesn't link to GitHub Gists"}) continue } - log.Info("Faucet funds requested", "gist", msg.URL) + if msg.Tier >= uint(*tiersFlag) { + websocket.JSON.Send(conn, map[string]string{"error": "Invalid funding tier requested"}) + continue + } + log.Info("Faucet funds requested", "gist", msg.URL, "tier", msg.Tier) // If captcha verifications are enabled, make sure we're not dealing with a robot if *captchaToken != "" { @@ -337,7 +376,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { } if !result.Success { log.Warn("Captcha verification failed", "err", string(result.Errors)) - websocket.JSON.Send(conn, map[string]string{"error": "Beep-boop, you're a robot!"}) + websocket.JSON.Send(conn, map[string]string{"error": "Beep-bop, you're a robot!"}) continue } } @@ -396,11 +435,15 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { f.lock.Lock() var ( fund bool - elapsed time.Duration + timeout time.Time ) - if elapsed = time.Since(f.history[gist.Owner.Login]); elapsed > time.Duration(*minutesFlag)*time.Minute { + if timeout = f.timeouts[gist.Owner.Login]; time.Now().After(timeout) { // User wasn't funded recently, create the funding transaction - tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether), big.NewInt(21000), f.price, nil) + amount := new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether) + amount = new(big.Int).Mul(amount, new(big.Int).Exp(big.NewInt(5), big.NewInt(int64(msg.Tier)), nil)) + amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil)) + + tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, big.NewInt(21000), f.price, nil) signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainId) if err != nil { websocket.JSON.Send(conn, map[string]string{"error": err.Error()}) @@ -419,14 +462,14 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { Time: time.Now(), Tx: signed, }) - f.history[gist.Owner.Login] = time.Now() + f.timeouts[gist.Owner.Login] = time.Now().Add(time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute) fund = true } f.lock.Unlock() // Send an error if too frequent funding, othewise a success if !fund { - websocket.JSON.Send(conn, map[string]string{"error": fmt.Sprintf("User already funded %s ago", common.PrettyDuration(elapsed))}) + websocket.JSON.Send(conn, map[string]string{"error": fmt.Sprintf("%s left until next allowance", common.PrettyDuration(timeout.Sub(time.Now())))}) continue } websocket.JSON.Send(conn, map[string]string{"success": fmt.Sprintf("Funding request accepted for %s into %s", gist.Owner.Login, address.Hex())}) |