From 324fb19b7ca924a07da1b0846c60d2be31357045 Mon Sep 17 00:00:00 2001 From: Dmitry Fedorov Date: Thu, 7 Jun 2018 00:01:24 +0300 Subject: [PATCH] multi: add listtickets json rpc command listtickets cmd return detailed info about ticket details, spender and state --- Gopkg.lock | 13 ++- Gopkg.toml | 3 +- internal/rpchelp/helpdescs_en_US.go | 31 +++++++ internal/rpchelp/methods.go | 1 + rpc/legacyrpc/methods.go | 34 +++++++ rpc/legacyrpc/rpcserverhelp.go | 5 +- wallet/wallet.go | 136 ++++++++++++++++++++++++++++ 7 files changed, 217 insertions(+), 6 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index c954b8598..2185adfaf 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -47,7 +47,7 @@ revision = "b3520e187fa8ebe65eb74245408cf4b83e6a65d3" [[projects]] - branch = "master" + branch = "add_listalltickets_rpc_method" name = "github.com/decred/dcrd" packages = [ "blockchain", @@ -77,7 +77,8 @@ "txscript", "wire" ] - revision = "678ff1efdd767f84f7787bb29d04542c6c94f636" + revision = "e2b0171e703c93b944c6698368d0f8f533295f77" + source = "github.com/klka/dcrd" [[projects]] name = "github.com/decred/slog" @@ -97,6 +98,12 @@ ] revision = "130e6b02ab059e7b717a096f397c5b60111cae74" +[[projects]] + name = "github.com/gorilla/websocket" + packages = ["."] + revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" + version = "v1.2.0" + [[projects]] name = "github.com/jessevdk/go-flags" packages = ["."] @@ -210,6 +217,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "0ad13264d89466ff4c9f2cbb6684367f285cf11bfa56dbaacfddddfca9317edd" + inputs-digest = "02461b4bb07a629a616f556174b71afdfcb596184f1ae383a88ccc4d93410b77" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 5bcb798fb..0bafd28ae 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -12,8 +12,9 @@ name = "github.com/btcsuite/websocket" [[constraint]] - branch = "master" + branch = "add_listalltickets_rpc_method" name = "github.com/decred/dcrd" + source = "github.com/klka/dcrd" [[constraint]] branch = "master" diff --git a/internal/rpchelp/helpdescs_en_US.go b/internal/rpchelp/helpdescs_en_US.go index e06536e6e..8ac4f8f83 100644 --- a/internal/rpchelp/helpdescs_en_US.go +++ b/internal/rpchelp/helpdescs_en_US.go @@ -321,6 +321,36 @@ var helpDescsEnUS = map[string]string{ "listsinceblockresult-transactions": "JSON array of objects containing verbose details of the each transaction", "listsinceblockresult-lastblock": "Hash of the latest-synced block to be used in later calls to listsinceblock", + // ListTicketCmd help. + "listtickets--synopsis": "List all ticket details with related spender and state info", + + // ListTicketsResult help. + "listticketsresult-status": "Status of ticket", + "listticketsresult-ticket": "Ticket tx details", + "listticketsresult-spender": "Spender tx details", + + // ListTicketsTransactionSummary + "listticketstransactionsummary-type": "Lists all tickets, including unmined tickets", + "listticketstransactionsummary-hash": "Hash of the transaction", + "listticketstransactionsummary-transaction": "Serialized string of the transaction", + "listticketstransactionsummary-myinputs": "Inputs of tx details", + "listticketstransactionsummary-myoutputs": "Outputs of tx details", + "listticketstransactionsummary-fee": "Total input value minus the total output value for sent transactions", + "listticketstransactionsummary-timestamp": "Timestamp of tx", + + // ListTicketsTransactionSummaryInput + "listticketstransactionsummaryinput-index": "Index of input in list of all tx inputs", + "listticketstransactionsummaryinput-previousaccount": "Input account", + "listticketstransactionsummaryinput-previousamount": "Input amount", + + // ListTicketsTransactionSummaryOutput + "listticketstransactionsummaryoutput-index": "Index of input in list of all tx outputs", + "listticketstransactionsummaryoutput-account": "Output account", + "listticketstransactionsummaryoutput-internal": "Flag if address was create for internal use", + "listticketstransactionsummaryoutput-amount": "Output amount", + "listticketstransactionsummaryoutput-address": "Address receiving amount", + "listticketstransactionsummaryoutput-outputscript": "Pkscript to receive amount", + // ListTransactionsResult help. "listtransactionsresult-account": "DEPRECATED -- Unset", "listtransactionsresult-address": "Payment address for a transaction output", @@ -685,6 +715,7 @@ var helpDescsEnUS = map[string]string{ "purchaseticket-nosplittransaction": "Use ticket purchase change outputs instead of a split transaction", "purchaseticket-comment": "Unused", "purchaseticket-ticketfee": "The transaction fee rate (DCR/kB) to use (overrides fees set by the wallet config or settxfee RPC)", + "purchaseticket-ticketchange": "Currently unused", // SetTicketFeeCmd help. "setticketfee--synopsis": "Modify the fee per kB of the serialized tx size used each time more fee is required for an authored stake transaction.", diff --git a/internal/rpchelp/methods.go b/internal/rpchelp/methods.go index fa05010e4..f000b04e9 100644 --- a/internal/rpchelp/methods.go +++ b/internal/rpchelp/methods.go @@ -55,6 +55,7 @@ var Methods = []struct { {"listreceivedbyaccount", []interface{}{(*[]dcrjson.ListReceivedByAccountResult)(nil)}}, {"listreceivedbyaddress", []interface{}{(*[]dcrjson.ListReceivedByAddressResult)(nil)}}, {"listsinceblock", []interface{}{(*dcrjson.ListSinceBlockResult)(nil)}}, + {"listtickets", []interface{}{(*dcrjson.ListTicketsResult)(nil)}}, {"listtransactions", returnsLTRArray}, {"listunspent", []interface{}{(*dcrjson.ListUnspentResult)(nil)}}, {"lockunspent", returnsBool}, diff --git a/rpc/legacyrpc/methods.go b/rpc/legacyrpc/methods.go index aeff3c096..0de9c9df7 100644 --- a/rpc/legacyrpc/methods.go +++ b/rpc/legacyrpc/methods.go @@ -94,6 +94,7 @@ var handlers = map[string]handler{ "listreceivedbyaddress": {fn: listReceivedByAddress}, "listsinceblock": {fn: listSinceBlock}, "listscripts": {fn: listScripts}, + "listtickets": {fn: listTickets}, "listtransactions": {fn: listTransactions}, "listunspent": {fn: listUnspent}, "lockunspent": {fn: lockUnspent}, @@ -1677,6 +1678,39 @@ func listAccounts(s *Server, icmd interface{}) (interface{}, error) { return accountBalances, nil } +// listTickets handles a listtickets request by returning a list +// of tickets containing ticket, spender details and state +func listTickets(s *Server, icmd interface{}) (interface{}, error) { + w, ok := s.walletLoader.LoadedWallet() + if !ok { + return nil, errUnloadedWallet + } + + var chainClient *dcrrpcclient.Client + n, err := w.NetworkBackend() + connected := err == nil + if connected { + chainClient, err = chain.RPCClientFromBackend(n) + if err != nil { + err := chainClient.Ping() + if err != nil { + log.Warnf("Ping failed on connected daemon client: %v", err) + connected = false + } + } + } + if !connected { + return nil, errClientNotConnected + } + + tickets, err := w.ListTickets(chainClient) + if err != nil { + return nil, err + } + + return tickets, nil +} + // listLockUnspent handles a listlockunspent request by returning an slice of // all locked outpoints. func listLockUnspent(s *Server, icmd interface{}) (interface{}, error) { diff --git a/rpc/legacyrpc/rpcserverhelp.go b/rpc/legacyrpc/rpcserverhelp.go index 5f450daeb..577b5c1b7 100644 --- a/rpc/legacyrpc/rpcserverhelp.go +++ b/rpc/legacyrpc/rpcserverhelp.go @@ -35,6 +35,7 @@ func helpDescsEnUS() map[string]string { "listreceivedbyaccount": "listreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\n\nDEPRECATED -- Returns a JSON array of objects listing all accounts and the total amount received by each account.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction is considered\n2. includeempty (boolean, optional, default=false) Unused\n3. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n[{\n \"account\": \"value\", (string) The name of the account\n \"amount\": n.nnn, (numeric) Total amount received by payment addresses of the account valued in decred\n \"confirmations\": n, (numeric) Number of block confirmations of the most recent transaction relevant to the account\n},...]\n", "listreceivedbyaddress": "listreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\n\nReturns a JSON array of objects listing wallet payment addresses and their total received amounts.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction is considered\n2. includeempty (boolean, optional, default=false) Unused\n3. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n[{\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) The payment address\n \"amount\": n.nnn, (numeric) Total amount received by the payment address valued in decred\n \"confirmations\": n, (numeric) Number of block confirmations of the most recent transaction relevant to the address\n \"txids\": [\"value\",...], (array of string) Transaction hashes of all transactions involving this address\n \"involvesWatchonly\": true|false, (boolean) Unset\n},...]\n", "listsinceblock": "listsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\n\nReturns a JSON array of objects listing details of all wallet transactions after some block.\n\nArguments:\n1. blockhash (string, optional) Hash of the parent block of the first block to consider transactions from, or unset to list all transactions\n2. targetconfirmations (numeric, optional, default=1) Minimum number of block confirmations of the last block in the result object. Must be 1 or greater. Note: The transactions array in the result object is not affected by this parameter\n3. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n{\n \"transactions\": [{ (array of object) JSON array of objects containing verbose details of the each transaction\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in decred\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"txid\": \"value\", (string) The hash of the transaction\n \"txtype\": \"value\", (string) The type of tx (regular tx, stake tx)\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n },...], \n \"lastblock\": \"value\", (string) Hash of the latest-synced block to be used in later calls to listsinceblock\n} \n", + "listtickets": "listtickets\n\nList all ticket details with related spender and state info\n\nArguments:\nNone\n\nResult:\n{\n \"ticket\": { (object) Ticket tx details\n \"hash\": \"value\", (string) Hash of the transaction\n \"transaction\": \"value\", (string) Serialized string of the transaction\n \"myinputs\": [{ (array of object) Inputs of tx details\n \"index\": n, (numeric) Index of input in list of all tx inputs\n \"previousaccount\": n, (numeric) Input account\n \"previousamount\": n.nnn, (numeric) Input amount\n },...], \n \"myoutputs\": [{ (array of object) Outputs of tx details\n \"index\": n, (numeric) Index of input in list of all tx outputs\n \"account\": n, (numeric) Output account\n \"internal\": true|false, (boolean) Flag if address was create for internal use\n \"amount\": n.nnn, (numeric) Output amount\n \"address\": unknown, (value) Address receiving amount\n \"outputscript\": \"value\", (string) Pkscript to receive amount\n },...], \n \"fee\": n.nnn, (numeric) Total input value minus the total output value for sent transactions\n \"timestamp\": n, (numeric) Timestamp of tx\n \"type\": n, (numeric) Lists all tickets, including unmined tickets\n }, \n \"spender\": { (object) Spender tx details\n \"hash\": \"value\", (string) Hash of the transaction\n \"transaction\": \"value\", (string) Serialized string of the transaction\n \"myinputs\": [{ (array of object) Inputs of tx details\n \"index\": n, (numeric) Index of input in list of all tx inputs\n \"previousaccount\": n, (numeric) Input account\n \"previousamount\": n.nnn, (numeric) Input amount\n },...], \n \"myoutputs\": [{ (array of object) Outputs of tx details\n \"index\": n, (numeric) Index of input in list of all tx outputs\n \"account\": n, (numeric) Output account\n \"internal\": true|false, (boolean) Flag if address was create for internal use\n \"amount\": n.nnn, (numeric) Output amount\n \"address\": unknown, (value) Address receiving amount\n \"outputscript\": \"value\", (string) Pkscript to receive amount\n },...], \n \"fee\": n.nnn, (numeric) Total input value minus the total output value for sent transactions\n \"timestamp\": n, (numeric) Timestamp of tx\n \"type\": n, (numeric) Lists all tickets, including unmined tickets\n }, \n \"status\": \"value\", (string) Status of ticket\n} \n", "listtransactions": "listtransactions (\"account\" count=10 from=0 includewatchonly=false)\n\nReturns a JSON array of objects containing verbose details for wallet transactions.\n\nArguments:\n1. account (string, optional) DEPRECATED -- Unused (must be unset or \"*\")\n2. count (numeric, optional, default=10) Maximum number of transactions to create results from\n3. from (numeric, optional, default=0) Number of transactions to skip before results are created\n4. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n[{\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in decred\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"txid\": \"value\", (string) The hash of the transaction\n \"txtype\": \"value\", (string) The type of tx (regular tx, stake tx)\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n},...]\n", "listunspent": "listunspent (minconf=1 maxconf=9999999 [\"address\",...])\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is considered\n2. maxconf (numeric, optional, default=9999999) Maximum number of block confirmations required before a transaction output is excluded\n3. addresses (array of string, optional) If set, limits the returned details to unspent outputs received by any of these payment addresses\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree the transaction comes from\n \"txtype\": n, (numeric) The type of the transaction\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) Unset\n \"amount\": n.nnn, (numeric) The amount of the output valued in decred\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n} \n", "lockunspent": "lockunspent unlock [{\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\n\nLocks or unlocks an unspent output.\nLocked outputs are not chosen for transaction inputs of authored transactions and are not included in 'listunspent' results.\nLocked outputs are volatile and are not saved across wallet restarts.\nIf unlock is true and no transaction outputs are specified, all locked outputs are marked unlocked.\n\nArguments:\n1. unlock (boolean, required) True to unlock outputs, false to lock\n2. transactions (array of object, required) Transaction outputs to lock or unlock\n[{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"tree\": n, (numeric) The tree to generate transaction for\n},...]\n\nResult:\ntrue|false (boolean) The boolean 'true'\n", @@ -69,7 +70,7 @@ func helpDescsEnUS() map[string]string { "renameaccount": "renameaccount \"oldaccount\" \"newaccount\"\n\nRenames an account.\n\nArguments:\n1. oldaccount (string, required) The old account name to rename\n2. newaccount (string, required) The new name for the account\n\nResult:\nNothing\n", "walletislocked": "walletislocked\n\nReturns whether or not the wallet is locked.\n\nArguments:\nNone\n\nResult:\ntrue|false (boolean) Whether the wallet is locked\n", "walletinfo": "walletinfo\n\nReturns global information about the wallet\n\nArguments:\nNone\n\nResult:\n{\n \"daemonconnected\": true|false, (boolean) Whether or not the wallet is currently connected to the daemon RPC\n \"unlocked\": true|false, (boolean) Whether or not the wallet is unlocked\n \"txfee\": n.nnn, (numeric) Transaction fee per kB of the serialized tx size in coins\n \"ticketfee\": n.nnn, (numeric) Ticket fee per kB of the serialized tx size in coins\n \"ticketpurchasing\": true|false, (boolean) Whether or not the wallet is currently purchasing tickets\n \"votebits\": n, (numeric) Vote bits setting\n \"votebitsextended\": \"value\", (string) Extended vote bits setting\n \"voteversion\": n, (numeric) Version of votes that will be generated\n \"voting\": true|false, (boolean) Whether or not the wallet is currently voting tickets\n} \n", - "purchaseticket": "purchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets \"pooladdress\" poolfees expiry \"comment\" nosplittransaction ticketfee)\n\nPurchase ticket using available funds.\n\nArguments:\n1. fromaccount (string, required) The account to use for purchase (default=\"default\")\n2. spendlimit (numeric, required) Limit on the amount to spend on ticket\n3. minconf (numeric, optional, default=1) Minimum number of block confirmations required\n4. ticketaddress (string, optional) Override the ticket address to which voting rights are given\n5. numtickets (numeric, optional) The number of tickets to purchase\n6. pooladdress (string, optional) The address to pay stake pool fees to\n7. poolfees (numeric, optional) The amount of fees to pay to the stake pool\n8. expiry (numeric, optional) Height at which the purchase tickets expire\n9. comment (string, optional) Unused\n10. nosplittransaction (boolean, optional) Use ticket purchase change outputs instead of a split transaction\n11. ticketfee (numeric, optional) The transaction fee rate (DCR/kB) to use (overrides fees set by the wallet config or settxfee RPC)\n\nResult:\n\"value\" (string) Hash of the resulting ticket\n", + "purchaseticket": "purchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets \"pooladdress\" poolfees expiry \"comment\" ticketchange ticketfee)\n\nPurchase ticket using available funds.\n\nArguments:\n1. fromaccount (string, required) The account to use for purchase (default=\"default\")\n2. spendlimit (numeric, required) Limit on the amount to spend on ticket\n3. minconf (numeric, optional, default=1) Minimum number of block confirmations required\n4. ticketaddress (string, optional) Override the ticket address to which voting rights are given\n5. numtickets (numeric, optional) The number of tickets to purchase\n6. pooladdress (string, optional) The address to pay stake pool fees to\n7. poolfees (numeric, optional) The amount of fees to pay to the stake pool\n8. expiry (numeric, optional) Height at which the purchase tickets expire\n9. comment (string, optional) Unused\n10. ticketchange (boolean, optional) Currently unused\n11. ticketfee (numeric, optional) The transaction fee rate (DCR/kB) to use (overrides fees set by the wallet config or settxfee RPC)\n\nResult:\n\"value\" (string) Hash of the resulting ticket\n", "generatevote": "generatevote \"blockhash\" height \"tickethash\" votebits \"votebitsext\"\n\nReturns the vote transaction encoded as a hexadecimal string\n\nArguments:\n1. blockhash (string, required) Block hash for the ticket\n2. height (numeric, required) Block height for the ticket\n3. tickethash (string, required) The hash of the ticket\n4. votebits (numeric, required) The voteBits to set for the ticket\n5. votebitsext (string, required) The extended voteBits to set for the ticket\n\nResult:\n{\n \"hex\": \"value\", (string) The hex encoded transaction\n} \n", "getstakeinfo": "getstakeinfo\n\nReturns statistics about staking from the wallet.\n\nArguments:\nNone\n\nResult:\n{\n \"blockheight\": n, (numeric) Current block height for stake info.\n \"poolsize\": n, (numeric) Number of live tickets in the ticket pool.\n \"difficulty\": n.nnn, (numeric) Current stake difficulty.\n \"allmempooltix\": n, (numeric) Number of tickets currently in the mempool\n \"ownmempooltix\": n, (numeric) Number of tickets submitted by this wallet currently in mempool\n \"immature\": n, (numeric) Number of tickets from this wallet that are in the blockchain but which are not yet mature\n \"live\": n, (numeric) Number of mature, active tickets owned by this wallet\n \"proportionlive\": n.nnn, (numeric) (Live / PoolSize)\n \"voted\": n, (numeric) Number of votes cast by this wallet\n \"totalsubsidy\": n.nnn, (numeric) Total amount of coins earned by stake mining\n \"missed\": n, (numeric) Number of missed tickets (failure to vote, not including expired)\n \"proportionmissed\": n.nnn, (numeric) (Missed / (Missed + Voted))\n \"revoked\": n, (numeric) Number of missed tickets that were missed and then revoked\n \"expired\": n, (numeric) Number of tickets that have expired\n} \n", "getticketfee": "getticketfee\n\nGet the current fee per kB of the serialized tx size used for an authored stake transaction.\n\nArguments:\nNone\n\nResult:\nn.nnn (numeric) The current fee\n", @@ -86,4 +87,4 @@ var localeHelpDescs = map[string]func() map[string]string{ "en_US": helpDescsEnUS, } -var requestUsages = "accountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naddmultisigaddress nrequired [\"key\",...] (\"account\")\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ndumpprivkey \"address\"\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblockhash\ngetblockcount\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngetvotechoices\nhelp (\"command\")\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nkeypoolrefill (newsize=100)\nlistaccounts (minconf=1)\nlistlockunspent\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\",...])\nlockunspent unlock [{\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrescanwallet (beginheight=0)\nrevoketickets\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\"\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nstartautobuyer \"account\" \"passphrase\" (balancetomaintain maxfeeperkb maxpricerelative maxpriceabsolute \"votingaddress\" \"pooladdress\" poolfees maxperblock)\nstopautobuyer\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nvalidateaddress \"address\"\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\ncreatenewaccount \"account\"\nexportwatchingwallet (\"account\" download=false)\ngetbestblock\ngetunconfirmedbalance (\"account\")\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nrenameaccount \"oldaccount\" \"newaccount\"\nwalletislocked\nwalletinfo\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets \"pooladdress\" poolfees expiry \"comment\" nosplittransaction ticketfee)\ngeneratevote \"blockhash\" height \"tickethash\" votebits \"votebitsext\"\ngetstakeinfo\ngetticketfee\nsetticketfee fee\ngetwalletfee\naddticket \"tickethex\"\nlistscripts\nstakepooluserinfo \"user\"\nticketsforaddress \"address\"" +var requestUsages = "accountaddressindex \"account\" branch\naccountsyncaddressindex \"account\" branch index\naddmultisigaddress nrequired [\"key\",...] (\"account\")\nconsolidate inputs (\"account\" \"address\")\ncreatemultisig nrequired [\"key\",...]\ndumpprivkey \"address\"\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetbalance (\"account\" minconf=1)\ngetbestblockhash\ngetblockcount\ngetinfo\ngetmasterpubkey (\"account\")\ngetmultisigoutinfo \"hash\" index\ngetnewaddress (\"account\" \"gappolicy\")\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngettickets includeimmature\ngettransaction \"txid\" (includewatchonly=false)\ngetvotechoices\nhelp (\"command\")\nimportprivkey \"privkey\" (\"label\" rescan=true scanfrom)\nimportscript \"hex\" (rescan=true scanfrom)\nkeypoolrefill (newsize=100)\nlistaccounts (minconf=1)\nlistlockunspent\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttickets\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...])\nlockunspent unlock [{\"txid\":\"value\",\"vout\":n,\"tree\":n},...]\nredeemmultisigout \"hash\" index tree (\"address\")\nredeemmultisigouts \"fromscraddress\" (\"toaddress\" number)\nrescanwallet (beginheight=0)\nrevoketickets\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsendtomultisig \"fromaccount\" amount [\"pubkey\",...] (nrequired=1 minconf=1 \"comment\")\nsettxfee amount\nsetvotechoice \"agendaid\" \"choiceid\"\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"tree\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nsignrawtransactions [\"rawtx\",...] (send=true)\nstartautobuyer \"account\" \"passphrase\" (balancetomaintain maxfeeperkb maxpricerelative maxpriceabsolute \"votingaddress\" \"pooladdress\" poolfees maxperblock)\nstopautobuyer\nsweepaccount \"sourceaccount\" \"destinationaddress\" (requiredconfirmations feeperkb)\nvalidateaddress \"address\"\nverifymessage \"address\" \"signature\" \"message\"\nversion\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\ncreatenewaccount \"account\"\nexportwatchingwallet (\"account\" download=false)\ngetbestblock\ngetunconfirmedbalance (\"account\")\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nrenameaccount \"oldaccount\" \"newaccount\"\nwalletislocked\nwalletinfo\npurchaseticket \"fromaccount\" spendlimit (minconf=1 \"ticketaddress\" numtickets \"pooladdress\" poolfees expiry \"comment\" ticketchange ticketfee)\ngeneratevote \"blockhash\" height \"tickethash\" votebits \"votebitsext\"\ngetstakeinfo\ngetticketfee\nsetticketfee fee\ngetwalletfee\naddticket \"tickethex\"\nlistscripts\nstakepooluserinfo \"user\"\nticketsforaddress \"address\"" diff --git a/wallet/wallet.go b/wallet/wallet.go index e9d609278..ef9dd4eeb 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -47,6 +47,23 @@ const ( // NOTE: at time of writing, public encryption only applies to public // data in the waddrmgr namespace. Transactions are not yet encrypted. InsecurePubPassphrase = "public" + + // TicketStatusUnknown any ticket that its status was unable to be determined. + TicketDetailsUnknown = "UNKNOWN" + // TicketStatusUnmined any not yet mined ticket. + TicketDetailsUnmined = "UNMINED" + // TicketStatusImmature any so to be live ticket. + TicketDetailsImmature = "IMMATURE" + // TicketStatusLive any currently live ticket. + TicketDetailsLive = "LIVE" + // TicketStatusVoted any ticket that was seen to have voted. + TicketDetailsVoted = "VOTED" + // TicketStatusRevoked any ticket that has been previously revoked. + TicketDetailsRevoked = "REVOKED" + // TicketStatusMissed any ticket that has yet to be revoked, and was missed. + TicketDetailsMissed = "MISSED" + // TicketStatusExpired any ticket that has yet to be revoked, and was expired. + TicketDetailsExpired = "EXPIRED" ) var ( @@ -2063,6 +2080,125 @@ func (w *Wallet) ListTransactionDetails(txHash *chainhash.Hash) ([]dcrjson.ListT return txList, nil } +// ListTickets returns the listtickets results +func (w *Wallet) ListTickets(chainClient *dcrrpcclient.Client) ([]*dcrjson.ListTicketsResult, error) { + const op errors.Op = "wallet.ListTickets" + + var tickets []*dcrjson.ListTicketsResult + err := walletdb.View(w.db, func(dbtx walletdb.ReadTx) error { + txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey) + + tickets = make([]*dcrjson.ListTicketsResult, 0) + rangeFn := func(details []udb.TxDetails) (bool, error) { + for i := range details { + ticketDetails, err := w.TxStore.TicketDetails(txmgrNs, &details[i]) + if err != nil { + return false, errors.Errorf("%v while trying to get ticket details for txhash: %v", err, &details[i].Hash) + } + // Continue if not a ticket + if ticketDetails == nil { + continue + } + ticketSummary := makeTicketSummary(chainClient, dbtx, w, ticketDetails) + ticketInputs := []dcrjson.ListTicketsTransactionSummaryInput{} + ticketOutputs := []dcrjson.ListTicketsTransactionSummaryOutput{} + for _, in := range ticketSummary.Ticket.MyInputs { + ticketInputs = append(ticketInputs, dcrjson.ListTicketsTransactionSummaryInput{ + Index: in.Index, + PreviousAccount: in.PreviousAccount, + PreviousAmount: in.PreviousAmount.ToCoin(), + }) + } + for _, out := range ticketSummary.Ticket.MyOutputs { + ticketOutputs = append(ticketOutputs, dcrjson.ListTicketsTransactionSummaryOutput{ + Index: out.Index, + Account: out.Account, + Internal: out.Internal, + Amount: out.Amount.ToCoin(), + Address: out.Address, + OutputScript: hex.EncodeToString(out.OutputScript), + }) + } + + ticketInfo := &dcrjson.ListTicketsResult{ + Ticket: &dcrjson.ListTicketsTransactionSummary{ + Hash: ticketSummary.Ticket.Hash.String(), + Transaction: hex.EncodeToString(ticketSummary.Ticket.Transaction), + MyInputs: ticketInputs, + MyOutputs: ticketOutputs, + Fee: ticketSummary.Ticket.Fee.ToCoin(), + Timestamp: ticketSummary.Ticket.Timestamp, + Type: int8(ticketSummary.Ticket.Type), + }, + } + + if ticketSummary.Spender != nil { + spenderOutputs := []dcrjson.ListTicketsTransactionSummaryOutput{} + spenderInputs := []dcrjson.ListTicketsTransactionSummaryInput{} + for _, in := range ticketSummary.Spender.MyInputs { + spenderInputs = append(spenderInputs, dcrjson.ListTicketsTransactionSummaryInput{ + Index: in.Index, + PreviousAccount: in.PreviousAccount, + PreviousAmount: in.PreviousAmount.ToCoin(), + }) + } + for _, out := range ticketSummary.Spender.MyOutputs { + spenderOutputs = append(spenderOutputs, dcrjson.ListTicketsTransactionSummaryOutput{ + Index: out.Index, + Account: out.Account, + Internal: out.Internal, + Amount: out.Amount.ToCoin(), + Address: out.Address, + OutputScript: hex.EncodeToString(out.OutputScript), + }) + } + ticketInfo.Spender = &dcrjson.ListTicketsTransactionSummary{ + Hash: ticketSummary.Spender.Hash.String(), + Transaction: hex.EncodeToString(ticketSummary.Spender.Transaction), + MyInputs: spenderInputs, + MyOutputs: spenderOutputs, + Fee: ticketSummary.Spender.Fee.ToCoin(), + Timestamp: ticketSummary.Spender.Timestamp, + Type: int8(ticketSummary.Spender.Type), + } + } + switch ticketSummary.Status { + case TicketStatusUnknown: + ticketInfo.Status = TicketDetailsUnknown + case TicketStatusUnmined: + ticketInfo.Status = TicketDetailsUnmined + case TicketStatusImmature: + ticketInfo.Status = TicketDetailsImmature + case TicketStatusLive: + ticketInfo.Status = TicketDetailsLive + case TicketStatusVoted: + ticketInfo.Status = TicketDetailsVoted + case TicketStatusRevoked: + ticketInfo.Status = TicketDetailsRevoked + case TicketStatusMissed: + ticketInfo.Status = TicketDetailsMissed + case TicketStatusExpired: + ticketInfo.Status = TicketDetailsExpired + } + tickets = append(tickets, ticketInfo) + } + + if len(tickets) == 0 { + return false, nil + } + + return false, nil + } + + return w.TxStore.RangeTransactions(txmgrNs, 0, -1, rangeFn) + }) + if err != nil { + return nil, errors.E(op, err) + } + + return tickets, nil +} + // BlockIdentifier identifies a block by either a height in the main chain or a // hash. type BlockIdentifier struct {