Skip to content

Commit

Permalink
config: merge in ticketbuyer config. (decred#507)
Browse files Browse the repository at this point in the history
The ticketbuyer configuration now lives in the main configuration file
under the [Ticket Buyer Options] section.
  • Loading branch information
dajohi authored and jrick committed Jan 3, 2017
1 parent c99afa7 commit 32b6ca9
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 243 deletions.
113 changes: 112 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015 The Decred developers
// Copyright (c) 2015-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

Expand All @@ -18,6 +18,7 @@ import (
"github.com/decred/dcrutil"
"github.com/decred/dcrwallet/internal/cfgutil"
"github.com/decred/dcrwallet/netparams"
"github.com/decred/dcrwallet/ticketbuyer"
"github.com/decred/dcrwallet/wallet"
"github.com/decred/dcrwallet/wallet/txrules"
flags "github.com/jessevdk/go-flags"
Expand Down Expand Up @@ -50,6 +51,21 @@ const (
defaultStakePoolColdExtKey = ""
defaultAllowHighFees = false

// ticket buyer options
defaultMaxFee = 1.0
defaultMinFee = 0.01
defaultMaxPriceScale = 2.0
defaultMinPriceScale = 0.7
defaultAvgVWAPPriceDelta = 2880
defaultMaxPerBlock = 5
defaultHighPricePenalty = 1.3
defaultBlocksToAvg = 11
defaultFeeTargetScaling = 1.05
defaultMaxInMempool = 40
defaultExpiryDelta = 16
defaultFeeSource = ticketbuyer.TicketFeeMean
defaultAvgPriceMode = ticketbuyer.PriceTargetVWAP

walletDbName = "wallet.db"
)

Expand Down Expand Up @@ -134,6 +150,9 @@ type config struct {
Username string `short:"u" long:"username" description:"Username for legacy RPC and dcrd authentication (if dcrdusername is unset)"`
Password string `short:"P" long:"password" default-mask:"-" description:"Password for legacy RPC and dcrd authentication (if dcrdpassword is unset)"`

TBOpts ticketBuyerOptions `group:"Ticket Buyer Options" namespace:"ticketbuyer"`
tbCfg *ticketbuyer.Config

// EXPERIMENTAL RPC server options
//
// These options will change (and require changes to config files, etc.)
Expand All @@ -145,6 +164,24 @@ type config struct {
EnableStakeMining bool `long:"enablestakemining" default-mask:"-" description:"DEPRECATED -- consider using enableticketbuyer and/or enablevoting instead"`
}

type ticketBuyerOptions struct {
MaxPriceScale float64 `long:"maxpricescale" description:"Attempt to prevent the stake difficulty from going above this multiplier (>1.0) by manipulation, 0 to disable"`
MinPriceScale float64 `long:"minpricescale" description:"Attempt to prevent the stake difficulty from going below this multiplier (<1.0) by manipulation, 0 to disable"`
PriceTarget float64 `long:"pricetarget" description:"A target to try to seek setting the stake price to rather than meeting the average price, 0 to disable"`
AvgPriceMode string `long:"avgpricemode" description:"The mode to use for calculating the average price if pricetarget is disabled (vwap, pool, dual)"`
AvgPriceVWAPDelta int `long:"avgpricevwapdelta" description:"The number of blocks to use from the current block to calculate the VWAP"`
MaxFee float64 `long:"maxfee" description:"Maximum ticket fee per KB"`
MinFee float64 `long:"minfee" description:"Minimum ticket fee per KB"`
FeeSource string `long:"feesource" description:"The fee source to use for ticket fee per KB (median or mean)"`
MaxPerBlock int `long:"maxperblock" description:"Maximum tickets per block, with negative numbers indicating buy one ticket every 1-in-n blocks"`
HighPricePenalty float64 `long:"highpricepenalty" description:"The exponential penalty to apply to the number of tickets to purchase above the ideal ticket pool price"`
BlocksToAvg int `long:"blockstoavg" description:"Number of blocks to average for fees calculation"`
FeeTargetScaling float64 `long:"feetargetscaling" description:"The amount above the mean fee in the previous blocks to purchase tickets with, proportional e.g. 1.05 = 105%"`
DontWaitForTickets bool `long:"dontwaitfortickets" description:"Don't wait until your last round of tickets have entered the blockchain to attempt to purchase more"`
MaxInMempool int `long:"maxinmempool" description:"The maximum number of your tickets allowed in mempool before purchasing more tickets"`
ExpiryDelta int `long:"expirydelta" description:"Number of blocks in the future before the ticket expires"`
}

// cleanAndExpandPath expands environement variables and leading ~ in the
// passed path, cleans the result, and returns it.
func cleanAndExpandPath(path string) string {
Expand Down Expand Up @@ -329,6 +366,23 @@ func loadConfig() (*config, []string, error) {
AllowHighFees: defaultAllowHighFees,
DataDir: defaultAppDataDir,
EnableStakeMining: defaultEnableStakeMining,

// Ticket Buyer Options
TBOpts: ticketBuyerOptions{
MinPriceScale: defaultMinPriceScale,
MaxPriceScale: defaultMaxPriceScale,
AvgPriceMode: defaultAvgPriceMode,
AvgPriceVWAPDelta: defaultAvgVWAPPriceDelta,
MaxFee: defaultMaxFee,
MinFee: defaultMinFee,
FeeSource: defaultFeeSource,
MaxPerBlock: defaultMaxPerBlock,
HighPricePenalty: defaultHighPricePenalty,
BlocksToAvg: defaultBlocksToAvg,
FeeTargetScaling: defaultFeeTargetScaling,
MaxInMempool: defaultMaxInMempool,
ExpiryDelta: defaultExpiryDelta,
},
}

// Pre-parse the command line options to see if an alternative config
Expand Down Expand Up @@ -418,6 +472,27 @@ func loadConfig() (*config, []string, error) {
cfg.EnableVoting = true
}

// Make sure the fee source type given is valid.
switch cfg.TBOpts.FeeSource {
case ticketbuyer.TicketFeeMean:
case ticketbuyer.TicketFeeMedian:
default:
str := "%s: Invalid fee source '%s'"
err := fmt.Errorf(str, funcName, cfg.TBOpts.FeeSource)
return loadConfigError(err)
}

// Make sure a valid average price mode is given.
switch cfg.TBOpts.AvgPriceMode {
case ticketbuyer.PriceTargetVWAP:
case ticketbuyer.PriceTargetPool:
case ticketbuyer.PriceTargetDual:
default:
str := "%s: Invalid average price mode '%s'"
err := fmt.Errorf(str, funcName, cfg.TBOpts.AvgPriceMode)
return loadConfigError(err)
}

// If an alternate data directory was specified, and paths with defaults
// relative to the data dir are unchanged, modify each path to be
// relative to the new data dir.
Expand Down Expand Up @@ -773,5 +848,41 @@ func loadConfig() (*config, []string, error) {
cfg.DcrdPassword = cfg.Password
}

// Warn if user still has an old ticket buyer configuration file.
oldTBConfigFile := filepath.Join(cfg.AppDataDir, "ticketbuyer.conf")
if _, err := os.Stat(oldTBConfigFile); err == nil {
log.Warnf("%s is no longer used and should be removed. "+
"Please prepend 'ticketbuyer.' to each option and "+
"move it under the [Ticket Buyer Options] section "+
"of %s\n",
oldTBConfigFile, configFilePath)
}

// Build ticketbuyer config
cfg.tbCfg = &ticketbuyer.Config{
AccountName: cfg.PurchaseAccount,
AvgPriceMode: cfg.TBOpts.AvgPriceMode,
AvgPriceVWAPDelta: cfg.TBOpts.AvgPriceVWAPDelta,
BalanceToMaintain: cfg.BalanceToMaintain,
BlocksToAvg: cfg.TBOpts.BlocksToAvg,
DontWaitForTickets: cfg.TBOpts.DontWaitForTickets,
ExpiryDelta: cfg.TBOpts.ExpiryDelta,
FeeSource: cfg.TBOpts.FeeSource,
FeeTargetScaling: cfg.TBOpts.FeeTargetScaling,
HighPricePenalty: cfg.TBOpts.HighPricePenalty,
MinFee: cfg.TBOpts.MinFee,
MinPriceScale: cfg.TBOpts.MinPriceScale,
MaxFee: cfg.TBOpts.MaxFee,
MaxPerBlock: cfg.TBOpts.MaxPerBlock,
MaxPriceAbsolute: cfg.TicketMaxPrice,
MaxPriceScale: cfg.TBOpts.MaxPriceScale,
MaxInMempool: cfg.TBOpts.MaxInMempool,
PoolAddress: cfg.PoolAddress,
PoolFees: cfg.PoolFees,
PriceTarget: cfg.TBOpts.PriceTarget,
TicketAddress: cfg.TicketAddress,
TxFee: cfg.RelayFee,
}

return &cfg, remainingArgs, nil
}
55 changes: 37 additions & 18 deletions dcrwallet.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Copyright (c) 2015 The Decred developers
// Copyright (c) 2015-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

Expand All @@ -18,6 +18,7 @@ import (
"time"

"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrrpcclient"
"github.com/decred/dcrwallet/chain"
"github.com/decred/dcrwallet/internal/prompt"
"github.com/decred/dcrwallet/internal/zero"
Expand All @@ -27,8 +28,7 @@ import (
)

var (
cfg *config
ticketBuyerCfg *ticketbuyer.Config
cfg *config
)

func main() {
Expand Down Expand Up @@ -90,20 +90,6 @@ func walletMain() error {
}()
}

// Load ticket buyer config, if any. A missing config file causes this to
// returns a default config and no error.
ticketBuyerCfg, err = loadTicketBuyerConfig(cfg)
if err != nil {
s := fmt.Sprintf("Failed to read ticket buyer config: %v", err)
// Fatal error when ticket buyer is enabled.
if cfg.EnableTicketBuyer {
log.Error(s)
return err
}
// Otherwise just warn.
log.Warn(s)
}

dbDir := networkDir(cfg.AppDataDir, activeNet.Params)
stakeOptions := &wallet.StakeOptions{
VoteBits: cfg.VoteBits,
Expand Down Expand Up @@ -303,7 +289,7 @@ func rpcClientConnectLoop(legacyRPCServer *legacyrpc.Server, loader *wallet.Load
legacyRPCServer.SetChainServer(chainClient)
}
if cfg.EnableTicketBuyer {
startTicketPurchase(w, chainClient.Client, nil, ticketBuyerCfg)
startTicketPurchase(w, chainClient.Client, nil, cfg.tbCfg)
}
}
mu := new(sync.Mutex)
Expand Down Expand Up @@ -372,3 +358,36 @@ func startChainRPC(certs []byte) (*chain.RPCClient, error) {
err = rpcc.Start()
return rpcc, err
}

// startTicketPurchase launches ticketbuyer to start purchasing tickets.
func startTicketPurchase(w *wallet.Wallet, dcrdClient *dcrrpcclient.Client,
passphrase []byte, ticketbuyerCfg *ticketbuyer.Config) {

tkbyLog.Info("Starting ticket buyer")

p, err := ticketbuyer.NewTicketPurchaser(ticketbuyerCfg,
dcrdClient, w, activeNet.Params)
if err != nil {
tkbyLog.Errorf("Error starting ticketbuyer: %v", err)
return
}
if passphrase != nil {
var unlockAfter <-chan time.Time
err = w.Unlock(passphrase, unlockAfter)
if err != nil {
tkbyLog.Errorf("Error unlocking wallet: %v", err)
return
}
}
quit := make(chan struct{})
n := w.NtfnServer.MainTipChangedNotifications()
pm := ticketbuyer.NewPurchaseManager(w, p, n.C, quit)
go pm.NotificationHandler()
go func() {
dcrdClient.WaitForShutdown()

tkbyLog.Info("Stopping ticket buyer")
n.Done()
close(quit)
}()
}
60 changes: 60 additions & 0 deletions sample-dcrwallet.conf
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,63 @@
; profile=:6062 ; listen on port 6062 on all interfaces (NOT recommended)
; profile=127.0.0.1:6062 ; listen on port 6062 on IPv4 loopback
; profile=[::1]:6062 ; listen on port 6062 on IPv6 loopback

[Ticket Buyer Options]

; ------------------------------------------------------------------------------
; Ticket Buyer settings
; ------------------------------------------------------------------------------

; Attempt to prevent the stake difficulty from going above this
; multiplier (>1.0) by manipulation, 0 to disable
; ticketbuyer.maxpricescale=2.0

; Attempt to prevent the stake difficulty from going below this
; multiplier (<1.0) by manipulation, 0 to disable
; ticketbuyer.minpricescale=0.7

; A target to try to seek setting the stake price to rather than meeting the
; average price, 0 to disable.
; ticketbuyer.pricetarget=0

; Maximum tickets per block, with negative numbers indicating buy one ticket
; every 1-in-n blocks
; ticketbuyer.maxperblock=5

; The amount above the mean fee in the previous blocks to purchase tickets with,
; proportional e.g. 1.05 = 105%
; ticketbuyer.feetargetscaling=1.05

; The mode to use for calculating the average price if pricetarget is disabled
; (vwap, pool, dual)
; ticketbuyer.avgpricemode=vwap

; The number of blocks to use from the current block to calculate the VWAP
; ticketbuyer.avgpricevwapdelta=2880

; The exponential penalty to apply to the number of tickets to purchase above
; the ideal ticket pool price
; ticketbuyer.highpricepenalty=1.3

; Maximum ticket fee per KB
; ticketbuyer.maxfee=1.0

; Minimum ticket fee per KB
; ticketbuyer.minfee=0.01

; The fee source to use for ticket fee per KB (median or mean)
; ticketbuyer.feesource=mean

; Number of blocks to average for fees calculation
; ticketbuyer.blockstoavg=11

; The maximum number of your tickets allowed in mempool before purchasing more
; tickets
; ticketbuyer.maxinmempool=40

; Don't wait until your last round of tickets have entered the blockchain to
; attempt to purchase more
; ticketbuyer.dontwaitfortickets=1

; Number of blocks in the future before the ticket expires
; ticketbuyer.expirydelta=16
Loading

0 comments on commit 32b6ca9

Please sign in to comment.