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