PRCYCoin  2.0.0.7rc1
P2P Digital Currency
prcycoin-cli.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2015 The Bitcoin developers
3 // Copyright (c) 2009-2015 The Dash developers
4 // Copyright (c) 2015-2018 The PIVX developers
5 // Copyright (c) 2018-2020 The DAPS Project developers
6 // Distributed under the MIT/X11 software license, see the accompanying
7 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
8 
9 #include "chainparamsbase.h"
10 #include "clientversion.h"
11 #include "fs.h"
12 #include "rpc/client.h"
13 #include "rpc/protocol.h"
14 #include "util.h"
15 #include "utilstrencodings.h"
16 
17 #include <univalue.h>
18 #include <stdio.h>
19 
20 #include <event2/event.h>
21 #include <event2/http.h>
22 #include <event2/buffer.h>
23 #include <event2/keyvalq_struct.h>
24 
25 
26 #define _(x) std::string(x) /* Keep the _() around in case gettext or such will be used later to translate non-UI */
27 
28 
29 static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
30 
31 std::string HelpMessageCli()
32 {
33  std::string strUsage;
34  strUsage += HelpMessageGroup(_("Options:"));
35  strUsage += HelpMessageOpt("-?", _("This help message"));
36  strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "prcycoin.conf"));
37  strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
38  strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
39  strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be "
40  "solved instantly. This is intended for regression testing tools and app development."));
41  strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1"));
42  strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), 59683, 59685));
43  strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
44  strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
45  strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
46  strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
47 
48  return strUsage;
49 }
50 
52 //
53 // Start
54 //
55 
56 //
57 // Exception thrown on connection error. This error is used to determine
58 // when to wait if -rpcwait is given.
59 //
60 class CConnectionFailed : public std::runtime_error
61 {
62 public:
63  explicit inline CConnectionFailed(const std::string& msg) : std::runtime_error(msg)
64  {
65  }
66 };
67 
68 static bool AppInitRPC(int argc, char* argv[])
69 {
70  //
71  // Parameters
72  //
73  ParseParameters(argc, argv);
74  if (argc < 2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) {
75  std::string strUsage = _("PRCY RPC client version") + " " + FormatFullVersion() + "\n";
76  if (!mapArgs.count("-version")) {
77  strUsage += "\n" + _("Usage:") + "\n" +
78  " prcycoin-cli [options] <command> [params] " + _("Send command to PRCY") + "\n" +
79  " prcycoin-cli [options] help " + _("List commands") + "\n" +
80  " prcycoin-cli [options] help <command> " + _("Get help for a command") + "\n";
81 
82  strUsage += "\n" + HelpMessageCli();
83  }
84 
85  fprintf(stdout, "%s", strUsage.c_str());
86  return false;
87  }
88  if (!fs::is_directory(GetDataDir(false))) {
89  fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
90  return false;
91  }
92  try {
94  } catch (const std::exception& e) {
95  fprintf(stderr, "Error reading configuration file: %s\n", e.what());
96  return false;
97  }
98  // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
100  fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
101  return false;
102  }
103  if (GetBoolArg("-rpcssl", false))
104  {
105  fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
106  return false;
107  }
108  return true;
109 }
110 
112 struct HTTPReply
113 {
114  int status;
115  std::string body;
116 };
117 
118 static void http_request_done(struct evhttp_request *req, void *ctx)
119 {
120  HTTPReply *reply = static_cast<HTTPReply*>(ctx);
121 
122  if (req == NULL) {
123  /* If req is NULL, it means an error occurred while connecting, but
124  * I'm not sure how to find out which one. We also don't really care.
125  */
126  reply->status = 0;
127  return;
128  }
129 
130  reply->status = evhttp_request_get_response_code(req);
131 
132  struct evbuffer *buf = evhttp_request_get_input_buffer(req);
133  if (buf)
134  {
135  size_t size = evbuffer_get_length(buf);
136  const char *data = (const char*)evbuffer_pullup(buf, size);
137  if (data)
138  reply->body = std::string(data, size);
139  evbuffer_drain(buf, size);
140  }
141 }
142 
143 UniValue CallRPC(const std::string& strMethod, const UniValue& params)
144 {
145  std::string host = GetArg("-rpcconnect", "127.0.0.1");
146  int port = GetArg("-rpcport", BaseParams().RPCPort());
147 
148  // Create event base
149  struct event_base *base = event_base_new(); // TODO RAII
150  if (!base)
151  throw std::runtime_error("cannot create event_base");
152 
153  // Synchronously look up hostname
154  struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
155  if (evcon == NULL)
156  throw std::runtime_error("create connection failed");
157  evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
158 
159  HTTPReply response;
160  struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
161  if (req == NULL)
162  throw std::runtime_error("create http request failed");
163 
164  // Get credentials
165  std::string strRPCUserColonPass;
166  if (mapArgs["-rpcpassword"] == "") {
167  // Try fall back to cookie-based authentication if no password is provided
168  if (!GetAuthCookie(&strRPCUserColonPass)) {
169  throw std::runtime_error(strprintf(
170  _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"),
171  GetConfigFile().string().c_str()));
172 
173  }
174  } else {
175  strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
176  }
177 
178  struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
179  assert(output_headers);
180  evhttp_add_header(output_headers, "Host", host.c_str());
181  evhttp_add_header(output_headers, "Connection", "close");
182  evhttp_add_header(output_headers, "Content-Type", "application/json");
183  evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
184 
185  // Attach request data
186  std::string strRequest = JSONRPCRequest(strMethod, params, 1);
187  struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
188  assert(output_buffer);
189  evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
190 
191  int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
192  if (r != 0) {
193  evhttp_connection_free(evcon);
194  event_base_free(base);
195  throw CConnectionFailed("send http request failed");
196  }
197 
198  event_base_dispatch(base);
199  evhttp_connection_free(evcon);
200  event_base_free(base);
201 
202  if (response.status == 0)
203  throw CConnectionFailed("couldn't connect to server");
204  else if (response.status == HTTP_UNAUTHORIZED)
205  throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
206  else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
207  throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
208  else if (response.body.empty())
209  throw std::runtime_error("no response from server");
210 
211  // Parse reply
212  UniValue valReply(UniValue::VSTR);
213  if (!valReply.read(response.body))
214  throw std::runtime_error("couldn't parse reply from server");
215  const UniValue& reply = valReply.get_obj();
216  if (reply.empty())
217  throw std::runtime_error("expected reply to have result, error and id properties");
218 
219  return reply;
220 }
221 
222 int CommandLineRPC(int argc, char* argv[])
223 {
224  std::string strPrint;
225  int nRet = 0;
226  try {
227  // Skip switches
228  while (argc > 1 && IsSwitchChar(argv[1][0])) {
229  argc--;
230  argv++;
231  }
232 
233  // Method
234  if (argc < 2)
235  throw std::runtime_error("too few parameters");
236  std::string strMethod = argv[1];
237 
238  // Parameters default to strings
239  std::vector<std::string> strParams(&argv[2], &argv[argc]);
240  UniValue params = RPCConvertValues(strMethod, strParams);
241 
242  // Execute and handle connection failures with -rpcwait
243  const bool fWait = GetBoolArg("-rpcwait", false);
244  do {
245  try {
246  const UniValue reply = CallRPC(strMethod, params);
247 
248  // Parse reply
249  const UniValue& result = find_value(reply, "result");
250  const UniValue& error = find_value(reply, "error");
251 
252  if (!error.isNull()) {
253  // Error
254  int code = error["code"].get_int();
255  if (fWait && code == RPC_IN_WARMUP)
256  throw CConnectionFailed("server in warmup");
257  strPrint = "error: " + error.write();
258  nRet = abs(code);
259  } else {
260  // Result
261  if (result.isNull())
262  strPrint = "";
263  else if (result.isStr())
264  strPrint = result.get_str();
265  else
266  strPrint = result.write(2);
267  }
268  // Connection succeeded, no need to retry.
269  break;
270  } catch (const CConnectionFailed& e) {
271  if (fWait)
272  MilliSleep(1000);
273  else
274  throw;
275  }
276  } while (fWait);
277  } catch (const boost::thread_interrupted&) {
278  throw;
279  } catch (const std::exception& e) {
280  strPrint = std::string("error: ") + e.what();
281  nRet = EXIT_FAILURE;
282  } catch (...) {
283  PrintExceptionContinue(NULL, "CommandLineRPC()");
284  throw;
285  }
286 
287  if (strPrint != "") {
288  fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
289  }
290  return nRet;
291 }
292 
293 int main(int argc, char* argv[])
294 {
296  if (!SetupNetworking()) {
297  fprintf(stderr, "Error: Initializing networking failed\n");
298  exit(1);
299  }
300 
301  try {
302  if (!AppInitRPC(argc, argv))
303  return EXIT_FAILURE;
304  } catch (const std::exception& e) {
305  PrintExceptionContinue(&e, "AppInitRPC()");
306  return EXIT_FAILURE;
307  } catch (...) {
308  PrintExceptionContinue(NULL, "AppInitRPC()");
309  return EXIT_FAILURE;
310  }
311 
312  int ret = EXIT_FAILURE;
313  try {
314  ret = CommandLineRPC(argc, argv);
315  } catch (const std::exception& e) {
316  PrintExceptionContinue(&e, "CommandLineRPC()");
317  } catch (...) {
318  PrintExceptionContinue(NULL, "CommandLineRPC()");
319  }
320  return ret;
321 }
HTTPReply
Reply structure for request_done to fill in.
Definition: prcycoin-cli.cpp:112
SetupEnvironment
void SetupEnvironment()
Definition: util.cpp:600
fs.h
SetupNetworking
bool SetupNetworking()
Definition: util.cpp:636
HelpMessageGroup
std::string HelpMessageGroup(const std::string &message)
Format a string to be used as group of options in help messages.
Definition: util.cpp:282
ReadConfigFile
void ReadConfigFile(std::map< std::string, std::string > &mapSettingsRet, std::map< std::string, std::vector< std::string > > &mapMultiSettingsRet)
Definition: util.cpp:395
SelectBaseParamsFromCommandLine
bool SelectBaseParamsFromCommandLine()
Calls NetworkIdFromCommandLine() and then calls SelectParams as appropriate.
Definition: chainparamsbase.cpp:98
mapArgs
std::map< std::string, std::string > mapArgs
Definition: util.cpp:111
chainparamsbase.h
HTTP_BAD_REQUEST
@ HTTP_BAD_REQUEST
Definition: protocol.h:21
HelpMessageOpt
std::string HelpMessageOpt(const std::string &option, const std::string &message)
Format a string to be used as option description in help messages.
Definition: util.cpp:286
HTTPReply::body
std::string body
Definition: prcycoin-cli.cpp:115
CommandLineRPC
int CommandLineRPC(int argc, char *argv[])
Definition: prcycoin-cli.cpp:222
protocol.h
clientversion.h
UniValue::read
bool read(const char *raw, size_t len)
Definition: univalue_read.cpp:253
UniValue::isNull
bool isNull() const
Definition: univalue.h:77
RPCConvertValues
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert strings to command-specific RPC representation.
Definition: client.cpp:193
CConnectionFailed::CConnectionFailed
CConnectionFailed(const std::string &msg)
Definition: prcycoin-cli.cpp:63
IsSwitchChar
bool IsSwitchChar(char c)
Definition: util.h:92
PrintExceptionContinue
void PrintExceptionContinue(const std::exception *pex, const char *pszThread)
Definition: util.cpp:309
UniValue
Definition: univalue.h:19
r
void const uint64_t uint64_t * r
Definition: field_5x52_asm_impl.h:10
UniValue::get_str
const std::string & get_str() const
Definition: univalue.cpp:309
UniValue::isStr
bool isStr() const
Definition: univalue.h:81
UniValue::get_obj
const UniValue & get_obj() const
Definition: univalue.cpp:346
univalue.h
HelpMessageCli
std::string HelpMessageCli()
Definition: prcycoin-cli.cpp:31
GetBoolArg
bool GetBoolArg(const std::string &strArg, bool fDefault)
Return boolean argument or default value.
Definition: util.cpp:255
RPC_IN_WARMUP
@ RPC_IN_WARMUP
Transaction already in chain.
Definition: protocol.h:51
HTTP_UNAUTHORIZED
@ HTTP_UNAUTHORIZED
Definition: protocol.h:22
EncodeBase64
std::string EncodeBase64(const unsigned char *pch, size_t len)
Definition: utilstrencodings.cpp:102
JSONRPCRequest
std::string JSONRPCRequest(const std::string &strMethod, const UniValue &params, const UniValue &id)
JSON-RPC protocol.
Definition: protocol.cpp:33
GetConfigFile
fs::path GetConfigFile()
Definition: util.cpp:383
strprintf
#define strprintf
Definition: tinyformat.h:1056
BaseParams
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
Definition: chainparamsbase.cpp:60
std
Definition: adjacency_graphs.hpp:25
ParseParameters
void ParseParameters(int argc, const char *const argv[])
Definition: util.cpp:208
HTTP_NOT_FOUND
@ HTTP_NOT_FOUND
Definition: protocol.h:24
GetDataDir
const fs::path & GetDataDir(bool fNetSpecific)
Definition: util.cpp:349
UniValue::empty
bool empty() const
Definition: univalue.h:67
MilliSleep
void MilliSleep(int64_t n)
Definition: utiltime.cpp:45
_
#define _(x)
Definition: prcycoin-cli.cpp:26
GetAuthCookie
bool GetAuthCookie(std::string *cookie_out)
Read the RPC authentication cookie from disk.
Definition: protocol.cpp:107
utilstrencodings.h
find_value
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:279
FormatFullVersion
std::string FormatFullVersion()
Definition: clientversion.cpp:80
CallRPC
UniValue CallRPC(const std::string &strMethod, const UniValue &params)
Definition: prcycoin-cli.cpp:143
mapMultiArgs
std::map< std::string, std::vector< std::string > > mapMultiArgs
Definition: util.cpp:112
GetArg
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Definition: util.cpp:241
UniValue::VSTR
@ VSTR
Definition: univalue.h:21
main
int main(int argc, char *argv[])
Definition: prcycoin-cli.cpp:293
util.h
CConnectionFailed
Definition: prcycoin-cli.cpp:60
client.h
UniValue::write
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
Definition: univalue_write.cpp:31
error
bool error(const char *fmt, const Args &... args)
Definition: util.h:61
HTTPReply::status
int status
Definition: prcycoin-cli.cpp:114
HTTP_INTERNAL_SERVER_ERROR
@ HTTP_INTERNAL_SERVER_ERROR
Definition: protocol.h:26