rippled
Loading...
Searching...
No Matches
DepositAuthorized_test.cpp
1#include <test/jtx.h>
2
3#include <xrpl/protocol/jss.h>
4
5namespace ripple {
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(result, "invalidParams", "ledgerIndexMalformed");
228 }
229 {
230 // Request a ledger that doesn't exist yet as a string.
231 Json::Value args{depositAuthArgs(alice, becky, "17")};
232 Json::Value const result{
233 env.rpc("json", "deposit_authorized", args.toStyledString())};
234 verifyErr(result, "lgrNotFound", "ledgerNotFound");
235 }
236 {
237 // Request a ledger that doesn't exist yet.
238 Json::Value args{depositAuthArgs(alice, becky)};
239 args[jss::ledger_index] = 17;
240 Json::Value const result{
241 env.rpc("json", "deposit_authorized", args.toStyledString())};
242 verifyErr(result, "lgrNotFound", "ledgerNotFound");
243 }
244 {
245 // alice is not yet funded.
246 Json::Value args{depositAuthArgs(alice, becky)};
247 Json::Value const result{
248 env.rpc("json", "deposit_authorized", args.toStyledString())};
249 verifyErr(result, "srcActNotFound", "Source account not found.");
250 }
251 env.fund(XRP(1000), alice);
252 env.close();
253 {
254 // becky is not yet funded.
255 Json::Value args{depositAuthArgs(alice, becky)};
256 Json::Value const result{
257 env.rpc("json", "deposit_authorized", args.toStyledString())};
258 verifyErr(
259 result, "dstActNotFound", "Destination account not found.");
260 }
261 env.fund(XRP(1000), becky);
262 env.close();
263 {
264 // Once becky is funded try it again and see it succeed.
265 Json::Value args{depositAuthArgs(alice, becky)};
266 Json::Value const result{
267 env.rpc("json", "deposit_authorized", args.toStyledString())};
268 validateDepositAuthResult(result, true);
269 }
270 }
271
272 void
274 Json::Value const& result,
275 jtx::Account const& src,
276 jtx::Account const& dst,
277 bool authorized,
278 std::vector<std::string> credentialIDs = {},
279 std::string_view error = "")
280 {
281 BEAST_EXPECT(
282 result[jss::status] == authorized ? jss::success : jss::error);
283 if (result.isMember(jss::deposit_authorized))
284 BEAST_EXPECT(result[jss::deposit_authorized] == authorized);
285 if (authorized)
286 BEAST_EXPECT(
287 result.isMember(jss::deposit_authorized) &&
288 (result[jss::deposit_authorized] == true));
289
290 BEAST_EXPECT(result.isMember(jss::error) == !error.empty());
291 if (!error.empty())
292 BEAST_EXPECT(result[jss::error].asString() == error);
293
294 if (authorized)
295 {
296 BEAST_EXPECT(result[jss::source_account] == src.human());
297 BEAST_EXPECT(result[jss::destination_account] == dst.human());
298
299 for (unsigned i = 0; i < credentialIDs.size(); ++i)
300 BEAST_EXPECT(result[jss::credentials][i] == credentialIDs[i]);
301 }
302 else
303 {
304 BEAST_EXPECT(result[jss::request].isObject());
305
306 auto const& request = result[jss::request];
307 BEAST_EXPECT(request[jss::command] == jss::deposit_authorized);
308 BEAST_EXPECT(request[jss::source_account] == src.human());
309 BEAST_EXPECT(request[jss::destination_account] == dst.human());
310
311 for (unsigned i = 0; i < credentialIDs.size(); ++i)
312 BEAST_EXPECT(request[jss::credentials][i] == credentialIDs[i]);
313 }
314 }
315
316 void
318 {
319 testcase("Credentials");
320
321 using namespace jtx;
322
323 char const credType[] = "abcde";
324
325 Account const alice{"alice"};
326 Account const becky{"becky"};
327 Account const diana{"diana"};
328 Account const carol{"carol"};
329
330 Env env(*this);
331 env.fund(XRP(1000), alice, becky, carol, diana);
332 env.close();
333
334 // carol recognize alice
335 env(credentials::create(alice, carol, credType));
336 env.close();
337 // retrieve the index of the credentials
338 auto const jv = credentials::ledgerEntry(env, alice, carol, credType);
339 std::string const credIdx = jv[jss::result][jss::index].asString();
340
341 // becky sets the DepositAuth flag in the current ledger.
342 env(fset(becky, asfDepositAuth));
343 env.close();
344
345 // becky authorize any account recognized by carol to make a payment
346 env(deposit::authCredentials(becky, {{carol, credType}}));
347 env.close();
348
349 {
350 testcase(
351 "deposit_authorized with credentials failure: empty array.");
352
353 auto args = depositAuthArgs(alice, becky, "validated");
354 args[jss::credentials] = Json::arrayValue;
355
356 auto const jv =
357 env.rpc("json", "deposit_authorized", args.toStyledString());
359 jv[jss::result], alice, becky, false, {}, "invalidParams");
360 }
361
362 {
363 testcase(
364 "deposit_authorized with credentials failure: not a string "
365 "credentials");
366
367 auto args = depositAuthArgs(alice, becky, "validated");
368 args[jss::credentials] = Json::arrayValue;
369 args[jss::credentials].append(1);
370 args[jss::credentials].append(3);
371
372 auto const jv =
373 env.rpc("json", "deposit_authorized", args.toStyledString());
375 jv[jss::result], alice, becky, false, {}, "invalidParams");
376 }
377
378 {
379 testcase(
380 "deposit_authorized with credentials failure: not a hex string "
381 "credentials");
382
383 auto args = depositAuthArgs(alice, becky, "validated");
384 args[jss::credentials] = Json::arrayValue;
385 args[jss::credentials].append("hello world");
386
387 auto const jv =
388 env.rpc("json", "deposit_authorized", args.toStyledString());
390 jv[jss::result],
391 alice,
392 becky,
393 false,
394 {"hello world"},
395 "invalidParams");
396 }
397
398 {
399 testcase(
400 "deposit_authorized with credentials failure: not a credential "
401 "index");
402
403 auto args = depositAuthArgs(
404 alice,
405 becky,
406 "validated",
407 {"0127AB8B4B29CCDBB61AA51C0799A8A6BB80B86A9899807C11ED576AF8516"
408 "473"});
409
410 auto const jv =
411 env.rpc("json", "deposit_authorized", args.toStyledString());
413 jv[jss::result],
414 alice,
415 becky,
416 false,
417 {"0127AB8B4B29CCDBB61AA51C0799A8A6BB80B86A9899807C11ED576AF8516"
418 "473"},
419 "badCredentials");
420 }
421
422 {
423 testcase(
424 "deposit_authorized with credentials not authorized: "
425 "credential not accepted");
426 auto const jv = env.rpc(
427 "json",
428 "deposit_authorized",
429 depositAuthArgs(alice, becky, "validated", {credIdx})
430 .toStyledString());
432 jv[jss::result],
433 alice,
434 becky,
435 false,
436 {credIdx},
437 "badCredentials");
438 }
439
440 // alice accept credentials
441 env(credentials::accept(alice, carol, credType));
442 env.close();
443
444 {
445 testcase("deposit_authorized with duplicates in credentials");
446 auto const jv = env.rpc(
447 "json",
448 "deposit_authorized",
449 depositAuthArgs(alice, becky, "validated", {credIdx, credIdx})
450 .toStyledString());
452 jv[jss::result],
453 alice,
454 becky,
455 false,
456 {credIdx, credIdx},
457 "badCredentials");
458 }
459
460 {
461 static std::vector<std::string> const credIds = {
462 "18004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
463 "E4",
464 "28004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
465 "E4",
466 "38004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
467 "E4",
468 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
469 "E4",
470 "58004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
471 "E4",
472 "68004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
473 "E4",
474 "78004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
475 "E4",
476 "88004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
477 "E4",
478 "98004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
479 "E4"};
480 assert(credIds.size() > maxCredentialsArraySize);
481
482 testcase("deposit_authorized too long credentials");
483 auto const jv = env.rpc(
484 "json",
485 "deposit_authorized",
486 depositAuthArgs(alice, becky, "validated", credIds)
487 .toStyledString());
489 jv[jss::result], alice, becky, false, credIds, "invalidParams");
490 }
491
492 {
493 testcase("deposit_authorized with credentials");
494 auto const jv = env.rpc(
495 "json",
496 "deposit_authorized",
497 depositAuthArgs(alice, becky, "validated", {credIdx})
498 .toStyledString());
500 jv[jss::result], alice, becky, true, {credIdx});
501 }
502
503 {
504 // diana recognize becky
505 env(credentials::create(becky, diana, credType));
506 env.close();
507 env(credentials::accept(becky, diana, credType));
508 env.close();
509
510 // retrieve the index of the credentials
511 auto jv = credentials::ledgerEntry(env, becky, diana, credType);
512 std::string const credBecky =
513 jv[jss::result][jss::index].asString();
514
515 testcase("deposit_authorized account without preauth");
516 jv = env.rpc(
517 "json",
518 "deposit_authorized",
519 depositAuthArgs(becky, alice, "validated", {credBecky})
520 .toStyledString());
522 jv[jss::result], becky, alice, true, {credBecky});
523 }
524
525 {
526 // carol recognize diana
527 env(credentials::create(diana, carol, credType));
528 env.close();
529 env(credentials::accept(diana, carol, credType));
530 env.close();
531 // retrieve the index of the credentials
532 auto jv = credentials::ledgerEntry(env, alice, carol, credType);
533 std::string const credDiana =
534 jv[jss::result][jss::index].asString();
535
536 // alice try to use credential for different account
537 jv = env.rpc(
538 "json",
539 "deposit_authorized",
540 depositAuthArgs(becky, alice, "validated", {credDiana})
541 .toStyledString());
543 jv[jss::result],
544 becky,
545 alice,
546 false,
547 {credDiana},
548 "badCredentials");
549 }
550
551 {
552 testcase("deposit_authorized with expired credentials");
553
554 // check expired credentials
555 char const credType2[] = "fghijk";
556 std::uint32_t const x = env.current()
557 ->info()
558 .parentCloseTime.time_since_epoch()
559 .count() +
560 40;
561
562 // create credentials with expire time 40s
563 auto jv = credentials::create(alice, carol, credType2);
564 jv[sfExpiration.jsonName] = x;
565 env(jv);
566 env.close();
567 env(credentials::accept(alice, carol, credType2));
568 env.close();
569 jv = credentials::ledgerEntry(env, alice, carol, credType2);
570 std::string const credIdx2 = jv[jss::result][jss::index].asString();
571
572 // becky sets the DepositAuth flag in the current ledger.
573 env(fset(becky, asfDepositAuth));
574 env.close();
575
576 // becky authorize any account recognized by carol to make a payment
577 env(deposit::authCredentials(becky, {{carol, credType2}}));
578 env.close();
579
580 {
581 // this should be fine
582 jv = env.rpc(
583 "json",
584 "deposit_authorized",
585 depositAuthArgs(alice, becky, "validated", {credIdx2})
586 .toStyledString());
588 jv[jss::result], alice, becky, true, {credIdx2});
589 }
590
591 // increase timer by 20s
592 env.close();
593 env.close();
594 {
595 // now credentials expired
596 jv = env.rpc(
597 "json",
598 "deposit_authorized",
599 depositAuthArgs(alice, becky, "validated", {credIdx2})
600 .toStyledString());
601
603 jv[jss::result],
604 alice,
605 becky,
606 false,
607 {credIdx2},
608 "badCredentials");
609 }
610 }
611 }
612
613 void
614 run() override
615 {
616 testValid();
617 testErrors();
619 }
620};
621
622BEAST_DEFINE_TESTSUITE(DepositAuthorized, rpc, ripple);
623
624} // namespace test
625} // namespace ripple
Represents a JSON value.
Definition json_value.h:130
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
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:312
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:103
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:772
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
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:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:13
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 auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:13
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:35
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
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
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:90
static bool authorized(Port const &port, std::map< std::string, std::string > const &h)
T size(T... args)