xrpld
Loading...
Searching...
No Matches
BookOffers.cpp
1#include <xrpld/app/main/Application.h>
2#include <xrpld/rpc/Context.h>
3#include <xrpld/rpc/detail/RPCHelpers.h>
4#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
5#include <xrpld/rpc/detail/Tuning.h>
6
7#include <xrpl/basics/Log.h>
8#include <xrpl/basics/base_uint.h>
9#include <xrpl/beast/utility/Journal.h>
10#include <xrpl/beast/utility/Zero.h>
11#include <xrpl/core/Job.h>
12#include <xrpl/json/json_value.h>
13#include <xrpl/ledger/ReadView.h>
14#include <xrpl/protocol/AccountID.h>
15#include <xrpl/protocol/Asset.h>
16#include <xrpl/protocol/Book.h>
17#include <xrpl/protocol/ErrorCodes.h>
18#include <xrpl/protocol/Issue.h>
19#include <xrpl/protocol/RPCErr.h>
20#include <xrpl/protocol/UintTypes.h>
21#include <xrpl/protocol/jss.h>
22#include <xrpl/resource/Fees.h>
23#include <xrpl/server/NetworkOPs.h>
24
25#include <memory>
26#include <optional>
27
28namespace xrpl {
29
30std::optional<json::Value>
32{
33 if (!taker.isMember(jss::currency) && !taker.isMember(jss::mpt_issuance_id))
34 {
35 return RPC::missingFieldError((boost::format("%s.currency") % name.cStr()).str());
36 }
37
38 if (taker.isMember(jss::mpt_issuance_id) &&
39 (taker.isMember(jss::currency) || taker.isMember(jss::issuer)))
40 {
41 return RPC::invalidFieldError(name.cStr());
42 }
43
44 if ((taker.isMember(jss::currency) && !taker[jss::currency].isString()) ||
45 (taker.isMember(jss::mpt_issuance_id) && !taker[jss::mpt_issuance_id].isString()))
46 {
48 (boost::format("%s.currency") % name.cStr()).str(), "string");
49 }
50
51 return std::nullopt;
52}
53
56 Asset& asset,
57 json::Value const& taker,
58 json::StaticString const& name,
60{
61 auto const assetError = [&]() {
62 if (name == jss::taker_pays)
63 return RpcSrcCurMalformed;
64 return RpcDstAmtMalformed;
65 }();
66
67 if (taker.isMember(jss::currency))
68 {
69 Issue issue = xrpIssue();
70
71 if (!toCurrency(issue.currency, taker[jss::currency].asString()))
72 {
73 JLOG(j.info()) << boost::format("Bad %s currency.") % name.cStr();
74 return RPC::makeError(
75 assetError,
76 (boost::format("Invalid field '%s.currency', bad currency.") % name.cStr()).str());
77 }
78 asset = issue;
79 }
80 else if (taker.isMember(jss::mpt_issuance_id))
81 {
82 MPTID mptid;
83 if (!mptid.parseHex(taker[jss::mpt_issuance_id].asString()))
84 {
85 return RPC::makeError(
86 assetError,
87 (boost::format("Invalid field '%s.mpt_issuance_id'") % name.cStr()).str());
88 }
89 asset = mptid;
90 }
91
92 return std::nullopt;
93}
94
97 Asset& asset,
98 json::Value const& taker,
99 json::StaticString const& name,
101{
102 auto const issuerError = [&]() {
103 if (name == jss::taker_pays)
104 return RpcSrcIsrMalformed;
105 return RpcDstIsrMalformed;
106 }();
107
108 if (taker.isMember(jss::currency))
109 {
110 Issue& issue = asset.get<Issue>();
111
112 if (taker.isMember(jss::issuer))
113 {
114 if (!taker[jss::issuer].isString())
115 {
117 (boost::format("%s.issuer") % name.cStr()).str(), "string");
118 }
119
120 if (!toIssuer(issue.account, taker[jss::issuer].asString()))
121 {
122 return RPC::makeError(
123 issuerError,
124 (boost::format("Invalid field '%s.issuer', bad issuer.") % name.cStr()).str());
125 }
126
127 if (issue.account == noAccount())
128 {
129 return RPC::makeError(
130 issuerError,
131 (boost::format("Invalid field '%s.issuer', bad issuer account one.") %
132 name.cStr())
133 .str());
134 }
135 }
136 else
137 {
138 issue.account = xrpAccount();
139 }
140
141 if (isXRP(issue.currency) && !isXRP(issue.account))
142 {
143 return RPC::makeError(
144 issuerError,
145 (boost::format(
146 "Unneeded field '%s.issuer' for XRP currency "
147 "specification.") %
148 name.cStr())
149 .str());
150 }
151
152 if (!isXRP(issue.currency) && isXRP(issue.account))
153 {
154 return RPC::makeError(
155 issuerError,
156 (boost::format("Invalid field '%s.issuer', expected non-XRP issuer.") % name.cStr())
157 .str());
158 }
159 }
160
161 return std::nullopt;
162}
163
166{
167 // VFALCO TODO Here is a terrible place for this kind of business
168 // logic. It needs to be moved elsewhere and documented,
169 // and encapsulated into a function.
170 if (context.app.getJobQueue().getJobCountGE(JtClient) > 200)
171 return rpcError(RpcTooBusy);
172
174 auto jvResult = RPC::lookupLedger(lpLedger, context);
175
176 if (!lpLedger)
177 return jvResult;
178
179 if (!context.params.isMember(jss::taker_pays))
180 return RPC::missingFieldError(jss::taker_pays);
181
182 if (!context.params.isMember(jss::taker_gets))
183 return RPC::missingFieldError(jss::taker_gets);
184
185 json::Value const& takerPays = context.params[jss::taker_pays];
186 json::Value const& takerGets = context.params[jss::taker_gets];
187
188 if (!takerPays.isObjectOrNull())
189 return RPC::objectFieldError(jss::taker_pays);
190
191 if (!takerGets.isObjectOrNull())
192 return RPC::objectFieldError(jss::taker_gets);
193
194 if (auto const err = validateTakerJSON(takerPays, jss::taker_pays))
195 return *err;
196
197 if (auto const err = validateTakerJSON(takerGets, jss::taker_gets))
198 return *err;
199
200 Book book;
201
202 if (auto const err = parseTakerAssetJSON(book.in, takerPays, jss::taker_pays, context.j))
203 return *err;
204
205 if (auto const err = parseTakerAssetJSON(book.out, takerGets, jss::taker_gets, context.j))
206 return *err;
207
208 if (auto const err = parseTakerIssuerJSON(book.in, takerPays, jss::taker_pays, context.j))
209 return *err;
210
211 if (auto const err = parseTakerIssuerJSON(book.out, takerGets, jss::taker_gets, context.j))
212 return *err;
213
215 if (context.params.isMember(jss::taker))
216 {
217 if (!context.params[jss::taker].isString())
218 return RPC::expectedFieldError(jss::taker, "string");
219
220 takerID = parseBase58<AccountID>(context.params[jss::taker].asString());
221 if (!takerID)
222 return RPC::invalidFieldError(jss::taker);
223 }
224
226 if (context.params.isMember(jss::domain))
227 {
228 uint256 num;
229 if (!context.params[jss::domain].isString() ||
230 !num.parseHex(context.params[jss::domain].asString()))
231 {
232 return RPC::makeError(RpcDomainMalformed, "Unable to parse domain.");
233 }
234
235 domain = num;
236 }
237
238 if (book.in == book.out)
239 {
240 JLOG(context.j.info()) << "taker_gets same as taker_pays.";
242 }
243
244 unsigned int limit = 0;
245 if (auto err = readLimitField(limit, RPC::Tuning::kBookOffers, context))
246 return *err;
247
248 bool const bProof(context.params.isMember(jss::proof));
249
250 json::Value const jvMarker(
251 context.params.isMember(jss::marker) ? context.params[jss::marker]
253
254 context.netOps.getBookPage(
255 lpLedger,
256 {book.in, book.out, domain},
257 takerID ? *takerID : beast::kZero,
258 bProof,
259 limit,
260 jvMarker,
261 jvResult);
262
264
265 return jvResult;
266}
267
268} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
Stream info() const
Definition Journal.h:303
Lightweight wrapper to tag static string.
Definition json_value.h:44
constexpr char const * cStr() const
Definition json_value.h:57
Represents a JSON value.
Definition json_value.h:130
bool isString() const
std::string asString() const
Returns the unquoted string value.
bool isObjectOrNull() const
bool isMember(char const *key) const
Return true if the object has a member named key.
constexpr TIss const & get() const
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:507
Specifies an order book.
Definition Book.h:16
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
AccountID account
Definition Issue.h:16
int getJobCountGE(JobType t) const
All waiting jobs at or greater than this priority.
Definition JobQueue.cpp:140
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
virtual JobQueue & getJobQueue()=0
@ Null
'null' value
Definition json_value.h:19
static constexpr LimitRange kBookOffers
Limits for the book_offers command.
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext const &context, json::Value &result)
Looks up a ledger from a request and fills a json::Value with ledger data.
json::Value objectFieldError(std::string const &name)
Definition ErrorCodes.h:249
json::Value makeError(ErrorCodeI code)
Returns a new json object that reflects the error code.
json::Value invalidFieldError(std::string const &name)
Definition ErrorCodes.h:273
json::Value missingFieldError(std::string const &name)
Definition ErrorCodes.h:231
json::Value expectedFieldError(std::string const &name, std::string const &type)
Definition ErrorCodes.h:297
Charge const kFeeMediumBurdenRpc
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::optional< json::Value > parseTakerIssuerJSON(Asset &asset, json::Value const &taker, json::StaticString const &name, beast::Journal j)
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
@ RpcDstAmtMalformed
Definition ErrorCodes.h:88
@ RpcBadMarket
Definition ErrorCodes.h:79
@ RpcDomainMalformed
Definition ErrorCodes.h:140
@ RpcSrcCurMalformed
Definition ErrorCodes.h:106
@ RpcSrcIsrMalformed
Definition ErrorCodes.h:107
@ RpcTooBusy
Definition ErrorCodes.h:38
@ RpcDstIsrMalformed
Definition ErrorCodes.h:90
std::optional< json::Value > parseTakerAssetJSON(Asset &asset, json::Value const &taker, json::StaticString const &name, beast::Journal j)
bool isXRP(AccountID const &c)
Definition AccountID.h:70
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
bool toCurrency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:65
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
@ JtClient
Definition Job.h:26
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:44
std::optional< json::Value > validateTakerJSON(json::Value const &taker, json::StaticString const &name)
AccountID const & noAccount()
A placeholder for empty accounts.
AccountID const & xrpAccount()
Compute AccountID from public key.
json::Value doBookOffers(RPC::JsonContext &)
bool toIssuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
BaseUInt< 256 > uint256
Definition base_uint.h:562
beast::Journal const j
Definition Context.h:20
Application & app
Definition Context.h:21
Resource::Charge & loadType
Definition Context.h:22
NetworkOPs & netOps
Definition Context.h:23
json::Value params
Definition Context.h:43