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