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