Enable pool closure (#80)

* Enable pool closure.

* Move homepage to its own file

* Docs and rename status>info
This commit is contained in:
Jamie Holdstock 2020-05-28 07:07:33 +01:00 committed by GitHub
parent e65a7fdbf3
commit 6a100811f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 96 additions and 75 deletions

View File

@ -29,6 +29,7 @@ var (
defaultWalletHost = "127.0.0.1"
defaultWebServerDebug = false
defaultBackupInterval = time.Minute * 3
defaultVspClosed = false
)
// config defines the configuration options for the VSP.
@ -51,6 +52,7 @@ type config struct {
WebServerDebug bool `long:"webserverdebug" ini-name:"webserverdebug" description:"Enable web server debug mode (verbose logging to terminal and live-reloading templates)."`
SupportEmail string `long:"supportemail" ini-name:"supportemail" description:"Email address for users in need of support."`
BackupInterval time.Duration `long:"backupinterval" ini-name:"backupinterval" description:"Time period between automatic database backups. Valid time units are {s,m,h}. Minimum 30 seconds."`
VspClosed bool `long:"vspclosed" ini-name:"vspclosed" description:"Closed prevents the VSP from accepting new tickets."`
dbPath string
netParams *netParams
@ -156,6 +158,7 @@ func loadConfig() (*config, error) {
WalletHost: defaultWalletHost,
WebServerDebug: defaultWebServerDebug,
BackupInterval: defaultBackupInterval,
VspClosed: defaultVspClosed,
}
// Pre-parse the command line options to see if an alternative config

View File

@ -15,13 +15,15 @@
## Expected usage
### Get VSP public key
### Get VSP info
Clients should first retrieve the VSP's public key so they can check the
signature on later API responses. A VSP should never change their public key so
it can be requested once and cached indefinitely.
Clients should retrieve the VSP's public key so they can check the signature on
future API responses. A VSP should never change their public key so it can be
requested once and cached indefinitely. `vspclosed` indicates that the VSP is
not currently accepting new tickets. Calling `/feeaddress` when a VSP is closed
will result in an error.
- `GET /api/pubkey`
- `GET /api/vspinfo`
No request body.
@ -29,8 +31,11 @@ it can be requested once and cached indefinitely.
```json
{
"timestamp":1590509065,
"pubkey":"bLNwVVcda3LqRLv+m0J5sjd60+twjO/fuhcx8RUErDQ="
"timestamp":1590599436,
"pubkey":"SjAmrAqH7LScCUwM1qo5O6Cu7aKhrM1ORszgZwD7HmU=",
"feepercentage":0.05,
"vspclosed":false,
"network":"testnet3"
}
```
@ -101,11 +106,7 @@ has 6 confirmations.
}
```
### Information requests
Clients can check the status of the server at any time.
// TODO
### Ticket Status
Clients can check the status of a ticket at any time after calling
`/feeaddress`.

View File

@ -44,6 +44,11 @@ func run(ctx context.Context) error {
return err
}
if cfg.VspClosed {
log.Warnf("Config --vspclosed is set. This will prevent vspd from " +
"accepting new tickets.")
}
// Waitgroup for services to signal when they have shutdown cleanly.
var shutdownWg sync.WaitGroup
defer log.Info("Shutdown complete")
@ -117,6 +122,7 @@ func run(ctx context.Context) error {
NetParams: cfg.netParams.Params,
FeeAddressExpiration: defaultFeeAddressExpiration,
SupportEmail: cfg.SupportEmail,
VspClosed: cfg.VspClosed,
}
err = webapi.Start(ctx, shutdownRequestChannel, &shutdownWg, cfg.Listen, db,
dcrdConnect, walletConnect, cfg.WebServerDebug, cfg.FeeXPub, apiCfg)

View File

@ -69,6 +69,11 @@ func feeAddress(c *gin.Context) {
commitmentAddress := c.MustGet("CommitmentAddress").(string)
dcrdClient := c.MustGet("DcrdClient").(*rpc.DcrdRPC)
if cfg.VspClosed {
sendErrorResponse("pool is not accepting new tickets", http.StatusBadRequest, c)
return
}
var feeAddressRequest FeeAddressRequest
if err := binding.JSON.BindBody(rawRequest, &feeAddressRequest); err != nil {
log.Warnf("Bad feeaddress request from %s: %v", c.ClientIP(), err)

44
webapi/homepage.go Normal file
View File

@ -0,0 +1,44 @@
package webapi
import (
"net/http"
"time"
"github.com/jholdstock/vspd/database"
"github.com/gin-gonic/gin"
)
type vspStats struct {
PubKey []byte
TotalTickets int
FeePaidTickets int
VSPFee float64
Network string
UpdateTime string
SupportEmail string
VspClosed bool
}
var stats *vspStats
func updateVSPStats(db *database.VspDatabase, cfg Config) (*vspStats, error) {
total, feePaid, err := db.CountTickets()
if err != nil {
return nil, err
}
return &vspStats{
PubKey: signPubKey,
TotalTickets: total,
FeePaidTickets: feePaid,
VSPFee: cfg.VSPFee,
Network: cfg.NetParams.Name,
UpdateTime: time.Now().Format("Mon Jan _2 15:04:05 2006"),
SupportEmail: cfg.SupportEmail,
VspClosed: cfg.VspClosed,
}, nil
}
func homepage(c *gin.Context) {
c.HTML(http.StatusOK, "homepage.html", stats)
}

View File

@ -1,23 +0,0 @@
package webapi
import (
"time"
"github.com/gin-gonic/gin"
)
// pubKey is the handler for "GET /pubkey".
func pubKey(c *gin.Context) {
sendJSONResponse(pubKeyResponse{
Timestamp: time.Now().Unix(),
PubKey: signPubKey,
}, c)
}
// fee is the handler for "GET /fee".
func fee(c *gin.Context) {
sendJSONResponse(feeResponse{
Timestamp: time.Now().Unix(),
FeePercentage: cfg.VSPFee,
}, c)
}

View File

@ -48,6 +48,7 @@
<tr><td>Network:</td><td>{{ .Network }}</td></tr>
<tr><td>Support:</td><td>{{ .SupportEmail }}</td></tr>
<tr><td>Pubkey:</td><td>{{ printf "%x" .PubKey }}</td></tr>
<tr><td>VSP closed:</td><td>{{ .VspClosed }}</td></tr>
</table>
<p>Last updated: {{.UpdateTime}}</p>
</body>

View File

@ -1,13 +1,11 @@
package webapi
type pubKeyResponse struct {
type vspInfoResponse struct {
Timestamp int64 `json:"timestamp" binding:"required"`
PubKey []byte `json:"pubkey" binding:"required"`
}
type feeResponse struct {
Timestamp int64 `json:"timestamp" binding:"required"`
FeePercentage float64 `json:"feepercentage" binding:"required"`
VspClosed bool `json:"vspclosed" binding:"required"`
Network string `json:"network" binding:"required"`
}
type FeeAddressRequest struct {

18
webapi/vspstatus.go Normal file
View File

@ -0,0 +1,18 @@
package webapi
import (
"time"
"github.com/gin-gonic/gin"
)
// vspInfo is the handler for "GET /vspinfo".
func vspInfo(c *gin.Context) {
sendJSONResponse(vspInfoResponse{
Timestamp: time.Now().Unix(),
PubKey: signPubKey,
FeePercentage: cfg.VSPFee,
Network: cfg.NetParams.Name,
VspClosed: cfg.VspClosed,
}, c)
}

View File

@ -24,6 +24,7 @@ type Config struct {
FeeAccountName string
FeeAddressExpiration time.Duration
SupportEmail string
VspClosed bool
}
const (
@ -34,18 +35,6 @@ const (
relayFee = 0.0001
)
type vspStats struct {
PubKey []byte
TotalTickets int
FeePaidTickets int
VSPFee float64
Network string
UpdateTime string
SupportEmail string
}
var stats *vspStats
var cfg Config
var db *database.VspDatabase
var dcrdConnect rpc.Connect
@ -185,8 +174,7 @@ func router(debugMode bool) *gin.Engine {
// These routes have no extra middleware. They can be accessed by anybody.
router.GET("/", homepage)
router.GET("/api/fee", fee)
router.GET("/api/pubkey", pubKey)
router.GET("/api/vspinfo", vspInfo)
// These API routes access dcrd and they need authentication.
feeOnly := router.Group("/api").Use(
@ -206,26 +194,6 @@ func router(debugMode bool) *gin.Engine {
return router
}
func updateVSPStats(db *database.VspDatabase, cfg Config) (*vspStats, error) {
total, feePaid, err := db.CountTickets()
if err != nil {
return nil, err
}
return &vspStats{
PubKey: signPubKey,
TotalTickets: total,
FeePaidTickets: feePaid,
VSPFee: cfg.VSPFee,
Network: cfg.NetParams.Name,
UpdateTime: time.Now().Format("Mon Jan _2 15:04:05 2006"),
SupportEmail: cfg.SupportEmail,
}, nil
}
func homepage(c *gin.Context) {
c.HTML(http.StatusOK, "homepage.html", stats)
}
func sendJSONResponse(resp interface{}, c *gin.Context) {
dec, err := json.Marshal(resp)
if err != nil {