diff --git a/config.go b/config.go index 7a1eeed5e..fbaf20e8f 100644 --- a/config.go +++ b/config.go @@ -65,6 +65,8 @@ const ( // ticket buyer options defaultBalanceToMaintainAbsolute = 0 defaultTicketbuyerLimit = 1 + // If max ticket price is set to 0, purchase will still be made even if ticket price is too high + defaultMaxTicketPrice = 0 walletDbName = "wallet.db" ) @@ -187,6 +189,7 @@ type ticketBuyerOptions struct { BalanceToMaintainAbsolute *cfgutil.AmountFlag `long:"balancetomaintainabsolute" description:"Amount of funds to keep in wallet when purchasing tickets"` Limit uint `long:"limit" description:"Buy no more than specified number of tickets per block"` VotingAccount string `long:"votingaccount" description:"Account used to derive addresses specifying voting rights"` + MaxTicketPrice *cfgutil.AmountFlag `long:"maxticketprice" description:"Don't buy when the price is too high"` } type vspOptions struct { @@ -382,6 +385,7 @@ func loadConfig(ctx context.Context) (*config, []string, error) { TBOpts: ticketBuyerOptions{ BalanceToMaintainAbsolute: cfgutil.NewAmountFlag(defaultBalanceToMaintainAbsolute), Limit: defaultTicketbuyerLimit, + MaxTicketPrice: cfgutil.NewAmountFlag(defaultMaxTicketPrice), }, VSPOpts: vspOptions{ @@ -573,6 +577,14 @@ func loadConfig(ctx context.Context) (*config, []string, error) { return loadConfigError(err) } + // Check valid Maxticketprice param + if cfg.TBOpts.MaxTicketPrice.ToCoin() < 0 { + str := "%s: maxticketprice cannot be negative: %v" + err := errors.Errorf(str, funcName, cfg.TBOpts.MaxTicketPrice) + fmt.Fprintln(os.Stderr, err) + return loadConfigError(err) + } + // Exit if you try to use a simulation wallet with a standard // data directory. if !cfg.AppDataDir.ExplicitlySet() && cfg.CreateTemp { diff --git a/dcrwallet.go b/dcrwallet.go index 7013e9462..89b14354e 100644 --- a/dcrwallet.go +++ b/dcrwallet.go @@ -335,6 +335,7 @@ func run(ctx context.Context) error { BuyTickets: cfg.EnableTicketBuyer, Account: purchaseAccount, Maintain: cfg.TBOpts.BalanceToMaintainAbsolute.Amount, + MaxTicketPrice: cfg.TBOpts.MaxTicketPrice.Amount, Limit: int(cfg.TBOpts.Limit), VotingAccount: votingAccount, Mixing: cfg.Mixing, diff --git a/sample-dcrwallet.conf b/sample-dcrwallet.conf index e2ec3d3ba..35590f99d 100644 --- a/sample-dcrwallet.conf +++ b/sample-dcrwallet.conf @@ -238,6 +238,7 @@ ; Amount of funds to keep in wallet when stake mining ; ticketbuyer.balancetomaintainabsolute=0 +; ticketbuyer.maxticketprice=0 [VSP Options] diff --git a/ticketbuyer/tb.go b/ticketbuyer/tb.go index a7b9f8a53..57a4a82ce 100644 --- a/ticketbuyer/tb.go +++ b/ticketbuyer/tb.go @@ -30,6 +30,9 @@ type Config struct { // Minimum amount to maintain in purchasing account Maintain dcrutil.Amount + // Maximum ticket price allowed to be purchased + MaxTicketPrice dcrutil.Amount + // Limit maximum number of purchased tickets per block Limit int @@ -282,11 +285,11 @@ func (tb *TB) buy(ctx context.Context, passphrase []byte, tip *wire.BlockHeader, } purchaseTicketReq := &wallet.PurchaseTicketsRequest{ - Count: buy, - SourceAccount: account, - MinConf: minconf, - Expiry: expiry, - + Count: buy, + SourceAccount: account, + MinConf: minconf, + Expiry: expiry, + MaxTicketPrice: cfg.MaxTicketPrice, // CSPP Mixing: mixing, VotingAccount: votingAccount, diff --git a/wallet/createtx.go b/wallet/createtx.go index fc62526a9..852f76994 100644 --- a/wallet/createtx.go +++ b/wallet/createtx.go @@ -1204,6 +1204,11 @@ func (w *Wallet) purchaseTickets(ctx context.Context, op errors.Op, return nil, err } + // If the ticket price exceeds the max ticket price, skipping purchase + if req.MaxTicketPrice > 0 && ticketPrice > req.MaxTicketPrice { + return nil, errors.E(op, errors.Invalid, "Skipping purchase: Ticket price exceeds max ticket price.") + } + const stakeSubmissionPkScriptSize = txsizes.P2PKHPkScriptSize + 1 // Make sure that we have enough funds. Calculate different diff --git a/wallet/wallet.go b/wallet/wallet.go index 2fd9bde22..3f2a683ad 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -1552,6 +1552,7 @@ type PurchaseTicketsRequest struct { SourceAccount uint32 MinConf int32 Expiry int32 + MaxTicketPrice dcrutil.Amount VotingAccount uint32 // Used when Mixing == true || UseVotingAccount == true UseVotingAccount bool // Forces use of supplied voting account. DontSignTx bool