rippled
Loading...
Searching...
No Matches
DepositAuthorized_test.cpp
1#include <test/jtx.h>
2
3#include <xrpl/protocol/jss.h>
4
5namespace xrpl {
6namespace test {
7
9{
10public:
11 // Helper function that builds arguments for a deposit_authorized command.
12 static Json::Value
14 jtx::Account const& source,
15 jtx::Account const& dest,
16 std::string const& ledger = "",
17 std::vector<std::string> const& credentials = {})
18 {
20 args[jss::source_account] = source.human();
21 args[jss::destination_account] = dest.human();
22 if (!ledger.empty())
23 args[jss::ledger_index] = ledger;
24
25 if (!credentials.empty())
26 {
27 auto& arr(args[jss::credentials] = Json::arrayValue);
28 for (auto const& s : credentials)
29 arr.append(s);
30 }
31
32 return args;
33 }
34
35 // Helper function that verifies a deposit_authorized request was
36 // successful and returned the expected value.
37 void
39 {
40 Json::Value const& results{result[jss::result]};
41 BEAST_EXPECT(results[jss::deposit_authorized] == authorized);
42 BEAST_EXPECT(results[jss::status] == jss::success);
43 }
44
45 // Test a variety of non-malformed cases.
46 void
48 {
49 testcase("Valid");
50 using namespace jtx;
51 Account const alice{"alice"};
52 Account const becky{"becky"};
53 Account const carol{"carol"};
54
55 Env env(*this);
56 env.fund(XRP(1000), alice, becky, carol);
57 env.close();
58
59 // becky is authorized to deposit to herself.
61 env.rpc(
62 "json",
63 "deposit_authorized",
64 depositAuthArgs(becky, becky, "validated").toStyledString()),
65 true);
66
67 // alice should currently be authorized to deposit to becky.
69 env.rpc(
70 "json",
71 "deposit_authorized",
72 depositAuthArgs(alice, becky, "validated").toStyledString()),
73 true);
74
75 // becky sets the DepositAuth flag in the current ledger.
76 env(fset(becky, asfDepositAuth));
77
78 // alice is no longer authorized to deposit to becky in current ledger.
80 env.rpc(
81 "json",
82 "deposit_authorized",
83 depositAuthArgs(alice, becky).toStyledString()),
84 false);
85 env.close();
86
87 // becky is still authorized to deposit to herself.
89 env.rpc(
90 "json",
91 "deposit_authorized",
92 depositAuthArgs(becky, becky, "validated").toStyledString()),
93 true);
94
95 // It's not a reciprocal arrangement. becky can deposit to alice.
97 env.rpc(
98 "json",
99 "deposit_authorized",
100 depositAuthArgs(becky, alice, "current").toStyledString()),
101 true);
102
103 // becky creates a deposit authorization for alice.
104 env(deposit::auth(becky, alice));
105 env.close();
106
107 // alice is now authorized to deposit to becky.
109 env.rpc(
110 "json",
111 "deposit_authorized",
112 depositAuthArgs(alice, becky, "closed").toStyledString()),
113 true);
114
115 // carol is still not authorized to deposit to becky.
117 env.rpc(
118 "json",
119 "deposit_authorized",
120 depositAuthArgs(carol, becky).toStyledString()),
121 false);
122
123 // becky clears the DepositAuth flag so carol becomes authorized.
124 env(fclear(becky, asfDepositAuth));
125 env.close();
126
128 env.rpc(
129 "json",
130 "deposit_authorized",
131 depositAuthArgs(carol, becky).toStyledString()),
132 true);
133
134 // alice is still authorized to deposit to becky.
136 env.rpc(
137 "json",
138 "deposit_authorized",
139 depositAuthArgs(alice, becky).toStyledString()),
140 true);
141 }
142
143 // Test malformed cases.
144 void
146 {
147 testcase("Errors");
148 using namespace jtx;
149 Account const alice{"alice"};
150 Account const becky{"becky"};
151
152 // Lambda that checks the (error) result of deposit_authorized.
153 auto verifyErr = [this](
154 Json::Value const& result,
155 char const* error,
156 char const* errorMsg) {
157 BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
158 BEAST_EXPECT(result[jss::result][jss::error] == error);
159 BEAST_EXPECT(result[jss::result][jss::error_message] == errorMsg);
160 };
161
162 Env env(*this);
163 {
164 // Missing source_account field.
165 Json::Value args{depositAuthArgs(alice, becky)};
166 args.removeMember(jss::source_account);
167 Json::Value const result{
168 env.rpc("json", "deposit_authorized", args.toStyledString())};
169 verifyErr(
170 result, "invalidParams", "Missing field 'source_account'.");
171 }
172 {
173 // Non-string source_account field.
174 Json::Value args{depositAuthArgs(alice, becky)};
175 args[jss::source_account] = 7.3;
176 Json::Value const result{
177 env.rpc("json", "deposit_authorized", args.toStyledString())};
178 verifyErr(
179 result,
180 "invalidParams",
181 "Invalid field 'source_account', not a string.");
182 }
183 {
184 // Corrupt source_account field.
185 Json::Value args{depositAuthArgs(alice, becky)};
186 args[jss::source_account] = "rG1QQv2nh2gr7RCZ!P8YYcBUKCCN633jCn";
187 Json::Value const result{
188 env.rpc("json", "deposit_authorized", args.toStyledString())};
189 verifyErr(result, "actMalformed", "Account malformed.");
190 }
191 {
192 // Missing destination_account field.
193 Json::Value args{depositAuthArgs(alice, becky)};
194 args.removeMember(jss::destination_account);
195 Json::Value const result{
196 env.rpc("json", "deposit_authorized", args.toStyledString())};
197 verifyErr(
198 result,
199 "invalidParams",
200 "Missing field 'destination_account'.");
201 }
202 {
203 // Non-string destination_account field.
204 Json::Value args{depositAuthArgs(alice, becky)};
205 args[jss::destination_account] = 7.3;
206 Json::Value const result{
207 env.rpc("json", "deposit_authorized", args.toStyledString())};
208 verifyErr(
209 result,
210 "invalidParams",
211 "Invalid field 'destination_account', not a string.");
212 }
213 {
214 // Corrupt destination_account field.
215 Json::Value args{depositAuthArgs(alice, becky)};
216 args[jss::destination_account] =
217 "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
218 Json::Value const result{
219 env.rpc("json", "deposit_authorized", args.toStyledString())};
220 verifyErr(result, "actMalformed", "Account malformed.");
221 }
222 {
223 // Request an invalid ledger.
224 Json::Value args{depositAuthArgs(alice, becky, "-1")};
225 Json::Value const result{
226 env.rpc("json", "deposit_authorized", args.toStyledString())};
227 verifyErr(
228 result,
229 "invalidParams",
230 "Invalid field 'ledger_index', not string or number.");
231 }
232 {
233 // Request a ledger that doesn't exist yet as a string.
234 Json::Value args{depositAuthArgs(alice, becky, "17")};
235 Json::Value const result{
236 env.rpc("json", "deposit_authorized", args.toStyledString())};
237 verifyErr(result, "lgrNotFound", "ledgerNotFound");
238 }
239 {
240 // Request a ledger that doesn't exist yet.
241 Json::Value args{depositAuthArgs(alice, becky)};
242 args[jss::ledger_index] = 17;
243 Json::Value const result{
244 env.rpc("json", "deposit_authorized", args.toStyledString())};
245 verifyErr(result, "lgrNotFound", "ledgerNotFound");
246 }
247 {
248 // alice is not yet funded.
249 Json::Value args{depositAuthArgs(alice, becky)};
250 Json::Value const result{
251 env.rpc("json", "deposit_authorized", args.toStyledString())};
252 verifyErr(result, "srcActNotFound", "Source account not found.");
253 }
254 env.fund(XRP(1000), alice);
255 env.close();
256 {
257 // becky is not yet funded.
258 Json::Value args{depositAuthArgs(alice, becky)};
259 Json::Value const result{
260 env.rpc("json", "deposit_authorized", args.toStyledString())};
261 verifyErr(
262 result, "dstActNotFound", "Destination account not found.");
263 }
264 env.fund(XRP(1000), becky);
265 env.close();
266 {
267 // Once becky is funded try it again and see it succeed.
268 Json::Value args{depositAuthArgs(alice, becky)};
269 Json::Value const result{
270 env.rpc("json", "deposit_authorized", args.toStyledString())};
271 validateDepositAuthResult(result, true);
272 }
273 }
274
275 void
277 Json::Value const& result,
278 jtx::Account const& src,
279 jtx::Account const& dst,
280 bool authorized,
281 std::vector<std::string> credentialIDs = {},
282 std::string_view error = "")
283 {
284 BEAST_EXPECT(
285 result[jss::status] == authorized ? jss::success : jss::error);
286 if (result.isMember(jss::deposit_authorized))
287 BEAST_EXPECT(result[jss::deposit_authorized] == authorized);
288 if (authorized)
289 BEAST_EXPECT(
290 result.isMember(jss::deposit_authorized) &&
291 (result[jss::deposit_authorized] == true));
292
293 BEAST_EXPECT(result.isMember(jss::error) == !error.empty());
294 if (!error.empty())
295 BEAST_EXPECT(result[jss::error].asString() == error);
296
297 if (authorized)
298 {
299 BEAST_EXPECT(result[jss::source_account] == src.human());
300 BEAST_EXPECT(result[jss::destination_account] == dst.human());
301
302 for (unsigned i = 0; i < credentialIDs.size(); ++i)
303 BEAST_EXPECT(result[jss::credentials][i] == credentialIDs[i]);
304 }
305 else
306 {
307 BEAST_EXPECT(result[jss::request].isObject());
308
309 auto const& request = result[jss::request];
310 BEAST_EXPECT(request[jss::command] == jss::deposit_authorized);
311 BEAST_EXPECT(request[jss::source_account] == src.human());
312 BEAST_EXPECT(request[jss::destination_account] == dst.human());
313
314 for (unsigned i = 0; i < credentialIDs.size(); ++i)
315 BEAST_EXPECT(request[jss::credentials][i] == credentialIDs[i]);
316 }
317 }
318
319 void
321 {
322 testcase("Credentials");
323
324 using namespace jtx;
325
326 char const credType[] = "abcde";
327
328 Account const alice{"alice"};
329 Account const becky{"becky"};
330 Account const diana{"diana"};
331 Account const carol{"carol"};
332
333 Env env(*this);
334 env.fund(XRP(1000), alice, becky, carol, diana);
335 env.close();
336
337 // carol recognize alice
338 env(credentials::create(alice, carol, credType));
339 env.close();
340 // retrieve the index of the credentials
341 auto const jv = credentials::ledgerEntry(env, alice, carol, credType);
342 std::string const credIdx = jv[jss::result][jss::index].asString();
343
344 // becky sets the DepositAuth flag in the current ledger.
345 env(fset(becky, asfDepositAuth));
346 env.close();
347
348 // becky authorize any account recognized by carol to make a payment
349 env(deposit::authCredentials(becky, {{carol, credType}}));
350 env.close();
351
352 {
353 testcase(
354 "deposit_authorized with credentials failure: empty array.");
355
356 auto args = depositAuthArgs(alice, becky, "validated");
357 args[jss::credentials] = Json::arrayValue;
358
359 auto const jv =
360 env.rpc("json", "deposit_authorized", args.toStyledString());
362 jv[jss::result], alice, becky, false, {}, "invalidParams");
363 }
364
365 {
366 testcase(
367 "deposit_authorized with credentials failure: not a string "
368 "credentials");
369
370 auto args = depositAuthArgs(alice, becky, "validated");
371 args[jss::credentials] = Json::arrayValue;
372 args[jss::credentials].append(1);
373 args[jss::credentials].append(3);
374
375 auto const jv =
376 env.rpc("json", "deposit_authorized", args.toStyledString());
378 jv[jss::result], alice, becky, false, {}, "invalidParams");
379 }
380
381 {
382 testcase(
383 "deposit_authorized with credentials failure: not a hex string "
384 "credentials");
385
386 auto args = depositAuthArgs(alice, becky, "validated");
387 args[jss::credentials] = Json::arrayValue;
388 args[jss::credentials].append("hello world");
389
390 auto const jv =
391 env.rpc("json", "deposit_authorized", args.toStyledString());
393 jv[jss::result],
394 alice,
395 becky,
396 false,
397 {"hello world"},
398 "invalidParams");
399 }
400
401 {
402 testcase(
403 "deposit_authorized with credentials failure: not a credential "
404 "index");
405
406 auto args = depositAuthArgs(
407 alice,
408 becky,
409 "validated",
410 {"0127AB8B4B29CCDBB61AA51C0799A8A6BB80B86A9899807C11ED576AF8516"
411 "473"});
412
413 auto const jv =
414 env.rpc("json", "deposit_authorized", args.toStyledString());
416 jv[jss::result],
417 alice,
418 becky,
419 false,
420 {"0127AB8B4B29CCDBB61AA51C0799A8A6BB80B86A9899807C11ED576AF8516"
421 "473"},
422 "badCredentials");
423 }
424
425 {
426 testcase(
427 "deposit_authorized with credentials not authorized: "
428 "credential not accepted");
429 auto const jv = env.rpc(
430 "json",
431 "deposit_authorized",
432 depositAuthArgs(alice, becky, "validated", {credIdx})
433 .toStyledString());
435 jv[jss::result],
436 alice,
437 becky,
438 false,
439 {credIdx},
440 "badCredentials");
441 }
442
443 // alice accept credentials
444 env(credentials::accept(alice, carol, credType));
445 env.close();
446
447 {
448 testcase("deposit_authorized with duplicates in credentials");
449 auto const jv = env.rpc(
450 "json",
451 "deposit_authorized",
452 depositAuthArgs(alice, becky, "validated", {credIdx, credIdx})
453 .toStyledString());
455 jv[jss::result],
456 alice,
457 becky,
458 false,
459 {credIdx, credIdx},
460 "badCredentials");
461 }
462
463 {
464 static std::vector<std::string> const credIds = {
465 "18004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
466 "E4",
467 "28004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
468 "E4",
469 "38004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
470 "E4",
471 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
472 "E4",
473 "58004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
474 "E4",
475 "68004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
476 "E4",
477 "78004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
478 "E4",
479 "88004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
480 "E4",
481 "98004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
482 "E4"};
483 assert(credIds.size() > maxCredentialsArraySize);
484
485 testcase("deposit_authorized too long credentials");
486 auto const jv = env.rpc(
487 "json",
488 "deposit_authorized",
489 depositAuthArgs(alice, becky, "validated", credIds)
490 .toStyledString());
492 jv[jss::result], alice, becky, false, credIds, "invalidParams");
493 }
494
495 {
496 testcase("deposit_authorized with credentials");
497 auto const jv = env.rpc(
498 "json",
499 "deposit_authorized",
500 depositAuthArgs(alice, becky, "validated", {credIdx})
501 .toStyledString());
503 jv[jss::result], alice, becky, true, {credIdx});
504 }
505
506 {
507 // diana recognize becky
508 env(credentials::create(becky, diana, credType));
509 env.close();
510 env(credentials::accept(becky, diana, credType));
511 env.close();
512
513 // retrieve the index of the credentials
514 auto jv = credentials::ledgerEntry(env, becky, diana, credType);
515 std::string const credBecky =
516 jv[jss::result][jss::index].asString();
517
518 testcase("deposit_authorized account without preauth");
519 jv = env.rpc(
520 "json",
521 "deposit_authorized",
522 depositAuthArgs(becky, alice, "validated", {credBecky})
523 .toStyledString());
525 jv[jss::result], becky, alice, true, {credBecky});
526 }
527
528 {
529 // carol recognize diana
530 env(credentials::create(diana, carol, credType));
531 env.close();
532 env(credentials::accept(diana, carol, credType));
533 env.close();
534 // retrieve the index of the credentials
535 auto jv = credentials::ledgerEntry(env, alice, carol, credType);
536 std::string const credDiana =
537 jv[jss::result][jss::index].asString();
538
539 // alice try to use credential for different account
540 jv = env.rpc(
541 "json",
542 "deposit_authorized",
543 depositAuthArgs(becky, alice, "validated", {credDiana})
544 .toStyledString());
546 jv[jss::result],
547 becky,
548 alice,
549 false,
550 {credDiana},
551 "badCredentials");
552 }
553
554 {
555 testcase("deposit_authorized with expired credentials");
556
557 // check expired credentials
558 char const credType2[] = "random";
559 std::uint32_t const x = env.current()
560 ->header()
561 .parentCloseTime.time_since_epoch()
562 .count() +
563 40;
564
565 // create credentials with expire time 40s
566 auto jv = credentials::create(alice, carol, credType2);
567 jv[sfExpiration.jsonName] = x;
568 env(jv);
569 env.close();
570 env(credentials::accept(alice, carol, credType2));
571 env.close();
572 jv = credentials::ledgerEntry(env, alice, carol, credType2);
573 std::string const credIdx2 = jv[jss::result][jss::index].asString();
574
575 // becky sets the DepositAuth flag in the current ledger.
576 env(fset(becky, asfDepositAuth));
577 env.close();
578
579 // becky authorize any account recognized by carol to make a payment
580 env(deposit::authCredentials(becky, {{carol, credType2}}));
581 env.close();
582
583 {
584 // this should be fine
585 jv = env.rpc(
586 "json",
587 "deposit_authorized",
588 depositAuthArgs(alice, becky, "validated", {credIdx2})
589 .toStyledString());
591 jv[jss::result], alice, becky, true, {credIdx2});
592 }
593
594 // increase timer by 20s
595 env.close();
596 env.close();
597 {
598 // now credentials expired
599 jv = env.rpc(
600 "json",
601 "deposit_authorized",
602 depositAuthArgs(alice, becky, "validated", {credIdx2})
603 .toStyledString());
604
606 jv[jss::result],
607 alice,
608 becky,
609 false,
610 {credIdx2},
611 "badCredentials");
612 }
613 }
614 }
615
616 void
617 run() override
618 {
619 testValid();
620 testErrors();
622 }
623};
624
625BEAST_DEFINE_TESTSUITE(DepositAuthorized, rpc, xrpl);
626
627} // namespace test
628} // namespace xrpl
Represents a JSON value.
Definition json_value.h:131
Value removeMember(char const *key)
Remove and return the named member.
bool isMember(char const *key) const
Return true if the object has a member named key.
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
void validateDepositAuthResult(Json::Value const &result, bool authorized)
void checkCredentialsResponse(Json::Value const &result, jtx::Account const &src, jtx::Account const &dst, bool authorized, std::vector< std::string > credentialIDs={}, std::string_view error="")
static Json::Value depositAuthArgs(jtx::Account const &source, jtx::Account const &dest, std::string const &ledger="", std::vector< std::string > const &credentials={})
Immutable cryptographic account descriptor.
Definition Account.h:20
std::string const & human() const
Returns the human readable public key.
Definition Account.h:99
A transaction testing environment.
Definition Env.h:102
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:104
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:272
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:774
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:314
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:16
@ arrayValue
array value (ordered list)
Definition json_value.h:26
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:29
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:59
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:13
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:35
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:13
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:102
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
static bool authorized(Port const &port, std::map< std::string, std::string > const &h)
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:66
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition Protocol.h:225
T size(T... args)