rippled
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 <ripple/app/main/Application.h>
21 #include <ripple/app/misc/NetworkOPs.h>
22 #include <ripple/app/ledger/LedgerMaster.h>
23 #include <ripple/basics/Log.h>
24 #include <ripple/ledger/ReadView.h>
25 #include <ripple/net/RPCErr.h>
26 #include <ripple/net/RPCSub.h>
27 #include <ripple/protocol/ErrorCodes.h>
28 #include <ripple/protocol/jss.h>
29 #include <ripple/resource/Fees.h>
30 #include <ripple/rpc/impl/RPCHelpers.h>
31 #include <ripple/rpc/Context.h>
32 #include <ripple/rpc/Role.h>
33 
34 namespace ripple {
35 
37 {
38  InfoSub::pointer ispSub;
39  Json::Value jvResult (Json::objectValue);
40 
41  if (! context.infoSub && ! context.params.isMember(jss::url))
42  {
43  // Must be a JSON-RPC call.
44  JLOG(context.j.info()) << "doSubscribe: RPC subscribe requires a url";
45  return rpcError (rpcINVALID_PARAMS);
46  }
47 
48  if (context.params.isMember(jss::url))
49  {
50  if (context.role != Role::ADMIN)
51  return rpcError(rpcNO_PERMISSION);
52 
53  std::string strUrl = context.params[jss::url].asString ();
54  std::string strUsername = context.params.isMember (jss::url_username) ?
55  context.params[jss::url_username].asString () : "";
56  std::string strPassword = context.params.isMember (jss::url_password) ?
57  context.params[jss::url_password].asString () : "";
58 
59  // DEPRECATED
60  if (context.params.isMember (jss::username))
61  strUsername = context.params[jss::username].asString ();
62 
63  // DEPRECATED
64  if (context.params.isMember (jss::password))
65  strPassword = context.params[jss::password].asString ();
66 
67  ispSub = context.netOps.findRpcSub(strUrl);
68  if (! ispSub)
69  {
70  JLOG (context.j.debug())
71  << "doSubscribe: building: " << strUrl;
72  try
73  {
74  auto rspSub = make_RPCSub (context.app.getOPs (),
75  context.app.getIOService (), context.app.getJobQueue (),
76  strUrl, strUsername, strPassword, context.app.logs ());
77  ispSub = context.netOps.addRpcSub (
78  strUrl, std::dynamic_pointer_cast<InfoSub> (rspSub));
79  }
80  catch (std::runtime_error& ex)
81  {
82  return RPC::make_param_error (ex.what());
83  }
84  }
85  else
86  {
87  JLOG (context.j.trace())
88  << "doSubscribe: reusing: " << strUrl;
89 
90  if (auto rpcSub = std::dynamic_pointer_cast<RPCSub> (ispSub))
91  {
92  // Why do we need to check isMember against jss::username and
93  // jss::password here instead of just setting the username and
94  // the password? What about url_username and url_password?
95  if (context.params.isMember (jss::username))
96  rpcSub->setUsername (strUsername);
97 
98  if (context.params.isMember (jss::password))
99  rpcSub->setPassword (strPassword);
100  }
101  }
102  }
103  else
104  {
105  ispSub = context.infoSub;
106  }
107 
108  if (context.params.isMember (jss::streams))
109  {
110  if (! context.params[jss::streams].isArray ())
111  {
112  JLOG (context.j.info())
113  << "doSubscribe: streams requires an array.";
114  return rpcError (rpcINVALID_PARAMS);
115  }
116 
117  for (auto const& it: context.params[jss::streams])
118  {
119  if (! it.isString())
121 
122  std::string streamName = it.asString ();
123  if (streamName == "server")
124  {
125  context.netOps.subServer (ispSub, jvResult,
126  context.role == Role::ADMIN);
127  }
128  else if (streamName == "ledger")
129  {
130  context.netOps.subLedger (ispSub, jvResult);
131  }
132  else if (streamName == "manifests")
133  {
134  context.netOps.subManifests (ispSub);
135  }
136  else if (streamName == "transactions")
137  {
138  context.netOps.subTransactions (ispSub);
139  }
140  else if (streamName == "transactions_proposed" ||
141  streamName == "rt_transactions") // DEPRECATED
142  {
143  context.netOps.subRTTransactions (ispSub);
144  }
145  else if (streamName == "validations")
146  {
147  context.netOps.subValidations (ispSub);
148  }
149  else if (streamName == "peer_status")
150  {
151  if (context.role != Role::ADMIN)
152  return rpcError(rpcNO_PERMISSION);
153  context.netOps.subPeerStatus (ispSub);
154  }
155  else if (streamName == "consensus")
156  {
157  context.netOps.subConsensus (ispSub);
158  }
159  else
160  {
162  }
163  }
164  }
165 
166  auto accountsProposed = context.params.isMember(jss::accounts_proposed)
167  ? jss::accounts_proposed : jss::rt_accounts; // DEPRECATED
168  if (context.params.isMember(accountsProposed))
169  {
170  if (! context.params[accountsProposed].isArray())
171  return rpcError(rpcINVALID_PARAMS);
172 
173  auto ids = RPC::parseAccountIds(context.params[accountsProposed]);
174  if (ids.empty())
175  return rpcError(rpcACT_MALFORMED);
176  context.netOps.subAccount(ispSub, ids, true);
177  }
178 
179  if (context.params.isMember(jss::accounts))
180  {
181  if (! context.params[jss::accounts].isArray())
182  return rpcError(rpcINVALID_PARAMS);
183 
184  auto ids = RPC::parseAccountIds(context.params[jss::accounts]);
185  if (ids.empty())
186  return rpcError(rpcACT_MALFORMED);
187  context.netOps.subAccount(ispSub, ids, false);
188  JLOG(context.j.debug()) << "doSubscribe: accounts: " << ids.size();
189  }
190 
191  if (context.params.isMember(jss::books))
192  {
193  if (! context.params[jss::books].isArray())
194  return rpcError (rpcINVALID_PARAMS);
195 
196  for (auto& j: context.params[jss::books])
197  {
198  if (!j.isObject()
199  || !j.isMember (jss::taker_pays)
200  || !j.isMember (jss::taker_gets)
201  || !j[jss::taker_pays].isObjectOrNull ()
202  || !j[jss::taker_gets].isObjectOrNull ())
203  return rpcError (rpcINVALID_PARAMS);
204 
205  Book book;
206  Json::Value taker_pays = j[jss::taker_pays];
207  Json::Value taker_gets = j[jss::taker_gets];
208 
209  // Parse mandatory currency.
210  if (! taker_pays.isMember (jss::currency) || ! to_currency (
211  book.in.currency, taker_pays[jss::currency].asString ()))
212  {
213  JLOG (context.j.info()) << "Bad taker_pays currency.";
215  }
216 
217  // Parse optional issuer.
218  if (((taker_pays.isMember (jss::issuer))
219  && (! taker_pays[jss::issuer].isString ()
220  || !to_issuer (book.in.account,
221  taker_pays[jss::issuer].asString ())))
222  // Don't allow illegal issuers.
223  || (!book.in.currency != !book.in.account)
224  || noAccount() == book.in.account)
225  {
226  JLOG (context.j.info()) << "Bad taker_pays issuer.";
228  }
229 
230  // Parse mandatory currency.
231  if (! taker_gets.isMember (jss::currency) || !to_currency (
232  book.out.currency, taker_gets[jss::currency].asString ()))
233  {
234  JLOG (context.j.info()) << "Bad taker_gets currency.";
236  }
237 
238  // Parse optional issuer.
239  if (((taker_gets.isMember (jss::issuer))
240  && (!taker_gets[jss::issuer].isString ()
241  || !to_issuer (book.out.account,
242  taker_gets[jss::issuer].asString ())))
243  // Don't allow illegal issuers.
244  || (!book.out.currency != !book.out.account)
245  || noAccount() == book.out.account)
246  {
247  JLOG (context.j.info()) << "Bad taker_gets issuer.";
249  }
250 
251  if (book.in.currency == book.out.currency
252  && book.in.account == book.out.account)
253  {
254  JLOG (context.j.info())
255  << "taker_gets same as taker_pays.";
256  return rpcError (rpcBAD_MARKET);
257  }
258 
259  boost::optional<AccountID> takerID;
260 
261  if (j.isMember (jss::taker))
262  {
263  takerID = parseBase58<AccountID>(
264  j[jss::taker].asString());
265  if (! takerID)
266  return rpcError (rpcBAD_ISSUER);
267  }
268 
269  if (!isConsistent (book))
270  {
271  JLOG (context.j.warn()) << "Bad market: " << book;
272  return rpcError (rpcBAD_MARKET);
273  }
274 
275  context.netOps.subBook (ispSub, book);
276 
277  // both_sides is deprecated.
278  bool const both =
279  (j.isMember(jss::both) && j[jss::both].asBool()) ||
280  (j.isMember(jss::both_sides) && j[jss::both_sides].asBool());
281 
282  if (both)
283  context.netOps.subBook(ispSub, reversed(book));
284 
285  // state_now is deprecated.
286  if ((j.isMember(jss::snapshot) && j[jss::snapshot].asBool()) ||
287  (j.isMember(jss::state_now) && j[jss::state_now].asBool()))
288  {
291  = context.app.getLedgerMaster().getPublishedLedger();
292  if (lpLedger)
293  {
294  const Json::Value jvMarker = Json::Value (Json::nullValue);
295  Json::Value jvOffers (Json::objectValue);
296 
297  auto add = [&](Json::StaticString field)
298  {
299  context.netOps.getBookPage (
300  lpLedger,
301  field == jss::asks ? reversed (book) : book,
302  takerID ? *takerID : noAccount(),
304  jvMarker, jvOffers);
305 
306  if (jvResult.isMember (field))
307  {
308  Json::Value& results (jvResult[field]);
309  for (auto const& e : jvOffers[jss::offers])
310  results.append (e);
311  }
312  else
313  {
314  jvResult[field] = jvOffers[jss::offers];
315  }
316  };
317 
318  if (both)
319  {
320  add (jss::bids);
321  add (jss::asks);
322  }
323  else
324  {
325  add (jss::offers);
326  }
327  }
328  }
329  }
330  }
331 
332  return jvResult;
333 }
334 
335 } // ripple
ripple::to_currency
bool to_currency(Currency &currency, std::string const &code)
Tries to convert a string to a Currency, returns true on success.
Definition: UintTypes.cpp:67
ripple::RPC::Context::infoSub
InfoSub::pointer infoSub
Definition: Context.h:49
ripple::RPC::JsonContext
Definition: Context.h:52
ripple::rpcDST_AMT_MALFORMED
@ rpcDST_AMT_MALFORMED
Definition: ErrorCodes.h:107
ripple::LedgerMaster::getPublishedLedger
std::shared_ptr< ReadView const > getPublishedLedger()
Definition: LedgerMaster.cpp:1524
std::string
STL class.
std::shared_ptr< InfoSub >
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:85
Json::Value::isString
bool isString() const
Definition: json_value.cpp:1049
ripple::isConsistent
bool isConsistent(Book const &book)
Definition: Book.cpp:25
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:287
ripple::Book::out
Issue out
Definition: Book.h:36
ripple::Resource::feeMediumBurdenRPC
const Charge feeMediumBurdenRPC
ripple::RPC::Context::loadType
Resource::Charge & loadType
Definition: Context.h:43
ripple::InfoSub::Source::subValidations
virtual bool subValidations(ref ispListener)=0
ripple::Issue::currency
Currency currency
Definition: Issue.h:37
beast::Journal::warn
Stream warn() const
Definition: Journal.h:302
ripple::RPC::Tuning::LimitRange::rdefault
unsigned int rdefault
Definition: rpc/impl/Tuning.h:32
ripple::InfoSub::Source::subRTTransactions
virtual bool subRTTransactions(ref ispListener)=0
ripple::InfoSub::Source::subTransactions
virtual bool subTransactions(ref ispListener)=0
ripple::RPC::Context::role
Role role
Definition: Context.h:47
ripple::RPC::Tuning::bookOffers
static constexpr LimitRange bookOffers
Limits for the book_offers command.
Definition: rpc/impl/Tuning.h:48
ripple::Application::getOPs
virtual NetworkOPs & getOPs()=0
ripple::InfoSub::Source::subBook
virtual bool subBook(ref ispListener, Book const &)=0
ripple::InfoSub::Source::findRpcSub
virtual pointer findRpcSub(std::string const &strUrl)=0
ripple::RPC::Context::j
const beast::Journal j
Definition: Context.h:41
ripple::InfoSub::Source::subManifests
virtual bool subManifests(ref ispListener)=0
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:907
ripple::Role::ADMIN
@ ADMIN
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:45
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::InfoSub::Source::subLedger
virtual bool subLedger(ref ispListener, Json::Value &jvResult)=0
ripple::NetworkOPs::getBookPage
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
ripple::reversed
Book reversed(Book const &book)
Definition: Book.cpp:45
ripple::Application::getJobQueue
virtual JobQueue & getJobQueue()=0
ripple::rpcSRC_ISR_MALFORMED
@ rpcSRC_ISR_MALFORMED
Definition: ErrorCodes.h:126
ripple::InfoSub::Source::addRpcSub
virtual pointer addRpcSub(std::string const &strUrl, ref rspEntry)=0
ripple::RPC::Context::app
Application & app
Definition: Context.h:42
beast::Journal::info
Stream info() const
Definition: Journal.h:297
ripple::rpcSTREAM_MALFORMED
@ rpcSTREAM_MALFORMED
Definition: ErrorCodes.h:127
ripple::Application::logs
virtual Logs & logs()=0
std::runtime_error
STL class.
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:961
ripple::rpcDST_ISR_MALFORMED
@ rpcDST_ISR_MALFORMED
Definition: ErrorCodes.h:109
ripple::rpcError
Json::Value rpcError(int iError, Json::Value jvResult)
Definition: RPCErr.cpp:28
ripple::make_RPCSub
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:208
ripple::RPC::Context::netOps
NetworkOPs & netOps
Definition: Context.h:44
ripple::Application::getIOService
virtual boost::asio::io_service & getIOService()=0
Json::Value::isArray
bool isArray() const
Definition: json_value.cpp:1056
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::rpcACT_MALFORMED
@ rpcACT_MALFORMED
Definition: ErrorCodes.h:91
ripple::rpcNO_PERMISSION
@ rpcNO_PERMISSION
Definition: ErrorCodes.h:54
Json::StaticString
Lightweight wrapper to tag static string.
Definition: json_value.h:62
Json::nullValue
@ nullValue
'null' value
Definition: json_value.h:38
beast::Journal::debug
Stream debug() const
Definition: Journal.h:292
ripple::Book
Specifies an order book.
Definition: Book.h:32
ripple::rpcBAD_MARKET
@ rpcBAD_MARKET
Definition: ErrorCodes.h:98
ripple::rpcSRC_CUR_MALFORMED
@ rpcSRC_CUR_MALFORMED
Definition: ErrorCodes.h:125
ripple::InfoSub::Source::subConsensus
virtual bool subConsensus(ref ispListener)=0
ripple::RPC::parseAccountIds
hash_set< AccountID > parseAccountIds(Json::Value const &jvArray)
Definition: RPCHelpers.cpp:517
ripple::InfoSub::Source::subPeerStatus
virtual bool subPeerStatus(ref ispListener)=0
ripple::RPC::make_param_error
Json::Value make_param_error(std::string const &message)
Returns a new json object that indicates invalid parameters.
Definition: ErrorCodes.h:219
ripple::rpcBAD_ISSUER
@ rpcBAD_ISSUER
Definition: ErrorCodes.h:97
ripple::RPC::JsonContext::params
Json::Value params
Definition: Context.h:63
ripple::InfoSub::Source::subServer
virtual bool subServer(ref ispListener, Json::Value &jvResult, bool admin)=0
ripple::InfoSub::Source::subAccount
virtual void subAccount(ref ispListener, hash_set< AccountID > const &vnaAccountIDs, bool realTime)=0
ripple::noAccount
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:156
ripple::Book::in
Issue in
Definition: Book.h:35
ripple::Issue::account
AccountID account
Definition: Issue.h:38
std::runtime_error::what
T what(T... args)
Json::Value
Represents a JSON value.
Definition: json_value.h:141
ripple::to_issuer
bool to_issuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
Definition: AccountID.cpp:163
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:482
ripple::doSubscribe
Json::Value doSubscribe(RPC::JsonContext &)
Definition: Subscribe.cpp:36