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#include <xrpl/basics/Log.h>
29#include <xrpl/protocol/ErrorCodes.h>
30#include <xrpl/protocol/RPCErr.h>
31#include <xrpl/protocol/jss.h>
32#include <xrpl/resource/Fees.h>
33
34namespace ripple {
35
38{
39 InfoSub::pointer ispSub;
41
42 if (!context.infoSub && !context.params.isMember(jss::url))
43 {
44 // Must be a JSON-RPC call.
45 JLOG(context.j.info()) << "doSubscribe: RPC subscribe requires a url";
47 }
48
49 if (context.params.isMember(jss::url))
50 {
51 if (context.role != Role::ADMIN)
53
54 std::string strUrl = context.params[jss::url].asString();
55 std::string strUsername = context.params.isMember(jss::url_username)
56 ? context.params[jss::url_username].asString()
57 : "";
58 std::string strPassword = context.params.isMember(jss::url_password)
59 ? context.params[jss::url_password].asString()
60 : "";
61
62 // DEPRECATED
63 if (context.params.isMember(jss::username))
64 strUsername = context.params[jss::username].asString();
65
66 // DEPRECATED
67 if (context.params.isMember(jss::password))
68 strPassword = context.params[jss::password].asString();
69
70 ispSub = context.netOps.findRpcSub(strUrl);
71 if (!ispSub)
72 {
73 JLOG(context.j.debug()) << "doSubscribe: building: " << strUrl;
74 try
75 {
76 auto rspSub = make_RPCSub(
77 context.app.getOPs(),
78 context.app.getIOService(),
79 context.app.getJobQueue(),
80 strUrl,
81 strUsername,
82 strPassword,
83 context.app.logs());
84 ispSub = context.netOps.addRpcSub(
85 strUrl, std::dynamic_pointer_cast<InfoSub>(rspSub));
86 }
87 catch (std::runtime_error& ex)
88 {
89 return RPC::make_param_error(ex.what());
90 }
91 }
92 else
93 {
94 JLOG(context.j.trace()) << "doSubscribe: reusing: " << strUrl;
95
96 if (auto rpcSub = std::dynamic_pointer_cast<RPCSub>(ispSub))
97 {
98 // Why do we need to check isMember against jss::username and
99 // jss::password here instead of just setting the username and
100 // the password? What about url_username and url_password?
101 if (context.params.isMember(jss::username))
102 rpcSub->setUsername(strUsername);
103
104 if (context.params.isMember(jss::password))
105 rpcSub->setPassword(strPassword);
106 }
107 }
108 }
109 else
110 {
111 ispSub = context.infoSub;
112 }
113 ispSub->setApiVersion(context.apiVersion);
114
115 if (context.params.isMember(jss::streams))
116 {
117 if (!context.params[jss::streams].isArray())
118 {
119 JLOG(context.j.info()) << "doSubscribe: streams requires an array.";
121 }
122
123 for (auto const& it : context.params[jss::streams])
124 {
125 if (!it.isString())
127
128 std::string streamName = it.asString();
129 if (streamName == "server")
130 {
131 context.netOps.subServer(
132 ispSub, jvResult, context.role == Role::ADMIN);
133 }
134 else if (streamName == "ledger")
135 {
136 context.netOps.subLedger(ispSub, jvResult);
137 }
138 else if (streamName == "book_changes")
139 {
140 context.netOps.subBookChanges(ispSub);
141 }
142 else if (streamName == "manifests")
143 {
144 context.netOps.subManifests(ispSub);
145 }
146 else if (streamName == "transactions")
147 {
148 context.netOps.subTransactions(ispSub);
149 }
150 else if (
151 streamName == "transactions_proposed" ||
152 streamName == "rt_transactions") // DEPRECATED
153 {
154 context.netOps.subRTTransactions(ispSub);
155 }
156 else if (streamName == "validations")
157 {
158 context.netOps.subValidations(ispSub);
159 }
160 else if (streamName == "peer_status")
161 {
162 if (context.role != Role::ADMIN)
164 context.netOps.subPeerStatus(ispSub);
165 }
166 else if (streamName == "consensus")
167 {
168 context.netOps.subConsensus(ispSub);
169 }
170 else
171 {
173 }
174 }
175 }
176
177 auto accountsProposed = context.params.isMember(jss::accounts_proposed)
178 ? jss::accounts_proposed
179 : jss::rt_accounts; // DEPRECATED
180 if (context.params.isMember(accountsProposed))
181 {
182 if (!context.params[accountsProposed].isArray())
184
185 auto ids = RPC::parseAccountIds(context.params[accountsProposed]);
186 if (ids.empty())
188 context.netOps.subAccount(ispSub, ids, true);
189 }
190
191 if (context.params.isMember(jss::accounts))
192 {
193 if (!context.params[jss::accounts].isArray())
195
196 auto ids = RPC::parseAccountIds(context.params[jss::accounts]);
197 if (ids.empty())
199 context.netOps.subAccount(ispSub, ids, false);
200 JLOG(context.j.debug()) << "doSubscribe: accounts: " << ids.size();
201 }
202
203 if (context.params.isMember(jss::account_history_tx_stream))
204 {
205 if (!context.app.config().useTxTables())
206 return rpcError(rpcNOT_ENABLED);
207
209 auto const& req = context.params[jss::account_history_tx_stream];
210 if (!req.isMember(jss::account) || !req[jss::account].isString())
212
213 auto const id = parseBase58<AccountID>(req[jss::account].asString());
214 if (!id)
216
217 if (auto result = context.netOps.subAccountHistory(ispSub, *id);
218 result != rpcSUCCESS)
219 {
220 return rpcError(result);
221 }
222
223 jvResult[jss::warning] =
224 "account_history_tx_stream is an experimental feature and likely "
225 "to be removed in the future";
226 JLOG(context.j.debug())
227 << "doSubscribe: account_history_tx_stream: " << toBase58(*id);
228 }
229
230 if (context.params.isMember(jss::books))
231 {
232 if (!context.params[jss::books].isArray())
234
235 for (auto& j : context.params[jss::books])
236 {
237 if (!j.isObject() || !j.isMember(jss::taker_pays) ||
238 !j.isMember(jss::taker_gets) ||
239 !j[jss::taker_pays].isObjectOrNull() ||
240 !j[jss::taker_gets].isObjectOrNull())
242
243 Book book;
244 Json::Value taker_pays = j[jss::taker_pays];
245 Json::Value taker_gets = j[jss::taker_gets];
246
247 // Parse mandatory currency.
248 if (!taker_pays.isMember(jss::currency) ||
250 book.in.currency, taker_pays[jss::currency].asString()))
251 {
252 JLOG(context.j.info()) << "Bad taker_pays currency.";
254 }
255
256 // Parse optional issuer.
257 if (((taker_pays.isMember(jss::issuer)) &&
258 (!taker_pays[jss::issuer].isString() ||
259 !to_issuer(
260 book.in.account, taker_pays[jss::issuer].asString())))
261 // Don't allow illegal issuers.
262 || (!book.in.currency != !book.in.account) ||
263 noAccount() == book.in.account)
264 {
265 JLOG(context.j.info()) << "Bad taker_pays issuer.";
267 }
268
269 // Parse mandatory currency.
270 if (!taker_gets.isMember(jss::currency) ||
272 book.out.currency, taker_gets[jss::currency].asString()))
273 {
274 JLOG(context.j.info()) << "Bad taker_gets currency.";
276 }
277
278 // Parse optional issuer.
279 if (((taker_gets.isMember(jss::issuer)) &&
280 (!taker_gets[jss::issuer].isString() ||
281 !to_issuer(
282 book.out.account, taker_gets[jss::issuer].asString())))
283 // Don't allow illegal issuers.
284 || (!book.out.currency != !book.out.account) ||
285 noAccount() == book.out.account)
286 {
287 JLOG(context.j.info()) << "Bad taker_gets issuer.";
289 }
290
291 if (book.in.currency == book.out.currency &&
292 book.in.account == book.out.account)
293 {
294 JLOG(context.j.info()) << "taker_gets same as taker_pays.";
295 return rpcError(rpcBAD_MARKET);
296 }
297
299
300 if (j.isMember(jss::taker))
301 {
302 takerID = parseBase58<AccountID>(j[jss::taker].asString());
303 if (!takerID)
304 return rpcError(rpcBAD_ISSUER);
305 }
306
307 if (!isConsistent(book))
308 {
309 JLOG(context.j.warn()) << "Bad market: " << book;
310 return rpcError(rpcBAD_MARKET);
311 }
312
313 context.netOps.subBook(ispSub, book);
314
315 // both_sides is deprecated.
316 bool const both =
317 (j.isMember(jss::both) && j[jss::both].asBool()) ||
318 (j.isMember(jss::both_sides) && j[jss::both_sides].asBool());
319
320 if (both)
321 context.netOps.subBook(ispSub, reversed(book));
322
323 // state_now is deprecated.
324 if ((j.isMember(jss::snapshot) && j[jss::snapshot].asBool()) ||
325 (j.isMember(jss::state_now) && j[jss::state_now].asBool()))
326 {
330 if (lpLedger)
331 {
332 const Json::Value jvMarker = Json::Value(Json::nullValue);
334
335 auto add = [&](Json::StaticString field) {
336 context.netOps.getBookPage(
337 lpLedger,
338 field == jss::asks ? reversed(book) : book,
339 takerID ? *takerID : noAccount(),
340 false,
342 jvMarker,
343 jvOffers);
344
345 if (jvResult.isMember(field))
346 {
347 Json::Value& results(jvResult[field]);
348 for (auto const& e : jvOffers[jss::offers])
349 results.append(e);
350 }
351 else
352 {
353 jvResult[field] = jvOffers[jss::offers];
354 }
355 };
356
357 if (both)
358 {
359 add(jss::bids);
360 add(jss::asks);
361 }
362 else
363 {
364 add(jss::offers);
365 }
366 }
367 }
368 }
369 }
370
371 return jvResult;
372}
373
374} // namespace ripple
Lightweight wrapper to tag static string.
Definition: json_value.h:61
Represents a JSON value.
Definition: json_value.h:147
bool isArray() const
bool isString() const
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:891
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:943
Stream debug() const
Definition: Journal.h:317
Stream info() const
Definition: Journal.h:323
Stream trace() const
Severity stream access functions.
Definition: Journal.h:311
Stream warn() const
Definition: Journal.h:329
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:34
Issue in
Definition: Book.h:36
Issue out
Definition: Book.h:37
bool useTxTables() const
Definition: Config.h:350
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:140
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:36
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
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:258
hash_set< AccountID > parseAccountIds(Json::Value const &jvArray)
Definition: RPCHelpers.cpp:658
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:177
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:106
bool isConsistent(Book const &book)
Definition: Book.cpp:25
bool to_issuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
Definition: AccountID.cpp:184
@ 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:45
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:215
Json::Value rpcError(int iError)
Definition: RPCErr.cpp:29
Json::Value doSubscribe(RPC::JsonContext &)
Definition: Subscribe.cpp:37
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition: UintTypes.cpp:80
unsigned int apiVersion
Definition: Context.h:50
Resource::Charge & loadType
Definition: Context.h:43
Application & app
Definition: Context.h:42
InfoSub::pointer infoSub
Definition: Context.h:49
beast::Journal const j
Definition: Context.h:41
NetworkOPs & netOps
Definition: Context.h:44
Json::Value params
Definition: Context.h:64
T what(T... args)