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