rippled
RPCCall.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/main/Application.h>
21 #include <ripple/basics/ByteUtilities.h>
22 #include <ripple/basics/Log.h>
23 #include <ripple/basics/StringUtilities.h>
24 #include <ripple/basics/base64.h>
25 #include <ripple/basics/contract.h>
26 #include <ripple/beast/core/LexicalCast.h>
27 #include <ripple/core/Config.h>
28 #include <ripple/json/Object.h>
29 #include <ripple/json/json_reader.h>
30 #include <ripple/json/to_string.h>
31 #include <ripple/net/HTTPClient.h>
32 #include <ripple/net/RPCCall.h>
33 #include <ripple/net/RPCErr.h>
34 #include <ripple/protocol/ErrorCodes.h>
35 #include <ripple/protocol/Feature.h>
36 #include <ripple/protocol/SystemParameters.h>
37 #include <ripple/protocol/UintTypes.h>
38 #include <ripple/protocol/jss.h>
39 #include <ripple/rpc/ServerHandler.h>
40 #include <ripple/rpc/impl/RPCHelpers.h>
41 
42 #include <boost/algorithm/string/predicate.hpp>
43 #include <boost/asio/streambuf.hpp>
44 #include <boost/beast/core/string.hpp>
45 #include <boost/optional.hpp>
46 #include <boost/regex.hpp>
47 
48 #include <array>
49 #include <iostream>
50 #include <type_traits>
51 #include <unordered_map>
52 
53 namespace ripple {
54 
55 class RPCParser;
56 
57 //
58 // HTTP protocol
59 //
60 // This ain't Apache. We're just using HTTP header for the length field
61 // and to be compatible with other JSON-RPC implementations.
62 //
63 
66  std::string const& strHost,
67  std::string const& strPath,
68  std::string const& strMsg,
69  std::unordered_map<std::string, std::string> const& mapRequestHeaders)
70 {
72 
73  // CHECKME this uses a different version than the replies below use. Is
74  // this by design or an accident or should it be using
75  // BuildInfo::getFullVersionString () as well?
76 
77  s << "POST " << (strPath.empty() ? "/" : strPath) << " HTTP/1.0\r\n"
78  << "User-Agent: " << systemName() << "-json-rpc/v1\r\n"
79  << "Host: " << strHost << "\r\n"
80  << "Content-Type: application/json\r\n"
81  << "Content-Length: " << strMsg.size() << "\r\n"
82  << "Accept: application/json\r\n";
83 
84  for (auto const& [k, v] : mapRequestHeaders)
85  s << k << ": " << v << "\r\n";
86 
87  s << "\r\n" << strMsg;
88 
89  return s.str();
90 }
91 
92 class RPCParser
93 {
94 private:
96 
97  // TODO New routine for parsing ledger parameters, other routines should
98  // standardize on this.
99  static bool
100  jvParseLedger(Json::Value& jvRequest, std::string const& strLedger)
101  {
102  if (strLedger == "current" || strLedger == "closed" ||
103  strLedger == "validated")
104  {
105  jvRequest[jss::ledger_index] = strLedger;
106  }
107  else if (strLedger.length() == 64)
108  {
109  // YYY Could confirm this is a uint256.
110  jvRequest[jss::ledger_hash] = strLedger;
111  }
112  else
113  {
114  jvRequest[jss::ledger_index] =
115  beast::lexicalCast<std::uint32_t>(strLedger);
116  }
117 
118  return true;
119  }
120 
121  // Build a object { "currency" : "XYZ", "issuer" : "rXYX" }
122  static Json::Value
123  jvParseCurrencyIssuer(std::string const& strCurrencyIssuer)
124  {
125  static boost::regex reCurIss("\\`([[:alpha:]]{3})(?:/(.+))?\\'");
126 
127  boost::smatch smMatch;
128 
129  if (boost::regex_match(strCurrencyIssuer, smMatch, reCurIss))
130  {
131  Json::Value jvResult(Json::objectValue);
132  std::string strCurrency = smMatch[1];
133  std::string strIssuer = smMatch[2];
134 
135  jvResult[jss::currency] = strCurrency;
136 
137  if (strIssuer.length())
138  {
139  // Could confirm issuer is a valid Ripple address.
140  jvResult[jss::issuer] = strIssuer;
141  }
142 
143  return jvResult;
144  }
145  else
146  {
147  return RPC::make_param_error(
148  std::string("Invalid currency/issuer '") + strCurrencyIssuer +
149  "'");
150  }
151  }
152 
153  static bool
155  {
156  if (parseBase58<PublicKey>(TokenType::AccountPublic, strPk))
157  return true;
158 
159  auto pkHex = strUnHex(strPk);
160  if (!pkHex)
161  return false;
162 
163  if (!publicKeyType(makeSlice(*pkHex)))
164  return false;
165 
166  return true;
167  }
168 
169 private:
170  using parseFuncPtr =
171  Json::Value (RPCParser::*)(Json::Value const& jvParams);
172 
174  parseAsIs(Json::Value const& jvParams)
175  {
177 
178  if (jvParams.isArray() && (jvParams.size() > 0))
179  v[jss::params] = jvParams;
180 
181  return v;
182  }
183 
186  {
187  Json::Value jvResult(Json::objectValue);
188  unsigned int sz{jvParams.size()};
189  unsigned int i{0};
190 
191  // If odd number of params then 'novalidate' may have been specified
192  if (sz & 1)
193  {
194  if (boost::iequals(jvParams[0u].asString(), "novalidate"))
195  ++i;
196  else if (!boost::iequals(jvParams[--sz].asString(), "novalidate"))
197  return rpcError(rpcINVALID_PARAMS);
198  }
199 
200  // Create the 'shards' array
202  for (; i < sz; i += 2)
203  {
205  shard[jss::index] = jvParams[i].asUInt();
206  shard[jss::url] = jvParams[i + 1].asString();
207  shards.append(std::move(shard));
208  }
209  jvResult[jss::shards] = std::move(shards);
210 
211  return jvResult;
212  }
213 
215  parseInternal(Json::Value const& jvParams)
216  {
218  v[jss::internal_command] = jvParams[0u];
219 
221 
222  for (unsigned i = 1; i < jvParams.size(); ++i)
223  params.append(jvParams[i]);
224 
225  v[jss::params] = params;
226 
227  return v;
228  }
229 
231  parseManifest(Json::Value const& jvParams)
232  {
233  if (jvParams.size() == 1)
234  {
235  Json::Value jvRequest(Json::objectValue);
236 
237  std::string const strPk = jvParams[0u].asString();
238  if (!validPublicKey(strPk))
240 
241  jvRequest[jss::public_key] = strPk;
242 
243  return jvRequest;
244  }
245 
246  return rpcError(rpcINVALID_PARAMS);
247  }
248 
249  // fetch_info [clear]
251  parseFetchInfo(Json::Value const& jvParams)
252  {
253  Json::Value jvRequest(Json::objectValue);
254  unsigned int iParams = jvParams.size();
255 
256  if (iParams != 0)
257  jvRequest[jvParams[0u].asString()] = true;
258 
259  return jvRequest;
260  }
261 
262  // account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary]
263  // [count] [descending]
266  {
267  Json::Value jvRequest(Json::objectValue);
268  unsigned int iParams = jvParams.size();
269 
270  auto const account = parseBase58<AccountID>(jvParams[0u].asString());
271  if (!account)
272  return rpcError(rpcACT_MALFORMED);
273 
274  jvRequest[jss::account] = toBase58(*account);
275 
276  bool bDone = false;
277 
278  while (!bDone && iParams >= 2)
279  {
280  // VFALCO Why is Json::StaticString appearing on the right side?
281  if (jvParams[iParams - 1].asString() == jss::binary)
282  {
283  jvRequest[jss::binary] = true;
284  --iParams;
285  }
286  else if (jvParams[iParams - 1].asString() == jss::count)
287  {
288  jvRequest[jss::count] = true;
289  --iParams;
290  }
291  else if (jvParams[iParams - 1].asString() == jss::descending)
292  {
293  jvRequest[jss::descending] = true;
294  --iParams;
295  }
296  else
297  {
298  bDone = true;
299  }
300  }
301 
302  if (1 == iParams)
303  {
304  }
305  else if (2 == iParams)
306  {
307  if (!jvParseLedger(jvRequest, jvParams[1u].asString()))
308  return jvRequest;
309  }
310  else
311  {
312  std::int64_t uLedgerMin = jvParams[1u].asInt();
313  std::int64_t uLedgerMax = jvParams[2u].asInt();
314 
315  if (uLedgerMax != -1 && uLedgerMax < uLedgerMin)
316  {
318  }
319 
320  jvRequest[jss::ledger_index_min] = jvParams[1u].asInt();
321  jvRequest[jss::ledger_index_max] = jvParams[2u].asInt();
322 
323  if (iParams >= 4)
324  jvRequest[jss::limit] = jvParams[3u].asInt();
325 
326  if (iParams >= 5)
327  jvRequest[jss::offset] = jvParams[4u].asInt();
328  }
329 
330  return jvRequest;
331  }
332 
333  // tx_account accountID [ledger_min [ledger_max [limit]]]] [binary] [count]
334  // [forward]
336  parseTxAccount(Json::Value const& jvParams)
337  {
338  Json::Value jvRequest(Json::objectValue);
339  unsigned int iParams = jvParams.size();
340 
341  auto const account = parseBase58<AccountID>(jvParams[0u].asString());
342  if (!account)
343  return rpcError(rpcACT_MALFORMED);
344 
345  jvRequest[jss::account] = toBase58(*account);
346 
347  bool bDone = false;
348 
349  while (!bDone && iParams >= 2)
350  {
351  if (jvParams[iParams - 1].asString() == jss::binary)
352  {
353  jvRequest[jss::binary] = true;
354  --iParams;
355  }
356  else if (jvParams[iParams - 1].asString() == jss::count)
357  {
358  jvRequest[jss::count] = true;
359  --iParams;
360  }
361  else if (jvParams[iParams - 1].asString() == jss::forward)
362  {
363  jvRequest[jss::forward] = true;
364  --iParams;
365  }
366  else
367  {
368  bDone = true;
369  }
370  }
371 
372  if (1 == iParams)
373  {
374  }
375  else if (2 == iParams)
376  {
377  if (!jvParseLedger(jvRequest, jvParams[1u].asString()))
378  return jvRequest;
379  }
380  else
381  {
382  std::int64_t uLedgerMin = jvParams[1u].asInt();
383  std::int64_t uLedgerMax = jvParams[2u].asInt();
384 
385  if (uLedgerMax != -1 && uLedgerMax < uLedgerMin)
386  {
388  }
389 
390  jvRequest[jss::ledger_index_min] = jvParams[1u].asInt();
391  jvRequest[jss::ledger_index_max] = jvParams[2u].asInt();
392 
393  if (iParams >= 4)
394  jvRequest[jss::limit] = jvParams[3u].asInt();
395  }
396 
397  return jvRequest;
398  }
399 
400  // book_offers <taker_pays> <taker_gets> [<taker> [<ledger> [<limit>
401  // [<proof> [<marker>]]]]] limit: 0 = no limit proof: 0 or 1
402  //
403  // Mnemonic: taker pays --> offer --> taker gets
405  parseBookOffers(Json::Value const& jvParams)
406  {
407  Json::Value jvRequest(Json::objectValue);
408 
409  Json::Value jvTakerPays =
410  jvParseCurrencyIssuer(jvParams[0u].asString());
411  Json::Value jvTakerGets =
412  jvParseCurrencyIssuer(jvParams[1u].asString());
413 
414  if (isRpcError(jvTakerPays))
415  {
416  return jvTakerPays;
417  }
418  else
419  {
420  jvRequest[jss::taker_pays] = jvTakerPays;
421  }
422 
423  if (isRpcError(jvTakerGets))
424  {
425  return jvTakerGets;
426  }
427  else
428  {
429  jvRequest[jss::taker_gets] = jvTakerGets;
430  }
431 
432  if (jvParams.size() >= 3)
433  {
434  jvRequest[jss::issuer] = jvParams[2u].asString();
435  }
436 
437  if (jvParams.size() >= 4 &&
438  !jvParseLedger(jvRequest, jvParams[3u].asString()))
439  return jvRequest;
440 
441  if (jvParams.size() >= 5)
442  {
443  int iLimit = jvParams[5u].asInt();
444 
445  if (iLimit > 0)
446  jvRequest[jss::limit] = iLimit;
447  }
448 
449  if (jvParams.size() >= 6 && jvParams[5u].asInt())
450  {
451  jvRequest[jss::proof] = true;
452  }
453 
454  if (jvParams.size() == 7)
455  jvRequest[jss::marker] = jvParams[6u];
456 
457  return jvRequest;
458  }
459 
460  // can_delete [<ledgerid>|<ledgerhash>|now|always|never]
462  parseCanDelete(Json::Value const& jvParams)
463  {
464  Json::Value jvRequest(Json::objectValue);
465 
466  if (!jvParams.size())
467  return jvRequest;
468 
469  std::string input = jvParams[0u].asString();
470  if (input.find_first_not_of("0123456789") == std::string::npos)
471  jvRequest["can_delete"] = jvParams[0u].asUInt();
472  else
473  jvRequest["can_delete"] = input;
474 
475  return jvRequest;
476  }
477 
478  // connect <ip> [port]
480  parseConnect(Json::Value const& jvParams)
481  {
482  Json::Value jvRequest(Json::objectValue);
483 
484  jvRequest[jss::ip] = jvParams[0u].asString();
485 
486  if (jvParams.size() == 2)
487  jvRequest[jss::port] = jvParams[1u].asUInt();
488 
489  return jvRequest;
490  }
491 
492  // deposit_authorized <source_account> <destination_account> [<ledger>]
495  {
496  Json::Value jvRequest(Json::objectValue);
497  jvRequest[jss::source_account] = jvParams[0u].asString();
498  jvRequest[jss::destination_account] = jvParams[1u].asString();
499 
500  if (jvParams.size() == 3)
501  jvParseLedger(jvRequest, jvParams[2u].asString());
502 
503  return jvRequest;
504  }
505 
506  // Return an error for attemping to subscribe/unsubscribe via RPC.
508  parseEvented(Json::Value const& jvParams)
509  {
510  return rpcError(rpcNO_EVENTS);
511  }
512 
513  // feature [<feature>] [accept|reject]
515  parseFeature(Json::Value const& jvParams)
516  {
517  Json::Value jvRequest(Json::objectValue);
518 
519  if (jvParams.size() > 0)
520  jvRequest[jss::feature] = jvParams[0u].asString();
521 
522  if (jvParams.size() > 1)
523  {
524  auto const action = jvParams[1u].asString();
525 
526  // This may look reversed, but it's intentional: jss::vetoed
527  // determines whether an amendment is vetoed - so "reject" means
528  // that jss::vetoed is true.
529  if (boost::iequals(action, "reject"))
530  jvRequest[jss::vetoed] = Json::Value(true);
531  else if (boost::iequals(action, "accept"))
532  jvRequest[jss::vetoed] = Json::Value(false);
533  else
534  return rpcError(rpcINVALID_PARAMS);
535  }
536 
537  return jvRequest;
538  }
539 
540  // get_counts [<min_count>]
542  parseGetCounts(Json::Value const& jvParams)
543  {
544  Json::Value jvRequest(Json::objectValue);
545 
546  if (jvParams.size())
547  jvRequest[jss::min_count] = jvParams[0u].asUInt();
548 
549  return jvRequest;
550  }
551 
552  // sign_for <account> <secret> <json> offline
553  // sign_for <account> <secret> <json>
555  parseSignFor(Json::Value const& jvParams)
556  {
557  bool const bOffline =
558  4 == jvParams.size() && jvParams[3u].asString() == "offline";
559 
560  if (3 == jvParams.size() || bOffline)
561  {
562  Json::Value txJSON;
563  Json::Reader reader;
564  if (reader.parse(jvParams[2u].asString(), txJSON))
565  {
566  // sign_for txJSON.
567  Json::Value jvRequest{Json::objectValue};
568 
569  jvRequest[jss::account] = jvParams[0u].asString();
570  jvRequest[jss::secret] = jvParams[1u].asString();
571  jvRequest[jss::tx_json] = txJSON;
572 
573  if (bOffline)
574  jvRequest[jss::offline] = true;
575 
576  return jvRequest;
577  }
578  }
579  return rpcError(rpcINVALID_PARAMS);
580  }
581 
582  // json <command> <json>
584  parseJson(Json::Value const& jvParams)
585  {
586  Json::Reader reader;
587  Json::Value jvRequest;
588 
589  JLOG(j_.trace()) << "RPC method: " << jvParams[0u];
590  JLOG(j_.trace()) << "RPC json: " << jvParams[1u];
591 
592  if (reader.parse(jvParams[1u].asString(), jvRequest))
593  {
594  if (!jvRequest.isObjectOrNull())
595  return rpcError(rpcINVALID_PARAMS);
596 
597  jvRequest[jss::method] = jvParams[0u];
598 
599  return jvRequest;
600  }
601 
602  return rpcError(rpcINVALID_PARAMS);
603  }
604 
605  bool
607  {
608  if (jv.isArray())
609  {
610  if (jv.size() == 0)
611  return false;
612  for (auto const& j : jv)
613  {
614  if (!isValidJson2(j))
615  return false;
616  }
617  return true;
618  }
619  if (jv.isObject())
620  {
621  if (jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0" &&
622  jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0" &&
623  jv.isMember(jss::id) && jv.isMember(jss::method))
624  {
625  if (jv.isMember(jss::params) &&
626  !(jv[jss::params].isNull() || jv[jss::params].isArray() ||
627  jv[jss::params].isObject()))
628  return false;
629  return true;
630  }
631  }
632  return false;
633  }
634 
636  parseJson2(Json::Value const& jvParams)
637  {
638  Json::Reader reader;
639  Json::Value jv;
640  bool valid_parse = reader.parse(jvParams[0u].asString(), jv);
641  if (valid_parse && isValidJson2(jv))
642  {
643  if (jv.isObject())
644  {
646  if (jv.isMember(jss::params))
647  {
648  auto const& params = jv[jss::params];
649  for (auto i = params.begin(); i != params.end(); ++i)
650  jv1[i.key().asString()] = *i;
651  }
652  jv1[jss::jsonrpc] = jv[jss::jsonrpc];
653  jv1[jss::ripplerpc] = jv[jss::ripplerpc];
654  jv1[jss::id] = jv[jss::id];
655  jv1[jss::method] = jv[jss::method];
656  return jv1;
657  }
658  // else jv.isArray()
660  for (Json::UInt j = 0; j < jv.size(); ++j)
661  {
662  if (jv[j].isMember(jss::params))
663  {
664  auto const& params = jv[j][jss::params];
665  for (auto i = params.begin(); i != params.end(); ++i)
666  jv1[j][i.key().asString()] = *i;
667  }
668  jv1[j][jss::jsonrpc] = jv[j][jss::jsonrpc];
669  jv1[j][jss::ripplerpc] = jv[j][jss::ripplerpc];
670  jv1[j][jss::id] = jv[j][jss::id];
671  jv1[j][jss::method] = jv[j][jss::method];
672  }
673  return jv1;
674  }
675  auto jv_error = rpcError(rpcINVALID_PARAMS);
676  if (jv.isMember(jss::jsonrpc))
677  jv_error[jss::jsonrpc] = jv[jss::jsonrpc];
678  if (jv.isMember(jss::ripplerpc))
679  jv_error[jss::ripplerpc] = jv[jss::ripplerpc];
680  if (jv.isMember(jss::id))
681  jv_error[jss::id] = jv[jss::id];
682  return jv_error;
683  }
684 
685  // ledger [id|index|current|closed|validated] [full|tx]
687  parseLedger(Json::Value const& jvParams)
688  {
689  Json::Value jvRequest(Json::objectValue);
690 
691  if (!jvParams.size())
692  {
693  return jvRequest;
694  }
695 
696  jvParseLedger(jvRequest, jvParams[0u].asString());
697 
698  if (2 == jvParams.size())
699  {
700  if (jvParams[1u].asString() == "full")
701  {
702  jvRequest[jss::full] = true;
703  }
704  else if (jvParams[1u].asString() == "tx")
705  {
706  jvRequest[jss::transactions] = true;
707  jvRequest[jss::expand] = true;
708  }
709  }
710 
711  return jvRequest;
712  }
713 
714  // ledger_header <id>|<index>
716  parseLedgerId(Json::Value const& jvParams)
717  {
718  Json::Value jvRequest(Json::objectValue);
719 
720  std::string strLedger = jvParams[0u].asString();
721 
722  if (strLedger.length() == 64)
723  {
724  jvRequest[jss::ledger_hash] = strLedger;
725  }
726  else
727  {
728  jvRequest[jss::ledger_index] =
729  beast::lexicalCast<std::uint32_t>(strLedger);
730  }
731 
732  return jvRequest;
733  }
734 
735  // log_level: Get log levels
736  // log_level <severity>: Set master log level to the
737  // specified severity log_level <partition> <severity>: Set specified
738  // partition to specified severity
740  parseLogLevel(Json::Value const& jvParams)
741  {
742  Json::Value jvRequest(Json::objectValue);
743 
744  if (jvParams.size() == 1)
745  {
746  jvRequest[jss::severity] = jvParams[0u].asString();
747  }
748  else if (jvParams.size() == 2)
749  {
750  jvRequest[jss::partition] = jvParams[0u].asString();
751  jvRequest[jss::severity] = jvParams[1u].asString();
752  }
753 
754  return jvRequest;
755  }
756 
757  // owner_info <account>|<account_public_key> [strict]
758  // owner_info <seed>|<pass_phrase>|<key> [<ledger>] [strict]
759  // account_info <account>|<account_public_key> [strict]
760  // account_info <seed>|<pass_phrase>|<key> [<ledger>] [strict]
761  // account_offers <account>|<account_public_key> [<ledger>] [strict]
764  {
765  return parseAccountRaw1(jvParams);
766  }
767 
770  {
771  return parseAccountRaw1(jvParams);
772  }
773 
774  // account_lines <account> <account>|"" [<ledger>]
777  {
778  return parseAccountRaw2(jvParams, jss::peer);
779  }
780 
781  // account_channels <account> <account>|"" [<ledger>]
784  {
785  return parseAccountRaw2(jvParams, jss::destination_account);
786  }
787 
788  // channel_authorize: <private_key> [<key_type>] <channel_id> <drops>
791  {
792  Json::Value jvRequest(Json::objectValue);
793 
794  unsigned int index = 0;
795 
796  if (jvParams.size() == 4)
797  {
798  jvRequest[jss::passphrase] = jvParams[index];
799  index++;
800 
801  if (!keyTypeFromString(jvParams[index].asString()))
802  return rpcError(rpcBAD_KEY_TYPE);
803  jvRequest[jss::key_type] = jvParams[index];
804  index++;
805  }
806  else
807  {
808  jvRequest[jss::secret] = jvParams[index];
809  index++;
810  }
811 
812  {
813  // verify the channel id is a valid 256 bit number
814  uint256 channelId;
815  if (!channelId.SetHexExact(jvParams[index].asString()))
817  jvRequest[jss::channel_id] = to_string(channelId);
818  index++;
819  }
820 
821  if (!jvParams[index].isString() ||
822  !to_uint64(jvParams[index].asString()))
824  jvRequest[jss::amount] = jvParams[index];
825 
826  // If additional parameters are appended, be sure to increment index
827  // here
828 
829  return jvRequest;
830  }
831 
832  // channel_verify <public_key> <channel_id> <drops> <signature>
835  {
836  std::string const strPk = jvParams[0u].asString();
837 
838  if (!validPublicKey(strPk))
840 
841  Json::Value jvRequest(Json::objectValue);
842 
843  jvRequest[jss::public_key] = strPk;
844  {
845  // verify the channel id is a valid 256 bit number
846  uint256 channelId;
847  if (!channelId.SetHexExact(jvParams[1u].asString()))
849  }
850  jvRequest[jss::channel_id] = jvParams[1u].asString();
851 
852  if (!jvParams[2u].isString() || !to_uint64(jvParams[2u].asString()))
854  jvRequest[jss::amount] = jvParams[2u];
855 
856  jvRequest[jss::signature] = jvParams[3u].asString();
857 
858  return jvRequest;
859  }
860 
862  parseAccountRaw2(Json::Value const& jvParams, char const* const acc2Field)
863  {
864  std::array<char const* const, 2> accFields{{jss::account, acc2Field}};
865  auto const nParams = jvParams.size();
866  Json::Value jvRequest(Json::objectValue);
867  for (auto i = 0; i < nParams; ++i)
868  {
869  std::string strParam = jvParams[i].asString();
870 
871  if (i == 1 && strParam.empty())
872  continue;
873 
874  // Parameters 0 and 1 are accounts
875  if (i < 2)
876  {
877  if (parseBase58<PublicKey>(
878  TokenType::AccountPublic, strParam) ||
879  parseBase58<AccountID>(strParam) ||
880  parseGenericSeed(strParam))
881  {
882  jvRequest[accFields[i]] = std::move(strParam);
883  }
884  else
885  {
886  return rpcError(rpcACT_MALFORMED);
887  }
888  }
889  else
890  {
891  if (jvParseLedger(jvRequest, strParam))
892  return jvRequest;
894  }
895  }
896 
897  return jvRequest;
898  }
899 
900  // TODO: Get index from an alternate syntax: rXYZ:<index>
902  parseAccountRaw1(Json::Value const& jvParams)
903  {
904  std::string strIdent = jvParams[0u].asString();
905  unsigned int iCursor = jvParams.size();
906  bool bStrict = false;
907 
908  if (iCursor >= 2 && jvParams[iCursor - 1] == jss::strict)
909  {
910  bStrict = true;
911  --iCursor;
912  }
913 
914  if (!parseBase58<PublicKey>(TokenType::AccountPublic, strIdent) &&
915  !parseBase58<AccountID>(strIdent) && !parseGenericSeed(strIdent))
916  return rpcError(rpcACT_MALFORMED);
917 
918  // Get info on account.
919  Json::Value jvRequest(Json::objectValue);
920 
921  jvRequest[jss::account] = strIdent;
922 
923  if (bStrict)
924  jvRequest[jss::strict] = 1;
925 
926  if (iCursor == 2 && !jvParseLedger(jvRequest, jvParams[1u].asString()))
928 
929  return jvRequest;
930  }
931 
932  // peer_reservations_add <public_key> [<name>]
935  {
936  Json::Value jvRequest;
937  jvRequest[jss::public_key] = jvParams[0u].asString();
938  if (jvParams.size() > 1)
939  {
940  jvRequest[jss::description] = jvParams[1u].asString();
941  }
942  return jvRequest;
943  }
944 
945  // peer_reservations_del <public_key>
948  {
949  Json::Value jvRequest;
950  jvRequest[jss::public_key] = jvParams[0u].asString();
951  return jvRequest;
952  }
953 
954  // ripple_path_find <json> [<ledger>]
957  {
958  Json::Reader reader;
959  Json::Value jvRequest{Json::objectValue};
960  bool bLedger = 2 == jvParams.size();
961 
962  JLOG(j_.trace()) << "RPC json: " << jvParams[0u];
963 
964  if (reader.parse(jvParams[0u].asString(), jvRequest))
965  {
966  if (bLedger)
967  {
968  jvParseLedger(jvRequest, jvParams[1u].asString());
969  }
970 
971  return jvRequest;
972  }
973 
974  return rpcError(rpcINVALID_PARAMS);
975  }
976 
977  // sign/submit any transaction to the network
978  //
979  // sign <private_key> <json> offline
980  // submit <private_key> <json>
981  // submit <tx_blob>
983  parseSignSubmit(Json::Value const& jvParams)
984  {
985  Json::Value txJSON;
986  Json::Reader reader;
987  bool const bOffline =
988  3 == jvParams.size() && jvParams[2u].asString() == "offline";
989 
990  if (1 == jvParams.size())
991  {
992  // Submitting tx_blob
993 
994  Json::Value jvRequest{Json::objectValue};
995 
996  jvRequest[jss::tx_blob] = jvParams[0u].asString();
997 
998  return jvRequest;
999  }
1000  else if (
1001  (2 == jvParams.size() || bOffline) &&
1002  reader.parse(jvParams[1u].asString(), txJSON))
1003  {
1004  // Signing or submitting tx_json.
1005  Json::Value jvRequest{Json::objectValue};
1006 
1007  jvRequest[jss::secret] = jvParams[0u].asString();
1008  jvRequest[jss::tx_json] = txJSON;
1009 
1010  if (bOffline)
1011  jvRequest[jss::offline] = true;
1012 
1013  return jvRequest;
1014  }
1015 
1016  return rpcError(rpcINVALID_PARAMS);
1017  }
1018 
1019  // submit any multisigned transaction to the network
1020  //
1021  // submit_multisigned <json>
1022  Json::Value
1024  {
1025  if (1 == jvParams.size())
1026  {
1027  Json::Value txJSON;
1028  Json::Reader reader;
1029  if (reader.parse(jvParams[0u].asString(), txJSON))
1030  {
1031  Json::Value jvRequest{Json::objectValue};
1032  jvRequest[jss::tx_json] = txJSON;
1033  return jvRequest;
1034  }
1035  }
1036 
1037  return rpcError(rpcINVALID_PARAMS);
1038  }
1039 
1040  // transaction_entry <tx_hash> <ledger_hash/ledger_index>
1041  Json::Value
1043  {
1044  // Parameter count should have already been verified.
1045  assert(jvParams.size() == 2);
1046 
1047  std::string const txHash = jvParams[0u].asString();
1048  if (txHash.length() != 64)
1049  return rpcError(rpcINVALID_PARAMS);
1050 
1051  Json::Value jvRequest{Json::objectValue};
1052  jvRequest[jss::tx_hash] = txHash;
1053 
1054  jvParseLedger(jvRequest, jvParams[1u].asString());
1055 
1056  // jvParseLedger inserts a "ledger_index" of 0 if it doesn't
1057  // find a match.
1058  if (jvRequest.isMember(jss::ledger_index) &&
1059  jvRequest[jss::ledger_index] == 0)
1060  return rpcError(rpcINVALID_PARAMS);
1061 
1062  return jvRequest;
1063  }
1064 
1065  // tx <transaction_id>
1066  Json::Value
1067  parseTx(Json::Value const& jvParams)
1068  {
1069  Json::Value jvRequest{Json::objectValue};
1070 
1071  if (jvParams.size() == 2 || jvParams.size() == 4)
1072  {
1073  if (jvParams[1u].asString() == jss::binary)
1074  jvRequest[jss::binary] = true;
1075  }
1076 
1077  if (jvParams.size() >= 3)
1078  {
1079  const auto offset = jvParams.size() == 3 ? 0 : 1;
1080 
1081  jvRequest[jss::min_ledger] = jvParams[1u + offset].asString();
1082  jvRequest[jss::max_ledger] = jvParams[2u + offset].asString();
1083  }
1084 
1085  jvRequest[jss::transaction] = jvParams[0u].asString();
1086  return jvRequest;
1087  }
1088 
1089  // tx_history <index>
1090  Json::Value
1091  parseTxHistory(Json::Value const& jvParams)
1092  {
1093  Json::Value jvRequest{Json::objectValue};
1094 
1095  jvRequest[jss::start] = jvParams[0u].asUInt();
1096 
1097  return jvRequest;
1098  }
1099 
1100  // validation_create [<pass_phrase>|<seed>|<seed_key>]
1101  //
1102  // NOTE: It is poor security to specify secret information on the command
1103  // line. This information might be saved in the command shell history file
1104  // (e.g. .bash_history) and it may be leaked via the process status command
1105  // (i.e. ps).
1106  Json::Value
1108  {
1109  Json::Value jvRequest{Json::objectValue};
1110 
1111  if (jvParams.size())
1112  jvRequest[jss::secret] = jvParams[0u].asString();
1113 
1114  return jvRequest;
1115  }
1116 
1117  // wallet_propose [<passphrase>]
1118  // <passphrase> is only for testing. Master seeds should only be generated
1119  // randomly.
1120  Json::Value
1122  {
1123  Json::Value jvRequest{Json::objectValue};
1124 
1125  if (jvParams.size())
1126  jvRequest[jss::passphrase] = jvParams[0u].asString();
1127 
1128  return jvRequest;
1129  }
1130 
1131  // parse gateway balances
1132  // gateway_balances [<ledger>] <issuer_account> [ <hotwallet> [ <hotwallet>
1133  // ]]
1134 
1135  Json::Value
1137  {
1138  unsigned int index = 0;
1139  const unsigned int size = jvParams.size();
1140 
1141  Json::Value jvRequest{Json::objectValue};
1142 
1143  std::string param = jvParams[index++].asString();
1144  if (param.empty())
1145  return RPC::make_param_error("Invalid first parameter");
1146 
1147  if (param[0] != 'r')
1148  {
1149  if (param.size() == 64)
1150  jvRequest[jss::ledger_hash] = param;
1151  else
1152  jvRequest[jss::ledger_index] = param;
1153 
1154  if (size <= index)
1155  return RPC::make_param_error("Invalid hotwallet");
1156 
1157  param = jvParams[index++].asString();
1158  }
1159 
1160  jvRequest[jss::account] = param;
1161 
1162  if (index < size)
1163  {
1164  Json::Value& hotWallets =
1165  (jvRequest["hotwallet"] = Json::arrayValue);
1166  while (index < size)
1167  hotWallets.append(jvParams[index++].asString());
1168  }
1169 
1170  return jvRequest;
1171  }
1172 
1173  // server_info [counters]
1174  Json::Value
1175  parseServerInfo(Json::Value const& jvParams)
1176  {
1177  Json::Value jvRequest(Json::objectValue);
1178  if (jvParams.size() == 1 && jvParams[0u].asString() == "counters")
1179  jvRequest[jss::counters] = true;
1180  return jvRequest;
1181  }
1182 
1183 public:
1184  //--------------------------------------------------------------------------
1185 
1186  explicit RPCParser(beast::Journal j) : j_(j)
1187  {
1188  }
1189 
1190  //--------------------------------------------------------------------------
1191 
1192  // Convert a rpc method and params to a request.
1193  // <-- { method: xyz, params: [... ] } or { error: ..., ... }
1194  Json::Value
1196  std::string strMethod,
1197  Json::Value jvParams,
1198  bool allowAnyCommand)
1199  {
1200  if (auto stream = j_.trace())
1201  {
1202  stream << "Method: '" << strMethod << "'";
1203  stream << "Params: " << jvParams;
1204  }
1205 
1206  struct Command
1207  {
1208  const char* name;
1209  parseFuncPtr parse;
1210  int minParams;
1211  int maxParams;
1212  };
1213 
1214  // FIXME: replace this with a function-static std::map and the lookup
1215  // code with std::map::find when the problem with magic statics on
1216  // Visual Studio is fixed.
1217  static Command const commands[] = {
1218  // Request-response methods
1219  // - Returns an error, or the request.
1220  // - To modify the method, provide a new method in the request.
1221  {"account_currencies", &RPCParser::parseAccountCurrencies, 1, 3},
1222  {"account_info", &RPCParser::parseAccountItems, 1, 3},
1223  {"account_lines", &RPCParser::parseAccountLines, 1, 5},
1224  {"account_channels", &RPCParser::parseAccountChannels, 1, 3},
1225  {"account_objects", &RPCParser::parseAccountItems, 1, 5},
1226  {"account_offers", &RPCParser::parseAccountItems, 1, 4},
1227  {"account_tx", &RPCParser::parseAccountTransactions, 1, 8},
1228  {"book_offers", &RPCParser::parseBookOffers, 2, 7},
1229  {"can_delete", &RPCParser::parseCanDelete, 0, 1},
1230  {"channel_authorize", &RPCParser::parseChannelAuthorize, 3, 4},
1231  {"channel_verify", &RPCParser::parseChannelVerify, 4, 4},
1232  {"connect", &RPCParser::parseConnect, 1, 2},
1233  {"consensus_info", &RPCParser::parseAsIs, 0, 0},
1234  {"deposit_authorized", &RPCParser::parseDepositAuthorized, 2, 3},
1235  {"download_shard", &RPCParser::parseDownloadShard, 2, -1},
1236  {"feature", &RPCParser::parseFeature, 0, 2},
1237  {"fetch_info", &RPCParser::parseFetchInfo, 0, 1},
1238  {"gateway_balances", &RPCParser::parseGatewayBalances, 1, -1},
1239  {"get_counts", &RPCParser::parseGetCounts, 0, 1},
1240  {"json", &RPCParser::parseJson, 2, 2},
1241  {"json2", &RPCParser::parseJson2, 1, 1},
1242  {"ledger", &RPCParser::parseLedger, 0, 2},
1243  {"ledger_accept", &RPCParser::parseAsIs, 0, 0},
1244  {"ledger_closed", &RPCParser::parseAsIs, 0, 0},
1245  {"ledger_current", &RPCParser::parseAsIs, 0, 0},
1246  // { "ledger_entry", &RPCParser::parseLedgerEntry,
1247  // -1, -1 },
1248  {"ledger_header", &RPCParser::parseLedgerId, 1, 1},
1249  {"ledger_request", &RPCParser::parseLedgerId, 1, 1},
1250  {"log_level", &RPCParser::parseLogLevel, 0, 2},
1251  {"logrotate", &RPCParser::parseAsIs, 0, 0},
1252  {"manifest", &RPCParser::parseManifest, 1, 1},
1253  {"owner_info", &RPCParser::parseAccountItems, 1, 3},
1254  {"peers", &RPCParser::parseAsIs, 0, 0},
1255  {"ping", &RPCParser::parseAsIs, 0, 0},
1256  {"print", &RPCParser::parseAsIs, 0, 1},
1257  // { "profile", &RPCParser::parseProfile, 1, 9
1258  // },
1259  {"random", &RPCParser::parseAsIs, 0, 0},
1260  {"peer_reservations_add",
1262  1,
1263  2},
1264  {"peer_reservations_del",
1266  1,
1267  1},
1268  {"peer_reservations_list", &RPCParser::parseAsIs, 0, 0},
1269  {"ripple_path_find", &RPCParser::parseRipplePathFind, 1, 2},
1270  {"sign", &RPCParser::parseSignSubmit, 2, 3},
1271  {"sign_for", &RPCParser::parseSignFor, 3, 4},
1272  {"submit", &RPCParser::parseSignSubmit, 1, 3},
1273  {"submit_multisigned", &RPCParser::parseSubmitMultiSigned, 1, 1},
1274  {"server_info", &RPCParser::parseServerInfo, 0, 1},
1275  {"server_state", &RPCParser::parseServerInfo, 0, 1},
1276  {"crawl_shards", &RPCParser::parseAsIs, 0, 2},
1277  {"stop", &RPCParser::parseAsIs, 0, 0},
1278  {"transaction_entry", &RPCParser::parseTransactionEntry, 2, 2},
1279  {"tx", &RPCParser::parseTx, 1, 4},
1280  {"tx_account", &RPCParser::parseTxAccount, 1, 7},
1281  {"tx_history", &RPCParser::parseTxHistory, 1, 1},
1282  {"unl_list", &RPCParser::parseAsIs, 0, 0},
1283  {"validation_create", &RPCParser::parseValidationCreate, 0, 1},
1284  {"validator_info", &RPCParser::parseAsIs, 0, 0},
1285  {"version", &RPCParser::parseAsIs, 0, 0},
1286  {"wallet_propose", &RPCParser::parseWalletPropose, 0, 1},
1287  {"internal", &RPCParser::parseInternal, 1, -1},
1288 
1289  // Evented methods
1290  {"path_find", &RPCParser::parseEvented, -1, -1},
1291  {"subscribe", &RPCParser::parseEvented, -1, -1},
1292  {"unsubscribe", &RPCParser::parseEvented, -1, -1},
1293  };
1294 
1295  auto const count = jvParams.size();
1296 
1297  for (auto const& command : commands)
1298  {
1299  if (strMethod == command.name)
1300  {
1301  if ((command.minParams >= 0 && count < command.minParams) ||
1302  (command.maxParams >= 0 && count > command.maxParams))
1303  {
1304  JLOG(j_.debug())
1305  << "Wrong number of parameters for " << command.name
1306  << " minimum=" << command.minParams
1307  << " maximum=" << command.maxParams
1308  << " actual=" << count;
1309 
1310  return rpcError(rpcBAD_SYNTAX);
1311  }
1312 
1313  return (this->*(command.parse))(jvParams);
1314  }
1315  }
1316 
1317  // The command could not be found
1318  if (!allowAnyCommand)
1319  return rpcError(rpcUNKNOWN_COMMAND);
1320 
1321  return parseAsIs(jvParams);
1322  }
1323 };
1324 
1325 //------------------------------------------------------------------------------
1326 
1327 //
1328 // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
1329 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
1330 // unspecified (HTTP errors and contents of 'error').
1331 //
1332 // 1.0 spec: http://json-rpc.org/wiki/specification
1333 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
1334 //
1335 
1338  std::string const& strMethod,
1339  Json::Value const& params,
1340  Json::Value const& id)
1341 {
1342  Json::Value request;
1343  request[jss::method] = strMethod;
1344  request[jss::params] = params;
1345  request[jss::id] = id;
1346  return to_string(request) + "\n";
1347 }
1348 
1349 namespace {
1350 // Special local exception type thrown when request can't be parsed.
1351 class RequestNotParseable : public std::runtime_error
1352 {
1353  using std::runtime_error::runtime_error; // Inherit constructors
1354 };
1355 }; // namespace
1356 
1358 {
1359  explicit RPCCallImp() = default;
1360 
1361  // VFALCO NOTE Is this a to-do comment or a doc comment?
1362  // Place the async result somewhere useful.
1363  static void
1364  callRPCHandler(Json::Value* jvOutput, Json::Value const& jvInput)
1365  {
1366  (*jvOutput) = jvInput;
1367  }
1368 
1369  static bool
1371  std::function<void(Json::Value const& jvInput)> callbackFuncP,
1372  const boost::system::error_code& ecResult,
1373  int iStatus,
1374  std::string const& strData,
1375  beast::Journal j)
1376  {
1377  if (callbackFuncP)
1378  {
1379  // Only care about the result, if we care to deliver it
1380  // callbackFuncP.
1381 
1382  // Receive reply
1383  if (iStatus == 401)
1384  Throw<std::runtime_error>(
1385  "incorrect rpcuser or rpcpassword (authorization failed)");
1386  else if (
1387  (iStatus >= 400) && (iStatus != 400) && (iStatus != 404) &&
1388  (iStatus != 500)) // ?
1389  Throw<std::runtime_error>(
1390  std::string("server returned HTTP error ") +
1391  std::to_string(iStatus));
1392  else if (strData.empty())
1393  Throw<std::runtime_error>("no response from server");
1394 
1395  // Parse reply
1396  JLOG(j.debug()) << "RPC reply: " << strData << std::endl;
1397  if (strData.find("Unable to parse request") == 0)
1398  Throw<RequestNotParseable>(strData);
1399  Json::Reader reader;
1400  Json::Value jvReply;
1401  if (!reader.parse(strData, jvReply))
1402  Throw<std::runtime_error>("couldn't parse reply from server");
1403 
1404  if (!jvReply)
1405  Throw<std::runtime_error>(
1406  "expected reply to have result, error and id properties");
1407 
1408  Json::Value jvResult(Json::objectValue);
1409 
1410  jvResult["result"] = jvReply;
1411 
1412  (callbackFuncP)(jvResult);
1413  }
1414 
1415  return false;
1416  }
1417 
1418  // Build the request.
1419  static void
1421  std::string const& strMethod,
1422  Json::Value const& jvParams,
1424  std::string const& strPath,
1425  boost::asio::streambuf& sb,
1426  std::string const& strHost,
1427  beast::Journal j)
1428  {
1429  JLOG(j.debug()) << "requestRPC: strPath='" << strPath << "'";
1430 
1431  std::ostream osRequest(&sb);
1432  osRequest << createHTTPPost(
1433  strHost,
1434  strPath,
1435  JSONRPCRequest(strMethod, jvParams, Json::Value(1)),
1436  headers);
1437  }
1438 };
1439 
1440 //------------------------------------------------------------------------------
1441 
1442 // Used internally by rpcClient.
1443 static Json::Value
1445  std::vector<std::string> const& args,
1446  Json::Value& retParams,
1447  beast::Journal j)
1448 {
1449  Json::Value jvRequest(Json::objectValue);
1450 
1451  RPCParser rpParser(j);
1452  Json::Value jvRpcParams(Json::arrayValue);
1453 
1454  for (int i = 1; i != args.size(); i++)
1455  jvRpcParams.append(args[i]);
1456 
1457  retParams = Json::Value(Json::objectValue);
1458 
1459  retParams[jss::method] = args[0];
1460  retParams[jss::params] = jvRpcParams;
1461 
1462  jvRequest = rpParser.parseCommand(args[0], jvRpcParams, true);
1463 
1464  auto insert_api_version = [](Json::Value& jr) {
1465  if (jr.isObject() && !jr.isMember(jss::error) &&
1466  !jr.isMember(jss::api_version))
1467  {
1468  jr[jss::api_version] = RPC::ApiMaximumSupportedVersion;
1469  }
1470  };
1471 
1472  if (jvRequest.isObject())
1473  insert_api_version(jvRequest);
1474  else if (jvRequest.isArray())
1475  std::for_each(jvRequest.begin(), jvRequest.end(), insert_api_version);
1476 
1477  JLOG(j.trace()) << "RPC Request: " << jvRequest << std::endl;
1478  return jvRequest;
1479 }
1480 
1483 {
1485  auto const paramsObj = rpcCmdLineToJson(args, jv, j);
1486 
1487  // Re-use jv to return our formatted result.
1488  jv.clear();
1489 
1490  // Allow parser to rewrite method.
1491  jv[jss::method] = paramsObj.isMember(jss::method)
1492  ? paramsObj[jss::method].asString()
1493  : args[0];
1494 
1495  // If paramsObj is not empty, put it in a [params] array.
1496  if (paramsObj.begin() != paramsObj.end())
1497  {
1498  auto& paramsArray = Json::setArray(jv, jss::params);
1499  paramsArray.append(paramsObj);
1500  }
1501  if (paramsObj.isMember(jss::jsonrpc))
1502  jv[jss::jsonrpc] = paramsObj[jss::jsonrpc];
1503  if (paramsObj.isMember(jss::ripplerpc))
1504  jv[jss::ripplerpc] = paramsObj[jss::ripplerpc];
1505  if (paramsObj.isMember(jss::id))
1506  jv[jss::id] = paramsObj[jss::id];
1507  return jv;
1508 }
1509 
1510 //------------------------------------------------------------------------------
1511 
1514  std::vector<std::string> const& args,
1515  Config const& config,
1516  Logs& logs,
1518 {
1519  static_assert(
1520  rpcBAD_SYNTAX == 1 && rpcSUCCESS == 0,
1521  "Expect specific rpc enum values.");
1522  if (args.empty())
1523  return {rpcBAD_SYNTAX, {}}; // rpcBAD_SYNTAX = print usage
1524 
1525  int nRet = rpcSUCCESS;
1526  Json::Value jvOutput;
1527  Json::Value jvRequest(Json::objectValue);
1528 
1529  try
1530  {
1532  jvRequest = rpcCmdLineToJson(args, jvRpc, logs.journal("RPCParser"));
1533 
1534  if (jvRequest.isMember(jss::error))
1535  {
1536  jvOutput = jvRequest;
1537  jvOutput["rpc"] = jvRpc;
1538  }
1539  else
1540  {
1541  ServerHandler::Setup setup;
1542  try
1543  {
1544  setup = setup_ServerHandler(
1545  config,
1546  beast::logstream{logs.journal("HTTPClient").warn()});
1547  }
1548  catch (std::exception const&)
1549  {
1550  // ignore any exceptions, so the command
1551  // line client works without a config file
1552  }
1553 
1554  if (config.rpc_ip)
1555  {
1556  setup.client.ip = config.rpc_ip->address().to_string();
1557  setup.client.port = config.rpc_ip->port();
1558  }
1559 
1560  Json::Value jvParams(Json::arrayValue);
1561 
1562  if (!setup.client.admin_user.empty())
1563  jvRequest["admin_user"] = setup.client.admin_user;
1564 
1565  if (!setup.client.admin_password.empty())
1566  jvRequest["admin_password"] = setup.client.admin_password;
1567 
1568  if (jvRequest.isObject())
1569  jvParams.append(jvRequest);
1570  else if (jvRequest.isArray())
1571  {
1572  for (Json::UInt i = 0; i < jvRequest.size(); ++i)
1573  jvParams.append(jvRequest[i]);
1574  }
1575 
1576  {
1577  boost::asio::io_service isService;
1579  isService,
1580  setup.client.ip,
1581  setup.client.port,
1582  setup.client.user,
1583  setup.client.password,
1584  "",
1585  jvRequest.isMember(
1586  jss::method) // Allow parser to rewrite method.
1587  ? jvRequest[jss::method].asString()
1588  : jvRequest.isArray() ? "batch" : args[0],
1589  jvParams, // Parsed, execute.
1590  setup.client.secure != 0, // Use SSL
1591  config.quiet(),
1592  logs,
1593  std::bind(
1595  &jvOutput,
1596  std::placeholders::_1),
1597  headers);
1598  isService.run(); // This blocks until there is no more
1599  // outstanding async calls.
1600  }
1601  if (jvOutput.isMember("result"))
1602  {
1603  // Had a successful JSON-RPC 2.0 call.
1604  jvOutput = jvOutput["result"];
1605 
1606  // jvOutput may report a server side error.
1607  // It should report "status".
1608  }
1609  else
1610  {
1611  // Transport error.
1612  Json::Value jvRpcError = jvOutput;
1613 
1614  jvOutput = rpcError(rpcJSON_RPC);
1615  jvOutput["result"] = jvRpcError;
1616  }
1617 
1618  // If had an error, supply invocation in result.
1619  if (jvOutput.isMember(jss::error))
1620  {
1621  jvOutput["rpc"] =
1622  jvRpc; // How the command was seen as method + params.
1623  jvOutput["request_sent"] =
1624  jvRequest; // How the command was translated.
1625  }
1626  }
1627 
1628  if (jvOutput.isMember(jss::error))
1629  {
1630  jvOutput[jss::status] = "error";
1631  if (jvOutput.isMember(jss::error_code))
1632  nRet = std::stoi(jvOutput[jss::error_code].asString());
1633  else if (jvOutput[jss::error].isMember(jss::error_code))
1634  nRet =
1635  std::stoi(jvOutput[jss::error][jss::error_code].asString());
1636  else
1637  nRet = rpcBAD_SYNTAX;
1638  }
1639 
1640  // YYY We could have a command line flag for single line output for
1641  // scripts. YYY We would intercept output here and simplify it.
1642  }
1643  catch (RequestNotParseable& e)
1644  {
1645  jvOutput = rpcError(rpcINVALID_PARAMS);
1646  jvOutput["error_what"] = e.what();
1647  nRet = rpcINVALID_PARAMS;
1648  }
1649  catch (std::exception& e)
1650  {
1651  jvOutput = rpcError(rpcINTERNAL);
1652  jvOutput["error_what"] = e.what();
1653  nRet = rpcINTERNAL;
1654  }
1655 
1656  return {nRet, std::move(jvOutput)};
1657 }
1658 
1659 //------------------------------------------------------------------------------
1660 
1661 namespace RPCCall {
1662 
1663 int
1665  Config const& config,
1666  const std::vector<std::string>& vCmd,
1667  Logs& logs)
1668 {
1669  auto const result = rpcClient(vCmd, config, logs);
1670 
1671  std::cout << result.second.toStyledString();
1672 
1673  return result.first;
1674 }
1675 
1676 //------------------------------------------------------------------------------
1677 
1678 void
1680  boost::asio::io_service& io_service,
1681  std::string const& strIp,
1682  const std::uint16_t iPort,
1683  std::string const& strUsername,
1684  std::string const& strPassword,
1685  std::string const& strPath,
1686  std::string const& strMethod,
1687  Json::Value const& jvParams,
1688  const bool bSSL,
1689  const bool quiet,
1690  Logs& logs,
1691  std::function<void(Json::Value const& jvInput)> callbackFuncP,
1693 {
1694  auto j = logs.journal("HTTPClient");
1695 
1696  // Connect to localhost
1697  if (!quiet)
1698  {
1699  JLOG(j.info()) << (bSSL ? "Securely connecting to " : "Connecting to ")
1700  << strIp << ":" << iPort << std::endl;
1701  }
1702 
1703  // HTTP basic authentication
1704  headers["Authorization"] =
1705  std::string("Basic ") + base64_encode(strUsername + ":" + strPassword);
1706 
1707  // Send request
1708 
1709  // Number of bytes to try to receive if no
1710  // Content-Length header received
1711  constexpr auto RPC_REPLY_MAX_BYTES = megabytes(256);
1712 
1713  using namespace std::chrono_literals;
1714  auto constexpr RPC_NOTIFY = 10min;
1715 
1717  bSSL,
1718  io_service,
1719  strIp,
1720  iPort,
1721  std::bind(
1723  strMethod,
1724  jvParams,
1725  headers,
1726  strPath,
1727  std::placeholders::_1,
1728  std::placeholders::_2,
1729  j),
1730  RPC_REPLY_MAX_BYTES,
1731  RPC_NOTIFY,
1732  std::bind(
1734  callbackFuncP,
1735  std::placeholders::_1,
1736  std::placeholders::_2,
1737  std::placeholders::_3,
1738  j),
1739  j);
1740 }
1741 
1742 } // namespace RPCCall
1743 
1744 } // namespace ripple
ripple::RPCParser::parseAsIs
Json::Value parseAsIs(Json::Value const &jvParams)
Definition: RPCCall.cpp:174
std::for_each
T for_each(T... args)
ripple::RPCParser::parseTxHistory
Json::Value parseTxHistory(Json::Value const &jvParams)
Definition: RPCCall.cpp:1091
ripple::ServerHandlerImp::Setup::client_t::user
std::string user
Definition: ServerHandlerImp.h:63
ripple::makeSlice
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:194
std::bind
T bind(T... args)
ripple::rpcLGR_IDXS_INVALID
@ rpcLGR_IDXS_INVALID
Definition: ErrorCodes.h:112
Json::Value::isObject
bool isObject() const
Definition: json_value.cpp:1027
std::string
STL class.
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
ripple::RPCParser::parseConnect
Json::Value parseConnect(Json::Value const &jvParams)
Definition: RPCCall.cpp:480
ripple::RPCParser::parseInternal
Json::Value parseInternal(Json::Value const &jvParams)
Definition: RPCCall.cpp:215
std::exception
STL class.
ripple::Logs
Manages partitions for logging.
Definition: Log.h:48
ripple::RPCParser::parseChannelVerify
Json::Value parseChannelVerify(Json::Value const &jvParams)
Definition: RPCCall.cpp:834
ripple::RPCParser::parseFeature
Json::Value parseFeature(Json::Value const &jvParams)
Definition: RPCCall.cpp:515
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::publicKeyType
boost::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:203
ripple::RPCParser::parseAccountLines
Json::Value parseAccountLines(Json::Value const &jvParams)
Definition: RPCCall.cpp:776
ripple::RPCParser::parseAccountRaw2
Json::Value parseAccountRaw2(Json::Value const &jvParams, char const *const acc2Field)
Definition: RPCCall.cpp:862
ripple::RPCParser::parseCanDelete
Json::Value parseCanDelete(Json::Value const &jvParams)
Definition: RPCCall.cpp:462
ripple::RPCCallImp
Definition: RPCCall.cpp:1357
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
std::pair< int, Json::Value >
ripple::ServerHandlerImp::Setup::client_t::secure
bool secure
Definition: ServerHandlerImp.h:60
Json::UInt
unsigned int UInt
Definition: json_forwards.h:27
std::vector< std::string >
ripple::ServerHandlerImp::Setup::client_t::admin_user
std::string admin_user
Definition: ServerHandlerImp.h:65
std::string::find
T find(T... args)
std::string::size
T size(T... args)
ripple::base64_encode
std::string base64_encode(std::uint8_t const *data, std::size_t len)
Definition: base64.cpp:236
ripple::rpcJSON_RPC
@ rpcJSON_RPC
Definition: ErrorCodes.h:47
ripple::ServerHandlerImp::Setup
Definition: ServerHandlerImp.h:49
ripple::to_uint64
boost::optional< std::uint64_t > to_uint64(std::string const &s)
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::RPCParser::parseFetchInfo
Json::Value parseFetchInfo(Json::Value const &jvParams)
Definition: RPCCall.cpp:251
ripple::strUnHex
boost::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
Definition: StringUtilities.h:72
ripple::RPCParser::parseJson2
Json::Value parseJson2(Json::Value const &jvParams)
Definition: RPCCall.cpp:636
Json::Value::isNull
bool isNull() const
isNull() tests to see if this field is null.
Definition: json_value.cpp:967
ripple::RPCParser::RPCParser
RPCParser(beast::Journal j)
Definition: RPCCall.cpp:1186
ripple::ServerHandlerImp::Setup::client_t::port
std::uint16_t port
Definition: ServerHandlerImp.h:62
std::function
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
Json::Value::end
const_iterator end() const
Definition: json_value.cpp:1064
ripple::createHTTPPost
std::string createHTTPPost(std::string const &strHost, std::string const &strPath, std::string const &strMsg, std::unordered_map< std::string, std::string > const &mapRequestHeaders)
Definition: RPCCall.cpp:65
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
ripple::RPCParser::jvParseLedger
static bool jvParseLedger(Json::Value &jvRequest, std::string const &strLedger)
Definition: RPCCall.cpp:100
ripple::RPCParser::parseManifest
Json::Value parseManifest(Json::Value const &jvParams)
Definition: RPCCall.cpp:231
std::string::find_first_not_of
T find_first_not_of(T... args)
ripple::RPCCallImp::callRPCHandler
static void callRPCHandler(Json::Value *jvOutput, Json::Value const &jvInput)
Definition: RPCCall.cpp:1364
iostream
ripple::RPCParser::parseChannelAuthorize
Json::Value parseChannelAuthorize(Json::Value const &jvParams)
Definition: RPCCall.cpp:790
ripple::RPCParser::parseAccountItems
Json::Value parseAccountItems(Json::Value const &jvParams)
Definition: RPCCall.cpp:763
ripple::RPCParser::jvParseCurrencyIssuer
static Json::Value jvParseCurrencyIssuer(std::string const &strCurrencyIssuer)
Definition: RPCCall.cpp:123
ripple::Config::quiet
bool quiet() const
Definition: Config.h:206
ripple::RPCParser::parseSubmitMultiSigned
Json::Value parseSubmitMultiSigned(Json::Value const &jvParams)
Definition: RPCCall.cpp:1023
ripple::RPCParser::j_
const beast::Journal j_
Definition: RPCCall.cpp:95
ripple::ServerHandlerImp::Setup::client_t::ip
std::string ip
Definition: ServerHandlerImp.h:61
ripple::RPCParser::parsePeerReservationsAdd
Json::Value parsePeerReservationsAdd(Json::Value const &jvParams)
Definition: RPCCall.cpp:934
ripple::setup_ServerHandler
ServerHandler::Setup setup_ServerHandler(Config const &config, std::ostream &&log)
Definition: ServerHandlerImp.cpp:1143
std::cout
ripple::keyTypeFromString
boost::optional< KeyType > keyTypeFromString(std::string const &s)
Definition: KeyType.h:34
ripple::RPCCallImp::onRequest
static void onRequest(std::string const &strMethod, Json::Value const &jvParams, std::unordered_map< std::string, std::string > const &headers, std::string const &strPath, boost::asio::streambuf &sb, std::string const &strHost, beast::Journal j)
Definition: RPCCall.cpp:1420
ripple::base_uint< 256 >
ripple::RPCParser::parseGatewayBalances
Json::Value parseGatewayBalances(Json::Value const &jvParams)
Definition: RPCCall.cpp:1136
ripple::isRpcError
bool isRpcError(Json::Value jvResult)
Definition: RPCErr.cpp:37
std::stoi
T stoi(T... args)
ripple::rpcSUCCESS
@ rpcSUCCESS
Definition: ErrorCodes.h:44
ripple::RPCParser::parseAccountRaw1
Json::Value parseAccountRaw1(Json::Value const &jvParams)
Definition: RPCCall.cpp:902
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
ripple::RPCParser::parseServerInfo
Json::Value parseServerInfo(Json::Value const &jvParams)
Definition: RPCCall.cpp:1175
ripple::Config
Definition: Config.h:66
ripple::RPCParser::parseAccountCurrencies
Json::Value parseAccountCurrencies(Json::Value const &jvParams)
Definition: RPCCall.cpp:769
std::ostream
STL class.
ripple::RPCParser::parseLogLevel
Json::Value parseLogLevel(Json::Value const &jvParams)
Definition: RPCCall.cpp:740
ripple::RPCParser::parseBookOffers
Json::Value parseBookOffers(Json::Value const &jvParams)
Definition: RPCCall.cpp:405
ripple::RPCParser::parseFuncPtr
Json::Value(RPCParser::*)(Json::Value const &jvParams) parseFuncPtr
Definition: RPCCall.cpp:171
ripple::megabytes
constexpr auto megabytes(T value) noexcept
Definition: ByteUtilities.h:34
ripple::RPCParser::parseTxAccount
Json::Value parseTxAccount(Json::Value const &jvParams)
Definition: RPCCall.cpp:336
ripple::rpcPUBLIC_MALFORMED
@ rpcPUBLIC_MALFORMED
Definition: ErrorCodes.h:117
ripple::RPCParser::parseTx
Json::Value parseTx(Json::Value const &jvParams)
Definition: RPCCall.cpp:1067
std::to_string
T to_string(T... args)
array
ripple::RPC::ApiMaximumSupportedVersion
constexpr unsigned int ApiMaximumSupportedVersion
Definition: RPCHelpers.h:214
Json::Value::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
ripple::parseGenericSeed
boost::optional< Seed > parseGenericSeed(std::string const &str)
Attempt to parse a string as a seed.
Definition: Seed.cpp:90
ripple::ServerHandlerImp::Setup::client
client_t client
Definition: ServerHandlerImp.h:70
beast::basic_logstream
Definition: Journal.h:428
std::runtime_error
STL class.
ripple::rpcNO_EVENTS
@ rpcNO_EVENTS
Definition: ErrorCodes.h:54
ripple::RPCParser::parseCommand
Json::Value parseCommand(std::string strMethod, Json::Value jvParams, bool allowAnyCommand)
Definition: RPCCall.cpp:1195
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::RPCParser::parseWalletPropose
Json::Value parseWalletPropose(Json::Value const &jvParams)
Definition: RPCCall.cpp:1121
std::int64_t
ripple::RPCParser::parseLedger
Json::Value parseLedger(Json::Value const &jvParams)
Definition: RPCCall.cpp:687
ripple::RPCParser::parseRipplePathFind
Json::Value parseRipplePathFind(Json::Value const &jvParams)
Definition: RPCCall.cpp:956
ripple::rpcError
Json::Value rpcError(int iError, Json::Value jvResult)
Definition: RPCErr.cpp:29
ripple::base_uint::SetHexExact
bool SetHexExact(const char *psz)
Parse a hex string into a base_uint The string must contain exactly bytes * 2 hex characters and must...
Definition: base_uint.h:370
ripple::RPCParser::parseAccountTransactions
Json::Value parseAccountTransactions(Json::Value const &jvParams)
Definition: RPCCall.cpp:265
ripple::RPCParser::parseValidationCreate
Json::Value parseValidationCreate(Json::Value const &jvParams)
Definition: RPCCall.cpp:1107
ripple::rpcINTERNAL
@ rpcINTERNAL
Definition: ErrorCodes.h:130
Json::Value::isArray
bool isArray() const
Definition: json_value.cpp:1015
Json::setArray
Json::Value & setArray(Json::Value &, Json::StaticString const &key)
Add a new subarray at a named key in a Json object.
Definition: Object.h:414
ripple::rpcCHANNEL_AMT_MALFORMED
@ rpcCHANNEL_AMT_MALFORMED
Definition: ErrorCodes.h:101
std::ostringstream
STL class.
ripple::RPCCall::fromCommandLine
int fromCommandLine(Config const &config, const std::vector< std::string > &vCmd, Logs &logs)
Definition: RPCCall.cpp:1664
ripple::RPCParser::parseEvented
Json::Value parseEvented(Json::Value const &jvParams)
Definition: RPCCall.cpp:508
ripple::ServerHandlerImp::Setup::client_t::admin_password
std::string admin_password
Definition: ServerHandlerImp.h:66
ripple::RPCCallImp::RPCCallImp
RPCCallImp()=default
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::JSONRPCRequest
std::string JSONRPCRequest(std::string const &strMethod, Json::Value const &params, Json::Value const &id)
Definition: RPCCall.cpp:1337
std::endl
T endl(T... args)
ripple::rpcACT_MALFORMED
@ rpcACT_MALFORMED
Definition: ErrorCodes.h:90
ripple::Logs::journal
beast::Journal journal(std::string const &name)
Definition: Log.cpp:144
ripple::rpcBAD_SYNTAX
@ rpcBAD_SYNTAX
Definition: ErrorCodes.h:46
Json::Value::clear
void clear()
Remove all object members and array elements.
Definition: json_value.cpp:753
ripple::rpcUNKNOWN_COMMAND
@ rpcUNKNOWN_COMMAND
Definition: ErrorCodes.h:85
ripple::cmdLineToJSONRPC
Json::Value cmdLineToJSONRPC(std::vector< std::string > const &args, beast::Journal j)
Given a rippled command line, return the corresponding JSON.
Definition: RPCCall.cpp:1482
ripple::RPCParser::parseDownloadShard
Json::Value parseDownloadShard(Json::Value const &jvParams)
Definition: RPCCall.cpp:185
ripple::RPCParser::parseDepositAuthorized
Json::Value parseDepositAuthorized(Json::Value const &jvParams)
Definition: RPCCall.cpp:494
ripple::rpcCmdLineToJson
static Json::Value rpcCmdLineToJson(std::vector< std::string > const &args, Json::Value &retParams, beast::Journal j)
Definition: RPCCall.cpp:1444
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
ripple::RPCCallImp::onResponse
static bool onResponse(std::function< void(Json::Value const &jvInput)> callbackFuncP, const boost::system::error_code &ecResult, int iStatus, std::string const &strData, beast::Journal j)
Definition: RPCCall.cpp:1370
ripple::rpcClient
std::pair< int, Json::Value > rpcClient(std::vector< std::string > const &args, Config const &config, Logs &logs, std::unordered_map< std::string, std::string > const &headers)
Internal invocation of RPC client.
Definition: RPCCall.cpp:1513
ripple::TokenType::AccountPublic
@ AccountPublic
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:73
ripple::RPCParser::parseLedgerId
Json::Value parseLedgerId(Json::Value const &jvParams)
Definition: RPCCall.cpp:716
ripple::rpcLGR_IDX_MALFORMED
@ rpcLGR_IDX_MALFORMED
Definition: ErrorCodes.h:113
ripple::rpcCHANNEL_MALFORMED
@ rpcCHANNEL_MALFORMED
Definition: ErrorCodes.h:100
std::string::empty
T empty(T... args)
ripple::RPCParser::parseGetCounts
Json::Value parseGetCounts(Json::Value const &jvParams)
Definition: RPCCall.cpp:542
ripple::systemName
static std::string const & systemName()
Definition: SystemParameters.h:33
ripple::HTTPClient::request
static void request(bool bSSL, boost::asio::io_service &io_service, std::string strSite, const unsigned short port, std::function< void(boost::asio::streambuf &sb, std::string const &strHost)> build, std::size_t responseMax, std::chrono::seconds timeout, std::function< bool(const boost::system::error_code &ecResult, int iStatus, std::string const &strData)> complete, beast::Journal &j)
Definition: HTTPClient.cpp:585
std::ostringstream::str
T str(T... args)
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
Json::Value::asInt
Int asInt() const
Definition: json_value.cpp:503
ripple::RPCParser::isValidJson2
bool isValidJson2(Json::Value const &jv)
Definition: RPCCall.cpp:606
ripple::RPCCall::fromNetwork
void fromNetwork(boost::asio::io_service &io_service, std::string const &strIp, const std::uint16_t iPort, std::string const &strUsername, std::string const &strPassword, std::string const &strPath, std::string const &strMethod, Json::Value const &jvParams, const bool bSSL, const bool quiet, Logs &logs, std::function< void(Json::Value const &jvInput)> callbackFuncP, std::unordered_map< std::string, std::string > headers)
Definition: RPCCall.cpp:1679
ripple::RPCParser::parseJson
Json::Value parseJson(Json::Value const &jvParams)
Definition: RPCCall.cpp:584
ripple::RPC::make_param_error
Json::Value make_param_error(std::string const &message)
Returns a new json object that indicates invalid parameters.
Definition: ErrorCodes.h:224
ripple::RPCParser::parseSignFor
Json::Value parseSignFor(Json::Value const &jvParams)
Definition: RPCCall.cpp:555
ripple::ServerHandlerImp::Setup::client_t::password
std::string password
Definition: ServerHandlerImp.h:64
Json::Value::begin
const_iterator begin() const
Definition: json_value.cpp:1046
ripple::RPCParser::parsePeerReservationsDel
Json::Value parsePeerReservationsDel(Json::Value const &jvParams)
Definition: RPCCall.cpp:947
unordered_map
ripple::RPCParser::parseAccountChannels
Json::Value parseAccountChannels(Json::Value const &jvParams)
Definition: RPCCall.cpp:783
ripple::Config::rpc_ip
boost::optional< beast::IP::Endpoint > rpc_ip
Definition: Config.h:177
type_traits
Json::Value::isObjectOrNull
bool isObjectOrNull() const
Definition: json_value.cpp:1033
ripple::RPCParser
Definition: RPCCall.cpp:92
ripple::RPCParser::validPublicKey
static bool validPublicKey(std::string const &strPk)
Definition: RPCCall.cpp:154
std::exception::what
T what(T... args)
ripple::RPCParser::parseTransactionEntry
Json::Value parseTransactionEntry(Json::Value const &jvParams)
Definition: RPCCall.cpp:1042
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::RPCParser::parseSignSubmit
Json::Value parseSignSubmit(Json::Value const &jvParams)
Definition: RPCCall.cpp:983
ripple::rpcBAD_KEY_TYPE
@ rpcBAD_KEY_TYPE
Definition: ErrorCodes.h:133
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469