]> Git Repo - VerusCoin.git/blame - src/bitcoin-cli.cpp
Merge pull request #97 from miketout/dev
[VerusCoin.git] / src / bitcoin-cli.cpp
CommitLineData
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 23using namespace std;
6e9f40ca 24
89bccddc 25static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
fadbbe1e 26static const int CONTINUE_EXECUTION=-1;
89bccddc 27
b750cf1f
WL
28std::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//
58class CConnectionFailed : public std::runtime_error
59{
60public:
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 79void 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 83uint32_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//
92static 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 */
147struct 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
156const 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
178static 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
204static 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 211UniValue 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
286int 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
372int 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}
This page took 0.337617 seconds and 5 git commands to generate.