diff --git a/config.go b/config.go index 714c92da5..cf75a1344 100644 --- a/config.go +++ b/config.go @@ -1,5 +1,5 @@ // Copyright (c) 2013-2016 The btcsuite developers -// Copyright (c) 2015-2020 The Decred developers +// Copyright (c) 2015-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -56,7 +56,6 @@ const ( defaultPass = "" defaultPromptPublicPass = false defaultGapLimit = wallet.DefaultGapLimit - defaultStakePoolColdExtKey = "" defaultAllowHighFees = false defaultAccountGapLimit = wallet.DefaultAccountGapLimit defaultDisableCoinTypeUpgrades = false @@ -115,7 +114,6 @@ type config struct { PoolFees float64 `long:"poolfees" description:"VSP fee percentage (1.00 equals 1.00% fee)"` GapLimit uint32 `long:"gaplimit" description:"Allowed unused address gap between used addresses of accounts"` WatchLast uint32 `long:"watchlast" description:"Limit watched previous addresses of each HD account branch"` - StakePoolColdExtKey string `long:"stakepoolcoldextkey" description:"xpub:maxindex for fee addresses (VSP-only option)"` ManualTickets bool `long:"manualtickets" description:"Do not discover new tickets through network synchronization"` AllowHighFees bool `long:"allowhighfees" description:"Do not perform high fee checks"` RelayFee *cfgutil.AmountFlag `long:"txfee" description:"Transaction fee per kilobyte"` @@ -380,7 +378,6 @@ func loadConfig(ctx context.Context) (*config, []string, error) { EnableVoting: defaultEnableVoting, PurchaseAccount: defaultPurchaseAccount, GapLimit: defaultGapLimit, - StakePoolColdExtKey: defaultStakePoolColdExtKey, AllowHighFees: defaultAllowHighFees, RelayFee: cfgutil.NewAmountFlag(txrules.DefaultRelayFeePerKb), PoolAddress: cfgutil.NewAddressFlag(), diff --git a/dcrwallet.go b/dcrwallet.go index c6ff9fd30..b2e32e0f1 100644 --- a/dcrwallet.go +++ b/dcrwallet.go @@ -1,5 +1,5 @@ // Copyright (c) 2013-2015 The btcsuite developers -// Copyright (c) 2015-2023 The Decred developers +// Copyright (c) 2015-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -167,11 +167,10 @@ func run(ctx context.Context) error { // wallet. Otherwise, loading is deferred so it can be performed over RPC. dbDir := networkDir(cfg.AppDataDir.Value, activeNet.Params) stakeOptions := &ldr.StakeOptions{ - VotingEnabled: cfg.EnableVoting, - VotingAddress: cfg.TBOpts.votingAddress, - PoolAddress: cfg.poolAddress, - PoolFees: cfg.PoolFees, - StakePoolColdExtKey: cfg.StakePoolColdExtKey, + VotingEnabled: cfg.EnableVoting, + VotingAddress: cfg.TBOpts.votingAddress, + PoolAddress: cfg.poolAddress, + PoolFees: cfg.PoolFees, } loader := ldr.NewLoader(activeNet.Params, dbDir, stakeOptions, cfg.GapLimit, cfg.WatchLast, cfg.AllowHighFees, cfg.RelayFee.Amount, diff --git a/internal/loader/loader.go b/internal/loader/loader.go index 000ea8dab..84cabbb3b 100644 --- a/internal/loader/loader.go +++ b/internal/loader/loader.go @@ -1,5 +1,5 @@ // Copyright (c) 2015-2018 The btcsuite developers -// Copyright (c) 2017-2020 The Decred developers +// Copyright (c) 2017-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -54,11 +54,10 @@ type Loader struct { // StakeOptions contains the various options necessary for stake mining. type StakeOptions struct { - VotingEnabled bool - VotingAddress stdaddr.StakeAddress - PoolAddress stdaddr.StakeAddress - PoolFees float64 - StakePoolColdExtKey string + VotingEnabled bool + VotingAddress stdaddr.StakeAddress + PoolAddress stdaddr.StakeAddress + PoolFees float64 } // NewLoader constructs a Loader. @@ -185,7 +184,6 @@ func (l *Loader) CreateWatchingOnlyWallet(ctx context.Context, extendedPubKey st AccountGapLimit: l.accountGapLimit, DisableCoinTypeUpgrades: l.disableCoinTypeUpgrades, DisableMixing: l.disableMixing, - StakePoolColdExtKey: so.StakePoolColdExtKey, ManualTickets: l.manualTickets, AllowHighFees: l.allowHighFees, RelayFee: l.relayFee, @@ -277,7 +275,6 @@ func (l *Loader) CreateNewWallet(ctx context.Context, pubPassphrase, privPassphr WatchLast: l.watchLast, AccountGapLimit: l.accountGapLimit, DisableCoinTypeUpgrades: l.disableCoinTypeUpgrades, - StakePoolColdExtKey: so.StakePoolColdExtKey, ManualTickets: l.manualTickets, AllowHighFees: l.allowHighFees, RelayFee: l.relayFee, @@ -338,7 +335,6 @@ func (l *Loader) OpenExistingWallet(ctx context.Context, pubPassphrase []byte) ( WatchLast: l.watchLast, AccountGapLimit: l.accountGapLimit, DisableCoinTypeUpgrades: l.disableCoinTypeUpgrades, - StakePoolColdExtKey: so.StakePoolColdExtKey, ManualTickets: l.manualTickets, AllowHighFees: l.allowHighFees, RelayFee: l.relayFee, diff --git a/internal/rpc/jsonrpc/methods.go b/internal/rpc/jsonrpc/methods.go index 0a0a420c4..0aa7f81b6 100644 --- a/internal/rpc/jsonrpc/methods.go +++ b/internal/rpc/jsonrpc/methods.go @@ -55,9 +55,9 @@ import ( // API version constants const ( - jsonrpcSemverString = "9.2.0" - jsonrpcSemverMajor = 9 - jsonrpcSemverMinor = 2 + jsonrpcSemverString = "10.0.0" + jsonrpcSemverMajor = 10 + jsonrpcSemverMinor = 0 jsonrpcSemverPatch = 0 ) @@ -171,11 +171,9 @@ var handlers = map[string]handler{ "signrawtransaction": {fn: (*Server).signRawTransaction}, "signrawtransactions": {fn: (*Server).signRawTransactions}, "spendoutputs": {fn: (*Server).spendOutputs}, - "stakepooluserinfo": {fn: (*Server).stakePoolUserInfo}, "sweepaccount": {fn: (*Server).sweepAccount}, "syncstatus": {fn: (*Server).syncStatus}, "ticketinfo": {fn: (*Server).ticketInfo}, - "ticketsforaddress": {fn: (*Server).ticketsForAddress}, "treasurypolicy": {fn: (*Server).treasuryPolicy}, "tspendpolicy": {fn: (*Server).tspendPolicy}, "unlockaccount": {fn: (*Server).unlockAccount}, @@ -4343,67 +4341,6 @@ func (s *Server) spendOutputs(ctx context.Context, icmd any) (any, error) { return hash.String(), nil } -// stakePoolUserInfo returns the ticket information for a given user from the -// stake pool. -func (s *Server) stakePoolUserInfo(ctx context.Context, icmd any) (any, error) { - cmd := icmd.(*types.StakePoolUserInfoCmd) - w, ok := s.walletLoader.LoadedWallet() - if !ok { - return nil, errUnloadedWallet - } - - userAddr, err := decodeStakeAddress(cmd.User, w.ChainParams()) - if err != nil { - return nil, err - } - spui, err := w.StakePoolUserInfo(ctx, userAddr) - if err != nil { - return nil, err - } - - resp := new(types.StakePoolUserInfoResult) - resp.Tickets = make([]types.PoolUserTicket, 0, len(spui.Tickets)) - resp.InvalidTickets = make([]string, 0, len(spui.InvalidTickets)) - _, height := w.MainChainTip(ctx) - for _, ticket := range spui.Tickets { - var ticketRes types.PoolUserTicket - - status := "" - switch ticket.Status { - case udb.TSImmatureOrLive: - maturedHeight := int32(ticket.HeightTicket + uint32(w.ChainParams().TicketMaturity) + 1) - - if height >= maturedHeight { - status = "live" - } else { - status = "immature" - } - case udb.TSVoted: - status = "voted" - case udb.TSMissed: - status = "missed" - if ticket.HeightSpent-ticket.HeightTicket >= w.ChainParams().TicketExpiry { - status = "expired" - } - } - ticketRes.Status = status - - ticketRes.Ticket = ticket.Ticket.String() - ticketRes.TicketHeight = ticket.HeightTicket - ticketRes.SpentBy = ticket.SpentBy.String() - ticketRes.SpentByHeight = ticket.HeightSpent - - resp.Tickets = append(resp.Tickets, ticketRes) - } - for _, invalid := range spui.InvalidTickets { - invalidTicket := invalid.String() - - resp.InvalidTickets = append(resp.InvalidTickets, invalidTicket) - } - - return resp, nil -} - func (s *Server) ticketInfo(ctx context.Context, icmd any) (any, error) { cmd := icmd.(*types.TicketInfoCmd) w, ok := s.walletLoader.LoadedWallet() @@ -4481,34 +4418,6 @@ func (s *Server) ticketInfo(ctx context.Context, icmd any) (any, error) { return res, err } -// ticketsForAddress retrieves all ticket hashes that have the passed voting -// address. It will only return tickets that are in the mempool or blockchain, -// and should not return pruned tickets. -func (s *Server) ticketsForAddress(ctx context.Context, icmd any) (any, error) { - cmd := icmd.(*types.TicketsForAddressCmd) - w, ok := s.walletLoader.LoadedWallet() - if !ok { - return nil, errUnloadedWallet - } - - addr, err := stdaddr.DecodeAddress(cmd.Address, w.ChainParams()) - if err != nil { - return nil, err - } - - ticketHashes, err := w.TicketHashesForVotingAddress(ctx, addr) - if err != nil { - return nil, err - } - - ticketHashStrs := make([]string, 0, len(ticketHashes)) - for _, hash := range ticketHashes { - ticketHashStrs = append(ticketHashStrs, hash.String()) - } - - return dcrdtypes.TicketsForAddressResult{Tickets: ticketHashStrs}, nil -} - func isNilOrEmpty(s *string) bool { return s == nil || *s == "" } diff --git a/internal/rpc/jsonrpc/rpcserverhelp.go b/internal/rpc/jsonrpc/rpcserverhelp.go index 0004ff15d..c2eca3b2b 100644 --- a/internal/rpc/jsonrpc/rpcserverhelp.go +++ b/internal/rpc/jsonrpc/rpcserverhelp.go @@ -90,11 +90,9 @@ func helpDescsEnUS() map[string]string { "signrawtransaction": "signrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\n\nSigns transaction inputs using private keys from this wallet and request.\nThe valid flags options are ALL, NONE, SINGLE, ALL|ANYONECANPAY, NONE|ANYONECANPAY, and SINGLE|ANYONECANPAY.\n\nArguments:\n1. rawtx (string, required) Unsigned or partially unsigned transaction to sign encoded as a hexadecimal string\n2. inputs (array of object, optional) Additional data regarding inputs that this wallet may not be tracking\n3. privkeys (array of string, optional) Additional WIF-encoded private keys to use when creating signatures\n4. flags (string, optional, default=\"ALL\") Sighash flags\n\nResult:\n{\n \"hex\": \"value\", (string) The resulting transaction encoded as a hexadecimal string\n \"complete\": true|false, (boolean) Whether all input signatures have been created\n \"errors\": [{ (array of object) Script verification errors (if exists)\n \"txid\": \"value\", (string) The transaction hash of the referenced previous output\n \"vout\": n, (numeric) The output index of the referenced previous output\n \"scriptSig\": \"value\", (string) The hex-encoded signature script\n \"sequence\": n, (numeric) Script sequence number\n \"error\": \"value\", (string) Verification or signing error related to the input\n },...], \n} \n", "signrawtransactions": "signrawtransactions [\"rawtx\",...] (send=true)\n\nSigns transaction inputs using private keys from this wallet and request for a list of transactions.\n\n\nArguments:\n1. rawtxs (array of string, required) A list of transactions to sign (and optionally send).\n2. send (boolean, optional, default=true) Set true to send the transactions after signing.\n\nResult:\n{\n \"results\": [{ (array of object) Returned values from the signrawtransactions command.\n \"signingresult\": { (object) Success or failure of signing.\n \"hex\": \"value\", (string) The resulting transaction encoded as a hexadecimal string\n \"complete\": true|false, (boolean) Whether all input signatures have been created\n \"errors\": [{ (array of object) Script verification errors (if exists)\n \"txid\": \"value\", (string) The transaction hash of the referenced previous output\n \"vout\": n, (numeric) The output index of the referenced previous output\n \"scriptSig\": \"value\", (string) The hex-encoded signature script\n \"sequence\": n, (numeric) Script sequence number\n \"error\": \"value\", (string) Verification or signing error related to the input\n },...], \n }, \n \"sent\": true|false, (boolean) Tells if the transaction was sent.\n \"txhash\": \"value\", (string) The hash of the signed tx.\n },...], \n} \n", "spendoutputs": "spendoutputs \"account\" [\"previousoutpoint\",...] [{\"address\":\"value\",\"amount\":n.nnn},...]\n\nCreate, sign, and publish a transaction spending the specified wallet outputs, and paying an array of address/amount pairs.\nOutputs must belong to the specified account, and change (if needed) is returned to an internal address of the same account.\n\nArguments:\n1. account (string, required) Account of specified previous outpoints, and account used to return change\n2. previousoutpoints (array of string, required) Array of outpoints in string encoding (\"hash:index\")\n3. outputs (array of object, required) Array of JSON objects, each specifying an address string and amount\n[{\n \"address\": \"value\", (string) Address to pay\n \"amount\": n.nnn, (numeric) Amount to pay the address\n},...]\n\nResult:\n\"value\" (string) The published transaction hash\n", - "stakepooluserinfo": "stakepooluserinfo \"user\"\n\nGet user info for stakepool\n\nArguments:\n1. user (string, required) The id of the user to be looked up\n\nResult:\n{\n \"tickets\": [{ (array of object) A list of valid tickets that the user has added\n \"status\": \"value\", (string) The current status of the added ticket\n \"ticket\": \"value\", (string) The hash of the added ticket\n \"ticketheight\": n, (numeric) The height in which the ticket was added\n \"spentby\": \"value\", (string) The vote in which the ticket was spent\n \"spentbyheight\": n, (numeric) The height in which the ticket was spent\n },...], \n \"invalid\": [\"value\",...], (array of string) A list of invalid tickets that the user has added\n} \n", "sweepaccount": "sweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\n\nMoves as much value as possible in a transaction from an account.\n\n\nArguments:\n1. sourceaccount (string, required) The account to be swept.\n2. destinationaddress (string, required) The destination address to pay to.\n3. requiredconfirmations (numeric, optional) The minimum utxo confirmation requirement (optional).\n4. feeperkb (numeric, optional) The minimum relay fee policy (optional).\n\nResult:\n{\n \"unsignedtransaction\": \"value\", (string) The hex encoded string of the unsigned transaction.\n \"totalpreviousoutputamount\": n.nnn, (numeric) The total transaction input amount.\n \"totaloutputamount\": n.nnn, (numeric) The total transaction output amount.\n \"estimatedsignedsize\": n, (numeric) The estimated size of the transaction when signed.\n} \n", "syncstatus": "syncstatus\n\nReturns information about this wallet's synchronization to the network.\n\nArguments:\nNone\n\nResult:\n{\n \"synced\": true|false, (boolean) Whether or not the wallet is fully caught up to the network.\n \"initialblockdownload\": true|false, (boolean) Best guess of whether this wallet is in the initial block download mode used to catch up the blockchain when it is far behind.\n \"headersfetchprogress\": n.nnn, (numeric) Estimated progress of the headers fetching stage of the current sync process.\n} \n", "ticketinfo": "ticketinfo (startheight=0)\n\nReturns details of each wallet ticket transaction\n\nArguments:\n1. startheight (numeric, optional, default=0) Specify the starting block height to scan from\n\nResult:\n[{\n \"hash\": \"value\", (string) Transaction hash of the ticket\n \"cost\": n.nnn, (numeric) Amount paid to purchase the ticket; this may be greater than the ticket price at time of purchase\n \"votingaddress\": \"value\", (string) Address of 0th output, which describes the requirements to spend the ticket\n \"status\": \"value\", (string) Description of ticket status (unknown, unmined, immature, mature, live, voted, missed, expired, unspent, revoked)\n \"blockhash\": \"value\", (string) Hash of block ticket is mined in\n \"blockheight\": n, (numeric) Height of block ticket is mined in\n \"vote\": \"value\", (string) Transaction hash of vote which spends the ticket\n \"revocation\": \"value\", (string) Transaction hash of revocation which spends the ticket\n \"choices\": [{ (array of object) Vote preferences set for the ticket\n \"agendaid\": \"value\", (string) The ID for the agenda the choice concerns\n \"agendadescription\": \"value\", (string) A description of the agenda the choice concerns\n \"choiceid\": \"value\", (string) The ID of the current choice for this agenda\n \"choicedescription\": \"value\", (string) A description of the current choice for this agenda\n },...], \n \"vsphost\": \"value\", (string) VSP Host associated with the ticket (if any)\n},...]\n", - "ticketsforaddress": "ticketsforaddress \"address\"\n\nRequest all the tickets for an address.\n\nArguments:\n1. address (string, required) Address to look for.\n\nResult:\ntrue|false (boolean) Tickets owned by the specified address.\n", "treasurypolicy": "treasurypolicy (\"key\" \"ticket\")\n\nReturn voting policies for treasury spend transactions by key\n\nArguments:\n1. key (string, optional) Return the policy for a particular key\n2. ticket (string, optional) Return policies used by a specific ticket hash\n\nResult (no key provided):\n[{\n \"key\": \"value\", (string) Treasury key associated with a policy\n \"policy\": \"value\", (string) Voting policy description (abstain, yes, or no)\n \"ticket\": \"value\", (string) Ticket hash of a per-ticket treasury key approval policy\n},...]\n\nResult (key specified):\n{\n \"key\": \"value\", (string) Treasury key associated with a policy\n \"policy\": \"value\", (string) Voting policy description (abstain, yes, or no)\n \"ticket\": \"value\", (string) Ticket hash of a per-ticket treasury key approval policy\n} \n", "tspendpolicy": "tspendpolicy (\"hash\" \"ticket\")\n\nReturn voting policies for treasury spend transactions\n\nArguments:\n1. hash (string, optional) Return the policy for a particular tspend hash\n2. ticket (string, optional) Return policies used by a specific ticket hash\n\nResult (no tspend hash provided):\n[{\n \"hash\": \"value\", (string) Treasury spend transaction hash\n \"policy\": \"value\", (string) Voting policy description (abstain, yes, or no)\n \"ticket\": \"value\", (string) Ticket hash of a per-ticket tspend approval policy\n},...]\n\nResult (tspend hash specified):\n{\n \"hash\": \"value\", (string) Treasury spend transaction hash\n \"policy\": \"value\", (string) Voting policy description (abstain, yes, or no)\n \"ticket\": \"value\", (string) Ticket hash of a per-ticket tspend approval policy\n} \n", "unlockaccount": "unlockaccount \"account\" \"passphrase\"\n\nUnlock an individually-encrypted account\n\nArguments:\n1. account (string, required) Account to unlock\n2. passphrase (string, required) Account passphrase\n\nResult:\nNothing\n", @@ -115,4 +113,4 @@ var localeHelpDescs = map[string]func() map[string]string{ "en_US": helpDescsEnUS, } -var requestUsages = "abandontransaction \"hash\"\naccountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naccountunlocked \"account\"\naddmultisigaddress nrequired [\"key\",...] (\"account\")\naddtransaction \"blockhash\" \"transaction\"\nauditreuse (since)\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ncreatenewaccount \"account\"\ncreaterawtransaction [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...] {\"address\":amount,...} (locktime expiry)\ncreatesignature \"address\" inputindex hashtype \"previouspkscript\" \"serializedtransaction\"\ndisapprovepercent\ndiscoverusage (\"startblock\" discoveraccounts gaplimit)\ndumpprivkey \"address\"\nfundrawtransaction \"hexstring\" \"fundaccount\" ({\"changeaddress\":changeaddress,\"feerate\":feerate,\"conftarget\":conftarget})\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblock\ngetbestblockhash\ngetblockcount\ngetblockhash index\ngetblockheader \"hash\" (verbose=true)\ngetblock \"hash\" (verbose=true verbosetx=false)\ngetcoinjoinsbyacct\ngetcurrentnet\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetpeerinfo\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngetstakeinfo\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngettxout \"txid\" vout tree (includemempool=true)\ngetunconfirmedbalance (\"account\")\ngetvotechoices (\"tickethash\")\ngetwalletfee\ngetcfilterv2 \"blockhash\"\nhelp (\"command\")\nimportcfiltersv2 startheight [\"filter\",...]\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportpubkey \"pubkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nimportxpub \"name\" \"xpub\"\nlistaccounts (minconf=1)\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nlistlockunspent (\"account\")\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\nlockaccount \"account\"\nlockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nmixaccount\nmixoutput \"outpoint\"\nprocessunmanagedticket \"tickethash\"\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets=1 \"pooladdress\" poolfees expiry \"comment\" dontsigntx)\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrenameaccount \"oldaccount\" \"newaccount\"\nrescanwallet (beginheight=0)\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendfromtreasury \"key\" amounts\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendrawtransaction \"hextx\" (allowhighfees=false)\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsendtotreasury amount\nsetaccountpassphrase \"account\" \"passphrase\"\nsetdisapprovepercent percent\nsettreasurypolicy \"key\" \"policy\" (\"ticket\")\nsettspendpolicy \"hash\" \"policy\" (\"ticket\")\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\" (\"tickethash\")\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nspendoutputs \"account\" [\"previousoutpoint\",...] [{\"address\":\"value\",\"amount\":n.nnn},...]\nstakepooluserinfo \"user\"\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nsyncstatus\nticketinfo (startheight=0)\nticketsforaddress \"address\"\ntreasurypolicy (\"key\" \"ticket\")\ntspendpolicy (\"hash\" \"ticket\")\nunlockaccount \"account\" \"passphrase\"\nvalidateaddress \"address\"\nvalidatepredcp0005cf\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletinfo\nwalletislocked\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\nwalletpubpassphrasechange \"oldpassphrase\" \"newpassphrase\"" +var requestUsages = "abandontransaction \"hash\"\naccountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naccountunlocked \"account\"\naddmultisigaddress nrequired [\"key\",...] (\"account\")\naddtransaction \"blockhash\" \"transaction\"\nauditreuse (since)\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ncreatenewaccount \"account\"\ncreaterawtransaction [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...] {\"address\":amount,...} (locktime expiry)\ncreatesignature \"address\" inputindex hashtype \"previouspkscript\" \"serializedtransaction\"\ndisapprovepercent\ndiscoverusage (\"startblock\" discoveraccounts gaplimit)\ndumpprivkey \"address\"\nfundrawtransaction \"hexstring\" \"fundaccount\" ({\"changeaddress\":changeaddress,\"feerate\":feerate,\"conftarget\":conftarget})\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblock\ngetbestblockhash\ngetblockcount\ngetblockhash index\ngetblockheader \"hash\" (verbose=true)\ngetblock \"hash\" (verbose=true verbosetx=false)\ngetcoinjoinsbyacct\ngetcurrentnet\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetpeerinfo\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngetstakeinfo\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngettxout \"txid\" vout tree (includemempool=true)\ngetunconfirmedbalance (\"account\")\ngetvotechoices (\"tickethash\")\ngetwalletfee\ngetcfilterv2 \"blockhash\"\nhelp (\"command\")\nimportcfiltersv2 startheight [\"filter\",...]\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportpubkey \"pubkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nimportxpub \"name\" \"xpub\"\nlistaccounts (minconf=1)\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nlistlockunspent (\"account\")\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...] \"account\")\nlockaccount \"account\"\nlockunspent unlock [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nmixaccount\nmixoutput \"outpoint\"\nprocessunmanagedticket \"tickethash\"\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets=1 \"pooladdress\" poolfees expiry \"comment\" dontsigntx)\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrenameaccount \"oldaccount\" \"newaccount\"\nrescanwallet (beginheight=0)\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendfromtreasury \"key\" amounts\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendrawtransaction \"hextx\" (allowhighfees=false)\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsendtotreasury amount\nsetaccountpassphrase \"account\" \"passphrase\"\nsetdisapprovepercent percent\nsettreasurypolicy \"key\" \"policy\" (\"ticket\")\nsettspendpolicy \"hash\" \"policy\" (\"ticket\")\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\" (\"tickethash\")\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nspendoutputs \"account\" [\"previousoutpoint\",...] [{\"address\":\"value\",\"amount\":n.nnn},...]\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nsyncstatus\nticketinfo (startheight=0)\ntreasurypolicy (\"key\" \"ticket\")\ntspendpolicy (\"hash\" \"ticket\")\nunlockaccount \"account\" \"passphrase\"\nvalidateaddress \"address\"\nvalidatepredcp0005cf\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletinfo\nwalletislocked\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\nwalletpubpassphrasechange \"oldpassphrase\" \"newpassphrase\"" diff --git a/internal/rpchelp/helpdescs_en_US.go b/internal/rpchelp/helpdescs_en_US.go index 1d5585bcc..582058bde 100644 --- a/internal/rpchelp/helpdescs_en_US.go +++ b/internal/rpchelp/helpdescs_en_US.go @@ -1,5 +1,5 @@ // Copyright (c) 2015 The btcsuite developers -// Copyright (c) 2015-2021 The Decred developers +// Copyright (c) 2015-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -879,19 +879,6 @@ var helpDescsEnUS = map[string]string{ "addressamountpair-address": "Address to pay", "addressamountpair-amount": "Amount to pay the address", - // StakePoolUserInfoCmd help. - "stakepooluserinfo--synopsis": "Get user info for stakepool", - "stakepooluserinfo-user": "The id of the user to be looked up", - - "stakepooluserinforesult-invalid": "A list of invalid tickets that the user has added", - "stakepooluserinforesult-tickets": "A list of valid tickets that the user has added", - - "pooluserticket-spentbyheight": "The height in which the ticket was spent", - "pooluserticket-spentby": "The vote in which the ticket was spent", - "pooluserticket-ticketheight": "The height in which the ticket was added", - "pooluserticket-ticket": "The hash of the added ticket", - "pooluserticket-status": "The current status of the added ticket", - // SweepAccount help. "sweepaccount--synopsis": "Moves as much value as possible in a transaction from an account.\n", "sweepaccount-sourceaccount": "The account to be swept.", @@ -920,11 +907,6 @@ var helpDescsEnUS = map[string]string{ "ticketinforesult-choices": "Vote preferences set for the ticket", "ticketinforesult-vsphost": "VSP Host associated with the ticket (if any)", - // TicketsForAddressCmd help. - "ticketsforaddress--synopsis": "Request all the tickets for an address.", - "ticketsforaddress-address": "Address to look for.", - "ticketsforaddress--result0": "Tickets owned by the specified address.", - // TransactionInput help. "transactioninput-amount": "The previous output amount", "transactioninput-txid": "The transaction hash of the referenced output", diff --git a/internal/rpchelp/methods.go b/internal/rpchelp/methods.go index 623408337..b7c8954f3 100644 --- a/internal/rpchelp/methods.go +++ b/internal/rpchelp/methods.go @@ -1,5 +1,5 @@ // Copyright (c) 2015 The btcsuite developers -// Copyright (c) 2015-2021 The Decred developers +// Copyright (c) 2015-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -113,11 +113,9 @@ var Methods = []struct { {"signrawtransaction", []any{(*types.SignRawTransactionResult)(nil)}}, {"signrawtransactions", []any{(*types.SignRawTransactionsResult)(nil)}}, {"spendoutputs", returnsString}, - {"stakepooluserinfo", []any{(*types.StakePoolUserInfoResult)(nil)}}, {"sweepaccount", []any{(*types.SweepAccountResult)(nil)}}, {"syncstatus", []any{(*types.SyncStatusResult)(nil)}}, {"ticketinfo", []any{(*[]types.TicketInfoResult)(nil)}}, - {"ticketsforaddress", returnsBool}, {"treasurypolicy", []any{(*[]types.TreasuryPolicyResult)(nil), (*types.TreasuryPolicyResult)(nil)}}, {"tspendpolicy", []any{(*[]types.TSpendPolicyResult)(nil), (*types.TSpendPolicyResult)(nil)}}, {"unlockaccount", nil}, diff --git a/rpc/client/dcrwallet/methods.go b/rpc/client/dcrwallet/methods.go index eafe2c566..29353cb07 100644 --- a/rpc/client/dcrwallet/methods.go +++ b/rpc/client/dcrwallet/methods.go @@ -1,5 +1,5 @@ // Copyright (c) 2014-2016 The btcsuite developers -// Copyright (c) 2015-2020 The Decred developers +// Copyright (c) 2015-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -749,16 +749,6 @@ func (c *Client) SetVoteChoice(ctx context.Context, agendaID, choiceID string) e return c.Call(ctx, "setvotechoice", nil, agendaID, choiceID) } -// TicketsForAddress returns a list of tickets paying to the passed address. -// If the daemon server is queried, it returns a search of tickets in the -// live ticket pool. If the wallet server is queried, it searches all tickets -// owned by the wallet. -func (c *Client) TicketsForAddress(ctx context.Context, addr stdaddr.Address) (*dcrdtypes.TicketsForAddressResult, error) { - res := new(dcrdtypes.TicketsForAddressResult) - err := c.Call(ctx, "ticketsforaddress", res, addr.String()) - return res, err -} - // WalletInfo returns wallet global state info for a given wallet. func (c *Client) WalletInfo(ctx context.Context) (*types.WalletInfoResult, error) { res := new(types.WalletInfoResult) diff --git a/rpc/jsonrpc/types/methods.go b/rpc/jsonrpc/types/methods.go index 9bf15f6ec..57b9589cd 100644 --- a/rpc/jsonrpc/types/methods.go +++ b/rpc/jsonrpc/types/methods.go @@ -1051,19 +1051,6 @@ func NewSignRawTransactionsCmd(hexEncodedTxs []string, } } -// StakePoolUserInfoCmd defines the stakepooluserinfo JSON-RPC command. -type StakePoolUserInfoCmd struct { - User string -} - -// NewStakePoolUserInfoCmd returns a new instance which can be used to issue a -// signrawtransactions JSON-RPC command. -func NewStakePoolUserInfoCmd(user string) *StakePoolUserInfoCmd { - return &StakePoolUserInfoCmd{ - User: user, - } -} - // SweepAccountCmd defines the sweep account JSON-RPC command. type SweepAccountCmd struct { SourceAccount string @@ -1305,7 +1292,6 @@ func init() { {"signrawtransaction", (*SignRawTransactionCmd)(nil)}, {"signrawtransactions", (*SignRawTransactionsCmd)(nil)}, {"spendoutputs", (*SpendOutputsCmd)(nil)}, - {"stakepooluserinfo", (*StakePoolUserInfoCmd)(nil)}, {"sweepaccount", (*SweepAccountCmd)(nil)}, {"syncstatus", (*SyncStatusCmd)(nil)}, {"ticketinfo", (*TicketInfoCmd)(nil)}, @@ -1340,7 +1326,6 @@ func init() { {"gettxout", (*GetTxOutCmd)(nil)}, {"help", (*HelpCmd)(nil)}, {"sendrawtransaction", (*SendRawTransactionCmd)(nil)}, - {"ticketsforaddress", (*TicketsForAddressCmd)(nil)}, {"validateaddress", (*ValidateAddressCmd)(nil)}, {"verifymessage", (*VerifyMessageCmd)(nil)}, {"version", (*VersionCmd)(nil)}, @@ -1376,7 +1361,6 @@ type ( GetTxOutCmd dcrdtypes.GetTxOutCmd HelpCmd dcrdtypes.HelpCmd SendRawTransactionCmd dcrdtypes.SendRawTransactionCmd - TicketsForAddressCmd dcrdtypes.TicketsForAddressCmd ValidateAddressCmd dcrdtypes.ValidateAddressCmd VerifyMessageCmd dcrdtypes.VerifyMessageCmd VersionCmd dcrdtypes.VersionCmd diff --git a/rpc/jsonrpc/types/results.go b/rpc/jsonrpc/types/results.go index 62666b102..64720716f 100644 --- a/rpc/jsonrpc/types/results.go +++ b/rpc/jsonrpc/types/results.go @@ -1,5 +1,5 @@ // Copyright (c) 2014 The btcsuite developers -// Copyright (c) 2015-2021 The Decred developers +// Copyright (c) 2015-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -330,23 +330,6 @@ type SignRawTransactionsResult struct { Results []SignedTransaction `json:"results"` } -// PoolUserTicket is the JSON struct corresponding to a stake pool user ticket -// object. -type PoolUserTicket struct { - Status string `json:"status"` - Ticket string `json:"ticket"` - TicketHeight uint32 `json:"ticketheight"` - SpentBy string `json:"spentby"` - SpentByHeight uint32 `json:"spentbyheight"` -} - -// StakePoolUserInfoResult models the data returned from the stakepooluserinfo -// command. -type StakePoolUserInfoResult struct { - Tickets []PoolUserTicket `json:"tickets"` - InvalidTickets []string `json:"invalid"` -} - // SweepAccountResult models the data returned from the sweepaccount // command. type SweepAccountResult struct { diff --git a/wallet/chainntfns.go b/wallet/chainntfns.go index 260eb10a7..f41a670f5 100644 --- a/wallet/chainntfns.go +++ b/wallet/chainntfns.go @@ -19,7 +19,6 @@ import ( blockchain "github.com/decred/dcrd/blockchain/standalone/v2" "github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/crypto/rand" - "github.com/decred/dcrd/dcrutil/v4" "github.com/decred/dcrd/txscript/v4" "github.com/decred/dcrd/txscript/v4/stdaddr" "github.com/decred/dcrd/txscript/v4/stdscript" @@ -285,85 +284,6 @@ func (w *Wallet) ChainSwitch(ctx context.Context, forest *SidechainForest, chain return prevChain, nil } -// evaluateStakePoolTicket evaluates a stake pool ticket to see if it's -// acceptable to the stake pool. The ticket must pay out to the stake -// pool cold wallet, and must have a sufficient fee. -func (w *Wallet) evaluateStakePoolTicket(rec *udb.TxRecord, blockHeight int32) bool { - tx := rec.MsgTx - - // Check the first commitment output (txOuts[1]) - // and ensure that the address found there exists - // in the list of approved addresses. Also ensure - // that the fee exists and is of the amount - // requested by the pool. - commitmentOut := tx.TxOut[1] - commitAddr, err := stake.AddrFromSStxPkScrCommitment( - commitmentOut.PkScript, w.chainParams) - if err != nil { - log.Warnf("Cannot parse commitment address from ticket %v: %v", - &rec.Hash, err) - return false - } - - // Extract the fee from the ticket. - in := dcrutil.Amount(0) - for i := range tx.TxOut { - if i%2 != 0 { - commitAmt, err := stake.AmountFromSStxPkScrCommitment( - tx.TxOut[i].PkScript) - if err != nil { - log.Warnf("Cannot parse commitment amount for output %i from ticket %v: %v", - i, &rec.Hash, err) - return false - } - in += commitAmt - } - } - out := dcrutil.Amount(0) - for i := range tx.TxOut { - out += dcrutil.Amount(tx.TxOut[i].Value) - } - fees := in - out - - _, exists := w.stakePoolColdAddrs[commitAddr.String()] - if exists { - commitAmt, err := stake.AmountFromSStxPkScrCommitment( - commitmentOut.PkScript) - if err != nil { - log.Warnf("Cannot parse commitment amount from ticket %v: %v", &rec.Hash, err) - return false - } - - // Calculate the fee required based on the current - // height and the required amount from the pool. - const dcp0010Active = false - const dcp0012Active = false - feeNeeded := txrules.StakePoolTicketFee(dcrutil.Amount( - tx.TxOut[0].Value), fees, blockHeight, w.poolFees, - w.chainParams, dcp0010Active, dcp0012Active) - if commitAmt < feeNeeded { - log.Warnf("User %s submitted ticket %v which "+ - "has less fees than are required to use this "+ - "stake pool and is being skipped (required: %v"+ - ", found %v)", commitAddr, - tx.TxHash(), feeNeeded, commitAmt) - - // Reject the entire transaction if it didn't - // pay the pool server fees. - return false - } - } else { - log.Warnf("Unknown pool commitment address %s for ticket %v", - commitAddr, tx.TxHash()) - return false - } - - log.Debugf("Accepted valid stake pool ticket %v committing %v in fees", - tx.TxHash(), tx.TxOut[0].Value) - - return true -} - // AddTransaction stores tx, marking it as mined in the block described by // blockHash, or recording it to the wallet's mempool when nil. // @@ -456,14 +376,8 @@ func (w *Wallet) processTransactionRecord(ctx context.Context, dbtx walletdb.Rea const op errors.Op = "wallet.processTransactionRecord" addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey) - stakemgrNs := dbtx.ReadWriteBucket(wstakemgrNamespaceKey) txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey) - height := int32(-1) - if header != nil { - height = int32(header.Height) - } - // At the moment all notified transactions are assumed to actually be // relevant. This assumption will not hold true when SPV support is // added, but until then, simply insert the transaction because there @@ -482,136 +396,6 @@ func (w *Wallet) processTransactionRecord(ctx context.Context, dbtx walletdb.Rea return nil, errors.E(op, err) } - // Handle incoming SStx; store them in the stake manager if we own - // the OP_SSTX tagged out, except if we're operating as a stake pool - // server. In that case, additionally consider the first commitment - // output as well. - if w.stakePoolEnabled && header != nil && rec.TxType == stake.TxTypeSStx { - // Errors don't matter here. If addrs is nil, the range below - // does nothing. - txOut := rec.MsgTx.TxOut[0] - _, addrs := stdscript.ExtractAddrs(txOut.Version, txOut.PkScript, w.chainParams) - insert := false - for _, addr := range addrs { - switch addr := addr.(type) { - case stdaddr.Hash160er: - if !w.manager.ExistsHash160(addrmgrNs, addr.Hash160()[:]) { - continue - } - default: - continue - } - - // We are operating as a stake pool. The below - // function will ONLY add the ticket into the - // stake pool if it has been found within a - // block. - if header == nil { - break - } - - if w.evaluateStakePoolTicket(rec, height) { - // Be sure to insert this into the user's stake - // pool entry into the stake manager. - poolTicket := &udb.PoolTicket{ - Ticket: rec.Hash, - HeightTicket: uint32(height), - Status: udb.TSImmatureOrLive, - } - err := w.stakeMgr.UpdateStakePoolUserTickets( - stakemgrNs, addr, poolTicket) - if err != nil { - log.Warnf("Failed to insert stake pool "+ - "user ticket: %v", err) - } - log.Debugf("Inserted stake pool ticket %v for user %v "+ - "into the stake store database", &rec.Hash, addr) - - insert = true - break - } - - // At this point the ticket must be invalid, so insert it into the - // list of invalid user tickets. - err := w.stakeMgr.UpdateStakePoolUserInvalTickets( - stakemgrNs, addr, &rec.Hash) - if err != nil { - log.Warnf("Failed to update pool user %v with "+ - "invalid ticket %v", addr, rec.Hash) - } - } - - if insert { - err := w.stakeMgr.InsertSStx(stakemgrNs, dcrutil.NewTx(&rec.MsgTx)) - if err != nil { - log.Errorf("Failed to insert SStx %v"+ - "into the stake store.", &rec.Hash) - } - } - } - - // Handle incoming mined votes (only in stakepool mode) - if w.stakePoolEnabled && rec.TxType == stake.TxTypeSSGen && header != nil { - ticketHash := &rec.MsgTx.TxIn[1].PreviousOutPoint.Hash - txInHeight := rec.MsgTx.TxIn[1].BlockHeight - poolTicket := &udb.PoolTicket{ - Ticket: *ticketHash, - HeightTicket: txInHeight, - Status: udb.TSVoted, - SpentBy: rec.Hash, - HeightSpent: uint32(height), - } - - poolUser, err := w.stakeMgr.SStxAddress(stakemgrNs, ticketHash) - if err != nil { - log.Warnf("Failed to fetch stake pool user for "+ - "ticket %v (voted ticket): %v", ticketHash, err) - } else { - err = w.stakeMgr.UpdateStakePoolUserTickets( - stakemgrNs, poolUser, poolTicket) - if err != nil { - log.Warnf("Failed to update stake pool ticket for "+ - "stake pool user %s after voting", - poolUser) - } else { - log.Debugf("Updated voted stake pool ticket %v "+ - "for user %v into the stake store database ("+ - "vote hash: %v)", ticketHash, poolUser, &rec.Hash) - } - } - } - - // Handle incoming mined revocations (only in stakepool mode) - if w.stakePoolEnabled && rec.TxType == stake.TxTypeSSRtx && header != nil { - txInHash := &rec.MsgTx.TxIn[0].PreviousOutPoint.Hash - txInHeight := rec.MsgTx.TxIn[0].BlockHeight - poolTicket := &udb.PoolTicket{ - Ticket: *txInHash, - HeightTicket: txInHeight, - Status: udb.TSMissed, - SpentBy: rec.Hash, - HeightSpent: uint32(height), - } - - poolUser, err := w.stakeMgr.SStxAddress(stakemgrNs, txInHash) - if err != nil { - log.Warnf("failed to fetch stake pool user for "+ - "ticket %v (missed ticket)", txInHash) - } else { - err = w.stakeMgr.UpdateStakePoolUserTickets( - stakemgrNs, poolUser, poolTicket) - if err != nil { - log.Warnf("failed to update stake pool ticket for "+ - "stake pool user %s after revoking", - poolUser) - } else { - log.Debugf("Updated missed stake pool ticket %v "+ - "for user %v into the stake store database ("+ - "revocation hash: %v)", txInHash, poolUser, &rec.Hash) - } - } - } - // Skip unlocking outpoints if the transaction is a vote or revocation as the lock // is not held. skipOutpoints := rec.TxType == stake.TxTypeSSGen || rec.TxType == stake.TxTypeSSRtx @@ -836,7 +620,7 @@ func (w *Wallet) processTransactionRecord(ctx context.Context, dbtx walletdb.Rea func selectOwnedTickets(w *Wallet, dbtx walletdb.ReadTx, tickets []*chainhash.Hash) []*chainhash.Hash { var owned []*chainhash.Hash for _, ticketHash := range tickets { - if w.txStore.OwnTicket(dbtx, ticketHash) || w.stakeMgr.OwnTicket(ticketHash) { + if w.txStore.OwnTicket(dbtx, ticketHash) { owned = append(owned, ticketHash) } } @@ -901,9 +685,6 @@ func (w *Wallet) VoteOnOwnedTickets(ctx context.Context, winningTicketHashes []* for i, ticketHash := range ticketHashes { ticketPurchase, err := w.txStore.Tx(txmgrNs, ticketHash) - if err != nil && errors.Is(err, errors.NotExist) { - ticketPurchase, err = w.stakeMgr.TicketPurchase(dbtx, ticketHash) - } if err != nil { log.Errorf("Failed to read ticket purchase transaction for "+ "owned winning ticket %v: %v", ticketHash, err) diff --git a/wallet/stakepool.go b/wallet/stakepool.go deleted file mode 100644 index b66030044..000000000 --- a/wallet/stakepool.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2016 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package wallet - -import ( - "context" - - "decred.org/dcrwallet/v5/errors" - "decred.org/dcrwallet/v5/wallet/udb" - "decred.org/dcrwallet/v5/wallet/walletdb" - "github.com/decred/dcrd/txscript/v4/stdaddr" -) - -// StakePoolUserInfo returns the stake pool user information for a user -// identified by their voting address. -func (w *Wallet) StakePoolUserInfo(ctx context.Context, userAddress stdaddr.StakeAddress) (*udb.StakePoolUser, error) { - const op errors.Op = "wallet.StakePoolUserInfo" - - if !w.stakePoolEnabled { - return nil, errors.E(op, errors.Invalid, "VSP features are disabled") - } - - var user *udb.StakePoolUser - err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error { - stakemgrNs := tx.ReadBucket(wstakemgrNamespaceKey) - var err error - user, err = w.stakeMgr.StakePoolUserInfo(stakemgrNs, userAddress) - return err - }) - if err != nil { - return nil, errors.E(op, err) - } - return user, nil -} diff --git a/wallet/tickets.go b/wallet/tickets.go index ec1f5b848..7cd651b75 100644 --- a/wallet/tickets.go +++ b/wallet/tickets.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2020 The Decred developers +// Copyright (c) 2016-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -10,23 +10,12 @@ import ( "decred.org/dcrwallet/v5/errors" "decred.org/dcrwallet/v5/wallet/walletdb" "github.com/decred/dcrd/chaincfg/chainhash" - dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v4" - "github.com/decred/dcrd/txscript/v4/stdaddr" "github.com/jrick/bitset" - "golang.org/x/sync/errgroup" ) // LiveTicketQuerier defines the functions required of a (trusted) network // backend that provides information about the live ticket pool. type LiveTicketQuerier interface { - // May be removed if/when there is a header commitment to the utxo - // set. - GetTxOut(ctx context.Context, txHash *chainhash.Hash, index uint32, tree int8, includeMempool bool) (*dcrdtypes.GetTxOutResult, error) - - // GetConfirmationHeight relies on the transaction index being enabled - // on the backing dcrd node. - GetConfirmationHeight(ctx context.Context, txHash *chainhash.Hash) (int32, error) - // ExistsLiveTickets relies on the node having the entire live ticket // pool available. May be removed if/when there is a header commitment // to the live ticket pool. @@ -42,24 +31,9 @@ func (w *Wallet) LiveTicketHashes(ctx context.Context, rpc LiveTicketQuerier, in var ticketHashes []chainhash.Hash var maybeLive []*chainhash.Hash - extraTickets := w.stakeMgr.DumpSStxHashes() - var tipHeight int32 // Assigned in view below. err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { - txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey) - - // Remove tickets from the extraTickets slice if they will appear in the - // ticket iteration below. - hashes := extraTickets - extraTickets = hashes[:0] - for i := range hashes { - h := &hashes[i] - if !w.txStore.ExistsTx(txmgrNs, h) { - extraTickets = append(extraTickets, *h) - } - } - _, tipHeight = w.txStore.MainChainTip(dbtx) it := w.txStore.IterateTickets(dbtx) @@ -93,62 +67,11 @@ func (w *Wallet) LiveTicketHashes(ctx context.Context, rpc LiveTicketQuerier, in return nil, errors.E(op, err) } - // SPV wallet can't evaluate extraTickets. + // SPV wallet can't evaluate possibly live tickets. if rpc == nil { return ticketHashes, nil } - // Determine if the extra tickets are immature or possibly live. Because - // these transactions are not part of the wallet's transaction history, dcrd - // must be queried for their blockchain height. This functionality requires - // the dcrd transaction index to be enabled. - var g errgroup.Group - type extraTicketResult struct { - valid bool // unspent with known height - height int32 - } - extraTicketResults := make([]extraTicketResult, len(extraTickets)) - for i := range extraTickets { - i := i - g.Go(func() error { - // gettxout is used first as an optimization to check that output 0 - // of the ticket is unspent. - const index = 0 - const tree = 1 - txOut, err := rpc.GetTxOut(ctx, &extraTickets[i], index, tree, true) - if err != nil || txOut == nil { - return nil - } - blockHeight, err := rpc.GetConfirmationHeight(ctx, &extraTickets[i]) - if err != nil { - return nil - } - extraTicketResults[i] = extraTicketResult{true, blockHeight} - return nil - }) - } - err = g.Wait() - if err != nil { - return nil, err - } - for i := range extraTickets { - r := &extraTicketResults[i] - if !r.valid { - continue - } - // Same checks as above in the db view. - if ticketExpired(w.chainParams, r.height, tipHeight) { - continue - } - if !ticketMatured(w.chainParams, r.height, tipHeight) { - if includeImmature { - ticketHashes = append(ticketHashes, extraTickets[i]) - } - continue - } - maybeLive = append(maybeLive, &extraTickets[i]) - } - // If there are no possibly live tickets to check, ticketHashes contains all // of the results. if len(maybeLive) == 0 { @@ -168,37 +91,3 @@ func (w *Wallet) LiveTicketHashes(ctx context.Context, rpc LiveTicketQuerier, in return ticketHashes, nil } - -// TicketHashesForVotingAddress returns the hashes of all tickets with voting -// rights delegated to votingAddr. This function does not return the hashes of -// pruned tickets. -func (w *Wallet) TicketHashesForVotingAddress(ctx context.Context, votingAddr stdaddr.Address) ([]chainhash.Hash, error) { - const op errors.Op = "wallet.TicketHashesForVotingAddress" - - var ticketHashes []chainhash.Hash - err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error { - stakemgrNs := tx.ReadBucket(wstakemgrNamespaceKey) - txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey) - - dump, err := w.stakeMgr.DumpSStxHashesForAddress( - stakemgrNs, votingAddr) - if err != nil { - return err - } - - // Exclude hashes for unsaved transactions. - ticketHashes = dump[:0] - for i := range dump { - h := &dump[i] - if w.txStore.ExistsTx(txmgrNs, h) { - ticketHashes = append(ticketHashes, *h) - } - } - - return nil - }) - if err != nil { - return nil, errors.E(op, err) - } - return ticketHashes, nil -} diff --git a/wallet/udb/addressmanager_test.go b/wallet/udb/addressmanager_test.go index 2e964901c..813eddccc 100644 --- a/wallet/udb/addressmanager_test.go +++ b/wallet/udb/addressmanager_test.go @@ -1,5 +1,5 @@ // Copyright (c) 2014 The btcsuite developers -// Copyright (c) 2015-2019 The Decred developers +// Copyright (c) 2015-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -432,7 +432,7 @@ func testImportScript(tc *testContext, wb walletdb.ReadWriteBucket) { func TestManagerImports(t *testing.T) { ctx := context.Background() - db, mgr, _, _, teardown, err := cloneDB(ctx, "imports.kv") + db, mgr, _, teardown, err := cloneDB(ctx, "imports.kv") defer teardown() if err != nil { t.Fatal(err) @@ -474,7 +474,7 @@ func TestManagerImports(t *testing.T) { // with the manager locked. func TestImportVotingAccount(t *testing.T) { ctx := context.Background() - db, mgr, _, _, teardown, err := cloneDB(ctx, "import_voting_account.kv") + db, mgr, _, teardown, err := cloneDB(ctx, "import_voting_account.kv") defer teardown() if err != nil { t.Fatal(err) @@ -607,7 +607,7 @@ func TestImportVotingAccount(t *testing.T) { // with the manager locked. func TestImportAccount(t *testing.T) { ctx := context.Background() - db, mgr, _, _, teardown, err := cloneDB(ctx, "import_account.kv") + db, mgr, _, teardown, err := cloneDB(ctx, "import_account.kv") defer teardown() if err != nil { t.Fatal(err) @@ -1073,7 +1073,7 @@ func testEncryptDecrypt(ctx context.Context, tc *testContext) { func TestManagerEncryptDecrypt(t *testing.T) { ctx := context.Background() - db, mgr, _, _, teardown, err := cloneDB(ctx, "encrypt_decrypt.kv") + db, mgr, _, teardown, err := cloneDB(ctx, "encrypt_decrypt.kv") defer teardown() if err != nil { t.Fatal(err) @@ -1095,7 +1095,7 @@ func TestManagerEncryptDecrypt(t *testing.T) { func TestChangePassphrase(t *testing.T) { ctx := context.Background() - db, mgr, _, _, teardown, err := cloneDB(ctx, "change_passphrase.kv") + db, mgr, _, teardown, err := cloneDB(ctx, "change_passphrase.kv") defer teardown() if err != nil { t.Fatal(err) @@ -1150,7 +1150,7 @@ func testManagerAPI(ctx context.Context, tc *testContext) { // copy as well as when it is opened from an existing namespace. func TestManagerWatchingOnly(t *testing.T) { ctx := context.Background() - db, mgr, _, _, teardown, err := cloneDB(ctx, "mgr_watching_only.kv") + db, mgr, _, teardown, err := cloneDB(ctx, "mgr_watching_only.kv") defer teardown() if err != nil { t.Fatal(err) @@ -1167,7 +1167,7 @@ func TestManagerWatchingOnly(t *testing.T) { }) mgr.Close() - mgr, _, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase) + mgr, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -1195,7 +1195,7 @@ func TestManagerWatchingOnly(t *testing.T) { }) // Open the watching-only manager and run all the tests again. - mgr, _, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase) + mgr, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -1220,7 +1220,7 @@ func TestManager(t *testing.T) { } ctx := context.Background() - db, mgr, _, _, teardown, err := cloneDB(ctx, "mgr_watching_only.kv") + db, mgr, _, teardown, err := cloneDB(ctx, "mgr_watching_only.kv") defer teardown() if err != nil { t.Fatal(err) @@ -1238,7 +1238,7 @@ func TestManager(t *testing.T) { // Open the manager and run all the tests again in open mode which // avoids reinserting new addresses like the create mode tests do. - mgr, _, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase) + mgr, _, err = Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase) if err != nil { t.Fatalf("Open: unexpected error: %v", err) } diff --git a/wallet/udb/cointype_test.go b/wallet/udb/cointype_test.go index 84360daae..47b44226a 100644 --- a/wallet/udb/cointype_test.go +++ b/wallet/udb/cointype_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017 The Decred developers +// Copyright (c) 2017-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -70,7 +70,7 @@ func TestCoinTypeUpgrade(t *testing.T) { t.Fatal(err) } - m, _, _, err := Open(ctx, db, params, pubPass) + m, _, err := Open(ctx, db, params, pubPass) if err != nil { t.Fatal(err) } diff --git a/wallet/udb/common_test.go b/wallet/udb/common_test.go index a1d8d5ccf..4d244b42b 100644 --- a/wallet/udb/common_test.go +++ b/wallet/udb/common_test.go @@ -1,5 +1,5 @@ // Copyright (c) 2014 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers +// Copyright (c) 2015-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -64,27 +64,27 @@ func createEmptyDB(ctx context.Context) error { return nil } -// cloneDB makes a copy of an empty wallet db. It returns a wallet db, store, a -// stake store and a teardown function. -func cloneDB(ctx context.Context, cloneName string) (walletdb.DB, *Manager, *Store, *StakeStore, func(), error) { +// cloneDB makes a copy of an empty wallet db. It returns a wallet db, store, +// and a teardown function. +func cloneDB(ctx context.Context, cloneName string) (walletdb.DB, *Manager, *Store, func(), error) { file, err := os.ReadFile(emptyDbPath) if err != nil { - return nil, nil, nil, nil, nil, fmt.Errorf("unexpected error: %v", err) + return nil, nil, nil, nil, fmt.Errorf("unexpected error: %v", err) } err = os.WriteFile(cloneName, file, 0644) if err != nil { - return nil, nil, nil, nil, nil, fmt.Errorf("unexpected error: %v", err) + return nil, nil, nil, nil, fmt.Errorf("unexpected error: %v", err) } db, err := walletdb.Open("bdb", cloneName) if err != nil { - return nil, nil, nil, nil, nil, fmt.Errorf("unexpected error: %v", err) + return nil, nil, nil, nil, fmt.Errorf("unexpected error: %v", err) } - mgr, txStore, stkStore, err := Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase) + mgr, txStore, err := Open(ctx, db, chaincfg.TestNet3Params(), pubPassphrase) if err != nil { - return nil, nil, nil, nil, nil, fmt.Errorf("unexpected error: %v", err) + return nil, nil, nil, nil, fmt.Errorf("unexpected error: %v", err) } teardown := func() { @@ -92,5 +92,5 @@ func cloneDB(ctx context.Context, cloneName string) (walletdb.DB, *Manager, *Sto db.Close() } - return db, mgr, txStore, stkStore, teardown, err + return db, mgr, txStore, teardown, err } diff --git a/wallet/udb/initialize.go b/wallet/udb/initialize.go index c3a0d5409..315972fdb 100644 --- a/wallet/udb/initialize.go +++ b/wallet/udb/initialize.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018 The Decred developers +// Copyright (c) 2017-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -25,12 +25,8 @@ func Initialize(ctx context.Context, db walletdb.DB, params *chaincfg.Params, se if err != nil { return errors.E(errors.IO, err) } - stakemgrNs, err := tx.CreateTopLevelBucket(wstakemgrBucketKey) - if err != nil { - return errors.E(errors.IO, err) - } - // Create the address manager, transaction store, and stake store. + // Create the address manager and transaction store. err = createAddressManager(addrmgrNs, seed, pubPass, privPass, params) if err != nil { return err @@ -39,10 +35,6 @@ func Initialize(ctx context.Context, db walletdb.DB, params *chaincfg.Params, se if err != nil { return err } - err = initializeEmpty(stakemgrNs) - if err != nil { - return err - } // Create the metadata bucket and write the current database version to // it. @@ -71,12 +63,8 @@ func InitializeWatchOnly(ctx context.Context, db walletdb.DB, params *chaincfg.P if err != nil { return errors.E(errors.IO, err) } - stakemgrNs, err := tx.CreateTopLevelBucket(wstakemgrBucketKey) - if err != nil { - return errors.E(errors.IO, err) - } - // Create the address manager, transaction store, and stake store. + // Create the address manager and transaction store. err = createWatchOnly(addrmgrNs, hdPubKey, pubPass, params) if err != nil { return err @@ -85,10 +73,6 @@ func InitializeWatchOnly(ctx context.Context, db walletdb.DB, params *chaincfg.P if err != nil { return err } - err = initializeEmpty(stakemgrNs) - if err != nil { - return err - } // Create the metadata bucket and write the current database version to // it. diff --git a/wallet/udb/migration.go b/wallet/udb/migration.go index ca9c709d6..f1cb0f7a9 100644 --- a/wallet/udb/migration.go +++ b/wallet/udb/migration.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018 The Decred developers +// Copyright (c) 2017-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -15,9 +15,8 @@ import ( // Old package namespace bucket keys. These are still used as of the very first // unified database layout. var ( - waddrmgrBucketKey = []byte("waddrmgr") - wtxmgrBucketKey = []byte("wtxmgr") - wstakemgrBucketKey = []byte("wstakemgr") + waddrmgrBucketKey = []byte("waddrmgr") + wtxmgrBucketKey = []byte("wtxmgr") ) // NeedsMigration checks whether the database needs to be converted to the @@ -39,9 +38,6 @@ func Migrate(ctx context.Context, db walletdb.DB, params *chaincfg.Params) error return walletdb.Update(ctx, db, func(tx walletdb.ReadWriteTx) error { addrmgrNs := tx.ReadWriteBucket(waddrmgrBucketKey) txmgrNs := tx.ReadWriteBucket(wtxmgrBucketKey) - stakemgrNs := tx.ReadWriteBucket(wstakemgrBucketKey) - - stakeStoreVersionName := []byte("stakestorever") // Perform any necessary upgrades for the old address manager. err := upgradeManager(addrmgrNs) @@ -68,10 +64,6 @@ func Migrate(ctx context.Context, db walletdb.DB, params *chaincfg.Params) error if err != nil { return errors.E(errors.IO, err) } - err = stakemgrNs.NestedReadWriteBucket(mainBucketName).Delete(stakeStoreVersionName) - if err != nil { - return errors.E(errors.IO, err) - } metadataBucket, err := tx.CreateTopLevelBucket(unifiedDBMetadata{}.rootBucketKey()) if err != nil { return errors.E(errors.IO, err) diff --git a/wallet/udb/open.go b/wallet/udb/open.go index b4dd8cd21..9d3c2773d 100644 --- a/wallet/udb/open.go +++ b/wallet/udb/open.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017 The Decred developers +// Copyright (c) 2017-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -18,7 +18,7 @@ import ( // A NotExist error will be returned if the database has not been initialized. // The recorded database version must match exactly with DBVersion. If the // version does not match, an Invalid error is returned. -func Open(ctx context.Context, db walletdb.DB, params *chaincfg.Params, pubPass []byte) (addrMgr *Manager, txStore *Store, stakeStore *StakeStore, err error) { +func Open(ctx context.Context, db walletdb.DB, params *chaincfg.Params, pubPass []byte) (addrMgr *Manager, txStore *Store, err error) { err = walletdb.View(ctx, db, func(tx walletdb.ReadTx) error { // Verify the database exists and the recorded version is supported by // this software version. @@ -38,7 +38,6 @@ func Open(ctx context.Context, db walletdb.DB, params *chaincfg.Params, pubPass } addrmgrNs := tx.ReadBucket(waddrmgrBucketKey) - stakemgrNs := tx.ReadBucket(wstakemgrBucketKey) addrMgr, err = loadManager(addrmgrNs, pubPass, params) if err != nil { @@ -49,7 +48,6 @@ func Open(ctx context.Context, db walletdb.DB, params *chaincfg.Params, pubPass acctLookupFunc: addrMgr.AddrAccount, manager: addrMgr, } - stakeStore, err = openStakeStore(stakemgrNs, addrMgr, params) return err }) return diff --git a/wallet/udb/stake.go b/wallet/udb/stake.go deleted file mode 100644 index 330b12239..000000000 --- a/wallet/udb/stake.go +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright (c) 2015-2024 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package udb - -import ( - "bytes" - "sync" - "time" - - "decred.org/dcrwallet/v5/errors" - "decred.org/dcrwallet/v5/wallet/walletdb" - "github.com/decred/dcrd/chaincfg/chainhash" - "github.com/decred/dcrd/chaincfg/v3" - "github.com/decred/dcrd/dcrutil/v4" - "github.com/decred/dcrd/txscript/v4/stdaddr" - "github.com/decred/dcrd/wire" -) - -// sstxRecord is the structure for a stored SStx. -type sstxRecord struct { - tx *dcrutil.Tx - ts time.Time - voteBitsSet bool // Removed in version 3 - voteBits uint16 // Removed in version 3 - voteBitsExt []byte // Removed in version 3 -} - -// TicketStatus is the current status of a stake pool ticket. -type TicketStatus uint8 - -const ( - // TSImmatureOrLive indicates that the ticket is either - // live or immature. - TSImmatureOrLive = iota - - // TSVoted indicates that the ticket was spent as a vote. - TSVoted - - // TSMissed indicates that the ticket was spent as a - // revocation. - TSMissed -) - -// PoolTicket is a 73-byte struct that is used to preserve user's -// ticket information when they have an account at the stake pool. -type PoolTicket struct { - Ticket chainhash.Hash - HeightTicket uint32 // Not used or guaranteed to be correct - Status TicketStatus - HeightSpent uint32 - SpentBy chainhash.Hash -} - -// StakePoolUser is a list of tickets for a given user (P2SH -// address) in the stake pool. -type StakePoolUser struct { - Tickets []*PoolTicket - InvalidTickets []*chainhash.Hash -} - -// StakeStore represents a safely accessible database of -// stake transactions. -type StakeStore struct { - Params *chaincfg.Params - Manager *Manager - - ownedSStxs map[chainhash.Hash]struct{} - mtx sync.RWMutex // only protects ownedSStxs -} - -// checkHashInStore checks if a hash exists in ownedSStxs. -func (s *StakeStore) checkHashInStore(hash *chainhash.Hash) bool { - _, exists := s.ownedSStxs[*hash] - return exists -} - -// OwnTicket returns whether the ticket is tracked by the stake manager. -func (s *StakeStore) OwnTicket(hash *chainhash.Hash) bool { - s.mtx.RLock() - owned := s.checkHashInStore(hash) - s.mtx.RUnlock() - return owned -} - -// addHashToStore adds a hash into ownedSStxs. -func (s *StakeStore) addHashToStore(hash *chainhash.Hash) { - s.ownedSStxs[*hash] = struct{}{} -} - -// insertSStx inserts an SStx into the store. -func (s *StakeStore) insertSStx(ns walletdb.ReadWriteBucket, sstx *dcrutil.Tx) error { - // If we already have the SStx, no need to - // try to include twice. - exists := s.checkHashInStore(sstx.Hash()) - if exists { - log.Tracef("Attempted to insert SStx %v into the stake store, "+ - "but the SStx already exists.", sstx.Hash()) - return nil - } - record := &sstxRecord{ - tx: sstx, - ts: time.Now(), - } - - // Add the SStx to the database. - err := putSStxRecord(ns, record) - if err != nil { - return err - } - - // Add the SStx's hash to the internal list in the store. - s.addHashToStore(sstx.Hash()) - - return nil -} - -// InsertSStx is the exported version of insertSStx that is safe for concurrent -// access. -func (s *StakeStore) InsertSStx(ns walletdb.ReadWriteBucket, sstx *dcrutil.Tx) error { - s.mtx.Lock() - err := s.insertSStx(ns, sstx) - s.mtx.Unlock() - return err -} - -// dumpSStxHashes dumps the hashes of all owned SStxs. Note -// that this doesn't use the DB. -func (s *StakeStore) dumpSStxHashes() []chainhash.Hash { - // Copy the hash list of sstxs. You could pass the pointer - // directly but you risk that the size of the internal - // ownedSStxs is later modified while the end user is - // working with the returned list. - ownedSStxs := make([]chainhash.Hash, len(s.ownedSStxs)) - - itr := 0 - for hash := range s.ownedSStxs { - ownedSStxs[itr] = hash - itr++ - } - - return ownedSStxs -} - -// DumpSStxHashes returns the hashes of all wallet ticket purchase transactions. -func (s *StakeStore) DumpSStxHashes() []chainhash.Hash { - defer s.mtx.RUnlock() - s.mtx.RLock() - - return s.dumpSStxHashes() -} - -// dumpSStxHashes dumps the hashes of all owned SStxs for some address. -func (s *StakeStore) dumpSStxHashesForAddress(ns walletdb.ReadBucket, addr stdaddr.Address) ([]chainhash.Hash, error) { - // Extract the HASH160 script hash; if it's not 20 bytes - // long, return an error. - var hash160 []byte - switch addr := addr.(type) { - case stdaddr.Hash160er: - hash160 = addr.Hash160()[:] - default: - err := errors.Errorf("cannot get hash160 from address %v", addr) - return nil, errors.E(errors.Invalid, err) - } - _, addrIsP2SH := addr.(*stdaddr.AddressScriptHashV0) - - allTickets := s.dumpSStxHashes() - var ticketsForAddr []chainhash.Hash - - // Access the database and store the result locally. - for _, h := range allTickets { - thisHash160, p2sh, err := fetchSStxRecordSStxTicketHash160(ns, &h) - if err != nil { - return nil, errors.E(errors.IO, err) - } - if addrIsP2SH != p2sh { - continue - } - - if bytes.Equal(hash160, thisHash160) { - ticketsForAddr = append(ticketsForAddr, h) - } - } - - return ticketsForAddr, nil -} - -// DumpSStxHashesForAddress returns the hashes of all wallet ticket purchase -// transactions for an address. -func (s *StakeStore) DumpSStxHashesForAddress(ns walletdb.ReadBucket, addr stdaddr.Address) ([]chainhash.Hash, error) { - defer s.mtx.RUnlock() - s.mtx.RLock() - - return s.dumpSStxHashesForAddress(ns, addr) -} - -// sstxAddress returns the address for a given ticket. -func (s *StakeStore) sstxAddress(ns walletdb.ReadBucket, hash *chainhash.Hash) (stdaddr.Address, error) { - // Access the database and store the result locally. - thisHash160, p2sh, err := fetchSStxRecordSStxTicketHash160(ns, hash) - if err != nil { - return nil, err - } - var addr stdaddr.Address - if p2sh { - addr, err = stdaddr.NewAddressScriptHashV0FromHash(thisHash160, s.Params) - } else { - addr, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(thisHash160, s.Params) - } - if err != nil { - return nil, err - } - - return addr, nil -} - -// SStxAddress is the exported, concurrency safe version of sstxAddress. -func (s *StakeStore) SStxAddress(ns walletdb.ReadBucket, hash *chainhash.Hash) (stdaddr.Address, error) { - return s.sstxAddress(ns, hash) -} - -// TicketPurchase returns the ticket purchase transaction recorded in the "stake -// manager" portion of the DB. -// -// TODO: This is redundant and should be looked up in from the transaction -// manager. Left for now for compatibility. -func (s *StakeStore) TicketPurchase(dbtx walletdb.ReadTx, hash *chainhash.Hash) (*wire.MsgTx, error) { - ns := dbtx.ReadBucket(wstakemgrBucketKey) - - ticketRecord, err := fetchSStxRecord(ns, hash, DBVersion) - if err != nil { - return nil, err - } - return ticketRecord.tx.MsgTx(), nil -} - -// updateStakePoolUserTickets updates a stake pool ticket for a given user. -// If the ticket does not currently exist in the database, it adds it. If it -// does exist (the ticket hash exists), it replaces the old record. -func (s *StakeStore) updateStakePoolUserTickets(ns walletdb.ReadWriteBucket, user stdaddr.Address, ticket *PoolTicket) error { - // Address must be a StakeAddress with support for the Hash160 method. - var hash160 *[20]byte - switch user := user.(type) { - case interface { - stdaddr.StakeAddress - stdaddr.Hash160er - }: - hash160 = user.Hash160() - default: - return errors.E(errors.Invalid, errors.Errorf("voting address type %T", user)) - } - - return updateStakePoolUserTickets(ns, *hash160, ticket) -} - -// UpdateStakePoolUserTickets is the exported and concurrency safe form of -// updateStakePoolUserTickets. -func (s *StakeStore) UpdateStakePoolUserTickets(ns walletdb.ReadWriteBucket, user stdaddr.Address, ticket *PoolTicket) error { - return s.updateStakePoolUserTickets(ns, user, ticket) -} - -// removeStakePoolUserInvalTickets prepares the user.Address and asks stakedb -// to remove the formerly invalid tickets. -func (s *StakeStore) removeStakePoolUserInvalTickets(ns walletdb.ReadWriteBucket, user stdaddr.Address, ticket *chainhash.Hash) error { - // Address must be a StakeAddress with support for the Hash160 method. - var hash160 *[20]byte - switch user := user.(type) { - case interface { - stdaddr.StakeAddress - Hash160() *[20]byte - }: - hash160 = user.Hash160() - default: - return errors.E(errors.Invalid, errors.Errorf("voting address type %T", user)) - } - - return removeStakePoolInvalUserTickets(ns, *hash160, ticket) -} - -// RemoveStakePoolUserInvalTickets is the exported and concurrency safe form of -// removetStakePoolUserInvalTickets. -func (s *StakeStore) RemoveStakePoolUserInvalTickets(ns walletdb.ReadWriteBucket, user stdaddr.Address, ticket *chainhash.Hash) error { - return s.removeStakePoolUserInvalTickets(ns, user, ticket) -} - -// updateStakePoolUserInvalTickets updates the list of invalid stake pool -// tickets for a given user. If the ticket does not currently exist in the -// database, it adds it. -func (s *StakeStore) updateStakePoolUserInvalTickets(ns walletdb.ReadWriteBucket, user stdaddr.Address, ticket *chainhash.Hash) error { - // Address must be a StakeAddress with support for the Hash160 method. - var hash160 *[20]byte - switch user := user.(type) { - case interface { - stdaddr.StakeAddress - stdaddr.Hash160er - }: - hash160 = user.Hash160() - default: - return errors.E(errors.Invalid, errors.Errorf("voting address type %T", user)) - } - - return updateStakePoolInvalUserTickets(ns, *hash160, ticket) -} - -// UpdateStakePoolUserInvalTickets is the exported and concurrency safe form of -// updateStakePoolUserInvalTickets. -func (s *StakeStore) UpdateStakePoolUserInvalTickets(ns walletdb.ReadWriteBucket, user stdaddr.Address, ticket *chainhash.Hash) error { - return s.updateStakePoolUserInvalTickets(ns, user, ticket) -} - -func stakePoolUserInfo(ns walletdb.ReadBucket, user stdaddr.Address) (*StakePoolUser, error) { - // Address must be a StakeAddress with support for the Hash160 method. - var hash160 *[20]byte - switch user := user.(type) { - case interface { - stdaddr.StakeAddress - stdaddr.Hash160er - }: - hash160 = user.Hash160() - default: - return nil, errors.E(errors.Invalid, errors.Errorf("voting address type %T", user)) - } - - stakePoolUser := new(StakePoolUser) - - // Catch missing user errors below and blank out the stake - // pool user information for the section if the user has - // no entries. - missingValidTickets, missingInvalidTickets := false, false - - userTickets, err := fetchStakePoolUserTickets(ns, *hash160) - if err != nil { - missingValidTickets = errors.Is(err, errors.NotExist) - if !missingValidTickets { - return nil, err - } - } - if missingValidTickets { - userTickets = make([]*PoolTicket, 0) - } - - invalTickets, err := fetchStakePoolUserInvalTickets(ns, *hash160) - if err != nil { - if errors.Is(err, errors.NotExist) { - missingInvalidTickets = true - } - if !missingInvalidTickets { - return nil, err - } - } - if missingInvalidTickets { - invalTickets = make([]*chainhash.Hash, 0) - } - - stakePoolUser.Tickets = userTickets - stakePoolUser.InvalidTickets = invalTickets - - return stakePoolUser, nil -} - -// StakePoolUserInfo returns the stake pool user information for a given stake -// pool user, keyed to their P2SH voting address. -func (s *StakeStore) StakePoolUserInfo(ns walletdb.ReadBucket, user stdaddr.Address) (*StakePoolUser, error) { - return stakePoolUserInfo(ns, user) -} - -// loadManager returns a new stake manager that results from loading it from -// the passed opened database. The public passphrase is required to decrypt the -// public keys. -func (s *StakeStore) loadOwnedSStxs(ns walletdb.ReadBucket) error { - // Regenerate the list of tickets. - // Perform all database lookups in a read-only view. - ticketList := make(map[chainhash.Hash]struct{}) - - // Open the sstx records database. - bucket := ns.NestedReadBucket(sstxRecordsBucketName) - - // Store each key sequentially. - err := bucket.ForEach(func(k []byte, v []byte) error { - var errNewHash error - var hash *chainhash.Hash - - hash, errNewHash = chainhash.NewHash(k) - if errNewHash != nil { - return errNewHash - } - ticketList[*hash] = struct{}{} - return nil - }) - if err != nil { - return err - } - - s.ownedSStxs = ticketList - return nil -} - -// newStakeStore initializes a new stake store with the given parameters. -func newStakeStore(params *chaincfg.Params, manager *Manager) *StakeStore { - return &StakeStore{ - Params: params, - Manager: manager, - ownedSStxs: make(map[chainhash.Hash]struct{}), - } -} - -// openStakeStore loads an existing stake manager from the given namespace, -// waddrmgr, and network parameters. -// -// A NotExist error is returned returned when the stake store is not written to -// the db. -func openStakeStore(ns walletdb.ReadBucket, manager *Manager, params *chaincfg.Params) (*StakeStore, error) { - // Return an error if the manager has NOT already been created in the - // given database namespace. - if !stakeStoreExists(ns) { - return nil, errors.E(errors.NotExist, "no stake store") - } - - ss := newStakeStore(params, manager) - - err := ss.loadOwnedSStxs(ns) - if err != nil { - return nil, err - } - - return ss, nil -} diff --git a/wallet/udb/stakedb.go b/wallet/udb/stakedb.go deleted file mode 100644 index 6bec7d0d7..000000000 --- a/wallet/udb/stakedb.go +++ /dev/null @@ -1,672 +0,0 @@ -// Copyright (c) 2015-2024 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package udb - -import ( - "bytes" - "encoding/binary" - "io" - "time" - - "decred.org/dcrwallet/v5/errors" - "decred.org/dcrwallet/v5/wallet/walletdb" - "github.com/decred/dcrd/blockchain/stake/v5" - "github.com/decred/dcrd/chaincfg/chainhash" - "github.com/decred/dcrd/dcrutil/v4" - "github.com/decred/dcrd/wire" -) - -const ( - // Size of various types in bytes. - int8Size = 1 - int16Size = 2 - int32Size = 4 - int64Size = 8 - hashSize = 32 - - // stakePoolUserTicketSize is the size - // of a serialized stake pool user - // ticket. - // hash + uint32 + uint8 + uint32 + hash - stakePoolUserTicketSize = 32 + 4 + 1 + 4 + 32 - - // stakePoolTicketsPrefixSize is the length of - // stakePoolTicketsPrefix. - stakePoolTicketsPrefixSize = 5 - - // stakePoolInvalidPrefixSize is the length of - // stakePoolInvalidPrefix. - stakePoolInvalidPrefixSize = 5 - - // scriptHashLen is the length of a HASH160 - // hash. - scriptHashSize = 20 -) - -var ( - // sstxTicket2PKHPrefix is the PkScript byte prefix for an SStx - // P2PKH ticket output. The entire prefix is 0xba76a914, but we - // only use the first 3 bytes. - sstxTicket2PKHPrefix = []byte{0xba, 0x76, 0xa9} - - // sstxTicket2SHPrefix is the PkScript byte prefix for an SStx - // P2SH ticket output. - sstxTicket2SHPrefix = []byte{0xba, 0xa9, 0x14} - - // stakePoolTicketsPrefix is the byte slice prefix for valid - // tickets in the stake pool for a given user. - stakePoolTicketsPrefix = []byte("tickt") - - // stakePoolInvalidPrefix is the byte slice prefix for invalid - // tickets in the stake pool for a given user. - stakePoolInvalidPrefix = []byte("invld") -) - -// Key names for various database fields. -// sstxRecords -// -// key: sstx tx hash -// val: sstxRecord -// -// ssgenRecords -// -// key: sstx tx hash -// val: serialized slice of ssgenRecords -var ( - // Bucket names. - sstxRecordsBucketName = []byte("sstxrecords") - ssgenRecordsBucketName = []byte("ssgenrecords") - ssrtxRecordsBucketName = []byte("ssrtxrecords") - - // Db related key names (main bucket). - stakeStoreCreateDateName = []byte("stakestorecreated") -) - -// deserializeSStxRecord deserializes the passed serialized tx record information. -func deserializeSStxRecord(serializedSStxRecord []byte, dbVersion uint32) (*sstxRecord, error) { - switch { - case dbVersion < 3: - record := new(sstxRecord) - - curPos := 0 - - // Read MsgTx size (as a uint64). - msgTxLen := int(binary.LittleEndian.Uint64( - serializedSStxRecord[curPos : curPos+int64Size])) - curPos += int64Size - - // Pretend to read the pkScrLoc for the 0th output pkScript. - curPos += int32Size - - // Read the intended voteBits and extended voteBits length (uint8). - record.voteBitsSet = false - voteBitsLen := int(serializedSStxRecord[curPos]) - if voteBitsLen != 0 { - record.voteBitsSet = true - } - curPos += int8Size - - // Read the assumed 2 byte VoteBits as well as the extended - // votebits (75 bytes max). - record.voteBits = binary.LittleEndian.Uint16( - serializedSStxRecord[curPos : curPos+int16Size]) - curPos += int16Size - if voteBitsLen != 0 { - record.voteBitsExt = make([]byte, voteBitsLen-int16Size) - copy(record.voteBitsExt, serializedSStxRecord[curPos:curPos+voteBitsLen-int16Size]) - } - curPos += stake.MaxSingleBytePushLength - int16Size - - // Prepare a buffer for the msgTx. - buf := bytes.NewBuffer(serializedSStxRecord[curPos : curPos+msgTxLen]) - curPos += msgTxLen - - // Deserialize transaction. - msgTx := new(wire.MsgTx) - err := msgTx.Deserialize(buf) - if err != nil { - if errors.Is(err, io.EOF) { - err = io.ErrUnexpectedEOF - } - return nil, err - } - - // Create and save the dcrutil.Tx of the read MsgTx and set its index. - tx := dcrutil.NewTx(msgTx) - tx.SetIndex(dcrutil.TxIndexUnknown) - tx.SetTree(wire.TxTreeStake) - record.tx = tx - - // Read received unix time (int64). - received := int64(binary.LittleEndian.Uint64( - serializedSStxRecord[curPos : curPos+int64Size])) - record.ts = time.Unix(received, 0) - - return record, nil - - case dbVersion >= 3: - // Don't need to read the pkscript location, so first four bytes are - // skipped. - serializedSStxRecord = serializedSStxRecord[4:] - - var tx wire.MsgTx - err := tx.Deserialize(bytes.NewReader(serializedSStxRecord)) - if err != nil { - return nil, err - } - unixTime := int64(binary.LittleEndian.Uint64(serializedSStxRecord[tx.SerializeSize():])) - return &sstxRecord{tx: dcrutil.NewTx(&tx), ts: time.Unix(unixTime, 0)}, nil - - default: - panic("unreachable") - } -} - -// deserializeSStxTicketHash160 deserializes and returns a 20 byte script -// hash for a ticket's 0th output. -func deserializeSStxTicketHash160(serializedSStxRecord []byte) (hash160 []byte, p2sh bool, err error) { - const pkscriptLocOffset = 0 - const txOffset = 4 - - pkscriptLoc := int(binary.LittleEndian.Uint32(serializedSStxRecord[pkscriptLocOffset:])) + txOffset - - // Pop off the script prefix, then pop off the 20 bytes - // HASH160 pubkey or script hash. - prefixBytes := serializedSStxRecord[pkscriptLoc : pkscriptLoc+3] - scriptHash := make([]byte, 20) - p2sh = false - switch { - case bytes.Equal(prefixBytes, sstxTicket2PKHPrefix): - scrHashLoc := pkscriptLoc + 4 - if scrHashLoc+20 >= len(serializedSStxRecord) { - return nil, false, errors.E(errors.IO, "bad sstx record size") - } - copy(scriptHash, serializedSStxRecord[scrHashLoc:scrHashLoc+20]) - case bytes.Equal(prefixBytes, sstxTicket2SHPrefix): - scrHashLoc := pkscriptLoc + 3 - if scrHashLoc+20 >= len(serializedSStxRecord) { - return nil, false, errors.E(errors.IO, "bad sstx record size") - } - copy(scriptHash, serializedSStxRecord[scrHashLoc:scrHashLoc+20]) - p2sh = true - } - - return scriptHash, p2sh, nil -} - -// serializeSSTxRecord returns the serialization of the passed txrecord row. -func serializeSStxRecord(record *sstxRecord) ([]byte, error) { - tx := record.tx.MsgTx() - txSize := tx.SerializeSize() - - buf := make([]byte, 4+txSize+8) // pkscript location + tx + unix timestamp - pkScrLoc := tx.PkScriptLocs() - binary.LittleEndian.PutUint32(buf, uint32(pkScrLoc[0])) - err := tx.Serialize(bytes.NewBuffer(buf[4:4])) - if err != nil { - return nil, err - } - binary.LittleEndian.PutUint64(buf[4+txSize:], uint64(record.ts.Unix())) - return buf, nil - -} - -// stakeStoreExists returns whether or not the stake store has already -// been created in the given database namespace. -func stakeStoreExists(ns walletdb.ReadBucket) bool { - mainBucket := ns.NestedReadBucket(mainBucketName) - return mainBucket != nil -} - -// fetchSStxRecord retrieves a tx record from the sstx records bucket -// with the given hash. -func fetchSStxRecord(ns walletdb.ReadBucket, hash *chainhash.Hash, dbVersion uint32) (*sstxRecord, error) { - bucket := ns.NestedReadBucket(sstxRecordsBucketName) - - key := hash[:] - val := bucket.Get(key) - if val == nil { - return nil, errors.E(errors.NotExist, errors.Errorf("no ticket purchase %v", hash)) - } - - return deserializeSStxRecord(val, dbVersion) -} - -// fetchSStxRecordSStxTicketHash160 retrieves a ticket 0th output script or -// pubkeyhash from the sstx records bucket with the given hash. -func fetchSStxRecordSStxTicketHash160(ns walletdb.ReadBucket, hash *chainhash.Hash) (hash160 []byte, p2sh bool, err error) { - bucket := ns.NestedReadBucket(sstxRecordsBucketName) - - key := hash[:] - val := bucket.Get(key) - if val == nil { - return nil, false, errors.E(errors.NotExist, errors.Errorf("no ticket purchase %v", hash)) - } - - return deserializeSStxTicketHash160(val) -} - -// putSStxRecord inserts a given SStx record to the SStxrecords bucket. -func putSStxRecord(ns walletdb.ReadWriteBucket, record *sstxRecord) error { - bucket := ns.NestedReadWriteBucket(sstxRecordsBucketName) - - // Write the serialized txrecord keyed by the tx hash. - serializedSStxRecord, err := serializeSStxRecord(record) - if err != nil { - return errors.E(errors.IO, err) - } - err = bucket.Put(record.tx.Hash()[:], serializedSStxRecord) - if err != nil { - return errors.E(errors.IO, err) - } - return nil -} - -// deserializeUserTicket deserializes the passed serialized user -// ticket information. -func deserializeUserTicket(serializedTicket []byte) (*PoolTicket, error) { - // Cursory check to make sure that the size of the - // ticket makes sense. - if len(serializedTicket)%stakePoolUserTicketSize != 0 { - return nil, errors.E(errors.IO, "invalid pool ticket record size") - } - - record := new(PoolTicket) - - curPos := 0 - - // Insert the ticket hash into the record. - copy(record.Ticket[:], serializedTicket[curPos:curPos+hashSize]) - curPos += hashSize - - // Insert the ticket height into the record. - record.HeightTicket = binary.LittleEndian.Uint32( - serializedTicket[curPos : curPos+int32Size]) - curPos += int32Size - - // Insert the status into the record. - record.Status = TicketStatus(serializedTicket[curPos]) - curPos += int8Size - - // Insert the spent by height into the record. - record.HeightSpent = binary.LittleEndian.Uint32( - serializedTicket[curPos : curPos+int32Size]) - curPos += int32Size - - // Insert the spending hash into the record. - copy(record.SpentBy[:], serializedTicket[curPos:curPos+hashSize]) - - return record, nil -} - -// deserializeUserTickets deserializes the passed serialized pool -// users tickets information. -func deserializeUserTickets(serializedTickets []byte) ([]*PoolTicket, error) { - // Cursory check to make sure that the number of records - // makes sense. - if len(serializedTickets)%stakePoolUserTicketSize != 0 { - err := io.ErrUnexpectedEOF - return nil, err - } - - numRecords := len(serializedTickets) / stakePoolUserTicketSize - - records := make([]*PoolTicket, numRecords) - - // Loop through all the records, deserialize them, and - // store them. - for i := 0; i < numRecords; i++ { - record, err := deserializeUserTicket( - serializedTickets[i*stakePoolUserTicketSize : (i+ - 1)*stakePoolUserTicketSize]) - if err != nil { - return nil, err - } - - records[i] = record - } - - return records, nil -} - -// serializeUserTicket returns the serialization of a single stake pool -// user ticket. -func serializeUserTicket(record *PoolTicket) []byte { - buf := make([]byte, stakePoolUserTicketSize) - - curPos := 0 - - // Write the ticket hash. - copy(buf[curPos:curPos+hashSize], record.Ticket[:]) - curPos += hashSize - - // Write the ticket block height. - binary.LittleEndian.PutUint32(buf[curPos:curPos+int32Size], record.HeightTicket) - curPos += int32Size - - // Write the ticket status. - buf[curPos] = byte(record.Status) - curPos += int8Size - - // Write the spending height. - binary.LittleEndian.PutUint32(buf[curPos:curPos+int32Size], record.HeightSpent) - curPos += int32Size - - // Write the spending tx hash. - copy(buf[curPos:curPos+hashSize], record.SpentBy[:]) - - return buf -} - -// serializeUserTickets returns the serialization of the passed stake pool -// user tickets slice. -func serializeUserTickets(records []*PoolTicket) []byte { - numRecords := len(records) - - buf := make([]byte, numRecords*stakePoolUserTicketSize) - - // Serialize and write each record into the slice sequentially. - for i := 0; i < numRecords; i++ { - recordBytes := serializeUserTicket(records[i]) - - copy(buf[i*stakePoolUserTicketSize:(i+1)*stakePoolUserTicketSize], - recordBytes) - } - - return buf -} - -// fetchStakePoolUserTickets retrieves pool user tickets from the meta bucket with -// the given hash. -func fetchStakePoolUserTickets(ns walletdb.ReadBucket, scriptHash [20]byte) ([]*PoolTicket, error) { - bucket := ns.NestedReadBucket(metaBucketName) - - key := make([]byte, stakePoolTicketsPrefixSize+scriptHashSize) - copy(key[0:stakePoolTicketsPrefixSize], stakePoolTicketsPrefix) - copy(key[stakePoolTicketsPrefixSize:stakePoolTicketsPrefixSize+scriptHashSize], - scriptHash[:]) - val := bucket.Get(key) - if val == nil { - return nil, errors.E(errors.NotExist, errors.Errorf("no ticket purchase for hash160 %x", &scriptHash)) - } - - return deserializeUserTickets(val) -} - -// duplicateExistsInUserTickets checks to see if an exact duplicated of a -// record already exists in a slice of user ticket records. -func duplicateExistsInUserTickets(record *PoolTicket, records []*PoolTicket) bool { - for _, r := range records { - if *r == *record { - return true - } - } - return false -} - -// recordExistsInUserTickets checks to see if a record already exists -// in a slice of user ticket records. If it does exist, it returns -// the location where it exists in the slice. -func recordExistsInUserTickets(record *PoolTicket, records []*PoolTicket) (bool, int) { - for i, r := range records { - if r.Ticket == record.Ticket { - return true, i - } - } - return false, 0 -} - -// updateStakePoolUserTickets updates a database entry for a pool user's tickets. -// The function pulls the current entry in the database, checks to see if the -// ticket is already there, updates it accordingly, or adds it to the list of -// tickets. -func updateStakePoolUserTickets(ns walletdb.ReadWriteBucket, scriptHash [20]byte, record *PoolTicket) error { - // Fetch the current content of the key. - // Possible buggy behaviour: If deserialization fails, - // we won't detect it here. We assume we're throwing - // ErrPoolUserTicketsNotFound. - oldRecords, _ := fetchStakePoolUserTickets(ns, scriptHash) - - // Don't reinsert duplicate records we already have. - if duplicateExistsInUserTickets(record, oldRecords) { - return nil - } - - // Does this modify an old record? If so, modify the record - // itself and push. Otherwise, we need to insert a new - // record. - var records []*PoolTicket - preExists, loc := recordExistsInUserTickets(record, oldRecords) - if preExists { - records = oldRecords - records[loc] = record - } else { - // Either create a slice if currently nothing exists for this - // key in the db, or append the entry to the slice. - if oldRecords == nil { - records = make([]*PoolTicket, 1) - records[0] = record - } else { - records = append(oldRecords, record) - } - } - - bucket := ns.NestedReadWriteBucket(metaBucketName) - key := make([]byte, stakePoolTicketsPrefixSize+scriptHashSize) - copy(key[0:stakePoolTicketsPrefixSize], stakePoolTicketsPrefix) - copy(key[stakePoolTicketsPrefixSize:stakePoolTicketsPrefixSize+scriptHashSize], - scriptHash[:]) - - // Write the serialized ticket data keyed by the script. - serializedRecords := serializeUserTickets(records) - - err := bucket.Put(key, serializedRecords) - if err != nil { - return errors.E(errors.IO, err) - } - return nil -} - -// deserializeUserInvalTickets deserializes the passed serialized pool -// users invalid tickets information. -func deserializeUserInvalTickets(serializedTickets []byte) ([]*chainhash.Hash, error) { - // Cursory check to make sure that the number of records - // makes sense. - if len(serializedTickets)%chainhash.HashSize != 0 { - err := io.ErrUnexpectedEOF - return nil, err - } - - numRecords := len(serializedTickets) / chainhash.HashSize - - records := make([]*chainhash.Hash, numRecords) - - // Loop through all the ssgen records, deserialize them, and - // store them. - for i := 0; i < numRecords; i++ { - start := i * chainhash.HashSize - end := (i + 1) * chainhash.HashSize - h, err := chainhash.NewHash(serializedTickets[start:end]) - if err != nil { - return nil, err - } - - records[i] = h - } - - return records, nil -} - -// serializeUserInvalTickets returns the serialization of the passed stake pool -// invalid user tickets slice. -func serializeUserInvalTickets(records []*chainhash.Hash) []byte { - numRecords := len(records) - - buf := make([]byte, numRecords*chainhash.HashSize) - - // Serialize and write each record into the slice sequentially. - for i := 0; i < numRecords; i++ { - start := i * chainhash.HashSize - end := (i + 1) * chainhash.HashSize - copy(buf[start:end], records[i][:]) - } - - return buf -} - -// fetchStakePoolUserInvalTickets retrieves the list of invalid pool user tickets -// from the meta bucket with the given hash. -func fetchStakePoolUserInvalTickets(ns walletdb.ReadBucket, scriptHash [20]byte) ([]*chainhash.Hash, error) { - bucket := ns.NestedReadBucket(metaBucketName) - - key := make([]byte, stakePoolInvalidPrefixSize+scriptHashSize) - copy(key[0:stakePoolInvalidPrefixSize], stakePoolInvalidPrefix) - copy(key[stakePoolInvalidPrefixSize:stakePoolInvalidPrefixSize+scriptHashSize], - scriptHash[:]) - val := bucket.Get(key) - if val == nil { - return nil, errors.E(errors.NotExist, errors.Errorf("no pool ticket for hash160 %x", &scriptHash)) - } - - return deserializeUserInvalTickets(val) -} - -// duplicateExistsInInvalTickets checks to see if an exact duplicated of a -// record already exists in a slice of invalid user ticket records. -func duplicateExistsInInvalTickets(record *chainhash.Hash, records []*chainhash.Hash) bool { - for _, r := range records { - if *r == *record { - return true - } - } - return false -} - -// removeStakePoolInvalUserTickets removes the ticket hash from the inval -// ticket bucket. -func removeStakePoolInvalUserTickets(ns walletdb.ReadWriteBucket, scriptHash [20]byte, record *chainhash.Hash) error { - // Fetch the current content of the key. - // Possible buggy behaviour: If deserialization fails, - // we won't detect it here. We assume we're throwing - // ErrPoolUserInvalTcktsNotFound. - oldRecords, _ := fetchStakePoolUserInvalTickets(ns, scriptHash) - - // Don't need to remove records that don't exist. - if !duplicateExistsInInvalTickets(record, oldRecords) { - return nil - } - - var newRecords []*chainhash.Hash - for i := range oldRecords { - if record.IsEqual(oldRecords[i]) { - newRecords = append(oldRecords[:i:i], oldRecords[i+1:]...) - } - } - - if newRecords == nil { - return nil - } - - bucket := ns.NestedReadWriteBucket(metaBucketName) - key := make([]byte, stakePoolInvalidPrefixSize+scriptHashSize) - copy(key[0:stakePoolInvalidPrefixSize], stakePoolInvalidPrefix) - copy(key[stakePoolInvalidPrefixSize:stakePoolInvalidPrefixSize+scriptHashSize], - scriptHash[:]) - - // Write the serialized invalid user ticket hashes. - serializedRecords := serializeUserInvalTickets(newRecords) - - err := bucket.Put(key, serializedRecords) - if err != nil { - return errors.E(errors.IO, err) - } - - return nil -} - -// updateStakePoolInvalUserTickets updates a database entry for a pool user's -// invalid tickets. The function pulls the current entry in the database, -// checks to see if the ticket is already there. If it is it returns, otherwise -// it adds it to the list of tickets. -func updateStakePoolInvalUserTickets(ns walletdb.ReadWriteBucket, scriptHash [20]byte, record *chainhash.Hash) error { - // Fetch the current content of the key. - // Possible buggy behaviour: If deserialization fails, - // we won't detect it here. We assume we're throwing - // ErrPoolUserInvalTcktsNotFound. - oldRecords, _ := fetchStakePoolUserInvalTickets(ns, scriptHash) - - // Don't reinsert duplicate records we already have. - if duplicateExistsInInvalTickets(record, oldRecords) { - return nil - } - - // Either create a slice if currently nothing exists for this - // key in the db, or append the entry to the slice. - var records []*chainhash.Hash - if oldRecords == nil { - records = make([]*chainhash.Hash, 1) - records[0] = record - } else { - records = append(oldRecords, record) - } - - bucket := ns.NestedReadWriteBucket(metaBucketName) - key := make([]byte, stakePoolInvalidPrefixSize+scriptHashSize) - copy(key[0:stakePoolInvalidPrefixSize], stakePoolInvalidPrefix) - copy(key[stakePoolInvalidPrefixSize:stakePoolInvalidPrefixSize+scriptHashSize], - scriptHash[:]) - - // Write the serialized invalid user ticket hashes. - serializedRecords := serializeUserInvalTickets(records) - - err := bucket.Put(key, serializedRecords) - if err != nil { - return errors.E(errors.IO, err) - } - return nil -} - -// initialize creates the DB if it doesn't exist, and otherwise -// loads the database. -func initializeEmpty(ns walletdb.ReadWriteBucket) error { - // Initialize the buckets and main db fields as needed. - mainBucket, err := ns.CreateBucketIfNotExists(mainBucketName) - if err != nil { - return errors.E(errors.IO, err) - } - - _, err = ns.CreateBucketIfNotExists(sstxRecordsBucketName) - if err != nil { - return errors.E(errors.IO, err) - } - - _, err = ns.CreateBucketIfNotExists(ssgenRecordsBucketName) - if err != nil { - return errors.E(errors.IO, err) - } - - _, err = ns.CreateBucketIfNotExists(ssrtxRecordsBucketName) - if err != nil { - return errors.E(errors.IO, err) - } - - _, err = ns.CreateBucketIfNotExists(metaBucketName) - if err != nil { - return errors.E(errors.IO, err) - } - - createBytes := mainBucket.Get(stakeStoreCreateDateName) - if createBytes == nil { - createDate := uint64(time.Now().Unix()) - var buf [8]byte - binary.LittleEndian.PutUint64(buf[:], createDate) - err := mainBucket.Put(stakeStoreCreateDateName, buf[:]) - if err != nil { - return errors.E(errors.IO, err) - } - } - - return nil -} diff --git a/wallet/udb/stakevalidation_test.go b/wallet/udb/stakevalidation_test.go index 4893083e5..3978ec52d 100644 --- a/wallet/udb/stakevalidation_test.go +++ b/wallet/udb/stakevalidation_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 The Decred developers +// Copyright (c) 2016-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -41,7 +41,7 @@ func insertMainChainHeaders(s *Store, dbtx walletdb.ReadWriteTx, func TestStakeInvalidationOfTip(t *testing.T) { ctx := context.Background() - db, _, s, _, teardown, err := cloneDB(ctx, "stake_inv_of_tip.kv") + db, _, s, teardown, err := cloneDB(ctx, "stake_inv_of_tip.kv") defer teardown() if err != nil { t.Fatal(err) diff --git a/wallet/udb/tx_test.go b/wallet/udb/tx_test.go index 993d8002e..b1a4c4ad0 100644 --- a/wallet/udb/tx_test.go +++ b/wallet/udb/tx_test.go @@ -1,5 +1,5 @@ // Copyright (c) 2013-2015 The btcsuite developers -// Copyright (c) 2019 The Decred developers +// Copyright (c) 2019-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -19,7 +19,7 @@ import ( func TestInsertsCreditsDebitsRollbacks(t *testing.T) { ctx := context.Background() - db, _, s, _, teardown, err := cloneDB(ctx, "inserts_credits_debits_rollbacks.kv") + db, _, s, teardown, err := cloneDB(ctx, "inserts_credits_debits_rollbacks.kv") defer teardown() if err != nil { t.Fatal(err) @@ -353,7 +353,7 @@ func spendOutput(txHash *chainhash.Hash, index uint32, tree int8, outputValues . func TestCoinbases(t *testing.T) { ctx := context.Background() - db, _, s, _, teardown, err := cloneDB(ctx, "coinbases.kv") + db, _, s, teardown, err := cloneDB(ctx, "coinbases.kv") defer teardown() if err != nil { t.Fatal(err) diff --git a/wallet/udb/txmined_test.go b/wallet/udb/txmined_test.go index b82f9fd56..3f645449c 100644 --- a/wallet/udb/txmined_test.go +++ b/wallet/udb/txmined_test.go @@ -31,7 +31,7 @@ func randomHash() chainhash.Hash { func TestSetBirthState(t *testing.T) { ctx := context.Background() - db, _, _, _, teardown, err := cloneDB(ctx, "mgr_watching_only.kv") + db, _, _, teardown, err := cloneDB(ctx, "mgr_watching_only.kv") defer teardown() if err != nil { t.Fatal(err) diff --git a/wallet/udb/upgrades.go b/wallet/udb/upgrades.go index e7e489b6a..b8aa65b42 100644 --- a/wallet/udb/upgrades.go +++ b/wallet/udb/upgrades.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021 The Decred developers +// Copyright (c) 2017-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -406,8 +406,6 @@ func votingPreferencesUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, const newVersion = 3 metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey()) - stakemgrBucket := tx.ReadWriteBucket(wstakemgrBucketKey) - ticketPurchasesBucket := stakemgrBucket.NestedReadWriteBucket(sstxRecordsBucketName) // Assert that this function is only called on version 2 databases. dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket) @@ -418,26 +416,11 @@ func votingPreferencesUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, return errors.E(errors.Invalid, "votingPreferencesUpgrade inappropriately called") } - // Update every ticket purchase with the new database version. This removes - // all per-ticket vote bits. - ticketPurchases := make(map[chainhash.Hash]*sstxRecord) - c := ticketPurchasesBucket.ReadCursor() - defer c.Close() - for k, _ := c.First(); k != nil; k, _ = c.Next() { - var hash chainhash.Hash - copy(hash[:], k) - ticketPurchase, err := fetchSStxRecord(stakemgrBucket, &hash, oldVersion) - if err != nil { - return err - } - ticketPurchases[hash] = ticketPurchase - } - for _, ticketPurchase := range ticketPurchases { - err := putSStxRecord(stakemgrBucket, ticketPurchase) - if err != nil { - return err - } - } + // This upgrade originally updated every ticket in the "wstakemgr" bucket, + // however that bucket became redundant due to the removal of legacy + // stakepool functionality. Therefore, that part of the upgrade has been + // removed, and the only thing remaining is the creation of a new + // "ticketsagendaprefs" bucket. // Create the top level bucket for agenda preferences. _, err = tx.CreateTopLevelBucket(agendaPreferences.defaultBucketKey()) diff --git a/wallet/udb/upgrades_test.go b/wallet/udb/upgrades_test.go index 4535a1e5c..6a24c443d 100644 --- a/wallet/udb/upgrades_test.go +++ b/wallet/udb/upgrades_test.go @@ -1,14 +1,12 @@ -// Copyright (c) 2017-2023 The Decred developers +// Copyright (c) 2017-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package udb import ( - "bytes" "compress/gzip" "context" - "encoding/hex" "fmt" "io" "os" @@ -91,7 +89,7 @@ func TestUpgrades(t *testing.T) { } func verifyV2Upgrade(ctx context.Context, t *testing.T, db walletdb.DB) { - amgr, _, _, err := Open(ctx, db, chaincfg.TestNet3Params(), pubPass) + amgr, _, err := Open(ctx, db, chaincfg.TestNet3Params(), pubPass) if err != nil { t.Fatalf("Open after Upgrade failed: %v", err) } @@ -160,61 +158,17 @@ func verifyV2Upgrade(ctx context.Context, t *testing.T, db walletdb.DB) { } func verifyV3Upgrade(ctx context.Context, t *testing.T, db walletdb.DB) { - _, _, smgr, err := Open(ctx, db, chaincfg.TestNet3Params(), pubPass) + _, _, err := Open(ctx, db, chaincfg.TestNet3Params(), pubPass) if err != nil { t.Fatalf("Open after Upgrade failed: %v", err) } err = walletdb.View(ctx, db, func(tx walletdb.ReadTx) error { - ns := tx.ReadBucket(wstakemgrBucketKey) - - const ( - ticketHashStr = "4516ef1d548f3284c1a27b3e706c4677392031df7071ad2022050af376837033" - votingAddrStr = "Tcu5oEdEp1W93fRT9FGSwMin7LonfRjNYe4" - ticketPurchaseHex = "01000000024bf0a303a7e6d174833d9eb761815b61f8ba8c6fa8852a6bf51c703daefc0ef60400000000ffffffff4bf0a303a7e6d174833d9eb761815b61f8ba8c6fa8852a6bf51c703daefc0ef60500000000ffffffff056f78d37a00000000000018baa914ec97b165a5f028b50fb12ae717c5f6c1b9057b5f8700000000000000000000206a1e7f686bc0e548bbb92f487db6da070e43a34117288ed59100000000000058000000000000000000001abd76a914000000000000000000000000000000000000000088ac00000000000000000000206a1e9d8e8bdc618035be32a14ab752af2e331f9abf3651074a7a000000000058000000000000000000001abd76a914000000000000000000000000000000000000000088ac00000000ad480000028ed59100000000009c480000010000006b483045022100c240bdd6a656c20e9035b839fc91faae6c766772f76149adb91a1fdcf20faf9c02203d68038b83263293f864b173c8f3f00e4371b67bf36fb9ec9f5132bdf68d2858012102adc226dec4de09a18c5a522f8f00917fb6d4eb2361a105218ac3f87d802ae3d451074a7a000000009c480000010000006a47304402205af53185f2662a30a22014b0d19760c1bfde8ec8f065b19cacab6a7abcec76a202204a2614cfcb4db3fc1c86eb0b1ca577f9039ec6db29e9c44ddcca2fe6e3c8bd5d012102adc226dec4de09a18c5a522f8f00917fb6d4eb2361a105218ac3f87d802ae3d4" - - // Stored timestamp uses time.Now(). The generated database test - // artifact uses this time (2017-04-10 11:50:04 -0400 EDT). If the - // db is ever regenerated, this expected value be updated as well. - timeStamp = 1491839404 - ) - - // Verify ticket purchase is still present with correct info, and no - // vote bits. - ticketPurchaseHash, err := chainhash.NewHashFromStr(ticketHashStr) - if err != nil { - return err - } - rec, err := fetchSStxRecord(ns, ticketPurchaseHash, 3) - if err != nil { - return err - } - if rec.voteBitsSet || rec.voteBits != 0 || rec.voteBitsExt != nil { - t.Errorf("Ticket purchase record still has vote bits") - } - votingAddr, err := smgr.SStxAddress(ns, ticketPurchaseHash) - if err != nil { - return err - } - if votingAddr.String() != votingAddrStr { - t.Errorf("Unexpected voting address, got %v want %v", - votingAddr.String(), votingAddrStr) - } - if rec.ts.Unix() != timeStamp { - t.Errorf("Unexpected timestamp, got %v want %v", rec.ts.Unix(), timeStamp) - } - var buf bytes.Buffer - err = rec.tx.MsgTx().Serialize(&buf) - if err != nil { - return err - } - expectedBytes, err := hex.DecodeString(ticketPurchaseHex) - if err != nil { - return err - } - if !bytes.Equal(buf.Bytes(), expectedBytes) { - t.Errorf("Serialized transaction does not match expected") - } + // This upgrade originally updated every ticket in the "wstakemgr" bucket, + // however that bucket became redundant due to the removal of legacy + // stakepool functionality. Therefore, that part of the upgrade has been + // removed, and the only thing remaining is the creation of a new + // "ticketsagendaprefs" bucket. // Verify that the agenda preferences bucket was created. if tx.ReadBucket(agendaPreferences.defaultBucketKey()) == nil { @@ -422,7 +376,7 @@ func verifyV8Upgrade(ctx context.Context, t *testing.T, db walletdb.DB) { // See the v11.db.go file for an explanation of the database layout and test // plan. func verifyV12Upgrade(ctx context.Context, t *testing.T, db walletdb.DB) { - _, txmgr, _, err := Open(ctx, db, chaincfg.TestNet3Params(), pubPass) + _, txmgr, err := Open(ctx, db, chaincfg.TestNet3Params(), pubPass) if err != nil { t.Fatalf("Open after Upgrade failed: %v", err) } diff --git a/wallet/udb/vsp.go b/wallet/udb/vsp.go index 44aec49dc..1f472efbc 100644 --- a/wallet/udb/vsp.go +++ b/wallet/udb/vsp.go @@ -16,6 +16,8 @@ var ( vspPubKeyBucketKey = []byte("vsppubkey") ) +const hashSize = 32 + // FeeStatus represents the current fee status of a ticket. type FeeStatus int diff --git a/wallet/wallet.go b/wallet/wallet.go index 263ddff54..210a6ce10 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -14,8 +14,6 @@ import ( "math/big" "runtime" "sort" - "strconv" - "strings" "sync" "sync/atomic" "time" @@ -75,9 +73,8 @@ var ( // Namespace bucket keys. var ( - waddrmgrNamespaceKey = []byte("waddrmgr") - wtxmgrNamespaceKey = []byte("wtxmgr") - wstakemgrNamespaceKey = []byte("wstakemgr") + waddrmgrNamespaceKey = []byte("waddrmgr") + wtxmgrNamespaceKey = []byte("wtxmgr") ) // The assumed output script version is defined to assist with refactoring to @@ -107,10 +104,9 @@ type Wallet struct { disapprovePercent atomic.Uint32 // Data stores - db walletdb.DB - manager *udb.Manager - txStore *udb.Store - stakeMgr *udb.StakeStore + db walletdb.DB + manager *udb.Manager + txStore *udb.Store // Handlers for stake system. stakeSettingsLock sync.Mutex @@ -119,8 +115,6 @@ type Wallet struct { poolAddress stdaddr.StakeAddress poolFees float64 manualTickets bool - stakePoolEnabled bool - stakePoolColdAddrs map[string]struct{} subsidyCache *blockchain.SubsidyCache tspends map[chainhash.Hash]wire.MsgTx tspendPolicy map[chainhash.Hash]stake.TreasuryVoteT @@ -199,11 +193,10 @@ type Config struct { DisableCoinTypeUpgrades bool DisableMixing bool - StakePoolColdExtKey string - ManualTickets bool - AllowHighFees bool - RelayFee dcrutil.Amount - Params *chaincfg.Params + ManualTickets bool + AllowHighFees bool + RelayFee dcrutil.Amount + Params *chaincfg.Params } // DisapprovePercent returns the wallet's block disapproval percentage. @@ -429,7 +422,6 @@ func (w *Wallet) AgendaChoices(ctx context.Context, ticketHash *chainhash.Hash) err = walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error { if ticketHash != nil { ownTicket = w.txStore.OwnTicket(tx, ticketHash) - ownTicket = ownTicket || w.stakeMgr.OwnTicket(ticketHash) if !ownTicket { return nil } @@ -486,7 +478,7 @@ func (w *Wallet) SetAgendaChoices(ctx context.Context, ticketHash *chainhash.Has // validate ticket ownership var ownTicket bool err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error { - ownTicket = w.txStore.OwnTicket(dbtx, ticketHash) || w.stakeMgr.OwnTicket(ticketHash) + ownTicket = w.txStore.OwnTicket(dbtx, ticketHash) return nil }) if err != nil { @@ -5333,67 +5325,6 @@ func CreateWatchOnly(ctx context.Context, db DB, extendedPubKey string, pubPass return nil } -// decodeStakePoolColdExtKey decodes the string of stake pool addresses -// to search incoming tickets for. The format for the passed string is: -// -// "xpub...:end" -// -// where xpub... is the extended public key and end is the last -// address index to scan to, exclusive. Effectively, it returns the derived -// addresses for this public key for the address indexes [0,end). The branch -// used for the derivation is always the external branch. -func decodeStakePoolColdExtKey(encStr string, params *chaincfg.Params) (map[string]struct{}, error) { - // Default option; stake pool is disabled. - if encStr == "" { - return nil, nil - } - - // Split the string. - splStrs := strings.Split(encStr, ":") - if len(splStrs) != 2 { - return nil, errors.Errorf("failed to correctly parse passed stakepool " + - "address public key and index") - } - - // Parse the extended public key and ensure it's the right network. - key, err := hdkeychain.NewKeyFromString(splStrs[0], params) - if err != nil { - return nil, err - } - - // Parse the ending index and ensure it's valid. - end, err := strconv.Atoi(splStrs[1]) - if err != nil { - return nil, err - } - if end < 0 || end > udb.MaxAddressesPerAccount { - return nil, errors.Errorf("pool address index is invalid (got %v)", - end) - } - - log.Infof("Please wait, deriving %v stake pool fees addresses "+ - "for extended public key %s", end, splStrs[0]) - - // Derive from external branch - branchKey, err := key.Child(udb.ExternalBranch) - if err != nil { - return nil, err - } - - // Derive the addresses from [0, end) for this extended public key. - addrs, err := deriveChildAddresses(branchKey, 0, uint32(end)+1, params) - if err != nil { - return nil, err - } - - addrMap := make(map[string]struct{}) - for i := range addrs { - addrMap[addrs[i].String()] = struct{}{} - } - - return addrMap, nil -} - // GapLimit returns the currently used gap limit. func (w *Wallet) GapLimit() uint32 { return w.gapLimit @@ -5495,7 +5426,7 @@ func Open(ctx context.Context, cfg *Config) (*Wallet, error) { } // Open database managers - w.manager, w.txStore, w.stakeMgr, err = udb.Open(ctx, db, params, cfg.PubPassphrase) + w.manager, w.txStore, err = udb.Open(ctx, db, params, cfg.PubPassphrase) if err != nil { return nil, errors.E(op, err) } @@ -5586,13 +5517,6 @@ func Open(ctx context.Context, cfg *Config) (*Wallet, error) { w.vspTSpendPolicy = vspTSpendPolicy w.vspTSpendKeyPolicy = vspTreasuryKeyPolicy - w.stakePoolColdAddrs, err = decodeStakePoolColdExtKey(cfg.StakePoolColdExtKey, - params) - if err != nil { - return nil, errors.E(op, err) - } - w.stakePoolEnabled = len(w.stakePoolColdAddrs) > 0 - // Amounts w.relayFee = cfg.RelayFee