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/ledger/ReadView.h>
24#include <xrpld/net/RPCSub.h>
25#include <xrpld/rpc/Context.h>
26#include <xrpld/rpc/Role.h>
27#include <xrpld/rpc/detail/RPCHelpers.h>
28
29#include <xrpl/basics/Log.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.getIOService(),
80 context.app.getJobQueue(),
81 strUrl,
82 strUsername,
83 strPassword,
84 context.app.logs());
85 ispSub = context.netOps.addRpcSub(
86 strUrl, std::dynamic_pointer_cast<InfoSub>(rspSub));
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 (!isConsistent(book))
309 {
310 JLOG(context.j.warn()) << "Bad market: " << book;
311 return rpcError(rpcBAD_MARKET);
312 }
313
314 context.netOps.subBook(ispSub, book);
315
316 // both_sides is deprecated.
317 bool const both =
318 (j.isMember(jss::both) && j[jss::both].asBool()) ||
319 (j.isMember(jss::both_sides) && j[jss::both_sides].asBool());
320
321 if (both)
322 context.netOps.subBook(ispSub, reversed(book));
323
324 // state_now is deprecated.
325 if ((j.isMember(jss::snapshot) && j[jss::snapshot].asBool()) ||
326 (j.isMember(jss::state_now) && j[jss::state_now].asBool()))
327 {
331 if (lpLedger)
332 {
333 const Json::Value jvMarker = Json::Value(Json::nullValue);
335
336 auto add = [&](Json::StaticString field) {
337 context.netOps.getBookPage(
338 lpLedger,
339 field == jss::asks ? reversed(book) : book,
340 takerID ? *takerID : noAccount(),
341 false,
343 jvMarker,
344 jvOffers);
345
346 if (jvResult.isMember(field))
347 {
348 Json::Value& results(jvResult[field]);
349 for (auto const& e : jvOffers[jss::offers])
350 results.append(e);
351 }
352 else
353 {
354 jvResult[field] = jvOffers[jss::offers];
355 }
356 };
357
358 if (both)
359 {
360 add(jss::bids);
361 add(jss::asks);
362 }
363 else
364 {
365 add(jss::offers);
366 }
367 }
368 }
369 }
370 }
371
372 return jvResult;
373}
374
375} // namespace ripple
Lightweight wrapper to tag static string.
Definition: json_value.h:62
Represents a JSON value.
Definition: json_value.h:148
bool isArray() const
bool isString() const
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:897
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:475
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:949
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 boost::asio::io_service & getIOService()=0
virtual LedgerMaster & getLedgerMaster()=0
virtual Logs & logs()=0
Specifies an order book.
Definition: Book.h:35
Issue in
Definition: Book.h:37
Issue out
Definition: Book.h:38
bool useTxTables() const
Definition: Config.h:343
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:39
Currency currency
Definition: Issue.h:38
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
@ nullValue
'null' value
Definition: json_value.h:37
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
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:261
hash_set< AccountID > parseAccountIds(Json::Value const &jvArray)
Definition: RPCHelpers.cpp:657
Charge const feeMediumBurdenRPC
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:185
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:114
bool isConsistent(Book const &book)
Definition: Book.cpp:29
bool to_issuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
Definition: AccountID.cpp:192
@ rpcBAD_ISSUER
Definition: ErrorCodes.h:96
@ rpcACT_MALFORMED
Definition: ErrorCodes.h:90
@ rpcBAD_MARKET
Definition: ErrorCodes.h:97
@ rpcSUCCESS
Definition: ErrorCodes.h:44
@ 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
std::shared_ptr< RPCSub > make_RPCSub(InfoSub::Source &source, boost::asio::io_service &io_service, JobQueue &jobQueue, std::string const &strUrl, std::string const &strUsername, std::string const &strPassword, Logs &logs)
Definition: RPCSub.cpp:217
Json::Value rpcError(int iError)
Definition: RPCErr.cpp:31
Json::Value doSubscribe(RPC::JsonContext &)
Definition: Subscribe.cpp:38
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
Json::Value params
Definition: Context.h:63
T what(T... args)