Skip to content

Commit

Permalink
Implement the debuglevel JSON-RPC method
Browse files Browse the repository at this point in the history
This allows debug levels to be made more or less verbose at runtime, without
requiring an application restart to change the config.

The semantics and usage is identical to dcrd's debuglevel method.
  • Loading branch information
jrick committed Dec 17, 2024
1 parent 562b34d commit 8886be1
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 2 deletions.
15 changes: 14 additions & 1 deletion internal/rpc/jsonrpc/config.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Copyright (c) 2013-2024 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

Expand Down Expand Up @@ -29,4 +29,17 @@ type Options struct {
VSPPubKey string
VSPMaxFee dcrutil.Amount
Dial func(ctx context.Context, network, addr string) (net.Conn, error)

Loggers Loggers
}

// Loggers provides access to manage all application subsystem loggers.
type Loggers interface {
// Subsystems returns all of the application logging subsystem names.
Subsystems() []string

// SetLevels sets all loggers, or a specific logger (when levelSpec is
// prefixed by the subsystem and an equals sign) to a particular debug
// level.
SetLevels(levelSpec string) error
}
18 changes: 18 additions & 0 deletions internal/rpc/jsonrpc/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ var handlers = map[string]handler{
"createnewaccount": {fn: (*Server).createNewAccount},
"createrawtransaction": {fn: (*Server).createRawTransaction},
"createsignature": {fn: (*Server).createSignature},
"debuglevel": {fn: (*Server).debugLevel},
"disapprovepercent": {fn: (*Server).disapprovePercent},
"discoverusage": {fn: (*Server).discoverUsage},
"dumpprivkey": {fn: (*Server).dumpPrivKey},
Expand Down Expand Up @@ -791,6 +792,23 @@ func (s *Server) createSignature(ctx context.Context, icmd any) (any, error) {
}, nil
}

func (s *Server) debugLevel(ctx context.Context, icmd any) (any, error) {
cmd := icmd.(*types.DebugLevelCmd)

if cmd.LevelSpec == "show" {
return fmt.Sprintf("Supported subsystems %v",
s.cfg.Loggers.Subsystems()), nil
}

err := s.cfg.Loggers.SetLevels(cmd.LevelSpec)
if err != nil {
return nil, rpcErrorf(dcrjson.ErrRPCInvalidParameter,
"invalid debug level %v: %v", cmd.LevelSpec, err)
}

return "Done.", nil
}

// disapprovePercent returns the wallets current disapprove percentage.
func (s *Server) disapprovePercent(ctx context.Context, _ any) (any, error) {
w, ok := s.walletLoader.LoadedWallet()
Expand Down
3 changes: 2 additions & 1 deletion internal/rpc/jsonrpc/rpcserverhelp.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func helpDescsEnUS() map[string]string {
"createnewaccount": "createnewaccount \"account\"\n\nCreates a new account.\nThe wallet must be unlocked for this request to succeed.\n\nArguments:\n1. account (string, required) Name of the new account\n\nResult:\nNothing\n",
"createrawtransaction": "createrawtransaction [{\"amount\":n.nnn,\"txid\":\"value\",\"vout\":n,\"tree\":n},...] {\"address\":amount,...} (locktime expiry)\n\nReturns a new transaction spending the provided inputs and sending to the provided addresses.\nThe transaction inputs are not signed in the created transaction.\nThe signrawtransaction RPC command provided by wallet must be used to sign the resulting transaction.\n\nArguments:\n1. inputs (array of object, required) The inputs to the transaction\n[{\n \"amount\": n.nnn, (numeric) The previous output amount\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},...]\n2. amounts (object, required) JSON object with the destination addresses as keys and amounts as values\n{\n \"address\": n.nnn, (object) The destination address as the key and the amount in DCR as the value\n ...\n}\n3. locktime (numeric, optional) Locktime value; a non-zero value will also locktime-activate the inputs\n4. expiry (numeric, optional) Expiry value; a non-zero value when the transaction expiry\n\nResult:\n\"value\" (string) Hex-encoded bytes of the serialized transaction\n",
"createsignature": "createsignature \"address\" inputindex hashtype \"previouspkscript\" \"serializedtransaction\"\n\nGenerate a signature for a transaction input script.\n\nArguments:\n1. address (string, required) The address of the private key to use to create the signature.\n2. inputindex (numeric, required) The index of the transaction input to sign.\n3. hashtype (numeric, required) The signature hash flags to use.\n4. previouspkscript (string, required) The hex encoded previous output script or P2SH redeem script.\n5. serializedtransaction (string, required) The hex encoded transaction to add input signatures to.\n\nResult:\n{\n \"signature\": \"value\", (string) The hex encoded signature.\n \"publickey\": \"value\", (string) The hex encoded serialized compressed pubkey of the address.\n} \n",
"debuglevel": "debuglevel \"levelspec\"\n\nDynamically changes the debug logging level.\nThe levelspec can either a debug level or of the form:\n<subsystem>=<level>,<subsystem2>=<level2>,...\nThe valid debug levels are trace, debug, info, warn, error, and critical.\nThe valid subsystems are CMGR, DCRW, GRPC, LODR, MIXC, MIXP, PEER, RPCS, SYNC, TKBY, VSPC, and WLLT.\nFinally the keyword 'show' will return a list of the available subsystems.\n\nArguments:\n1. levelspec (string, required) The debug level(s) to use or the keyword 'show'\n\nResult:\n\"value\" (string) The string 'Done.'\n",
"disapprovepercent": "disapprovepercent\n\nReturns the wallet's current block disapprove percent per vote. i.e. 100 means that all votes disapprove the block they are called on. Only used for testing purposes.\n\nArguments:\nNone\n\nResult:\nn (numeric) The disapprove percent. When voting, this percent of votes will randomly disapprove the block they are called on.\n",
"discoverusage": "discoverusage (\"startblock\" discoveraccounts gaplimit)\n\nPerform address and/or account discovery\n\nArguments:\n1. startblock (string, optional) Hash of block to begin discovery from, or null to scan from the genesis block\n2. discoveraccounts (boolean, optional) Perform account discovery in addition to address discovery. Requires unlocked wallet.\n3. gaplimit (numeric, optional) Allowed unused address gap.\n\nResult:\nNothing\n",
"dumpprivkey": "dumpprivkey \"address\"\n\nReturns the private key in WIF encoding that controls some wallet address.\n\nArguments:\n1. address (string, required) The address to return a private key for\n\nResult:\n\"value\" (string) The WIF-encoded private key\n",
Expand Down Expand Up @@ -113,4 +114,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 numtickets=1 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\""
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\"\ndebuglevel \"levelspec\"\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 numtickets=1 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\""
13 changes: 13 additions & 0 deletions internal/rpchelp/helpdescs_en_US.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ var helpDescsEnUS = map[string]string{
"createsignature-hashtype": "The signature hash flags to use.",
"createsignature-previouspkscript": "The hex encoded previous output script or P2SH redeem script.",

// DebugLevelCmd help.
"debuglevel--synopsis": "Dynamically changes the debug logging level.\n" +
"The levelspec can either a debug level or of the form:\n" +
"<subsystem>=<level>,<subsystem2>=<level2>,...\n" +
"The valid debug levels are trace, debug, info, warn, error, and critical.\n" +
"The valid subsystems are CMGR, DCRW, GRPC, LODR, MIXC, MIXP, PEER, RPCS, SYNC, TKBY, VSPC, and WLLT.\n" +
"Finally the keyword 'show' will return a list of the available subsystems.",
"debuglevel-levelspec": "The debug level(s) to use or the keyword 'show'",
"debuglevel--condition0": "levelspec!=show",
"debuglevel--condition1": "levelspec=show",
"debuglevel--result0": "The string 'Done.'",
"debuglevel--result1": "The list of subsystems",

// DisapprovePercentCmd help.
"disapprovepercent--synopsis": "Returns the wallet's current block disapprove percent per vote. i.e. 100 means that all votes disapprove the block they are called on. Only used for testing purposes.",
"disapprovepercent--result0": "The disapprove percent. When voting, this percent of votes will randomly disapprove the block they are called on.",
Expand Down
1 change: 1 addition & 0 deletions internal/rpchelp/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var Methods = []struct {
{"createnewaccount", nil},
{"createrawtransaction", returnsString},
{"createsignature", []any{(*types.CreateSignatureResult)(nil)}},
{"debuglevel", returnsString},
{"disapprovepercent", []any{(*uint32)(nil)}},
{"discoverusage", nil},
{"dumpprivkey", returnsString},
Expand Down
2 changes: 2 additions & 0 deletions rpc/jsonrpc/types/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,7 @@ func init() {
// dcrd methods also implemented by dcrwallet
register = []registeredMethod{
{"createrawtransaction", (*CreateRawTransactionCmd)(nil)},
{"debuglevel", (*DebugLevelCmd)(nil)},
{"getbestblock", (*GetBestBlockCmd)(nil)},
{"getbestblockhash", (*GetBestBlockHashCmd)(nil)},
{"getblockcount", (*GetBlockCountCmd)(nil)},
Expand Down Expand Up @@ -1341,6 +1342,7 @@ func init() {
type (
AuthenticateCmd dcrdtypes.AuthenticateCmd
CreateRawTransactionCmd dcrdtypes.CreateRawTransactionCmd
DebugLevelCmd dcrdtypes.DebugLevelCmd
GetBestBlockCmd dcrdtypes.GetBestBlockCmd
GetBestBlockHashCmd dcrdtypes.GetBestBlockHashCmd
GetBlockCountCmd dcrdtypes.GetBlockCountCmd
Expand Down
17 changes: 17 additions & 0 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -229,6 +230,21 @@ func generateClientKeyPair(caPriv any, ca *x509.Certificate) (cert, key []byte,
return cert, key, nil
}

type rpcLoggers struct{}

func (rpcLoggers) Subsystems() []string {
s := make([]string, 0, len(subsystemLoggers))
for name := range subsystemLoggers {
s = append(s, name)
}
sort.Strings(s)
return s
}

func (rpcLoggers) SetLevels(levelSpec string) error {
return parseAndSetDebugLevels(levelSpec)
}

func startRPCServers(walletLoader *loader.Loader) (*grpc.Server, *jsonrpc.Server, error) {
var jsonrpcAddrNotifier jsonrpcListenerEventServer
var grpcAddrNotifier grpcListenerEventServer
Expand Down Expand Up @@ -366,6 +382,7 @@ func startRPCServers(walletLoader *loader.Loader) (*grpc.Server, *jsonrpc.Server
VSPMaxFee: cfg.VSPOpts.MaxFee.Amount,
TicketSplitAccount: cfg.TicketSplitAccount,
Dial: cfg.dial,
Loggers: rpcLoggers{},
}
jsonrpcServer = jsonrpc.NewServer(&opts, activeNet.Params, walletLoader, listeners)
for _, lis := range listeners {
Expand Down

0 comments on commit 8886be1

Please sign in to comment.