commit 7963d88ddcfb030f5087f49770f95c72f023a750
parent 7cfcb196df76260b1ccac1abc1d8d395e4b1cb6d
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Thu, 14 Dec 2023 03:05:08 -0500
prevent submitting the same form twice
Diffstat:
3 files changed, 85 insertions(+), 71 deletions(-)
diff --git a/pkg/web/handlers/data.go b/pkg/web/handlers/data.go
@@ -937,6 +937,7 @@ type pokerData struct {
MinWithdrawAmount database.Piconero
WithdrawAmount database.Piconero
WithdrawAddress string
+ WithdrawUnique int64
Error string
}
diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go
@@ -24,6 +24,7 @@ import (
"image"
_ "image/gif"
"image/png"
+ "math/rand"
"net/http"
"net/url"
"os"
@@ -753,6 +754,8 @@ func BHCHandler(c echo.Context) error {
return c.Render(http.StatusOK, "bhc", data)
}
+var pokerWithdrawCache = cache.NewWithKey[database.UserID, int64](10*time.Minute, time.Hour)
+
func PokerHomeHandler(c echo.Context) error {
db := c.Get("database").(*database.DkfDB)
authUser := c.Get("authUser").(*database.User)
@@ -772,6 +775,9 @@ func PokerHomeHandler(c echo.Context) error {
data.Transactions, _ = db.GetUserPokerXmrTransactions(authUser.ID)
data.PokerXmrSubAddress = authUser.PokerXmrSubAddress
data.XmrBalanceStagenet = authUser.XmrBalanceStagenet
+ data.WithdrawUnique = rand.Int63()
+ withdrawUniqueOrig, _ := pokerWithdrawCache.Get(authUser.ID)
+ pokerWithdrawCache.SetD(authUser.ID, data.WithdrawUnique)
data.Tables, _ = db.GetPokerTables()
data.WithdrawAmount = minWithdrawAmount
if authUser.PokerXmrSubAddress != "" {
@@ -779,81 +785,87 @@ func PokerHomeHandler(c echo.Context) error {
data.Img = getImgStr(b)
}
- if c.Request().Method == http.MethodPost {
- data.WithdrawAmount = database.Piconero(utils.DoParseUint64(c.Request().PostFormValue("withdraw_amount")))
- data.WithdrawAddress = c.Request().PostFormValue("withdraw_address")
- if len(data.WithdrawAddress) != 95 {
- data.Error = "invalid xmr address"
- return c.Render(http.StatusOK, "poker", data)
- }
- if data.WithdrawAddress == authUser.PokerXmrSubAddress {
- data.Error = "cannot withdraw to the deposit address"
- return c.Render(http.StatusOK, "poker", data)
- }
- withdrawAmount := data.WithdrawAmount
- if withdrawAmount < minWithdrawAmount {
- data.Error = fmt.Sprintf("minimum withdraw amount is %d", minWithdrawAmount)
- return c.Render(http.StatusOK, "poker", data)
- }
- userChips := authUser.XmrBalanceStagenet
- if withdrawAmount > userChips {
- data.Error = fmt.Sprintf("maximum withdraw amount is %d (%d)", userChips, withdrawAmount)
- return c.Render(http.StatusOK, "poker", data)
- }
- withdrawAmount = utils.Clamp(withdrawAmount, minWithdrawAmount, userChips)
-
- res, err := config.Xmr().Transfer(&wallet1.RequestTransfer{
- DoNotRelay: true,
- GetTxMetadata: true,
- Destinations: []*wallet1.Destination{
- {Address: data.WithdrawAddress,
- Amount: uint64(withdrawAmount)}}})
- if err != nil {
- logrus.Error(err)
- data.Error = err.Error()
- return c.Render(http.StatusOK, "poker", data)
- }
+ if c.Request().Method == http.MethodGet {
+ return c.Render(http.StatusOK, "poker", data)
+ }
+
+ data.WithdrawAmount = database.Piconero(utils.DoParseUint64(c.Request().PostFormValue("withdraw_amount")))
+ data.WithdrawAddress = c.Request().PostFormValue("withdraw_address")
+ withdrawUniqueSub := utils.DoParseInt64(c.Request().PostFormValue("withdraw_unique"))
+ if withdrawUniqueOrig == 0 || withdrawUniqueSub != withdrawUniqueOrig {
+ data.Error = "form submitted twice, try again"
+ return c.Render(http.StatusOK, "poker", data)
+ }
+ if len(data.WithdrawAddress) != 95 {
+ data.Error = "invalid xmr address"
+ return c.Render(http.StatusOK, "poker", data)
+ }
+ if data.WithdrawAddress == authUser.PokerXmrSubAddress {
+ data.Error = "cannot withdraw to the deposit address"
+ return c.Render(http.StatusOK, "poker", data)
+ }
+ withdrawAmount := data.WithdrawAmount
+ if withdrawAmount < minWithdrawAmount {
+ data.Error = fmt.Sprintf("minimum withdraw amount is %d", minWithdrawAmount)
+ return c.Render(http.StatusOK, "poker", data)
+ }
+ userChips := authUser.XmrBalanceStagenet
+ if withdrawAmount > userChips {
+ data.Error = fmt.Sprintf("maximum withdraw amount is %d (%d)", userChips, withdrawAmount)
+ return c.Render(http.StatusOK, "poker", data)
+ }
+ withdrawAmount = utils.Clamp(withdrawAmount, minWithdrawAmount, userChips)
+
+ res, err := config.Xmr().Transfer(&wallet1.RequestTransfer{
+ DoNotRelay: true,
+ GetTxMetadata: true,
+ Destinations: []*wallet1.Destination{
+ {Address: data.WithdrawAddress,
+ Amount: uint64(withdrawAmount)}}})
+ if err != nil {
+ logrus.Error(err)
+ data.Error = err.Error()
+ return c.Render(http.StatusOK, "poker", data)
+ }
- transactionFee := database.Piconero(res.Fee)
+ transactionFee := database.Piconero(res.Fee)
- if withdrawAmount+transactionFee > authUser.XmrBalanceStagenet {
- data.Error = fmt.Sprintf("not enough funds to pay for transaction fee %d (%s xmr)", transactionFee, transactionFee.XmrStr())
- return c.Render(http.StatusOK, "poker", data)
- }
+ if withdrawAmount+transactionFee > authUser.XmrBalanceStagenet {
+ data.Error = fmt.Sprintf("not enough funds to pay for transaction fee %d (%s xmr)", transactionFee, transactionFee.XmrStr())
+ return c.Render(http.StatusOK, "poker", data)
+ }
- tx := db.Begin()
- if err := authUser.SubXmrBalanceStagenet(db, withdrawAmount+transactionFee); err != nil {
- data.Error = err.Error()
- tx.Rollback()
- return c.Render(http.StatusOK, "poker", data)
- }
- xmrBalanceStagenet, err := authUser.GetXmrBalanceStagenet(tx)
- if err != nil {
- data.Error = err.Error()
- tx.Rollback()
- return c.Render(http.StatusOK, "poker", data)
- }
- if xmrBalanceStagenet < 0 {
- data.Error = "negative balance"
- tx.Rollback()
- return c.Render(http.StatusOK, "poker", data)
- }
- if _, err := config.Xmr().RelayTx(&wallet1.RequestRelayTx{Hex: res.TxMetadata}); err != nil {
- logrus.Error(err)
- data.Error = err.Error()
- tx.Rollback()
- return c.Render(http.StatusOK, "poker", data)
- }
- if _, err := tx.CreatePokerXmrTransaction(authUser.ID, res); err != nil {
- logrus.Error("failed to create poker xmr transaction", err)
- data.Error = err.Error()
- tx.Commit()
- return c.Render(http.StatusOK, "poker", data)
- }
+ tx := db.Begin()
+ if err := authUser.SubXmrBalanceStagenet(db, withdrawAmount+transactionFee); err != nil {
+ data.Error = err.Error()
+ tx.Rollback()
+ return c.Render(http.StatusOK, "poker", data)
+ }
+ xmrBalanceStagenet, err := authUser.GetXmrBalanceStagenet(tx)
+ if err != nil {
+ data.Error = err.Error()
+ tx.Rollback()
+ return c.Render(http.StatusOK, "poker", data)
+ }
+ if xmrBalanceStagenet < 0 {
+ data.Error = "negative balance"
+ tx.Rollback()
+ return c.Render(http.StatusOK, "poker", data)
+ }
+ if _, err := config.Xmr().RelayTx(&wallet1.RequestRelayTx{Hex: res.TxMetadata}); err != nil {
+ logrus.Error(err)
+ data.Error = err.Error()
+ tx.Rollback()
+ return c.Render(http.StatusOK, "poker", data)
+ }
+ if _, err := tx.CreatePokerXmrTransaction(authUser.ID, res); err != nil {
+ logrus.Error("failed to create poker xmr transaction", err)
+ data.Error = err.Error()
tx.Commit()
-
- return c.Redirect(http.StatusFound, c.Request().Referer())
+ return c.Render(http.StatusOK, "poker", data)
}
+ tx.Commit()
- return c.Render(http.StatusOK, "poker", data)
+ pokerWithdrawCache.Delete(authUser.ID)
+ return c.Redirect(http.StatusFound, c.Request().Referer())
}
diff --git a/pkg/web/public/views/pages/poker.gohtml b/pkg/web/public/views/pages/poker.gohtml
@@ -29,6 +29,7 @@
{{ end }}
<form method="post" class="form-inline">
<input type="hidden" name="csrf" value="{{ .CSRF }}" />
+ <input type="hidden" name="withdraw_unique" value="{{ .Data.WithdrawUnique }}" />
<input type="text" name="withdraw_address" value="{{ .Data.WithdrawAddress }}" placeholder="address" class="form-control mr-2" style="width: 400px;" />
<input type="number" name="withdraw_amount" value="{{ .Data.WithdrawAmount }}" min="{{ .Data.MinWithdrawAmount }}" max="{{ .Data.XmrBalanceStagenet }}" placeholder="amount" class="form-control mr-2" style="width: 150px;" />
<button class="btn btn-primary">Withdraw</button>