rippled
Loading...
Searching...
No Matches
Subscribe.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2014 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 <xrpld/app/ledger/LedgerMaster.h>
21#include <xrpld/app/main/Application.h>
22#include <xrpld/app/misc/NetworkOPs.h>
23#include <xrpld/rpc/Context.h>
24#include <xrpld/rpc/RPCSub.h>
25#include <xrpld/rpc/Role.h>
26#include <xrpld/rpc/detail/RPCHelpers.h>
27
28#include <xrpl/basics/Log.h>
29#include <xrpl/ledger/ReadView.h>
30#include <xrpl/protocol/ErrorCodes.h>
31#include <xrpl/protocol/RPCErr.h>
32#include <xrpl/protocol/jss.h>
33#include <xrpl/resource/Fees.h>
34
35namespace ripple {
36
39{
40 InfoSub::pointer ispSub;
42
43 if (!context.infoSub && !context.params.isMember(jss::url))
44 {
45 // Must be a JSON-RPC call.
46 JLOG(context.j.info()) << "doSubscribe: RPC subscribe requires a url";
48 }
49
50 if (context.params.isMember(jss::url))
51 {
52 if (context.role != Role::ADMIN)
54
55 std::string strUrl = context.params[jss::url].asString();
56 std::string strUsername = context.params.isMember(jss::url_username)
57 ? context.params[jss::url_username].asString()
58 : "";
59 std::string strPassword = context.params.isMember(jss::url_password)
60 ? context.params[jss::url_password].asString()
61 : "";
62
63 // DEPRECATED
64 if (context.params.isMember(jss::username))
65 strUsername = context.params[jss::username].asString();
66
67 // DEPRECATED
68 if (context.params.isMember(jss::password))
69 strPassword = context.params[jss::password].asString();
70
71 ispSub = context.netOps.findRpcSub(strUrl);
72 if (!ispSub)
73 {
74 JLOG(context.j.debug()) << "doSubscribe: building: " << strUrl;
75 try
76 {
77 auto rspSub = make_RPCSub(
78 context.app.getOPs(),
79 context.app.getIOContext(),
80 context.app.getJobQueue(),
81 strUrl,
82 strUsername,
83 strPassword,
84 context.app.logs());
85 ispSub = context.netOps.addRpcSub(
87 }
88 catch (std::runtime_error& ex)
89 {
90 return RPC::make_param_error(ex.what());
91 }
92 }
93 else
94 {
95 JLOG(context.j.trace()) << "doSubscribe: reusing: " << strUrl;
96
97 if (auto rpcSub = std::dynamic_pointer_cast<RPCSub>(ispSub))
98 {
99 // Why do we need to check isMember against jss::username and
100 // jss::password here instead of just setting the username and
101 // the password? What about url_username and url_password?
102 if (context.params.isMember(jss::username))
103 rpcSub->setUsername(strUsername);
104
105 if (context.params.isMember(jss::password))
106 rpcSub->setPassword(strPassword);
107 }
108 }
109 }
110 else
111 {
112 ispSub = context.infoSub;
113 }
114 ispSub->setApiVersion(context.apiVersion);
115
116 if (context.params.isMember(jss::streams))
117 {
118 if (!context.params[jss::streams].isArray())
119 {
120 JLOG(context.j.info()) << "doSubscribe: streams requires an array.";
122 }
123
124 for (auto const& it : context.params[jss::streams])
125 {
126 if (!it.isString())
128
129 std::string streamName = it.asString();
130 if (streamName == "server")
131 {
132 context.netOps.subServer(
133 ispSub, jvResult, context.role == Role::ADMIN);
134 }
135 else if (streamName == "ledger")
136 {
137 context.netOps.subLedger(ispSub, jvResult);
138 }
139 else if (streamName == "book_changes")
140 {
141 context.netOps.subBookChanges(ispSub);
142 }
143 else if (streamName == "manifests")
144 {
145 context.netOps.subManifests(ispSub);
146 }
147 else if (streamName == "transactions")
148 {
149 context.netOps.subTransactions(ispSub);
150 }
151 else if (
152 streamName == "transactions_proposed" ||
153 streamName == "rt_transactions") // DEPRECATED
154 {
155 context.netOps.subRTTransactions(ispSub);
156 }
157 else if (streamName == "validations")
158 {
159 context.netOps.subValidations(ispSub);
160 }
161 else if (streamName == "peer_status")
162 {
163 if (context.role != Role::ADMIN)
165 context.netOps.subPeerStatus(ispSub);
166 }
167 else if (streamName == "consensus")
168 {
169 context.netOps.subConsensus(ispSub);
170 }
171 else
172 {
174 }
175 }
176 }
177
178 auto accountsProposed = context.params.isMember(jss::accounts_proposed)
179 ? jss::accounts_proposed
180 : jss::rt_accounts; // DEPRECATED
181 if (context.params.isMember(accountsProposed))
182 {
183 if (!context.params[accountsProposed].isArray())
185
186 auto ids = RPC::parseAccountIds(context.params[accountsProposed]);
187 if (ids.empty())
189 context.netOps.subAccount(ispSub, ids, true);
190 }
191
192 if (context.params.isMember(jss::accounts))
193 {
194 if (!context.params[jss::accounts].isArray())
196
197 auto ids = RPC::parseAccountIds(context.params[jss::accounts]);
198 if (ids.empty())
200 context.netOps.subAccount(ispSub, ids, false);
201 JLOG(context.j.debug()) << "doSubscribe: accounts: " << ids.size();
202 }
203
204 if (context.params.isMember(jss::account_history_tx_stream))
205 {
206 if (!context.app.config().useTxTables())
207 return rpcError(rpcNOT_ENABLED);
208
210 auto const& req = context.params[jss::account_history_tx_stream];
211 if (!req.isMember(jss::account) || !req[jss::account].isString())
213
214 auto const id = parseBase58<AccountID>(req[jss::account].asString());
215 if (!id)
217
218 if (auto result = context.netOps.subAccountHistory(ispSub, *id);
219 result != rpcSUCCESS)
220 {
221 return rpcError(result);
222 }
223
224 jvResult[jss::warning] =
225 "account_history_tx_stream is an experimental feature and likely "
226 "to be removed in the future";
227 JLOG(context.j.debug())
228 << "doSubscribe: account_history_tx_stream: " << toBase58(*id);
229 }
230
231 if (context.params.isMember(jss::books))
232 {
233 if (!context.params[jss::books].isArray())
235
236 for (auto& j : context.params[jss::books])
237 {
238 if (!j.isObject() || !j.isMember(jss::taker_pays) ||
239 !j.isMember(jss::taker_gets) ||
240 !j[jss::taker_pays].isObjectOrNull() ||
241 !j[jss::taker_gets].isObjectOrNull())
243
244 Book book;
245 Json::Value taker_pays = j[jss::taker_pays];
246 Json::Value taker_gets = j[jss::taker_gets];
247
248 // Parse mandatory currency.
249 if (!taker_pays.isMember(jss::currency) ||
251 book.in.currency, taker_pays[jss::currency].asString()))
252 {
253 JLOG(context.j.info()) << "Bad taker_pays currency.";
255 }
256
257 // Parse optional issuer.
258 if (((taker_pays.isMember(jss::issuer)) &&
259 (!taker_pays[jss::issuer].isString() ||
260 !to_issuer(
261 book.in.account, taker_pays[jss::issuer].asString())))
262 // Don't allow illegal issuers.
263 || (!book.in.currency != !book.in.account) ||
264 noAccount() == book.in.account)
265 {
266 JLOG(context.j.info()) << "Bad taker_pays issuer.";
268 }
269
270 // Parse mandatory currency.
271 if (!taker_gets.isMember(jss::currency) ||
273 book.out.currency, taker_gets[jss::currency].asString()))
274 {
275 JLOG(context.j.info()) << "Bad taker_gets currency.";
277 }
278
279 // Parse optional issuer.
280 if (((taker_gets.isMember(jss::issuer)) &&
281 (!taker_gets[jss::issuer].isString() ||
282 !to_issuer(
283 book.out.account, taker_gets[jss::issuer].asString())))
284 // Don't allow illegal issuers.
285 || (!book.out.currency != !book.out.account) ||
286 noAccount() == book.out.account)
287 {
288 JLOG(context.j.info()) << "Bad taker_gets issuer.";
290 }
291
292 if (book.in.currency == book.out.currency &&
293 book.in.account == book.out.account)
294 {
295 JLOG(context.j.info()) << "taker_gets same as taker_pays.";
296 return rpcError(rpcBAD_MARKET);
297 }
298
300
301 if (j.isMember(jss::taker))
302 {
303 takerID = parseBase58<AccountID>(j[jss::taker].asString());
304 if (!takerID)
305 return rpcError(rpcBAD_ISSUER);
306 }
307
308 if (j.isMember(jss::domain))
309 {
310 uint256 domain;
311 if (!j[jss::domain].isString() ||
312 !domain.parseHex(j[jss::domain].asString()))
313 {
315 }
316 else
317 {
318 book.domain = domain;
319 }
320 }
321
322 if (!isConsistent(book))
323 {
324 JLOG(context.j.warn()) << "Bad market: " << book;
325 return rpcError(rpcBAD_MARKET);
326 }
327
328 context.netOps.subBook(ispSub, book);
329
330 // both_sides is deprecated.
331 bool const both =
332 (j.isMember(jss::both) && j[jss::both].asBool()) ||
333 (j.isMember(jss::both_sides) && j[jss::both_sides].asBool());
334
335 if (both)
336 context.netOps.subBook(ispSub, reversed(book));
337
338 // state_now is deprecated.
339 if ((j.isMember(jss::snapshot) && j[jss::snapshot].asBool()) ||
340 (j.isMember(jss::state_now) && j[jss::state_now].asBool()))
341 {
345 if (lpLedger)
346 {
347 Json::Value const jvMarker = Json::Value(Json::nullValue);
349
350 auto add = [&](Json::StaticString field) {
351 context.netOps.getBookPage(
352 lpLedger,
353 field == jss::asks ? reversed(book) : book,
354 takerID ? *takerID : noAccount(),
355 false,
357 jvMarker,
358 jvOffers);
359
360 if (jvResult.isMember(field))
361 {
362 Json::Value& results(jvResult[field]);
363 for (auto const& e : jvOffers[jss::offers])
364 results.append(e);
365 }
366 else
367 {
368 jvResult[field] = jvOffers[jss::offers];
369 }
370 };
371
372 if (both)
373 {
374 add(jss::bids);
375 add(jss::asks);
376 }
377 else
378 {
379 add(jss::offers);
380 }
381 }
382 }
383 }
384 }
385
386 return jvResult;
387}
388
389} // namespace ripple
Lightweight wrapper to tag static string.
Definition json_value.h:63
Represents a JSON value.
Definition json_value.h:149
bool isArray() const
Value & append(Value const &value)
Append value to array at the end.
bool isString() const
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
Stream debug() const
Definition Journal.h:328
Stream info() const
Definition Journal.h:334
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
Stream warn() const
Definition Journal.h:340
virtual Config & config()=0
virtual JobQueue & getJobQueue()=0
virtual NetworkOPs & getOPs()=0
virtual LedgerMaster & getLedgerMaster()=0
virtual Logs & logs()=0
virtual boost::asio::io_context & getIOContext()=0
Specifies an order book.
Definition Book.h:36
Issue in
Definition Book.h:38
Issue out
Definition Book.h:39
std::optional< uint256 > domain
Definition Book.h:40
bool useTxTables() const
Definition Config.h:342
virtual error_code_i subAccountHistory(ref ispListener, AccountID const &account)=0
subscribe an account's new transactions and retrieve the account's historical transactions
virtual bool subValidations(ref ispListener)=0
virtual bool subBook(ref ispListener, Book const &)=0
virtual bool subServer(ref ispListener, Json::Value &jvResult, bool admin)=0
virtual bool subBookChanges(ref ispListener)=0
virtual bool subConsensus(ref ispListener)=0
virtual bool subManifests(ref ispListener)=0
virtual pointer findRpcSub(std::string const &strUrl)=0
virtual bool subPeerStatus(ref ispListener)=0
virtual bool subTransactions(ref ispListener)=0
virtual bool subLedger(ref ispListener, Json::Value &jvResult)=0
virtual pointer addRpcSub(std::string const &strUrl, ref rspEntry)=0
virtual void subAccount(ref ispListener, hash_set< AccountID > const &vnaAccountIDs, bool realTime)=0
virtual bool subRTTransactions(ref ispListener)=0
void setApiVersion(unsigned int apiVersion)
Definition InfoSub.cpp:139
AccountID account
Definition Issue.h:36
Currency currency
Definition Issue.h:35
std::shared_ptr< ReadView const > getPublishedLedger()
virtual void getBookPage(std::shared_ptr< ReadView const > &lpLedger, Book const &book, AccountID const &uTakerID, bool const bProof, unsigned int iLimit, Json::Value const &jvMarker, Json::Value &jvResult)=0
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:503
T is_same_v
@ nullValue
'null' value
Definition json_value.h:38
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:45
static LimitRange constexpr bookOffers
Limits for the book_offers command.
Json::Value make_param_error(std::string const &message)
Returns a new json object that indicates invalid parameters.
Definition ErrorCodes.h:271
hash_set< AccountID > parseAccountIds(Json::Value const &jvArray)
Charge const feeMediumBurdenRPC
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
AccountID const & noAccount()
A placeholder for empty accounts.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
bool isConsistent(Book const &book)
Definition Book.cpp:29
bool to_issuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
@ rpcBAD_ISSUER
Definition ErrorCodes.h:96
@ rpcACT_MALFORMED
Definition ErrorCodes.h:90
@ rpcBAD_MARKET
Definition ErrorCodes.h:97
@ rpcSUCCESS
Definition ErrorCodes.h:44
@ rpcDOMAIN_MALFORMED
Definition ErrorCodes.h:158
@ rpcDST_ISR_MALFORMED
Definition ErrorCodes.h:108
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:84
@ rpcSTREAM_MALFORMED
Definition ErrorCodes.h:126
@ rpcSRC_ISR_MALFORMED
Definition ErrorCodes.h:125
@ rpcDST_AMT_MALFORMED
Definition ErrorCodes.h:106
@ rpcNOT_ENABLED
Definition ErrorCodes.h:59
@ rpcSRC_CUR_MALFORMED
Definition ErrorCodes.h:124
@ rpcNO_PERMISSION
Definition ErrorCodes.h:53
Book reversed(Book const &book)
Definition Book.cpp:49
Json::Value rpcError(int iError)
Definition RPCErr.cpp:31
Json::Value doSubscribe(RPC::JsonContext &)
Definition Subscribe.cpp:38
std::shared_ptr< RPCSub > make_RPCSub(InfoSub::Source &source, boost::asio::io_context &io_context, JobQueue &jobQueue, std::string const &strUrl, std::string const &strUsername, std::string const &strPassword, Logs &logs)
Definition RPCSub.cpp:208
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:84
unsigned int apiVersion
Definition Context.h:49
Resource::Charge & loadType
Definition Context.h:42
Application & app
Definition Context.h:41
InfoSub::pointer infoSub
Definition Context.h:48
beast::Journal const j
Definition Context.h:40
NetworkOPs & netOps
Definition Context.h:43
T what(T... args)