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