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 <format>
26#include <memory>
27#include <optional>
28
29namespace xrpl {
30
31std::optional<json::Value>
33{
34 if (!taker.isMember(jss::currency) && !taker.isMember(jss::mpt_issuance_id))
35 {
36 return rpc::missingFieldError(std::format("{}.currency", name.cStr()));
37 }
38
39 if (taker.isMember(jss::mpt_issuance_id) &&
40 (taker.isMember(jss::currency) || taker.isMember(jss::issuer)))
41 {
42 return rpc::invalidFieldError(name.cStr());
43 }
44
45 if ((taker.isMember(jss::currency) && !taker[jss::currency].isString()) ||
46 (taker.isMember(jss::mpt_issuance_id) && !taker[jss::mpt_issuance_id].isString()))
47 {
48 return rpc::expectedFieldError(std::format("{}.currency", name.cStr()), "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()) << std::format("Bad {} currency.", name.cStr());
74 return rpc::makeError(
75 assetError, std::format("Invalid field '{}.currency', bad currency.", name.cStr()));
76 }
77 asset = issue;
78 }
79 else if (taker.isMember(jss::mpt_issuance_id))
80 {
81 MPTID mptid;
82 if (!mptid.parseHex(taker[jss::mpt_issuance_id].asString()))
83 {
84 return rpc::makeError(
85 assetError, std::format("Invalid field '{}.mpt_issuance_id'", name.cStr()));
86 }
87 asset = mptid;
88 }
89
90 return std::nullopt;
91}
92
95 Asset& asset,
96 json::Value const& taker,
97 json::StaticString const& name,
99{
100 auto const issuerError = [&]() {
101 if (name == jss::taker_pays)
102 return RpcSrcIsrMalformed;
103 return RpcDstIsrMalformed;
104 }();
105
106 if (taker.isMember(jss::currency))
107 {
108 auto& issue = asset.get<Issue>();
109
110 if (taker.isMember(jss::issuer))
111 {
112 if (!taker[jss::issuer].isString())
113 {
114 return rpc::expectedFieldError(std::format("{}.issuer", name.cStr()), "string");
115 }
116
117 if (!toIssuer(issue.account, taker[jss::issuer].asString()))
118 {
119 return rpc::makeError(
120 issuerError,
121 std::format("Invalid field '{}.issuer', bad issuer.", name.cStr()));
122 }
123
124 if (issue.account == noAccount())
125 {
126 return rpc::makeError(
127 issuerError,
128 std::format("Invalid field '{}.issuer', bad issuer account one.", name.cStr()));
129 }
130 }
131 else
132 {
133 issue.account = xrpAccount();
134 }
135
136 if (isXRP(issue.currency) && !isXRP(issue.account))
137 {
138 return rpc::makeError(
139 issuerError,
141 "Unneeded field '{}.issuer' for XRP currency "
142 "specification.",
143 name.cStr()));
144 }
145
146 if (!isXRP(issue.currency) && isXRP(issue.account))
147 {
148 return rpc::makeError(
149 issuerError,
150 std::format("Invalid field '{}.issuer', expected non-XRP issuer.", name.cStr()));
151 }
152 }
153
154 return std::nullopt;
155}
156
159{
160 // VFALCO TODO Here is a terrible place for this kind of business
161 // logic. It needs to be moved elsewhere and documented,
162 // and encapsulated into a function.
163 if (context.app.getJobQueue().getJobCountGE(JtClient) > 200)
164 return rpcError(RpcTooBusy);
165
167 auto jvResult = rpc::lookupLedger(lpLedger, context);
168
169 if (!lpLedger)
170 return jvResult;
171
172 if (!context.params.isMember(jss::taker_pays))
173 return rpc::missingFieldError(jss::taker_pays);
174
175 if (!context.params.isMember(jss::taker_gets))
176 return rpc::missingFieldError(jss::taker_gets);
177
178 json::Value const& takerPays = context.params[jss::taker_pays];
179 json::Value const& takerGets = context.params[jss::taker_gets];
180
181 if (!takerPays.isObjectOrNull())
182 return rpc::objectFieldError(jss::taker_pays);
183
184 if (!takerGets.isObjectOrNull())
185 return rpc::objectFieldError(jss::taker_gets);
186
187 if (auto const err = validateTakerJSON(takerPays, jss::taker_pays))
188 return *err;
189
190 if (auto const err = validateTakerJSON(takerGets, jss::taker_gets))
191 return *err;
192
193 Book book;
194
195 if (auto const err = parseTakerAssetJSON(book.in, takerPays, jss::taker_pays, context.j))
196 return *err;
197
198 if (auto const err = parseTakerAssetJSON(book.out, takerGets, jss::taker_gets, context.j))
199 return *err;
200
201 if (auto const err = parseTakerIssuerJSON(book.in, takerPays, jss::taker_pays, context.j))
202 return *err;
203
204 if (auto const err = parseTakerIssuerJSON(book.out, takerGets, jss::taker_gets, context.j))
205 return *err;
206
208 if (context.params.isMember(jss::taker))
209 {
210 if (!context.params[jss::taker].isString())
211 return rpc::expectedFieldError(jss::taker, "string");
212
213 takerID = parseBase58<AccountID>(context.params[jss::taker].asString());
214 if (!takerID)
215 return rpc::invalidFieldError(jss::taker);
216 }
217
219 if (context.params.isMember(jss::domain))
220 {
221 uint256 num;
222 if (!context.params[jss::domain].isString() ||
223 !num.parseHex(context.params[jss::domain].asString()))
224 {
225 return rpc::makeError(RpcDomainMalformed, "Unable to parse domain.");
226 }
227
228 domain = num;
229 }
230
231 if (book.in == book.out)
232 {
233 JLOG(context.j.info()) << "taker_gets same as taker_pays.";
235 }
236
237 unsigned int limit = 0;
238 if (auto err = readLimitField(limit, rpc::tuning::kBookOffers, context))
239 return *err;
240
241 bool const bProof(context.params.isMember(jss::proof));
242
243 json::Value const jvMarker(
244 context.params.isMember(jss::marker) ? context.params[jss::marker]
246
247 context.netOps.getBookPage(
248 lpLedger,
249 {book.in, book.out, domain},
250 takerID ? *takerID : beast::kZero,
251 bProof,
252 limit,
253 jvMarker,
254 jvResult);
255
257
258 return jvResult;
259}
260
261} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream info() const
Definition Journal.h:350
Lightweight wrapper to tag static string.
Definition json_value.h:48
constexpr char const * cStr() const
Definition json_value.h:61
Represents a JSON value.
Definition json_value.h:117
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:525
Specifies an order book.
Definition Book.h:28
A currency issued by an account.
Definition Issue.h:18
Currency currency
Definition Issue.h:20
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
T format(T... args)
constexpr Zero kZero
Definition Zero.h:30
@ Null
'null' value
Definition json_value.h:22
Charge const kFeeMediumBurdenRpc
static constexpr LimitRange kBookOffers
Limits for the book_offers command.
json::Value expectedFieldError(std::string const &name, std::string const &type)
Definition ErrorCodes.h:309
json::Value invalidFieldError(std::string const &name)
Definition ErrorCodes.h:285
json::Value missingFieldError(std::string const &name)
Definition ErrorCodes.h:243
json::Value makeError(ErrorCodeI code)
Returns a new json object that reflects the error code.
json::Value objectFieldError(std::string const &name)
Definition ErrorCodes.h:261
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.
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:108
@ RpcDstAmtMalformed
Definition ErrorCodes.h:89
@ RpcBadMarket
Definition ErrorCodes.h:80
@ RpcDomainMalformed
Definition ErrorCodes.h:141
@ RpcSrcCurMalformed
Definition ErrorCodes.h:107
@ RpcSrcIsrMalformed
Definition ErrorCodes.h:108
@ RpcTooBusy
Definition ErrorCodes.h:39
@ RpcDstIsrMalformed
Definition ErrorCodes.h:91
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:84
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 doBookOffers(rpc::JsonContext &)
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
@ JtClient
Definition Job.h:31
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:54
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.
bool toIssuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
BaseUInt< 256 > uint256
Definition base_uint.h:580
resource::Charge & loadType
Definition Context.h:30
beast::Journal const j
Definition Context.h:28
NetworkOPs & netOps
Definition Context.h:31
Application & app
Definition Context.h:29
json::Value params
Definition Context.h:51