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