]>
Commit | Line | Data |
---|---|---|
2a03a390 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2013 The Bitcoin Core developers |
78253fcb | 3 | // Distributed under the MIT software license, see the accompanying |
2a03a390 WL |
4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | ||
611116d4 | 6 | #include "chainparamsbase.h" |
71697f97 | 7 | #include "clientversion.h" |
4519a766 DC |
8 | #include "rpc/client.h" |
9 | #include "rpc/protocol.h" | |
611116d4 | 10 | #include "util.h" |
ad49c256 | 11 | #include "utilstrencodings.h" |
2a03a390 | 12 | |
51ed9ec9 | 13 | #include <boost/filesystem/operations.hpp> |
afd64f76 WL |
14 | #include <stdio.h> |
15 | ||
afd64f76 WL |
16 | #include <event2/buffer.h> |
17 | #include <event2/keyvalq_struct.h> | |
68377e18 | 18 | #include "support/events.h" |
51ed9ec9 | 19 | |
a10a6e2a | 20 | #include <univalue.h> |
51ed9ec9 | 21 | |
09eb201b | 22 | using namespace std; |
6e9f40ca | 23 | |
89bccddc | 24 | static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900; |
fadbbe1e | 25 | static const int CONTINUE_EXECUTION=-1; |
89bccddc | 26 | |
b750cf1f WL |
27 | std::string HelpMessageCli() |
28 | { | |
3b26f5c3 | 29 | std::string strUsage; |
1fdb9fa3 LV |
30 | strUsage += HelpMessageGroup(_("Options:")); |
31 | strUsage += HelpMessageOpt("-?", _("This help message")); | |
5166804f | 32 | strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "komodo.conf")); |
1fdb9fa3 LV |
33 | strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); |
34 | strUsage += HelpMessageOpt("-testnet", _("Use the test network")); | |
35 | strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be " | |
36 | "solved instantly. This is intended for regression testing tools and app development.")); | |
37 | strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1")); | |
3985a40d | 38 | strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), 8232, 18232)); |
1fdb9fa3 LV |
39 | strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start")); |
40 | strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); | |
41 | strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections")); | |
629a8752 | 42 | strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); |
21e33231 | 43 | strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases)")); |
b750cf1f WL |
44 | |
45 | return strUsage; | |
46 | } | |
47 | ||
2a03a390 WL |
48 | ////////////////////////////////////////////////////////////////////////////// |
49 | // | |
50 | // Start | |
51 | // | |
af82884a DK |
52 | |
53 | // | |
54 | // Exception thrown on connection error. This error is used to determine | |
55 | // when to wait if -rpcwait is given. | |
56 | // | |
57 | class CConnectionFailed : public std::runtime_error | |
58 | { | |
59 | public: | |
60 | ||
61 | explicit inline CConnectionFailed(const std::string& msg) : | |
62 | std::runtime_error(msg) | |
63 | {} | |
64 | ||
65 | }; | |
66 | ||
95c46396 | 67 | #define FROM_CLI |
d73f0799 | 68 | #include "uint256.h" |
ba381f65 | 69 | #include "arith_uint256.h" |
39eb0ec7 | 70 | |
3face669 | 71 | #include "komodo_structs.h" |
3824fe8c | 72 | |
98f2e433 | 73 | #include "komodo_globals.h" |
6dede29e | 74 | #include "komodo_utils.h" |
d430a5e8 | 75 | #include "komodo_cJSON.c" |
0e2400aa | 76 | #include "komodo_notary.h" |
9a2f3a40 | 77 | |
f8f61a6d | 78 | int32_t uni_get_int(UniValue uv, int32_t def) |
79 | { | |
80 | try | |
81 | { | |
82 | return uv.get_int(); | |
83 | } | |
84 | catch(const std::exception& e) | |
85 | { | |
86 | return def; | |
87 | } | |
88 | } | |
89 | ||
90 | int64_t uni_get_int64(UniValue uv, int64_t def) | |
91 | { | |
92 | try | |
93 | { | |
94 | return uv.get_int64(); | |
95 | } | |
96 | catch(const std::exception& e) | |
97 | { | |
98 | return def; | |
99 | } | |
100 | } | |
101 | ||
102 | std::string uni_get_str(UniValue uv, std::string def) | |
103 | { | |
104 | try | |
105 | { | |
106 | return uv.get_str(); | |
107 | } | |
108 | catch(const std::exception& e) | |
109 | { | |
110 | return def; | |
111 | } | |
112 | } | |
113 | ||
114 | std::vector<UniValue> uni_getValues(UniValue uv, std::vector<UniValue> def) | |
115 | { | |
116 | try | |
117 | { | |
118 | return uv.getValues(); | |
119 | } | |
120 | catch(const std::exception& e) | |
121 | { | |
122 | return def; | |
123 | } | |
124 | } | |
125 | ||
c88fa588 | 126 | void komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals,int32_t KMDheight,uint32_t KMDtimestamp,uint64_t opretvalue,uint8_t *opretbuf,uint16_t opretlen,uint16_t vout,uint256 MoM,int32_t MoMdepth) |
725800f9 | 127 | { |
128 | ||
129 | } | |
130 | ||
6e1d2e7d | 131 | uint32_t komodo_heightstamp(int32_t height) |
132 | { | |
133 | return(0); | |
134 | } | |
135 | ||
fadbbe1e U |
136 | // |
137 | // This function returns either one of EXIT_ codes when it's expected to stop the process or | |
138 | // CONTINUE_EXECUTION when it's expected to continue further. | |
139 | // | |
140 | static int AppInitRPC(int argc, char* argv[]) | |
2a03a390 | 141 | { |
abf5dd31 JG |
142 | static_assert(CONTINUE_EXECUTION != EXIT_FAILURE, |
143 | "CONTINUE_EXECUTION should be different from EXIT_FAILURE"); | |
144 | static_assert(CONTINUE_EXECUTION != EXIT_SUCCESS, | |
145 | "CONTINUE_EXECUTION should be different from EXIT_SUCCESS"); | |
2a03a390 WL |
146 | // |
147 | // Parameters | |
148 | // | |
149 | ParseParameters(argc, argv); | |
59d405e8 | 150 | komodo_args(argv[0]); |
af6edac0 | 151 | if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) { |
945f015d | 152 | std::string strUsage = _("Komodo RPC client version") + " " + FormatFullVersion() + "\n" + PrivacyInfo(); |
3d0a1ce1 PJ |
153 | if (!mapArgs.count("-version")) { |
154 | strUsage += "\n" + _("Usage:") + "\n" + | |
5166804f | 155 | " komodo-cli [options] <command> [params] " + _("Send command to Komodo") + "\n" + |
156 | " komodo-cli [options] help " + _("List commands") + "\n" + | |
157 | " komodo-cli [options] help <command> " + _("Get help for a command") + "\n"; | |
3d0a1ce1 PJ |
158 | |
159 | strUsage += "\n" + HelpMessageCli(); | |
af021144 S |
160 | } else { |
161 | strUsage += LicenseInfo(); | |
3d0a1ce1 PJ |
162 | } |
163 | ||
164 | fprintf(stdout, "%s", strUsage.c_str()); | |
fadbbe1e U |
165 | if (argc < 2) { |
166 | fprintf(stderr, "Error: too few parameters\n"); | |
167 | return EXIT_FAILURE; | |
168 | } | |
169 | return EXIT_SUCCESS; | |
3d0a1ce1 | 170 | } |
3ce7e669 | 171 | if (!boost::filesystem::is_directory(GetDataDir(false))) { |
2a03a390 | 172 | fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str()); |
fadbbe1e | 173 | return EXIT_FAILURE; |
2a03a390 | 174 | } |
4ae5e721 WL |
175 | try { |
176 | ReadConfigFile(mapArgs, mapMultiArgs); | |
27df4123 | 177 | } catch (const std::exception& e) { |
4ae5e721 | 178 | fprintf(stderr,"Error reading configuration file: %s\n", e.what()); |
fadbbe1e | 179 | return EXIT_FAILURE; |
4ae5e721 | 180 | } |
84ce18ca WL |
181 | // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause) |
182 | if (!SelectBaseParamsFromCommandLine()) { | |
9d2b73d1 | 183 | fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n"); |
fadbbe1e | 184 | return EXIT_FAILURE; |
9d2b73d1 | 185 | } |
afd64f76 WL |
186 | if (GetBoolArg("-rpcssl", false)) |
187 | { | |
188 | fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n"); | |
fadbbe1e | 189 | return EXIT_FAILURE; |
afd64f76 | 190 | } |
fadbbe1e | 191 | return CONTINUE_EXECUTION; |
2a03a390 WL |
192 | } |
193 | ||
afd64f76 WL |
194 | |
195 | /** Reply structure for request_done to fill in */ | |
196 | struct HTTPReply | |
b750cf1f | 197 | { |
6415573a WL |
198 | HTTPReply(): status(0), error(-1) {} |
199 | ||
afd64f76 | 200 | int status; |
6415573a | 201 | int error; |
afd64f76 WL |
202 | std::string body; |
203 | }; | |
204 | ||
6415573a WL |
205 | const char *http_errorstring(int code) |
206 | { | |
207 | switch(code) { | |
208 | #if LIBEVENT_VERSION_NUMBER >= 0x02010300 | |
209 | case EVREQ_HTTP_TIMEOUT: | |
210 | return "timeout reached"; | |
211 | case EVREQ_HTTP_EOF: | |
212 | return "EOF reached"; | |
213 | case EVREQ_HTTP_INVALID_HEADER: | |
214 | return "error while reading header, or invalid header"; | |
215 | case EVREQ_HTTP_BUFFER_ERROR: | |
216 | return "error encountered while reading or writing"; | |
217 | case EVREQ_HTTP_REQUEST_CANCEL: | |
218 | return "request was canceled"; | |
219 | case EVREQ_HTTP_DATA_TOO_LONG: | |
220 | return "response body is larger than allowed"; | |
221 | #endif | |
222 | default: | |
223 | return "unknown"; | |
224 | } | |
225 | } | |
226 | ||
afd64f76 WL |
227 | static void http_request_done(struct evhttp_request *req, void *ctx) |
228 | { | |
229 | HTTPReply *reply = static_cast<HTTPReply*>(ctx); | |
230 | ||
231 | if (req == NULL) { | |
6415573a WL |
232 | /* If req is NULL, it means an error occurred while connecting: the |
233 | * error code will have been passed to http_error_cb. | |
afd64f76 WL |
234 | */ |
235 | reply->status = 0; | |
236 | return; | |
237 | } | |
b750cf1f | 238 | |
afd64f76 WL |
239 | reply->status = evhttp_request_get_response_code(req); |
240 | ||
241 | struct evbuffer *buf = evhttp_request_get_input_buffer(req); | |
242 | if (buf) | |
243 | { | |
244 | size_t size = evbuffer_get_length(buf); | |
245 | const char *data = (const char*)evbuffer_pullup(buf, size); | |
246 | if (data) | |
247 | reply->body = std::string(data, size); | |
248 | evbuffer_drain(buf, size); | |
249 | } | |
250 | } | |
251 | ||
6415573a WL |
252 | #if LIBEVENT_VERSION_NUMBER >= 0x02010300 |
253 | static void http_error_cb(enum evhttp_request_error err, void *ctx) | |
b750cf1f | 254 | { |
6415573a WL |
255 | HTTPReply *reply = static_cast<HTTPReply*>(ctx); |
256 | reply->error = err; | |
257 | } | |
258 | #endif | |
259 | ||
3b26f5c3 | 260 | UniValue CallRPC(const std::string& strMethod, const UniValue& params) |
afd64f76 WL |
261 | { |
262 | std::string host = GetArg("-rpcconnect", "127.0.0.1"); | |
263 | int port = GetArg("-rpcport", BaseParams().RPCPort()); | |
05c2ba63 | 264 | BITCOIND_RPCPORT = port; |
68377e18 KJA |
265 | // Obtain event base |
266 | raii_event_base base = obtain_event_base(); | |
afd64f76 WL |
267 | |
268 | // Synchronously look up hostname | |
68377e18 KJA |
269 | raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port); |
270 | evhttp_connection_set_timeout(evcon.get(), GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT)); | |
afd64f76 WL |
271 | |
272 | HTTPReply response; | |
68377e18 | 273 | raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response); |
afd64f76 | 274 | if (req == NULL) |
3b26f5c3 | 275 | throw std::runtime_error("create http request failed"); |
6415573a | 276 | #if LIBEVENT_VERSION_NUMBER >= 0x02010300 |
68377e18 | 277 | evhttp_request_set_error_cb(req.get(), http_error_cb); |
6415573a | 278 | #endif |
afd64f76 WL |
279 | |
280 | // Get credentials | |
e957192c WL |
281 | std::string strRPCUserColonPass; |
282 | if (mapArgs["-rpcpassword"] == "") { | |
283 | // Try fall back to cookie-based authentication if no password is provided | |
284 | if (!GetAuthCookie(&strRPCUserColonPass)) { | |
3b26f5c3 | 285 | throw std::runtime_error(strprintf( |
206e2b97 JG |
286 | _("Could not locate RPC credentials. No authentication cookie could be found,\n" |
287 | "and no rpcpassword is set in the configuration file (%s)."), | |
e957192c WL |
288 | GetConfigFile().string().c_str())); |
289 | ||
290 | } | |
291 | } else { | |
292 | strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; | |
293 | } | |
294 | ||
68377e18 | 295 | struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get()); |
afd64f76 WL |
296 | assert(output_headers); |
297 | evhttp_add_header(output_headers, "Host", host.c_str()); | |
298 | evhttp_add_header(output_headers, "Connection", "close"); | |
299 | evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); | |
300 | ||
301 | // Attach request data | |
302 | std::string strRequest = JSONRPCRequest(strMethod, params, 1); | |
68377e18 | 303 | struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get()); |
afd64f76 WL |
304 | assert(output_buffer); |
305 | evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); | |
306 | ||
68377e18 KJA |
307 | int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/"); |
308 | req.release(); // ownership moved to evcon in above call | |
afd64f76 | 309 | if (r != 0) { |
afd64f76 WL |
310 | throw CConnectionFailed("send http request failed"); |
311 | } | |
b750cf1f | 312 | |
68377e18 | 313 | event_base_dispatch(base.get()); |
b750cf1f | 314 | |
afd64f76 | 315 | if (response.status == 0) |
7c2ab059 | 316 | throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error)); |
afd64f76 | 317 | else if (response.status == HTTP_UNAUTHORIZED) |
3b26f5c3 | 318 | throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); |
afd64f76 | 319 | else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR) |
3b26f5c3 | 320 | throw std::runtime_error(strprintf("server returned HTTP error %d", response.status)); |
afd64f76 | 321 | else if (response.body.empty()) |
3b26f5c3 | 322 | throw std::runtime_error("no response from server"); |
b750cf1f WL |
323 | |
324 | // Parse reply | |
851f58f9 | 325 | UniValue valReply(UniValue::VSTR); |
afd64f76 | 326 | if (!valReply.read(response.body)) |
3b26f5c3 | 327 | throw std::runtime_error("couldn't parse reply from server"); |
d014114d | 328 | const UniValue& reply = valReply.get_obj(); |
b750cf1f | 329 | if (reply.empty()) |
3b26f5c3 | 330 | throw std::runtime_error("expected reply to have result, error and id properties"); |
b750cf1f WL |
331 | |
332 | return reply; | |
333 | } | |
334 | ||
335 | int CommandLineRPC(int argc, char *argv[]) | |
336 | { | |
3b26f5c3 | 337 | std::string strPrint; |
b750cf1f | 338 | int nRet = 0; |
3ce7e669 | 339 | try { |
b750cf1f | 340 | // Skip switches |
3ce7e669 | 341 | while (argc > 1 && IsSwitchChar(argv[1][0])) { |
b750cf1f WL |
342 | argc--; |
343 | argv++; | |
344 | } | |
21e33231 WL |
345 | std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]); |
346 | if (GetBoolArg("-stdin", false)) { | |
347 | // Read one arg per line from stdin and append | |
348 | std::string line; | |
349 | while (std::getline(std::cin,line)) | |
350 | args.push_back(line); | |
351 | } | |
352 | if (args.size() < 1) | |
3b26f5c3 | 353 | throw std::runtime_error("too few parameters (need at least command)"); |
21e33231 WL |
354 | std::string strMethod = args[0]; |
355 | UniValue params = RPCConvertValues(strMethod, std::vector<std::string>(args.begin()+1, args.end())); | |
b750cf1f | 356 | |
af82884a DK |
357 | // Execute and handle connection failures with -rpcwait |
358 | const bool fWait = GetBoolArg("-rpcwait", false); | |
359 | do { | |
360 | try { | |
851f58f9 | 361 | const UniValue reply = CallRPC(strMethod, params); |
af82884a DK |
362 | |
363 | // Parse reply | |
d014114d JS |
364 | const UniValue& result = find_value(reply, "result"); |
365 | const UniValue& error = find_value(reply, "error"); | |
af82884a | 366 | |
ed21d5bd | 367 | if (!error.isNull()) { |
af82884a | 368 | // Error |
ed21d5bd | 369 | int code = error["code"].get_int(); |
af82884a DK |
370 | if (fWait && code == RPC_IN_WARMUP) |
371 | throw CConnectionFailed("server in warmup"); | |
d014114d | 372 | strPrint = "error: " + error.write(); |
af82884a | 373 | nRet = abs(code); |
f061578b JS |
374 | if (error.isObject()) |
375 | { | |
376 | UniValue errCode = find_value(error, "code"); | |
377 | UniValue errMsg = find_value(error, "message"); | |
378 | strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n"; | |
379 | ||
380 | if (errMsg.isStr()) | |
381 | strPrint += "error message:\n"+errMsg.get_str(); | |
382 | } | |
af82884a DK |
383 | } else { |
384 | // Result | |
ed21d5bd | 385 | if (result.isNull()) |
af82884a | 386 | strPrint = ""; |
ed21d5bd | 387 | else if (result.isStr()) |
af82884a DK |
388 | strPrint = result.get_str(); |
389 | else | |
ed21d5bd | 390 | strPrint = result.write(2); |
af82884a | 391 | } |
af82884a DK |
392 | // Connection succeeded, no need to retry. |
393 | break; | |
394 | } | |
27df4123 | 395 | catch (const CConnectionFailed&) { |
af82884a DK |
396 | if (fWait) |
397 | MilliSleep(1000); | |
398 | else | |
399 | throw; | |
400 | } | |
401 | } while (fWait); | |
b750cf1f | 402 | } |
27df4123 | 403 | catch (const boost::thread_interrupted&) { |
b750cf1f WL |
404 | throw; |
405 | } | |
27df4123 | 406 | catch (const std::exception& e) { |
3b26f5c3 | 407 | strPrint = std::string("error: ") + e.what(); |
b750cf1f WL |
408 | nRet = EXIT_FAILURE; |
409 | } | |
410 | catch (...) { | |
411 | PrintExceptionContinue(NULL, "CommandLineRPC()"); | |
412 | throw; | |
413 | } | |
414 | ||
3ce7e669 | 415 | if (strPrint != "") { |
b750cf1f WL |
416 | fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); |
417 | } | |
418 | return nRet; | |
419 | } | |
420 | ||
2a03a390 WL |
421 | int main(int argc, char* argv[]) |
422 | { | |
5248ff40 | 423 | SetupEnvironment(); |
167b6231 WL |
424 | if (!SetupNetworking()) { |
425 | fprintf(stderr, "Error: Initializing networking failed\n"); | |
e8f5b027 | 426 | return EXIT_FAILURE; |
167b6231 | 427 | } |
5248ff40 | 428 | |
3ce7e669 | 429 | try { |
fadbbe1e U |
430 | int ret = AppInitRPC(argc, argv); |
431 | if (ret != CONTINUE_EXECUTION) | |
432 | return ret; | |
2a03a390 | 433 | } |
27df4123 | 434 | catch (const std::exception& e) { |
2a03a390 | 435 | PrintExceptionContinue(&e, "AppInitRPC()"); |
0cafb630 | 436 | return EXIT_FAILURE; |
2a03a390 WL |
437 | } catch (...) { |
438 | PrintExceptionContinue(NULL, "AppInitRPC()"); | |
0cafb630 | 439 | return EXIT_FAILURE; |
2a03a390 WL |
440 | } |
441 | ||
0cafb630 | 442 | int ret = EXIT_FAILURE; |
3ce7e669 | 443 | try { |
a7199038 | 444 | ret = CommandLineRPC(argc, argv); |
2a03a390 | 445 | } |
27df4123 | 446 | catch (const std::exception& e) { |
2a03a390 WL |
447 | PrintExceptionContinue(&e, "CommandLineRPC()"); |
448 | } catch (...) { | |
449 | PrintExceptionContinue(NULL, "CommandLineRPC()"); | |
450 | } | |
a7199038 | 451 | return ret; |
2a03a390 | 452 | } |