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