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