rippled
Loading...
Searching...
No Matches
LedgerEntry_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2025 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 <test/jtx.h>
21#include <test/jtx/Oracle.h>
22#include <test/jtx/attester.h>
23#include <test/jtx/delegate.h>
24#include <test/jtx/multisign.h>
25#include <test/jtx/xchain_bridge.h>
26
27#include <xrpl/beast/unit_test.h>
28#include <xrpl/json/json_value.h>
29#include <xrpl/protocol/AccountID.h>
30#include <xrpl/protocol/ErrorCodes.h>
31#include <xrpl/protocol/STXChainBridge.h>
32#include <xrpl/protocol/jss.h>
33
34namespace ripple {
35
36namespace test {
37
39{
40 void
42 Json::Value const& jv,
43 std::string const& err,
44 std::string const& msg)
45 {
46 if (BEAST_EXPECT(jv.isMember(jss::status)))
47 BEAST_EXPECT(jv[jss::status] == "error");
48 if (BEAST_EXPECT(jv.isMember(jss::error)))
49 BEAST_EXPECT(jv[jss::error] == err);
50 if (msg.empty())
51 {
52 BEAST_EXPECT(
53 jv[jss::error_message] == Json::nullValue ||
54 jv[jss::error_message] == "");
55 }
56 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
57 BEAST_EXPECT(jv[jss::error_message] == msg);
58 }
59
60 // Corrupt a valid address by replacing the 10th character with '!'.
61 // '!' is not part of the ripple alphabet.
64 {
65 std::string ret = std::move(good);
66 ret.replace(10, 1, 1, '!');
67 return ret;
68 }
69
70 void
72 {
73 testcase("Invalid requests");
74 using namespace test::jtx;
75 Env env{*this};
76 Account const alice{"alice"};
77 env.fund(XRP(10000), alice);
78 env.close();
79
80 {
81 // Missing ledger_entry ledger_hash
82 Json::Value jvParams;
83 jvParams[jss::account_root] = alice.human();
84 jvParams[jss::ledger_hash] =
85 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
86 "AA";
87 auto const jrr = env.rpc(
88 "json", "ledger_entry", to_string(jvParams))[jss::result];
89 checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
90 }
91
92 {
93 // ask for an zero index
94 Json::Value jvParams;
95 jvParams[jss::ledger_index] = "validated";
96 jvParams[jss::index] =
97 "00000000000000000000000000000000000000000000000000000000000000"
98 "0000";
99 auto const jrr = env.rpc(
100 "json", "ledger_entry", to_string(jvParams))[jss::result];
101 checkErrorValue(jrr, "malformedRequest", "");
102 }
103 }
104
105 void
107 {
108 testcase("ledger_entry Request AccountRoot");
109 using namespace test::jtx;
110
111 auto cfg = envconfig();
112 cfg->FEES.reference_fee = 10;
113 Env env{*this, std::move(cfg)};
114
115 Account const alice{"alice"};
116 env.fund(XRP(10000), alice);
117 env.close();
118
119 std::string const ledgerHash{to_string(env.closed()->info().hash)};
120 {
121 // Exercise ledger_closed along the way.
122 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
123 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
124 BEAST_EXPECT(jrr[jss::ledger_index] == 3);
125 }
126
127 std::string accountRootIndex;
128 {
129 // Request alice's account root.
130 Json::Value jvParams;
131 jvParams[jss::account_root] = alice.human();
132 jvParams[jss::ledger_hash] = ledgerHash;
133 Json::Value const jrr = env.rpc(
134 "json", "ledger_entry", to_string(jvParams))[jss::result];
135 BEAST_EXPECT(jrr.isMember(jss::node));
136 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
137 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
138 accountRootIndex = jrr[jss::index].asString();
139 }
140 {
141 constexpr char alicesAcctRootBinary[]{
142 "1100612200800000240000000425000000032D00000000559CE54C3B934E4"
143 "73A995B477E92EC229F99CED5B62BF4D2ACE4DC42719103AE2F6240000002"
144 "540BE4008114AE123A8556F3CF91154711376AFB0F894F832B3D"};
145
146 // Request alice's account root, but with binary == true;
147 Json::Value jvParams;
148 jvParams[jss::account_root] = alice.human();
149 jvParams[jss::binary] = 1;
150 jvParams[jss::ledger_hash] = ledgerHash;
151 Json::Value const jrr = env.rpc(
152 "json", "ledger_entry", to_string(jvParams))[jss::result];
153 BEAST_EXPECT(jrr.isMember(jss::node_binary));
154 BEAST_EXPECT(jrr[jss::node_binary] == alicesAcctRootBinary);
155 }
156 {
157 // Request alice's account root using the index.
158 Json::Value jvParams;
159 jvParams[jss::index] = accountRootIndex;
160 Json::Value const jrr = env.rpc(
161 "json", "ledger_entry", to_string(jvParams))[jss::result];
162 BEAST_EXPECT(!jrr.isMember(jss::node_binary));
163 BEAST_EXPECT(jrr.isMember(jss::node));
164 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
165 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
166 }
167 {
168 // Request alice's account root by index, but with binary == false.
169 Json::Value jvParams;
170 jvParams[jss::index] = accountRootIndex;
171 jvParams[jss::binary] = 0;
172 Json::Value const jrr = env.rpc(
173 "json", "ledger_entry", to_string(jvParams))[jss::result];
174 BEAST_EXPECT(jrr.isMember(jss::node));
175 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
176 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
177 }
178 {
179 // Request using a corrupted AccountID.
180 Json::Value jvParams;
181 jvParams[jss::account_root] = makeBadAddress(alice.human());
182 jvParams[jss::ledger_hash] = ledgerHash;
183 Json::Value const jrr = env.rpc(
184 "json", "ledger_entry", to_string(jvParams))[jss::result];
185 checkErrorValue(jrr, "malformedAddress", "");
186 }
187 {
188 // Request an account that is not in the ledger.
189 Json::Value jvParams;
190 jvParams[jss::account_root] = Account("bob").human();
191 jvParams[jss::ledger_hash] = ledgerHash;
192 Json::Value const jrr = env.rpc(
193 "json", "ledger_entry", to_string(jvParams))[jss::result];
194 checkErrorValue(jrr, "entryNotFound", "");
195 }
196 }
197
198 void
200 {
201 testcase("ledger_entry Request Check");
202 using namespace test::jtx;
203 Env env{*this};
204 Account const alice{"alice"};
205 env.fund(XRP(10000), alice);
206 env.close();
207
208 auto const checkId = keylet::check(env.master, env.seq(env.master));
209
210 env(check::create(env.master, alice, XRP(100)));
211 env.close();
212
213 std::string const ledgerHash{to_string(env.closed()->info().hash)};
214 {
215 // Request a check.
216 Json::Value jvParams;
217 jvParams[jss::check] = to_string(checkId.key);
218 jvParams[jss::ledger_hash] = ledgerHash;
219 Json::Value const jrr = env.rpc(
220 "json", "ledger_entry", to_string(jvParams))[jss::result];
221 BEAST_EXPECT(
222 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
223 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
224 }
225 {
226 // Request an index that is not a check. We'll use alice's
227 // account root index.
228 std::string accountRootIndex;
229 {
230 Json::Value jvParams;
231 jvParams[jss::account_root] = alice.human();
232 Json::Value const jrr = env.rpc(
233 "json", "ledger_entry", to_string(jvParams))[jss::result];
234 accountRootIndex = jrr[jss::index].asString();
235 }
236 Json::Value jvParams;
237 jvParams[jss::check] = accountRootIndex;
238 jvParams[jss::ledger_hash] = ledgerHash;
239 Json::Value const jrr = env.rpc(
240 "json", "ledger_entry", to_string(jvParams))[jss::result];
241 checkErrorValue(jrr, "unexpectedLedgerType", "");
242 }
243 }
244
245 void
247 {
248 testcase("ledger_entry credentials");
249
250 using namespace test::jtx;
251
252 Env env(*this);
253 Account const issuer{"issuer"};
254 Account const alice{"alice"};
255 Account const bob{"bob"};
256 char const credType[] = "abcde";
257
258 env.fund(XRP(5000), issuer, alice, bob);
259 env.close();
260
261 // Setup credentials with DepositAuth object for Alice and Bob
262 env(credentials::create(alice, issuer, credType));
263 env.close();
264
265 {
266 // Succeed
267 auto jv = credentials::ledgerEntry(env, alice, issuer, credType);
268 BEAST_EXPECT(
269 jv.isObject() && jv.isMember(jss::result) &&
270 !jv[jss::result].isMember(jss::error) &&
271 jv[jss::result].isMember(jss::node) &&
272 jv[jss::result][jss::node].isMember(
273 sfLedgerEntryType.jsonName) &&
274 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
275 jss::Credential);
276
277 std::string const credIdx = jv[jss::result][jss::index].asString();
278
279 jv = credentials::ledgerEntry(env, credIdx);
280 BEAST_EXPECT(
281 jv.isObject() && jv.isMember(jss::result) &&
282 !jv[jss::result].isMember(jss::error) &&
283 jv[jss::result].isMember(jss::node) &&
284 jv[jss::result][jss::node].isMember(
285 sfLedgerEntryType.jsonName) &&
286 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
287 jss::Credential);
288 }
289
290 {
291 // Fail, index not a hash
292 auto const jv = credentials::ledgerEntry(env, "");
293 checkErrorValue(jv[jss::result], "malformedRequest", "");
294 }
295
296 {
297 // Fail, credential doesn't exist
298 auto const jv = credentials::ledgerEntry(
299 env,
300 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
301 "E4");
302 checkErrorValue(jv[jss::result], "entryNotFound", "");
303 }
304
305 {
306 // Fail, invalid subject
307 Json::Value jv;
308 jv[jss::ledger_index] = jss::validated;
309 jv[jss::credential][jss::subject] = 42;
310 jv[jss::credential][jss::issuer] = issuer.human();
311 jv[jss::credential][jss::credential_type] =
312 strHex(std::string_view(credType));
313 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
314 checkErrorValue(jrr[jss::result], "malformedRequest", "");
315 }
316
317 {
318 // Fail, invalid issuer
319 Json::Value jv;
320 jv[jss::ledger_index] = jss::validated;
321 jv[jss::credential][jss::subject] = alice.human();
322 jv[jss::credential][jss::issuer] = 42;
323 jv[jss::credential][jss::credential_type] =
324 strHex(std::string_view(credType));
325 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
326 checkErrorValue(jrr[jss::result], "malformedRequest", "");
327 }
328
329 {
330 // Fail, invalid credentials type
331 Json::Value jv;
332 jv[jss::ledger_index] = jss::validated;
333 jv[jss::credential][jss::subject] = alice.human();
334 jv[jss::credential][jss::issuer] = issuer.human();
335 jv[jss::credential][jss::credential_type] = 42;
336 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
337 checkErrorValue(jrr[jss::result], "malformedRequest", "");
338 }
339
340 {
341 // Fail, empty subject
342 Json::Value jv;
343 jv[jss::ledger_index] = jss::validated;
344 jv[jss::credential][jss::subject] = "";
345 jv[jss::credential][jss::issuer] = issuer.human();
346 jv[jss::credential][jss::credential_type] =
347 strHex(std::string_view(credType));
348 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
349 checkErrorValue(jrr[jss::result], "malformedRequest", "");
350 }
351
352 {
353 // Fail, empty issuer
354 Json::Value jv;
355 jv[jss::ledger_index] = jss::validated;
356 jv[jss::credential][jss::subject] = alice.human();
357 jv[jss::credential][jss::issuer] = "";
358 jv[jss::credential][jss::credential_type] =
359 strHex(std::string_view(credType));
360 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
361 checkErrorValue(jrr[jss::result], "malformedRequest", "");
362 }
363
364 {
365 // Fail, empty credentials type
366 Json::Value jv;
367 jv[jss::ledger_index] = jss::validated;
368 jv[jss::credential][jss::subject] = alice.human();
369 jv[jss::credential][jss::issuer] = issuer.human();
370 jv[jss::credential][jss::credential_type] = "";
371 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
372 checkErrorValue(jrr[jss::result], "malformedRequest", "");
373 }
374
375 {
376 // Fail, no subject
377 Json::Value jv;
378 jv[jss::ledger_index] = jss::validated;
379 jv[jss::credential][jss::issuer] = issuer.human();
380 jv[jss::credential][jss::credential_type] =
381 strHex(std::string_view(credType));
382 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
383 checkErrorValue(jrr[jss::result], "malformedRequest", "");
384 }
385
386 {
387 // Fail, no issuer
388 Json::Value jv;
389 jv[jss::ledger_index] = jss::validated;
390 jv[jss::credential][jss::subject] = alice.human();
391 jv[jss::credential][jss::credential_type] =
392 strHex(std::string_view(credType));
393 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
394 checkErrorValue(jrr[jss::result], "malformedRequest", "");
395 }
396
397 {
398 // Fail, no credentials type
399 Json::Value jv;
400 jv[jss::ledger_index] = jss::validated;
401 jv[jss::credential][jss::subject] = alice.human();
402 jv[jss::credential][jss::issuer] = issuer.human();
403 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
404 checkErrorValue(jrr[jss::result], "malformedRequest", "");
405 }
406
407 {
408 // Fail, not AccountID subject
409 Json::Value jv;
410 jv[jss::ledger_index] = jss::validated;
411 jv[jss::credential][jss::subject] = "wehsdbvasbdfvj";
412 jv[jss::credential][jss::issuer] = issuer.human();
413 jv[jss::credential][jss::credential_type] =
414 strHex(std::string_view(credType));
415 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
416 checkErrorValue(jrr[jss::result], "malformedRequest", "");
417 }
418
419 {
420 // Fail, not AccountID issuer
421 Json::Value jv;
422 jv[jss::ledger_index] = jss::validated;
423 jv[jss::credential][jss::subject] = alice.human();
424 jv[jss::credential][jss::issuer] = "c4p93ugndfbsiu";
425 jv[jss::credential][jss::credential_type] =
426 strHex(std::string_view(credType));
427 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
428 checkErrorValue(jrr[jss::result], "malformedRequest", "");
429 }
430
431 {
432 // Fail, credentials type isn't hex encoded
433 Json::Value jv;
434 jv[jss::ledger_index] = jss::validated;
435 jv[jss::credential][jss::subject] = alice.human();
436 jv[jss::credential][jss::issuer] = issuer.human();
437 jv[jss::credential][jss::credential_type] = "12KK";
438 auto const jrr = env.rpc("json", "ledger_entry", to_string(jv));
439 checkErrorValue(jrr[jss::result], "malformedRequest", "");
440 }
441 }
442
443 void
445 {
446 testcase("ledger_entry Delegate");
447
448 using namespace test::jtx;
449
450 Env env{*this};
451 Account const alice{"alice"};
452 Account const bob{"bob"};
453 env.fund(XRP(10000), alice, bob);
454 env.close();
455 env(delegate::set(alice, bob, {"Payment", "CheckCreate"}));
456 env.close();
457 std::string const ledgerHash{to_string(env.closed()->info().hash)};
458 std::string delegateIndex;
459 {
460 // Request by account and authorize
461 Json::Value jvParams;
462 jvParams[jss::delegate][jss::account] = alice.human();
463 jvParams[jss::delegate][jss::authorize] = bob.human();
464 jvParams[jss::ledger_hash] = ledgerHash;
465 Json::Value const jrr = env.rpc(
466 "json", "ledger_entry", to_string(jvParams))[jss::result];
467 BEAST_EXPECT(
468 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate);
469 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
470 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human());
471 delegateIndex = jrr[jss::node][jss::index].asString();
472 }
473 {
474 // Request by index.
475 Json::Value jvParams;
476 jvParams[jss::delegate] = delegateIndex;
477 jvParams[jss::ledger_hash] = ledgerHash;
478 Json::Value const jrr = env.rpc(
479 "json", "ledger_entry", to_string(jvParams))[jss::result];
480 BEAST_EXPECT(
481 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate);
482 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
483 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human());
484 }
485 {
486 // Malformed request: delegate neither object nor string.
487 Json::Value jvParams;
488 jvParams[jss::delegate] = 5;
489 jvParams[jss::ledger_hash] = ledgerHash;
490 Json::Value const jrr = env.rpc(
491 "json", "ledger_entry", to_string(jvParams))[jss::result];
492 checkErrorValue(jrr, "malformedRequest", "");
493 }
494 {
495 // Malformed request: delegate not hex string.
496 Json::Value jvParams;
497 jvParams[jss::delegate] = "0123456789ABCDEFG";
498 jvParams[jss::ledger_hash] = ledgerHash;
499 Json::Value const jrr = env.rpc(
500 "json", "ledger_entry", to_string(jvParams))[jss::result];
501 checkErrorValue(jrr, "malformedRequest", "");
502 }
503 {
504 // Malformed request: account not a string
505 Json::Value jvParams;
506 jvParams[jss::delegate][jss::account] = 5;
507 jvParams[jss::delegate][jss::authorize] = bob.human();
508 jvParams[jss::ledger_hash] = ledgerHash;
509 Json::Value const jrr = env.rpc(
510 "json", "ledger_entry", to_string(jvParams))[jss::result];
511 checkErrorValue(jrr, "malformedAddress", "");
512 }
513 {
514 // Malformed request: authorize not a string
515 Json::Value jvParams;
516 jvParams[jss::delegate][jss::account] = alice.human();
517 jvParams[jss::delegate][jss::authorize] = 5;
518 jvParams[jss::ledger_hash] = ledgerHash;
519 Json::Value const jrr = env.rpc(
520 "json", "ledger_entry", to_string(jvParams))[jss::result];
521 checkErrorValue(jrr, "malformedAddress", "");
522 }
523 {
524 // this lambda function is used test malformed account and authroize
525 auto testMalformedAccount =
526 [&](std::optional<std::string> const& account,
527 std::optional<std::string> const& authorize,
528 std::string const& error) {
529 Json::Value jvParams;
530 jvParams[jss::ledger_hash] = ledgerHash;
531 if (account)
532 jvParams[jss::delegate][jss::account] = *account;
533 if (authorize)
534 jvParams[jss::delegate][jss::authorize] = *authorize;
535 auto const jrr = env.rpc(
536 "json",
537 "ledger_entry",
538 to_string(jvParams))[jss::result];
539 checkErrorValue(jrr, error, "");
540 };
541 // missing account
542 testMalformedAccount(std::nullopt, bob.human(), "malformedRequest");
543 // missing authorize
544 testMalformedAccount(
545 alice.human(), std::nullopt, "malformedRequest");
546 // malformed account
547 testMalformedAccount("-", bob.human(), "malformedAddress");
548 // malformed authorize
549 testMalformedAccount(alice.human(), "-", "malformedAddress");
550 }
551 }
552
553 void
555 {
556 testcase("ledger_entry Deposit Preauth");
557
558 using namespace test::jtx;
559
560 Env env{*this};
561 Account const alice{"alice"};
562 Account const becky{"becky"};
563
564 env.fund(XRP(10000), alice, becky);
565 env.close();
566
567 env(deposit::auth(alice, becky));
568 env.close();
569
570 std::string const ledgerHash{to_string(env.closed()->info().hash)};
571 std::string depositPreauthIndex;
572 {
573 // Request a depositPreauth by owner and authorized.
574 Json::Value jvParams;
575 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
576 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
577 jvParams[jss::ledger_hash] = ledgerHash;
578 Json::Value const jrr = env.rpc(
579 "json", "ledger_entry", to_string(jvParams))[jss::result];
580
581 BEAST_EXPECT(
582 jrr[jss::node][sfLedgerEntryType.jsonName] ==
583 jss::DepositPreauth);
584 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
585 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
586 depositPreauthIndex = jrr[jss::node][jss::index].asString();
587 }
588 {
589 // Request a depositPreauth by index.
590 Json::Value jvParams;
591 jvParams[jss::deposit_preauth] = depositPreauthIndex;
592 jvParams[jss::ledger_hash] = ledgerHash;
593 Json::Value const jrr = env.rpc(
594 "json", "ledger_entry", to_string(jvParams))[jss::result];
595
596 BEAST_EXPECT(
597 jrr[jss::node][sfLedgerEntryType.jsonName] ==
598 jss::DepositPreauth);
599 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
600 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
601 }
602 {
603 // Malformed request: deposit_preauth neither object nor string.
604 Json::Value jvParams;
605 jvParams[jss::deposit_preauth] = -5;
606 jvParams[jss::ledger_hash] = ledgerHash;
607 Json::Value const jrr = env.rpc(
608 "json", "ledger_entry", to_string(jvParams))[jss::result];
609 checkErrorValue(jrr, "malformedRequest", "");
610 }
611 {
612 // Malformed request: deposit_preauth not hex string.
613 Json::Value jvParams;
614 jvParams[jss::deposit_preauth] = "0123456789ABCDEFG";
615 jvParams[jss::ledger_hash] = ledgerHash;
616 Json::Value const jrr = env.rpc(
617 "json", "ledger_entry", to_string(jvParams))[jss::result];
618 checkErrorValue(jrr, "malformedRequest", "");
619 }
620 {
621 // Malformed request: missing [jss::deposit_preauth][jss::owner]
622 Json::Value jvParams;
623 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
624 jvParams[jss::ledger_hash] = ledgerHash;
625 Json::Value const jrr = env.rpc(
626 "json", "ledger_entry", to_string(jvParams))[jss::result];
627 checkErrorValue(jrr, "malformedRequest", "");
628 }
629 {
630 // Malformed request: [jss::deposit_preauth][jss::owner] not string.
631 Json::Value jvParams;
632 jvParams[jss::deposit_preauth][jss::owner] = 7;
633 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
634 jvParams[jss::ledger_hash] = ledgerHash;
635 Json::Value const jrr = env.rpc(
636 "json", "ledger_entry", to_string(jvParams))[jss::result];
637 checkErrorValue(jrr, "malformedRequest", "");
638 }
639 {
640 // Malformed: missing [jss::deposit_preauth][jss::authorized]
641 Json::Value jvParams;
642 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
643 jvParams[jss::ledger_hash] = ledgerHash;
644 Json::Value const jrr = env.rpc(
645 "json", "ledger_entry", to_string(jvParams))[jss::result];
646 checkErrorValue(jrr, "malformedRequest", "");
647 }
648 {
649 // Malformed: [jss::deposit_preauth][jss::authorized] not string.
650 Json::Value jvParams;
651 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
652 jvParams[jss::deposit_preauth][jss::authorized] = 47;
653 jvParams[jss::ledger_hash] = ledgerHash;
654 Json::Value const jrr = env.rpc(
655 "json", "ledger_entry", to_string(jvParams))[jss::result];
656 checkErrorValue(jrr, "malformedRequest", "");
657 }
658 {
659 // Malformed: [jss::deposit_preauth][jss::owner] is malformed.
660 Json::Value jvParams;
661 jvParams[jss::deposit_preauth][jss::owner] =
662 "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
663
664 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
665 jvParams[jss::ledger_hash] = ledgerHash;
666 Json::Value const jrr = env.rpc(
667 "json", "ledger_entry", to_string(jvParams))[jss::result];
668 checkErrorValue(jrr, "malformedOwner", "");
669 }
670 {
671 // Malformed: [jss::deposit_preauth][jss::authorized] is malformed.
672 Json::Value jvParams;
673 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
674 jvParams[jss::deposit_preauth][jss::authorized] =
675 "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
676
677 jvParams[jss::ledger_hash] = ledgerHash;
678 Json::Value const jrr = env.rpc(
679 "json", "ledger_entry", to_string(jvParams))[jss::result];
680 checkErrorValue(jrr, "malformedAuthorized", "");
681 }
682 }
683
684 void
686 {
687 testcase("ledger_entry Deposit Preauth with credentials");
688
689 using namespace test::jtx;
690
691 Env env(*this);
692 Account const issuer{"issuer"};
693 Account const alice{"alice"};
694 Account const bob{"bob"};
695 char const credType[] = "abcde";
696
697 env.fund(XRP(5000), issuer, alice, bob);
698 env.close();
699
700 {
701 // Setup Bob with DepositAuth
702 env(fset(bob, asfDepositAuth));
703 env.close();
704 env(deposit::authCredentials(bob, {{issuer, credType}}));
705 env.close();
706 }
707
708 {
709 // Succeed
710 Json::Value jvParams;
711 jvParams[jss::ledger_index] = jss::validated;
712 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
713
714 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
716 auto& arr(
717 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
718
719 Json::Value jo;
720 jo[jss::issuer] = issuer.human();
721 jo[jss::credential_type] = strHex(std::string_view(credType));
722 arr.append(std::move(jo));
723 auto const jrr =
724 env.rpc("json", "ledger_entry", to_string(jvParams));
725
726 BEAST_EXPECT(
727 jrr.isObject() && jrr.isMember(jss::result) &&
728 !jrr[jss::result].isMember(jss::error) &&
729 jrr[jss::result].isMember(jss::node) &&
730 jrr[jss::result][jss::node].isMember(
731 sfLedgerEntryType.jsonName) &&
732 jrr[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
733 jss::DepositPreauth);
734 }
735
736 {
737 // Failed, invalid account
738 Json::Value jvParams;
739 jvParams[jss::ledger_index] = jss::validated;
740 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
741
742 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
744 auto& arr(
745 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
746
747 Json::Value jo;
748 jo[jss::issuer] = to_string(xrpAccount());
749 jo[jss::credential_type] = strHex(std::string_view(credType));
750 arr.append(std::move(jo));
751 auto const jrr =
752 env.rpc("json", "ledger_entry", to_string(jvParams));
754 jrr[jss::result], "malformedAuthorizedCredentials", "");
755 }
756
757 {
758 // Failed, duplicates in credentials
759 Json::Value jvParams;
760 jvParams[jss::ledger_index] = jss::validated;
761 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
762
763 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
765 auto& arr(
766 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
767
768 Json::Value jo;
769 jo[jss::issuer] = issuer.human();
770 jo[jss::credential_type] = strHex(std::string_view(credType));
771 arr.append(jo);
772 arr.append(std::move(jo));
773 auto const jrr =
774 env.rpc("json", "ledger_entry", to_string(jvParams));
776 jrr[jss::result], "malformedAuthorizedCredentials", "");
777 }
778
779 {
780 // Failed, invalid credential_type
781 Json::Value jvParams;
782 jvParams[jss::ledger_index] = jss::validated;
783 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
784
785 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
787 auto& arr(
788 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
789
790 Json::Value jo;
791 jo[jss::issuer] = issuer.human();
792 jo[jss::credential_type] = "";
793 arr.append(std::move(jo));
794
795 auto const jrr =
796 env.rpc("json", "ledger_entry", to_string(jvParams));
798 jrr[jss::result], "malformedAuthorizedCredentials", "");
799 }
800
801 {
802 // Failed, authorized and authorized_credentials both present
803 Json::Value jvParams;
804 jvParams[jss::ledger_index] = jss::validated;
805 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
806 jvParams[jss::deposit_preauth][jss::authorized] = alice.human();
807
808 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
810 auto& arr(
811 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
812
813 Json::Value jo;
814 jo[jss::issuer] = issuer.human();
815 jo[jss::credential_type] = strHex(std::string_view(credType));
816 arr.append(std::move(jo));
817
818 auto const jrr =
819 env.rpc("json", "ledger_entry", to_string(jvParams));
820 checkErrorValue(jrr[jss::result], "malformedRequest", "");
821 }
822
823 {
824 // Failed, authorized_credentials is not an array
825 Json::Value jvParams;
826 jvParams[jss::ledger_index] = jss::validated;
827 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
828 jvParams[jss::deposit_preauth][jss::authorized_credentials] = 42;
829
830 auto const jrr =
831 env.rpc("json", "ledger_entry", to_string(jvParams));
832 checkErrorValue(jrr[jss::result], "malformedRequest", "");
833 }
834
835 {
836 // Failed, authorized_credentials contains string data
837 Json::Value jvParams;
838 jvParams[jss::ledger_index] = jss::validated;
839 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
840 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
842 auto& arr(
843 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
844 arr.append("foobar");
845
846 auto const jrr =
847 env.rpc("json", "ledger_entry", to_string(jvParams));
849 jrr[jss::result], "malformedAuthorizedCredentials", "");
850 }
851
852 {
853 // Failed, authorized_credentials contains arrays
854 Json::Value jvParams;
855 jvParams[jss::ledger_index] = jss::validated;
856 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
857 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
859 auto& arr(
860 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
862 payload.append(42);
863 arr.append(std::move(payload));
864
865 auto const jrr =
866 env.rpc("json", "ledger_entry", to_string(jvParams));
868 jrr[jss::result], "malformedAuthorizedCredentials", "");
869 }
870
871 {
872 // Failed, authorized_credentials is empty array
873 Json::Value jvParams;
874 jvParams[jss::ledger_index] = jss::validated;
875 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
876 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
878
879 auto const jrr =
880 env.rpc("json", "ledger_entry", to_string(jvParams));
882 jrr[jss::result], "malformedAuthorizedCredentials", "");
883 }
884
885 {
886 // Failed, authorized_credentials is too long
887
888 static std::string_view const credTypes[] = {
889 "cred1",
890 "cred2",
891 "cred3",
892 "cred4",
893 "cred5",
894 "cred6",
895 "cred7",
896 "cred8",
897 "cred9"};
898 static_assert(
899 sizeof(credTypes) / sizeof(credTypes[0]) >
901
902 Json::Value jvParams;
903 jvParams[jss::ledger_index] = jss::validated;
904 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
905 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
907
908 auto& arr(
909 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
910
911 for (unsigned i = 0; i < sizeof(credTypes) / sizeof(credTypes[0]);
912 ++i)
913 {
914 Json::Value jo;
915 jo[jss::issuer] = issuer.human();
916 jo[jss::credential_type] =
917 strHex(std::string_view(credTypes[i]));
918 arr.append(std::move(jo));
919 }
920
921 auto const jrr =
922 env.rpc("json", "ledger_entry", to_string(jvParams));
924 jrr[jss::result], "malformedAuthorizedCredentials", "");
925 }
926
927 {
928 // Failed, issuer is not set
929 Json::Value jvParams;
930 jvParams[jss::ledger_index] = jss::validated;
931 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
932
933 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
935 auto& arr(
936 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
937
938 Json::Value jo;
939 jo[jss::credential_type] = strHex(std::string_view(credType));
940 arr.append(std::move(jo));
941
942 auto const jrr =
943 env.rpc("json", "ledger_entry", to_string(jvParams));
945 jrr[jss::result], "malformedAuthorizedCredentials", "");
946 }
947
948 {
949 // Failed, issuer isn't string
950 Json::Value jvParams;
951 jvParams[jss::ledger_index] = jss::validated;
952 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
953
954 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
956 auto& arr(
957 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
958
959 Json::Value jo;
960 jo[jss::issuer] = 42;
961 jo[jss::credential_type] = strHex(std::string_view(credType));
962 arr.append(std::move(jo));
963
964 auto const jrr =
965 env.rpc("json", "ledger_entry", to_string(jvParams));
967 jrr[jss::result], "malformedAuthorizedCredentials", "");
968 }
969
970 {
971 // Failed, issuer is an array
972 Json::Value jvParams;
973 jvParams[jss::ledger_index] = jss::validated;
974 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
975
976 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
978 auto& arr(
979 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
980
981 Json::Value jo;
983 payload.append(42);
984 jo[jss::issuer] = std::move(payload);
985 jo[jss::credential_type] = strHex(std::string_view(credType));
986 arr.append(std::move(jo));
987
988 auto const jrr =
989 env.rpc("json", "ledger_entry", to_string(jvParams));
991 jrr[jss::result], "malformedAuthorizedCredentials", "");
992 }
993
994 {
995 // Failed, issuer isn't valid encoded account
996 Json::Value jvParams;
997 jvParams[jss::ledger_index] = jss::validated;
998 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
999
1000 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1002 auto& arr(
1003 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1004
1005 Json::Value jo;
1006 jo[jss::issuer] = "invalid_account";
1007 jo[jss::credential_type] = strHex(std::string_view(credType));
1008 arr.append(std::move(jo));
1009
1010 auto const jrr =
1011 env.rpc("json", "ledger_entry", to_string(jvParams));
1013 jrr[jss::result], "malformedAuthorizedCredentials", "");
1014 }
1015
1016 {
1017 // Failed, credential_type is not set
1018 Json::Value jvParams;
1019 jvParams[jss::ledger_index] = jss::validated;
1020 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1021
1022 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1024 auto& arr(
1025 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1026
1027 Json::Value jo;
1028 jo[jss::issuer] = issuer.human();
1029 arr.append(std::move(jo));
1030
1031 auto const jrr =
1032 env.rpc("json", "ledger_entry", to_string(jvParams));
1034 jrr[jss::result], "malformedAuthorizedCredentials", "");
1035 }
1036
1037 {
1038 // Failed, credential_type isn't string
1039 Json::Value jvParams;
1040 jvParams[jss::ledger_index] = jss::validated;
1041 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1042
1043 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1045 auto& arr(
1046 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1047
1048 Json::Value jo;
1049 jo[jss::issuer] = issuer.human();
1050 jo[jss::credential_type] = 42;
1051 arr.append(std::move(jo));
1052
1053 auto const jrr =
1054 env.rpc("json", "ledger_entry", to_string(jvParams));
1056 jrr[jss::result], "malformedAuthorizedCredentials", "");
1057 }
1058
1059 {
1060 // Failed, credential_type is an array
1061 Json::Value jvParams;
1062 jvParams[jss::ledger_index] = jss::validated;
1063 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1064
1065 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1067 auto& arr(
1068 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1069
1070 Json::Value jo;
1071 jo[jss::issuer] = issuer.human();
1072 Json::Value payload = Json::arrayValue;
1073 payload.append(42);
1074 jo[jss::credential_type] = std::move(payload);
1075 arr.append(std::move(jo));
1076
1077 auto const jrr =
1078 env.rpc("json", "ledger_entry", to_string(jvParams));
1080 jrr[jss::result], "malformedAuthorizedCredentials", "");
1081 }
1082
1083 {
1084 // Failed, credential_type isn't hex encoded
1085 Json::Value jvParams;
1086 jvParams[jss::ledger_index] = jss::validated;
1087 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1088
1089 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1091 auto& arr(
1092 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1093
1094 Json::Value jo;
1095 jo[jss::issuer] = issuer.human();
1096 jo[jss::credential_type] = "12KK";
1097 arr.append(std::move(jo));
1098
1099 auto const jrr =
1100 env.rpc("json", "ledger_entry", to_string(jvParams));
1102 jrr[jss::result], "malformedAuthorizedCredentials", "");
1103 }
1104 }
1105
1106 void
1108 {
1109 testcase("ledger_entry Request Directory");
1110 using namespace test::jtx;
1111 Env env{*this};
1112 Account const alice{"alice"};
1113 Account const gw{"gateway"};
1114 auto const USD = gw["USD"];
1115 env.fund(XRP(10000), alice, gw);
1116 env.close();
1117
1118 env.trust(USD(1000), alice);
1119 env.close();
1120
1121 // Run up the number of directory entries so alice has two
1122 // directory nodes.
1123 for (int d = 1'000'032; d >= 1'000'000; --d)
1124 {
1125 env(offer(alice, USD(1), drops(d)));
1126 }
1127 env.close();
1128
1129 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1130 {
1131 // Exercise ledger_closed along the way.
1132 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
1133 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
1134 BEAST_EXPECT(jrr[jss::ledger_index] == 5);
1135 }
1136
1137 std::string const dirRootIndex =
1138 "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D";
1139 {
1140 // Locate directory by index.
1141 Json::Value jvParams;
1142 jvParams[jss::directory] = dirRootIndex;
1143 jvParams[jss::ledger_hash] = ledgerHash;
1144 Json::Value const jrr = env.rpc(
1145 "json", "ledger_entry", to_string(jvParams))[jss::result];
1146 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32);
1147 }
1148 {
1149 // Locate directory by directory root.
1150 Json::Value jvParams;
1151 jvParams[jss::directory] = Json::objectValue;
1152 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1153 Json::Value const jrr = env.rpc(
1154 "json", "ledger_entry", to_string(jvParams))[jss::result];
1155 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
1156 }
1157 {
1158 // Locate directory by owner.
1159 Json::Value jvParams;
1160 jvParams[jss::directory] = Json::objectValue;
1161 jvParams[jss::directory][jss::owner] = alice.human();
1162 jvParams[jss::ledger_hash] = ledgerHash;
1163 Json::Value const jrr = env.rpc(
1164 "json", "ledger_entry", to_string(jvParams))[jss::result];
1165 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
1166 }
1167 {
1168 // Locate directory by directory root and sub_index.
1169 Json::Value jvParams;
1170 jvParams[jss::directory] = Json::objectValue;
1171 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1172 jvParams[jss::directory][jss::sub_index] = 1;
1173 Json::Value const jrr = env.rpc(
1174 "json", "ledger_entry", to_string(jvParams))[jss::result];
1175 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
1176 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
1177 }
1178 {
1179 // Locate directory by owner and sub_index.
1180 Json::Value jvParams;
1181 jvParams[jss::directory] = Json::objectValue;
1182 jvParams[jss::directory][jss::owner] = alice.human();
1183 jvParams[jss::directory][jss::sub_index] = 1;
1184 jvParams[jss::ledger_hash] = ledgerHash;
1185 Json::Value const jrr = env.rpc(
1186 "json", "ledger_entry", to_string(jvParams))[jss::result];
1187 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
1188 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
1189 }
1190 {
1191 // Null directory argument.
1192 Json::Value jvParams;
1193 jvParams[jss::directory] = Json::nullValue;
1194 jvParams[jss::ledger_hash] = ledgerHash;
1195 Json::Value const jrr = env.rpc(
1196 "json", "ledger_entry", to_string(jvParams))[jss::result];
1197 checkErrorValue(jrr, "malformedRequest", "");
1198 }
1199 {
1200 // Non-integer sub_index.
1201 Json::Value jvParams;
1202 jvParams[jss::directory] = Json::objectValue;
1203 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1204 jvParams[jss::directory][jss::sub_index] = 1.5;
1205 jvParams[jss::ledger_hash] = ledgerHash;
1206 Json::Value const jrr = env.rpc(
1207 "json", "ledger_entry", to_string(jvParams))[jss::result];
1208 checkErrorValue(jrr, "malformedRequest", "");
1209 }
1210 {
1211 // Malformed owner entry.
1212 Json::Value jvParams;
1213 jvParams[jss::directory] = Json::objectValue;
1214
1215 std::string const badAddress = makeBadAddress(alice.human());
1216 jvParams[jss::directory][jss::owner] = badAddress;
1217 jvParams[jss::ledger_hash] = ledgerHash;
1218 Json::Value const jrr = env.rpc(
1219 "json", "ledger_entry", to_string(jvParams))[jss::result];
1220 checkErrorValue(jrr, "malformedAddress", "");
1221 }
1222 {
1223 // Malformed directory object. Specify both dir_root and owner.
1224 Json::Value jvParams;
1225 jvParams[jss::directory] = Json::objectValue;
1226 jvParams[jss::directory][jss::owner] = alice.human();
1227 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1228 jvParams[jss::ledger_hash] = ledgerHash;
1229 Json::Value const jrr = env.rpc(
1230 "json", "ledger_entry", to_string(jvParams))[jss::result];
1231 checkErrorValue(jrr, "malformedRequest", "");
1232 }
1233 {
1234 // Incomplete directory object. Missing both dir_root and owner.
1235 Json::Value jvParams;
1236 jvParams[jss::directory] = Json::objectValue;
1237 jvParams[jss::directory][jss::sub_index] = 1;
1238 jvParams[jss::ledger_hash] = ledgerHash;
1239 Json::Value const jrr = env.rpc(
1240 "json", "ledger_entry", to_string(jvParams))[jss::result];
1241 checkErrorValue(jrr, "malformedRequest", "");
1242 }
1243 }
1244
1245 void
1247 {
1248 testcase("ledger_entry Request Escrow");
1249 using namespace test::jtx;
1250 Env env{*this};
1251 Account const alice{"alice"};
1252 env.fund(XRP(10000), alice);
1253 env.close();
1254
1255 // Lambda to create an escrow.
1256 auto escrowCreate = [](test::jtx::Account const& account,
1257 test::jtx::Account const& to,
1258 STAmount const& amount,
1259 NetClock::time_point const& cancelAfter) {
1260 Json::Value jv;
1261 jv[jss::TransactionType] = jss::EscrowCreate;
1262 jv[jss::Flags] = tfUniversal;
1263 jv[jss::Account] = account.human();
1264 jv[jss::Destination] = to.human();
1265 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1266 jv[sfFinishAfter.jsonName] =
1267 cancelAfter.time_since_epoch().count() + 2;
1268 return jv;
1269 };
1270
1271 using namespace std::chrono_literals;
1272 env(escrowCreate(alice, alice, XRP(333), env.now() + 2s));
1273 env.close();
1274
1275 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1276 std::string escrowIndex;
1277 {
1278 // Request the escrow using owner and sequence.
1279 Json::Value jvParams;
1280 jvParams[jss::escrow] = Json::objectValue;
1281 jvParams[jss::escrow][jss::owner] = alice.human();
1282 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1283 Json::Value const jrr = env.rpc(
1284 "json", "ledger_entry", to_string(jvParams))[jss::result];
1285 BEAST_EXPECT(
1286 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1287 escrowIndex = jrr[jss::index].asString();
1288 }
1289 {
1290 // Request the escrow by index.
1291 Json::Value jvParams;
1292 jvParams[jss::escrow] = escrowIndex;
1293 jvParams[jss::ledger_hash] = ledgerHash;
1294 Json::Value const jrr = env.rpc(
1295 "json", "ledger_entry", to_string(jvParams))[jss::result];
1296 BEAST_EXPECT(
1297 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1298 }
1299 {
1300 // Malformed owner entry.
1301 Json::Value jvParams;
1302 jvParams[jss::escrow] = Json::objectValue;
1303
1304 std::string const badAddress = makeBadAddress(alice.human());
1305 jvParams[jss::escrow][jss::owner] = badAddress;
1306 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1307 jvParams[jss::ledger_hash] = ledgerHash;
1308 Json::Value const jrr = env.rpc(
1309 "json", "ledger_entry", to_string(jvParams))[jss::result];
1310 checkErrorValue(jrr, "malformedOwner", "");
1311 }
1312 {
1313 // Missing owner.
1314 Json::Value jvParams;
1315 jvParams[jss::escrow] = Json::objectValue;
1316 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1317 jvParams[jss::ledger_hash] = ledgerHash;
1318 Json::Value const jrr = env.rpc(
1319 "json", "ledger_entry", to_string(jvParams))[jss::result];
1320 checkErrorValue(jrr, "malformedRequest", "");
1321 }
1322 {
1323 // Missing sequence.
1324 Json::Value jvParams;
1325 jvParams[jss::escrow] = Json::objectValue;
1326 jvParams[jss::escrow][jss::owner] = alice.human();
1327 jvParams[jss::ledger_hash] = ledgerHash;
1328 Json::Value const jrr = env.rpc(
1329 "json", "ledger_entry", to_string(jvParams))[jss::result];
1330 checkErrorValue(jrr, "malformedRequest", "");
1331 }
1332 {
1333 // Non-integer sequence.
1334 Json::Value jvParams;
1335 jvParams[jss::escrow] = Json::objectValue;
1336 jvParams[jss::escrow][jss::owner] = alice.human();
1337 jvParams[jss::escrow][jss::seq] =
1338 std::to_string(env.seq(alice) - 1);
1339 jvParams[jss::ledger_hash] = ledgerHash;
1340 Json::Value const jrr = env.rpc(
1341 "json", "ledger_entry", to_string(jvParams))[jss::result];
1342 checkErrorValue(jrr, "malformedRequest", "");
1343 }
1344 }
1345
1346 void
1348 {
1349 testcase("ledger_entry Request Offer");
1350 using namespace test::jtx;
1351 Env env{*this};
1352 Account const alice{"alice"};
1353 Account const gw{"gateway"};
1354 auto const USD = gw["USD"];
1355 env.fund(XRP(10000), alice, gw);
1356 env.close();
1357
1358 env(offer(alice, USD(321), XRP(322)));
1359 env.close();
1360
1361 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1362 std::string offerIndex;
1363 {
1364 // Request the offer using owner and sequence.
1365 Json::Value jvParams;
1366 jvParams[jss::offer] = Json::objectValue;
1367 jvParams[jss::offer][jss::account] = alice.human();
1368 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1369 jvParams[jss::ledger_hash] = ledgerHash;
1370 Json::Value const jrr = env.rpc(
1371 "json", "ledger_entry", to_string(jvParams))[jss::result];
1372 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1373 offerIndex = jrr[jss::index].asString();
1374 }
1375 {
1376 // Request the offer using its index.
1377 Json::Value jvParams;
1378 jvParams[jss::offer] = offerIndex;
1379 Json::Value const jrr = env.rpc(
1380 "json", "ledger_entry", to_string(jvParams))[jss::result];
1381 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1382 }
1383 {
1384 // Malformed account entry.
1385 Json::Value jvParams;
1386 jvParams[jss::offer] = Json::objectValue;
1387
1388 std::string const badAddress = makeBadAddress(alice.human());
1389 jvParams[jss::offer][jss::account] = badAddress;
1390 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1391 jvParams[jss::ledger_hash] = ledgerHash;
1392 Json::Value const jrr = env.rpc(
1393 "json", "ledger_entry", to_string(jvParams))[jss::result];
1394 checkErrorValue(jrr, "malformedAddress", "");
1395 }
1396 {
1397 // Malformed offer object. Missing account member.
1398 Json::Value jvParams;
1399 jvParams[jss::offer] = Json::objectValue;
1400 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1401 jvParams[jss::ledger_hash] = ledgerHash;
1402 Json::Value const jrr = env.rpc(
1403 "json", "ledger_entry", to_string(jvParams))[jss::result];
1404 checkErrorValue(jrr, "malformedRequest", "");
1405 }
1406 {
1407 // Malformed offer object. Missing seq member.
1408 Json::Value jvParams;
1409 jvParams[jss::offer] = Json::objectValue;
1410 jvParams[jss::offer][jss::account] = alice.human();
1411 jvParams[jss::ledger_hash] = ledgerHash;
1412 Json::Value const jrr = env.rpc(
1413 "json", "ledger_entry", to_string(jvParams))[jss::result];
1414 checkErrorValue(jrr, "malformedRequest", "");
1415 }
1416 {
1417 // Malformed offer object. Non-integral seq member.
1418 Json::Value jvParams;
1419 jvParams[jss::offer] = Json::objectValue;
1420 jvParams[jss::offer][jss::account] = alice.human();
1421 jvParams[jss::offer][jss::seq] = std::to_string(env.seq(alice) - 1);
1422 jvParams[jss::ledger_hash] = ledgerHash;
1423 Json::Value const jrr = env.rpc(
1424 "json", "ledger_entry", to_string(jvParams))[jss::result];
1425 checkErrorValue(jrr, "malformedRequest", "");
1426 }
1427 }
1428
1429 void
1431 {
1432 testcase("ledger_entry Request Pay Chan");
1433 using namespace test::jtx;
1434 using namespace std::literals::chrono_literals;
1435 Env env{*this};
1436 Account const alice{"alice"};
1437
1438 env.fund(XRP(10000), alice);
1439 env.close();
1440
1441 // Lambda to create a PayChan.
1442 auto payChanCreate = [](test::jtx::Account const& account,
1443 test::jtx::Account const& to,
1444 STAmount const& amount,
1445 NetClock::duration const& settleDelay,
1446 PublicKey const& pk) {
1447 Json::Value jv;
1448 jv[jss::TransactionType] = jss::PaymentChannelCreate;
1449 jv[jss::Account] = account.human();
1450 jv[jss::Destination] = to.human();
1451 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1452 jv[sfSettleDelay.jsonName] = settleDelay.count();
1453 jv[sfPublicKey.jsonName] = strHex(pk.slice());
1454 return jv;
1455 };
1456
1457 env(payChanCreate(alice, env.master, XRP(57), 18s, alice.pk()));
1458 env.close();
1459
1460 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1461
1462 uint256 const payChanIndex{
1463 keylet::payChan(alice, env.master, env.seq(alice) - 1).key};
1464 {
1465 // Request the payment channel using its index.
1466 Json::Value jvParams;
1467 jvParams[jss::payment_channel] = to_string(payChanIndex);
1468 jvParams[jss::ledger_hash] = ledgerHash;
1469 Json::Value const jrr = env.rpc(
1470 "json", "ledger_entry", to_string(jvParams))[jss::result];
1471 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
1472 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
1473 BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
1474 }
1475 {
1476 // Request an index that is not a payment channel.
1477 Json::Value jvParams;
1478 jvParams[jss::payment_channel] = ledgerHash;
1479 jvParams[jss::ledger_hash] = ledgerHash;
1480 Json::Value const jrr = env.rpc(
1481 "json", "ledger_entry", to_string(jvParams))[jss::result];
1482 checkErrorValue(jrr, "entryNotFound", "");
1483 }
1484 }
1485
1486 void
1488 {
1489 testcase("ledger_entry Request RippleState");
1490 using namespace test::jtx;
1491 Env env{*this};
1492 Account const alice{"alice"};
1493 Account const gw{"gateway"};
1494 auto const USD = gw["USD"];
1495 env.fund(XRP(10000), alice, gw);
1496 env.close();
1497
1498 env.trust(USD(999), alice);
1499 env.close();
1500
1501 env(pay(gw, alice, USD(97)));
1502 env.close();
1503
1504 // check both aliases
1505 for (auto const& fieldName : {jss::ripple_state, jss::state})
1506 {
1507 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1508 {
1509 // Request the trust line using the accounts and currency.
1510 Json::Value jvParams;
1511 jvParams[fieldName] = Json::objectValue;
1512 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1513 jvParams[fieldName][jss::accounts][0u] = alice.human();
1514 jvParams[fieldName][jss::accounts][1u] = gw.human();
1515 jvParams[fieldName][jss::currency] = "USD";
1516 jvParams[jss::ledger_hash] = ledgerHash;
1517 Json::Value const jrr = env.rpc(
1518 "json", "ledger_entry", to_string(jvParams))[jss::result];
1519 BEAST_EXPECT(
1520 jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
1521 BEAST_EXPECT(
1522 jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
1523 }
1524 {
1525 // ripple_state is not an object.
1526 Json::Value jvParams;
1527 jvParams[fieldName] = "ripple_state";
1528 jvParams[jss::ledger_hash] = ledgerHash;
1529 Json::Value const jrr = env.rpc(
1530 "json", "ledger_entry", to_string(jvParams))[jss::result];
1531 checkErrorValue(jrr, "malformedRequest", "");
1532 }
1533 {
1534 // ripple_state.currency is missing.
1535 Json::Value jvParams;
1536 jvParams[fieldName] = Json::objectValue;
1537 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1538 jvParams[fieldName][jss::accounts][0u] = alice.human();
1539 jvParams[fieldName][jss::accounts][1u] = gw.human();
1540 jvParams[jss::ledger_hash] = ledgerHash;
1541 Json::Value const jrr = env.rpc(
1542 "json", "ledger_entry", to_string(jvParams))[jss::result];
1543 checkErrorValue(jrr, "malformedRequest", "");
1544 }
1545 {
1546 // ripple_state accounts is not an array.
1547 Json::Value jvParams;
1548 jvParams[fieldName] = Json::objectValue;
1549 jvParams[fieldName][jss::accounts] = 2;
1550 jvParams[fieldName][jss::currency] = "USD";
1551 jvParams[jss::ledger_hash] = ledgerHash;
1552 Json::Value const jrr = env.rpc(
1553 "json", "ledger_entry", to_string(jvParams))[jss::result];
1554 checkErrorValue(jrr, "malformedRequest", "");
1555 }
1556 {
1557 // ripple_state one of the accounts is missing.
1558 Json::Value jvParams;
1559 jvParams[fieldName] = Json::objectValue;
1560 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1561 jvParams[fieldName][jss::accounts][0u] = alice.human();
1562 jvParams[fieldName][jss::currency] = "USD";
1563 jvParams[jss::ledger_hash] = ledgerHash;
1564 Json::Value const jrr = env.rpc(
1565 "json", "ledger_entry", to_string(jvParams))[jss::result];
1566 checkErrorValue(jrr, "malformedRequest", "");
1567 }
1568 {
1569 // ripple_state more than 2 accounts.
1570 Json::Value jvParams;
1571 jvParams[fieldName] = Json::objectValue;
1572 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1573 jvParams[fieldName][jss::accounts][0u] = alice.human();
1574 jvParams[fieldName][jss::accounts][1u] = gw.human();
1575 jvParams[fieldName][jss::accounts][2u] = alice.human();
1576 jvParams[fieldName][jss::currency] = "USD";
1577 jvParams[jss::ledger_hash] = ledgerHash;
1578 Json::Value const jrr = env.rpc(
1579 "json", "ledger_entry", to_string(jvParams))[jss::result];
1580 checkErrorValue(jrr, "malformedRequest", "");
1581 }
1582 {
1583 // ripple_state account[0] is not a string.
1584 Json::Value jvParams;
1585 jvParams[fieldName] = Json::objectValue;
1586 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1587 jvParams[fieldName][jss::accounts][0u] = 44;
1588 jvParams[fieldName][jss::accounts][1u] = gw.human();
1589 jvParams[fieldName][jss::currency] = "USD";
1590 jvParams[jss::ledger_hash] = ledgerHash;
1591 Json::Value const jrr = env.rpc(
1592 "json", "ledger_entry", to_string(jvParams))[jss::result];
1593 checkErrorValue(jrr, "malformedRequest", "");
1594 }
1595 {
1596 // ripple_state account[1] is not a string.
1597 Json::Value jvParams;
1598 jvParams[fieldName] = Json::objectValue;
1599 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1600 jvParams[fieldName][jss::accounts][0u] = alice.human();
1601 jvParams[fieldName][jss::accounts][1u] = 21;
1602 jvParams[fieldName][jss::currency] = "USD";
1603 jvParams[jss::ledger_hash] = ledgerHash;
1604 Json::Value const jrr = env.rpc(
1605 "json", "ledger_entry", to_string(jvParams))[jss::result];
1606 checkErrorValue(jrr, "malformedRequest", "");
1607 }
1608 {
1609 // ripple_state account[0] == account[1].
1610 Json::Value jvParams;
1611 jvParams[fieldName] = Json::objectValue;
1612 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1613 jvParams[fieldName][jss::accounts][0u] = alice.human();
1614 jvParams[fieldName][jss::accounts][1u] = alice.human();
1615 jvParams[fieldName][jss::currency] = "USD";
1616 jvParams[jss::ledger_hash] = ledgerHash;
1617 Json::Value const jrr = env.rpc(
1618 "json", "ledger_entry", to_string(jvParams))[jss::result];
1619 checkErrorValue(jrr, "malformedRequest", "");
1620 }
1621 {
1622 // ripple_state malformed account[0].
1623 Json::Value jvParams;
1624 jvParams[fieldName] = Json::objectValue;
1625 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1626 jvParams[fieldName][jss::accounts][0u] =
1627 makeBadAddress(alice.human());
1628 jvParams[fieldName][jss::accounts][1u] = gw.human();
1629 jvParams[fieldName][jss::currency] = "USD";
1630 jvParams[jss::ledger_hash] = ledgerHash;
1631 Json::Value const jrr = env.rpc(
1632 "json", "ledger_entry", to_string(jvParams))[jss::result];
1633 checkErrorValue(jrr, "malformedAddress", "");
1634 }
1635 {
1636 // ripple_state malformed account[1].
1637 Json::Value jvParams;
1638 jvParams[fieldName] = Json::objectValue;
1639 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1640 jvParams[fieldName][jss::accounts][0u] = alice.human();
1641 jvParams[fieldName][jss::accounts][1u] =
1642 makeBadAddress(gw.human());
1643 jvParams[fieldName][jss::currency] = "USD";
1644 jvParams[jss::ledger_hash] = ledgerHash;
1645 Json::Value const jrr = env.rpc(
1646 "json", "ledger_entry", to_string(jvParams))[jss::result];
1647 checkErrorValue(jrr, "malformedAddress", "");
1648 }
1649 {
1650 // ripple_state malformed currency.
1651 Json::Value jvParams;
1652 jvParams[fieldName] = Json::objectValue;
1653 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1654 jvParams[fieldName][jss::accounts][0u] = alice.human();
1655 jvParams[fieldName][jss::accounts][1u] = gw.human();
1656 jvParams[fieldName][jss::currency] = "USDollars";
1657 jvParams[jss::ledger_hash] = ledgerHash;
1658 Json::Value const jrr = env.rpc(
1659 "json", "ledger_entry", to_string(jvParams))[jss::result];
1660 checkErrorValue(jrr, "malformedCurrency", "");
1661 }
1662 }
1663 }
1664
1665 void
1667 {
1668 testcase("ledger_entry Request Ticket");
1669 using namespace test::jtx;
1670 Env env{*this};
1671 env.close();
1672
1673 // Create two tickets.
1674 std::uint32_t const tkt1{env.seq(env.master) + 1};
1675 env(ticket::create(env.master, 2));
1676 env.close();
1677
1678 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1679 // Request four tickets: one before the first one we created, the
1680 // two created tickets, and the ticket that would come after the
1681 // last created ticket.
1682 {
1683 // Not a valid ticket requested by index.
1684 Json::Value jvParams;
1685 jvParams[jss::ticket] =
1686 to_string(getTicketIndex(env.master, tkt1 - 1));
1687 jvParams[jss::ledger_hash] = ledgerHash;
1688 Json::Value const jrr = env.rpc(
1689 "json", "ledger_entry", to_string(jvParams))[jss::result];
1690 checkErrorValue(jrr, "entryNotFound", "");
1691 }
1692 {
1693 // First real ticket requested by index.
1694 Json::Value jvParams;
1695 jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1));
1696 jvParams[jss::ledger_hash] = ledgerHash;
1697 Json::Value const jrr = env.rpc(
1698 "json", "ledger_entry", to_string(jvParams))[jss::result];
1699 BEAST_EXPECT(
1700 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Ticket);
1701 BEAST_EXPECT(jrr[jss::node][sfTicketSequence.jsonName] == tkt1);
1702 }
1703 {
1704 // Second real ticket requested by account and sequence.
1705 Json::Value jvParams;
1706 jvParams[jss::ticket] = Json::objectValue;
1707 jvParams[jss::ticket][jss::account] = env.master.human();
1708 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 1;
1709 jvParams[jss::ledger_hash] = ledgerHash;
1710 Json::Value const jrr = env.rpc(
1711 "json", "ledger_entry", to_string(jvParams))[jss::result];
1712 BEAST_EXPECT(
1713 jrr[jss::node][jss::index] ==
1714 to_string(getTicketIndex(env.master, tkt1 + 1)));
1715 }
1716 {
1717 // Not a valid ticket requested by account and sequence.
1718 Json::Value jvParams;
1719 jvParams[jss::ticket] = Json::objectValue;
1720 jvParams[jss::ticket][jss::account] = env.master.human();
1721 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 2;
1722 jvParams[jss::ledger_hash] = ledgerHash;
1723 Json::Value const jrr = env.rpc(
1724 "json", "ledger_entry", to_string(jvParams))[jss::result];
1725 checkErrorValue(jrr, "entryNotFound", "");
1726 }
1727 {
1728 // Request a ticket using an account root entry.
1729 Json::Value jvParams;
1730 jvParams[jss::ticket] = to_string(keylet::account(env.master).key);
1731 jvParams[jss::ledger_hash] = ledgerHash;
1732 Json::Value const jrr = env.rpc(
1733 "json", "ledger_entry", to_string(jvParams))[jss::result];
1734 checkErrorValue(jrr, "unexpectedLedgerType", "");
1735 }
1736 {
1737 // Malformed account entry.
1738 Json::Value jvParams;
1739 jvParams[jss::ticket] = Json::objectValue;
1740
1741 std::string const badAddress = makeBadAddress(env.master.human());
1742 jvParams[jss::ticket][jss::account] = badAddress;
1743 jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
1744 jvParams[jss::ledger_hash] = ledgerHash;
1745 Json::Value const jrr = env.rpc(
1746 "json", "ledger_entry", to_string(jvParams))[jss::result];
1747 checkErrorValue(jrr, "malformedAddress", "");
1748 }
1749 {
1750 // Malformed ticket object. Missing account member.
1751 Json::Value jvParams;
1752 jvParams[jss::ticket] = Json::objectValue;
1753 jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
1754 jvParams[jss::ledger_hash] = ledgerHash;
1755 Json::Value const jrr = env.rpc(
1756 "json", "ledger_entry", to_string(jvParams))[jss::result];
1757 checkErrorValue(jrr, "malformedRequest", "");
1758 }
1759 {
1760 // Malformed ticket object. Missing seq member.
1761 Json::Value jvParams;
1762 jvParams[jss::ticket] = Json::objectValue;
1763 jvParams[jss::ticket][jss::account] = env.master.human();
1764 jvParams[jss::ledger_hash] = ledgerHash;
1765 Json::Value const jrr = env.rpc(
1766 "json", "ledger_entry", to_string(jvParams))[jss::result];
1767 checkErrorValue(jrr, "malformedRequest", "");
1768 }
1769 {
1770 // Malformed ticket object. Non-integral seq member.
1771 Json::Value jvParams;
1772 jvParams[jss::ticket] = Json::objectValue;
1773 jvParams[jss::ticket][jss::account] = env.master.human();
1774 jvParams[jss::ticket][jss::ticket_seq] =
1775 std::to_string(env.seq(env.master) - 1);
1776 jvParams[jss::ledger_hash] = ledgerHash;
1777 Json::Value const jrr = env.rpc(
1778 "json", "ledger_entry", to_string(jvParams))[jss::result];
1779 checkErrorValue(jrr, "malformedRequest", "");
1780 }
1781 }
1782
1783 void
1785 {
1786 testcase("ledger_entry Request DID");
1787 using namespace test::jtx;
1788 using namespace std::literals::chrono_literals;
1789 Env env{*this};
1790 Account const alice{"alice"};
1791
1792 env.fund(XRP(10000), alice);
1793 env.close();
1794
1795 // Lambda to create a DID.
1796 auto didCreate = [](test::jtx::Account const& account) {
1797 Json::Value jv;
1798 jv[jss::TransactionType] = jss::DIDSet;
1799 jv[jss::Account] = account.human();
1800 jv[sfDIDDocument.jsonName] = strHex(std::string{"data"});
1801 jv[sfURI.jsonName] = strHex(std::string{"uri"});
1802 return jv;
1803 };
1804
1805 env(didCreate(alice));
1806 env.close();
1807
1808 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1809
1810 {
1811 // Request the DID using its index.
1812 Json::Value jvParams;
1813 jvParams[jss::did] = alice.human();
1814 jvParams[jss::ledger_hash] = ledgerHash;
1815 Json::Value const jrr = env.rpc(
1816 "json", "ledger_entry", to_string(jvParams))[jss::result];
1817 BEAST_EXPECT(
1818 jrr[jss::node][sfDIDDocument.jsonName] ==
1819 strHex(std::string{"data"}));
1820 BEAST_EXPECT(
1821 jrr[jss::node][sfURI.jsonName] == strHex(std::string{"uri"}));
1822 }
1823 {
1824 // Request an index that is not a DID.
1825 Json::Value jvParams;
1826 jvParams[jss::did] = env.master.human();
1827 jvParams[jss::ledger_hash] = ledgerHash;
1828 Json::Value const jrr = env.rpc(
1829 "json", "ledger_entry", to_string(jvParams))[jss::result];
1830 checkErrorValue(jrr, "entryNotFound", "");
1831 }
1832 }
1833
1834 void
1835 testLedgerEntryInvalidParams(unsigned int apiVersion)
1836 {
1837 testcase(
1838 "ledger_entry Request With Invalid Parameters v" +
1839 std::to_string(apiVersion));
1840 using namespace test::jtx;
1841 Env env{*this};
1842
1843 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1844
1845 auto makeParams = [&apiVersion](std::function<void(Json::Value&)> f) {
1846 Json::Value params;
1847 params[jss::api_version] = apiVersion;
1848 f(params);
1849 return params;
1850 };
1851 // "features" is not an option supported by ledger_entry.
1852 {
1853 auto const jvParams =
1854 makeParams([&ledgerHash](Json::Value& jvParams) {
1855 jvParams[jss::features] = ledgerHash;
1856 jvParams[jss::ledger_hash] = ledgerHash;
1857 });
1858 Json::Value const jrr = env.rpc(
1859 "json", "ledger_entry", to_string(jvParams))[jss::result];
1860
1861 if (apiVersion < 2u)
1862 checkErrorValue(jrr, "unknownOption", "");
1863 else
1864 checkErrorValue(jrr, "invalidParams", "");
1865 }
1866 Json::Value const injectObject = []() {
1868 obj[jss::account] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
1869 obj[jss::ledger_index] = "validated";
1870 return obj;
1871 }();
1872 Json::Value const injectArray = []() {
1874 arr[0u] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
1875 arr[1u] = "validated";
1876 return arr;
1877 }();
1878
1879 // invalid input for fields that can handle an object, but can't handle
1880 // an array
1881 for (auto const& field :
1882 {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm})
1883 {
1884 auto const jvParams =
1885 makeParams([&field, &injectArray](Json::Value& jvParams) {
1886 jvParams[field] = injectArray;
1887 });
1888
1889 Json::Value const jrr = env.rpc(
1890 "json", "ledger_entry", to_string(jvParams))[jss::result];
1891
1892 if (apiVersion < 2u)
1893 checkErrorValue(jrr, "internal", "Internal error.");
1894 else
1895 checkErrorValue(jrr, "invalidParams", "");
1896 }
1897 // Fields that can handle objects just fine
1898 for (auto const& field :
1899 {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm})
1900 {
1901 auto const jvParams =
1902 makeParams([&field, &injectObject](Json::Value& jvParams) {
1903 jvParams[field] = injectObject;
1904 });
1905
1906 Json::Value const jrr = env.rpc(
1907 "json", "ledger_entry", to_string(jvParams))[jss::result];
1908
1909 checkErrorValue(jrr, "malformedRequest", "");
1910 }
1911
1912 for (auto const& inject : {injectObject, injectArray})
1913 {
1914 // invalid input for fields that can't handle an object or an array
1915 for (auto const& field :
1916 {jss::index,
1917 jss::account_root,
1918 jss::check,
1919 jss::payment_channel})
1920 {
1921 auto const jvParams =
1922 makeParams([&field, &inject](Json::Value& jvParams) {
1923 jvParams[field] = inject;
1924 });
1925
1926 Json::Value const jrr = env.rpc(
1927 "json", "ledger_entry", to_string(jvParams))[jss::result];
1928
1929 if (apiVersion < 2u)
1930 checkErrorValue(jrr, "internal", "Internal error.");
1931 else
1932 checkErrorValue(jrr, "invalidParams", "");
1933 }
1934 // directory sub-fields
1935 for (auto const& field : {jss::dir_root, jss::owner})
1936 {
1937 auto const jvParams =
1938 makeParams([&field, &inject](Json::Value& jvParams) {
1939 jvParams[jss::directory][field] = inject;
1940 });
1941
1942 Json::Value const jrr = env.rpc(
1943 "json", "ledger_entry", to_string(jvParams))[jss::result];
1944
1945 if (apiVersion < 2u)
1946 checkErrorValue(jrr, "internal", "Internal error.");
1947 else
1948 checkErrorValue(jrr, "invalidParams", "");
1949 }
1950 // escrow sub-fields
1951 {
1952 auto const jvParams =
1953 makeParams([&inject](Json::Value& jvParams) {
1954 jvParams[jss::escrow][jss::owner] = inject;
1955 jvParams[jss::escrow][jss::seq] = 99;
1956 });
1957
1958 Json::Value const jrr = env.rpc(
1959 "json", "ledger_entry", to_string(jvParams))[jss::result];
1960
1961 if (apiVersion < 2u)
1962 checkErrorValue(jrr, "internal", "Internal error.");
1963 else
1964 checkErrorValue(jrr, "invalidParams", "");
1965 }
1966 // offer sub-fields
1967 {
1968 auto const jvParams =
1969 makeParams([&inject](Json::Value& jvParams) {
1970 jvParams[jss::offer][jss::account] = inject;
1971 jvParams[jss::offer][jss::seq] = 99;
1972 });
1973
1974 Json::Value const jrr = env.rpc(
1975 "json", "ledger_entry", to_string(jvParams))[jss::result];
1976
1977 if (apiVersion < 2u)
1978 checkErrorValue(jrr, "internal", "Internal error.");
1979 else
1980 checkErrorValue(jrr, "invalidParams", "");
1981 }
1982 // ripple_state sub-fields
1983 {
1984 auto const jvParams =
1985 makeParams([&inject](Json::Value& jvParams) {
1987 rs[jss::currency] = "FOO";
1988 rs[jss::accounts] = Json::Value(Json::arrayValue);
1989 rs[jss::accounts][0u] =
1990 "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
1991 rs[jss::accounts][1u] =
1992 "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv";
1993 rs[jss::currency] = inject;
1994 jvParams[jss::ripple_state] = std::move(rs);
1995 });
1996
1997 Json::Value const jrr = env.rpc(
1998 "json", "ledger_entry", to_string(jvParams))[jss::result];
1999
2000 if (apiVersion < 2u)
2001 checkErrorValue(jrr, "internal", "Internal error.");
2002 else
2003 checkErrorValue(jrr, "invalidParams", "");
2004 }
2005 // ticket sub-fields
2006 {
2007 auto const jvParams =
2008 makeParams([&inject](Json::Value& jvParams) {
2009 jvParams[jss::ticket][jss::account] = inject;
2010 jvParams[jss::ticket][jss::ticket_seq] = 99;
2011 });
2012
2013 Json::Value const jrr = env.rpc(
2014 "json", "ledger_entry", to_string(jvParams))[jss::result];
2015
2016 if (apiVersion < 2u)
2017 checkErrorValue(jrr, "internal", "Internal error.");
2018 else
2019 checkErrorValue(jrr, "invalidParams", "");
2020 }
2021
2022 // Fields that can handle malformed inputs just fine
2023 for (auto const& field : {jss::nft_page, jss::deposit_preauth})
2024 {
2025 auto const jvParams =
2026 makeParams([&field, &inject](Json::Value& jvParams) {
2027 jvParams[field] = inject;
2028 });
2029
2030 Json::Value const jrr = env.rpc(
2031 "json", "ledger_entry", to_string(jvParams))[jss::result];
2032
2033 checkErrorValue(jrr, "malformedRequest", "");
2034 }
2035 // Subfields of deposit_preauth that can handle malformed inputs
2036 // fine
2037 for (auto const& field : {jss::owner, jss::authorized})
2038 {
2039 auto const jvParams =
2040 makeParams([&field, &inject](Json::Value& jvParams) {
2041 auto pa = Json::Value(Json::objectValue);
2042 pa[jss::owner] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2043 pa[jss::authorized] =
2044 "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv";
2045 pa[field] = inject;
2046 jvParams[jss::deposit_preauth] = std::move(pa);
2047 });
2048
2049 Json::Value const jrr = env.rpc(
2050 "json", "ledger_entry", to_string(jvParams))[jss::result];
2051
2052 checkErrorValue(jrr, "malformedRequest", "");
2053 }
2054 }
2055 }
2056
2057 void
2059 {
2060 testcase("Invalid Oracle Ledger Entry");
2061 using namespace ripple::test::jtx;
2062 using namespace ripple::test::jtx::oracle;
2063
2064 Env env(*this);
2065 Account const owner("owner");
2066 env.fund(XRP(1'000), owner);
2067 Oracle oracle(
2068 env,
2069 {.owner = owner,
2070 .fee = static_cast<int>(env.current()->fees().base.drops())});
2071
2072 // Malformed document id
2073 auto res = Oracle::ledgerEntry(env, owner, NoneTag);
2074 BEAST_EXPECT(res[jss::error].asString() == "invalidParams");
2075 std::vector<AnyValue> invalid = {-1, 1.2, "", "Invalid"};
2076 for (auto const& v : invalid)
2077 {
2078 auto const res = Oracle::ledgerEntry(env, owner, v);
2079 BEAST_EXPECT(res[jss::error].asString() == "malformedDocumentID");
2080 }
2081 // Missing document id
2082 res = Oracle::ledgerEntry(env, owner, std::nullopt);
2083 BEAST_EXPECT(res[jss::error].asString() == "malformedRequest");
2084
2085 // Missing account
2086 res = Oracle::ledgerEntry(env, std::nullopt, 1);
2087 BEAST_EXPECT(res[jss::error].asString() == "malformedRequest");
2088
2089 // Malformed account
2090 std::string malfAccount = to_string(owner.id());
2091 malfAccount.replace(10, 1, 1, '!');
2092 res = Oracle::ledgerEntry(env, malfAccount, 1);
2093 BEAST_EXPECT(res[jss::error].asString() == "malformedAddress");
2094 }
2095
2096 void
2098 {
2099 testcase("Oracle Ledger Entry");
2100 using namespace ripple::test::jtx;
2101 using namespace ripple::test::jtx::oracle;
2102
2103 Env env(*this);
2104 auto const baseFee =
2105 static_cast<int>(env.current()->fees().base.drops());
2106 std::vector<AccountID> accounts;
2108 for (int i = 0; i < 10; ++i)
2109 {
2110 Account const owner(std::string("owner") + std::to_string(i));
2111 env.fund(XRP(1'000), owner);
2112 // different accounts can have the same asset pair
2113 Oracle oracle(
2114 env, {.owner = owner, .documentID = i, .fee = baseFee});
2115 accounts.push_back(owner.id());
2116 oracles.push_back(oracle.documentID());
2117 // same account can have different asset pair
2118 Oracle oracle1(
2119 env, {.owner = owner, .documentID = i + 10, .fee = baseFee});
2120 accounts.push_back(owner.id());
2121 oracles.push_back(oracle1.documentID());
2122 }
2123 for (int i = 0; i < accounts.size(); ++i)
2124 {
2125 auto const jv = [&]() {
2126 // document id is uint32
2127 if (i % 2)
2128 return Oracle::ledgerEntry(env, accounts[i], oracles[i]);
2129 // document id is string
2130 return Oracle::ledgerEntry(
2131 env, accounts[i], std::to_string(oracles[i]));
2132 }();
2133 try
2134 {
2135 BEAST_EXPECT(
2136 jv[jss::node][jss::Owner] == to_string(accounts[i]));
2137 }
2138 catch (...)
2139 {
2140 fail();
2141 }
2142 }
2143 }
2144
2145 void
2147 {
2148 testcase("ledger_entry Request MPT");
2149 using namespace test::jtx;
2150 using namespace std::literals::chrono_literals;
2151 Env env{*this};
2152 Account const alice{"alice"};
2153 Account const bob("bob");
2154
2155 MPTTester mptAlice(env, alice, {.holders = {bob}});
2156 mptAlice.create(
2157 {.transferFee = 10,
2158 .metadata = "123",
2159 .ownerCount = 1,
2162 mptAlice.authorize({.account = bob, .holderCount = 1});
2163
2164 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2165
2166 std::string const badMptID =
2167 "00000193B9DDCAF401B5B3B26875986043F82CD0D13B4315";
2168 {
2169 // Request the MPTIssuance using its MPTIssuanceID.
2170 Json::Value jvParams;
2171 jvParams[jss::mpt_issuance] = strHex(mptAlice.issuanceID());
2172 jvParams[jss::ledger_hash] = ledgerHash;
2173 Json::Value const jrr = env.rpc(
2174 "json", "ledger_entry", to_string(jvParams))[jss::result];
2175 BEAST_EXPECT(
2176 jrr[jss::node][sfMPTokenMetadata.jsonName] ==
2177 strHex(std::string{"123"}));
2178 BEAST_EXPECT(
2179 jrr[jss::node][jss::mpt_issuance_id] ==
2180 strHex(mptAlice.issuanceID()));
2181 }
2182 {
2183 // Request an index that is not a MPTIssuance.
2184 Json::Value jvParams;
2185 jvParams[jss::mpt_issuance] = badMptID;
2186 jvParams[jss::ledger_hash] = ledgerHash;
2187 Json::Value const jrr = env.rpc(
2188 "json", "ledger_entry", to_string(jvParams))[jss::result];
2189 checkErrorValue(jrr, "entryNotFound", "");
2190 }
2191 {
2192 // Request the MPToken using its owner + mptIssuanceID.
2193 Json::Value jvParams;
2194 jvParams[jss::mptoken] = Json::objectValue;
2195 jvParams[jss::mptoken][jss::account] = bob.human();
2196 jvParams[jss::mptoken][jss::mpt_issuance_id] =
2197 strHex(mptAlice.issuanceID());
2198 jvParams[jss::ledger_hash] = ledgerHash;
2199 Json::Value const jrr = env.rpc(
2200 "json", "ledger_entry", to_string(jvParams))[jss::result];
2201 BEAST_EXPECT(
2202 jrr[jss::node][sfMPTokenIssuanceID.jsonName] ==
2203 strHex(mptAlice.issuanceID()));
2204 }
2205 {
2206 // Request the MPToken using a bad mptIssuanceID.
2207 Json::Value jvParams;
2208 jvParams[jss::mptoken] = Json::objectValue;
2209 jvParams[jss::mptoken][jss::account] = bob.human();
2210 jvParams[jss::mptoken][jss::mpt_issuance_id] = badMptID;
2211 jvParams[jss::ledger_hash] = ledgerHash;
2212 Json::Value const jrr = env.rpc(
2213 "json", "ledger_entry", to_string(jvParams))[jss::result];
2214 checkErrorValue(jrr, "entryNotFound", "");
2215 }
2216 }
2217
2218 void
2220 {
2221 testcase("ledger_entry PermissionedDomain");
2222
2223 using namespace test::jtx;
2224
2225 Env env(*this, supported_amendments() | featurePermissionedDomains);
2226 Account const issuer{"issuer"};
2227 Account const alice{"alice"};
2228 Account const bob{"bob"};
2229
2230 env.fund(XRP(5000), issuer, alice, bob);
2231 env.close();
2232
2233 auto const seq = env.seq(alice);
2234 env(pdomain::setTx(alice, {{alice, "first credential"}}));
2235 env.close();
2236 auto const objects = pdomain::getObjects(alice, env);
2237 if (!BEAST_EXPECT(objects.size() == 1))
2238 return;
2239
2240 {
2241 // Succeed
2242 Json::Value params;
2243 params[jss::ledger_index] = jss::validated;
2244 params[jss::permissioned_domain][jss::account] = alice.human();
2245 params[jss::permissioned_domain][jss::seq] = seq;
2246 auto jv = env.rpc("json", "ledger_entry", to_string(params));
2247 BEAST_EXPECT(
2248 jv.isObject() && jv.isMember(jss::result) &&
2249 !jv[jss::result].isMember(jss::error) &&
2250 jv[jss::result].isMember(jss::node) &&
2251 jv[jss::result][jss::node].isMember(
2252 sfLedgerEntryType.jsonName) &&
2253 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
2254 jss::PermissionedDomain);
2255
2256 std::string const pdIdx = jv[jss::result][jss::index].asString();
2257 BEAST_EXPECT(
2258 strHex(keylet::permissionedDomain(alice, seq).key) == pdIdx);
2259
2260 params.clear();
2261 params[jss::ledger_index] = jss::validated;
2262 params[jss::permissioned_domain] = pdIdx;
2263 jv = env.rpc("json", "ledger_entry", to_string(params));
2264 BEAST_EXPECT(
2265 jv.isObject() && jv.isMember(jss::result) &&
2266 !jv[jss::result].isMember(jss::error) &&
2267 jv[jss::result].isMember(jss::node) &&
2268 jv[jss::result][jss::node].isMember(
2269 sfLedgerEntryType.jsonName) &&
2270 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
2271 jss::PermissionedDomain);
2272 }
2273
2274 {
2275 // Fail, invalid permissioned domain index
2276 Json::Value params;
2277 params[jss::ledger_index] = jss::validated;
2278 params[jss::permissioned_domain] =
2279 "12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A"
2280 "DE";
2281 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2282 checkErrorValue(jrr[jss::result], "entryNotFound", "");
2283 }
2284
2285 {
2286 // Fail, invalid permissioned domain index
2287 Json::Value params;
2288 params[jss::ledger_index] = jss::validated;
2289 params[jss::permissioned_domain] = "NotAHexString";
2290 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2291 checkErrorValue(jrr[jss::result], "malformedRequest", "");
2292 }
2293
2294 {
2295 // Fail, permissioned domain is not an object
2296 Json::Value params;
2297 params[jss::ledger_index] = jss::validated;
2298 params[jss::permissioned_domain] = 10;
2299 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2300 checkErrorValue(jrr[jss::result], "malformedRequest", "");
2301 }
2302
2303 {
2304 // Fail, invalid account
2305 Json::Value params;
2306 params[jss::ledger_index] = jss::validated;
2307 params[jss::permissioned_domain][jss::account] = 1;
2308 params[jss::permissioned_domain][jss::seq] = seq;
2309 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2310 checkErrorValue(jrr[jss::result], "malformedAddress", "");
2311 }
2312
2313 {
2314 // Fail, account is an object
2315 Json::Value params;
2316 params[jss::ledger_index] = jss::validated;
2317 params[jss::permissioned_domain][jss::account] =
2319 params[jss::permissioned_domain][jss::seq] = seq;
2320 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2321 checkErrorValue(jrr[jss::result], "malformedAddress", "");
2322 }
2323
2324 {
2325 // Fail, no account
2326 Json::Value params;
2327 params[jss::ledger_index] = jss::validated;
2328 params[jss::permissioned_domain][jss::account] = "";
2329 params[jss::permissioned_domain][jss::seq] = seq;
2330 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2331 checkErrorValue(jrr[jss::result], "malformedAddress", "");
2332 }
2333
2334 {
2335 // Fail, invalid sequence
2336 Json::Value params;
2337 params[jss::ledger_index] = jss::validated;
2338 params[jss::permissioned_domain][jss::account] = alice.human();
2339 params[jss::permissioned_domain][jss::seq] = "12g";
2340 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2341 checkErrorValue(jrr[jss::result], "malformedRequest", "");
2342 }
2343 }
2344
2345 void
2347 {
2348 testcase("ledger_entry command-line");
2349 using namespace test::jtx;
2350
2351 Env env{*this};
2352 Account const alice{"alice"};
2353 env.fund(XRP(10000), alice);
2354 env.close();
2355
2356 auto const checkId = keylet::check(env.master, env.seq(env.master));
2357
2358 env(check::create(env.master, alice, XRP(100)));
2359 env.close();
2360
2361 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2362 {
2363 // Request a check.
2364 Json::Value const jrr =
2365 env.rpc("ledger_entry", to_string(checkId.key))[jss::result];
2366 BEAST_EXPECT(
2367 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
2368 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
2369 }
2370 }
2371
2372public:
2373 void
2374 run() override
2375 {
2395
2398 }
2399};
2400
2403{
2404 void
2406 Json::Value const& jv,
2407 std::string const& err,
2408 std::string const& msg)
2409 {
2410 if (BEAST_EXPECT(jv.isMember(jss::status)))
2411 BEAST_EXPECT(jv[jss::status] == "error");
2412 if (BEAST_EXPECT(jv.isMember(jss::error)))
2413 BEAST_EXPECT(jv[jss::error] == err);
2414 if (msg.empty())
2415 {
2416 BEAST_EXPECT(
2417 jv[jss::error_message] == Json::nullValue ||
2418 jv[jss::error_message] == "");
2419 }
2420 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
2421 BEAST_EXPECT(jv[jss::error_message] == msg);
2422 }
2423
2424 void
2426 {
2427 testcase("ledger_entry: bridge");
2428 using namespace test::jtx;
2429
2430 Env mcEnv{*this, features};
2431 Env scEnv(*this, envconfig(), features);
2432
2433 createBridgeObjects(mcEnv, scEnv);
2434
2435 std::string const ledgerHash{to_string(mcEnv.closed()->info().hash)};
2436 std::string bridge_index;
2437 Json::Value mcBridge;
2438 {
2439 // request the bridge via RPC
2440 Json::Value jvParams;
2441 jvParams[jss::bridge_account] = mcDoor.human();
2442 jvParams[jss::bridge] = jvb;
2443 Json::Value const jrr = mcEnv.rpc(
2444 "json", "ledger_entry", to_string(jvParams))[jss::result];
2445
2446 BEAST_EXPECT(jrr.isMember(jss::node));
2447 auto r = jrr[jss::node];
2448 // std::cout << to_string(r) << '\n';
2449
2450 BEAST_EXPECT(r.isMember(jss::Account));
2451 BEAST_EXPECT(r[jss::Account] == mcDoor.human());
2452
2453 BEAST_EXPECT(r.isMember(jss::Flags));
2454
2455 BEAST_EXPECT(r.isMember(sfLedgerEntryType.jsonName));
2456 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::Bridge);
2457
2458 // we not created an account yet
2459 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
2460 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 0);
2461
2462 // we have not claimed a locking chain tx yet
2463 BEAST_EXPECT(r.isMember(sfXChainAccountClaimCount.jsonName));
2464 BEAST_EXPECT(r[sfXChainAccountClaimCount.jsonName].asInt() == 0);
2465
2466 BEAST_EXPECT(r.isMember(jss::index));
2467 bridge_index = r[jss::index].asString();
2468 mcBridge = r;
2469 }
2470 {
2471 // request the bridge via RPC by index
2472 Json::Value jvParams;
2473 jvParams[jss::index] = bridge_index;
2474 Json::Value const jrr = mcEnv.rpc(
2475 "json", "ledger_entry", to_string(jvParams))[jss::result];
2476
2477 BEAST_EXPECT(jrr.isMember(jss::node));
2478 BEAST_EXPECT(jrr[jss::node] == mcBridge);
2479 }
2480 {
2481 // swap door accounts and make sure we get an error value
2482 Json::Value jvParams;
2483 // Sidechain door account is "master", not scDoor
2484 jvParams[jss::bridge_account] = Account::master.human();
2485 jvParams[jss::bridge] = jvb;
2486 jvParams[jss::ledger_hash] = ledgerHash;
2487 Json::Value const jrr = mcEnv.rpc(
2488 "json", "ledger_entry", to_string(jvParams))[jss::result];
2489
2490 checkErrorValue(jrr, "entryNotFound", "");
2491 }
2492 {
2493 // create two claim ids and verify that the bridge counter was
2494 // incremented
2496 mcEnv.close();
2498 mcEnv.close();
2499
2500 // request the bridge via RPC
2501 Json::Value jvParams;
2502 jvParams[jss::bridge_account] = mcDoor.human();
2503 jvParams[jss::bridge] = jvb;
2504 // std::cout << to_string(jvParams) << '\n';
2505 Json::Value const jrr = mcEnv.rpc(
2506 "json", "ledger_entry", to_string(jvParams))[jss::result];
2507
2508 BEAST_EXPECT(jrr.isMember(jss::node));
2509 auto r = jrr[jss::node];
2510
2511 // we executed two create claim id txs
2512 BEAST_EXPECT(r.isMember(sfXChainClaimID.jsonName));
2513 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
2514 }
2515 }
2516
2517 void
2519 {
2520 testcase("ledger_entry: xchain_claim_id");
2521 using namespace test::jtx;
2522
2523 Env mcEnv{*this, features};
2524 Env scEnv(*this, envconfig(), features);
2525
2526 createBridgeObjects(mcEnv, scEnv);
2527
2529 scEnv.close();
2531 scEnv.close();
2532
2533 std::string bridge_index;
2534 {
2535 // request the xchain_claim_id via RPC
2536 Json::Value jvParams;
2537 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
2538 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
2539 1;
2540 // std::cout << to_string(jvParams) << '\n';
2541 Json::Value const jrr = scEnv.rpc(
2542 "json", "ledger_entry", to_string(jvParams))[jss::result];
2543
2544 BEAST_EXPECT(jrr.isMember(jss::node));
2545 auto r = jrr[jss::node];
2546 // std::cout << to_string(r) << '\n';
2547
2548 BEAST_EXPECT(r.isMember(jss::Account));
2549 BEAST_EXPECT(r[jss::Account] == scAlice.human());
2550 BEAST_EXPECT(
2551 r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
2552 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 1);
2553 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
2554 }
2555
2556 {
2557 // request the xchain_claim_id via RPC
2558 Json::Value jvParams;
2559 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
2560 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
2561 2;
2562 Json::Value const jrr = scEnv.rpc(
2563 "json", "ledger_entry", to_string(jvParams))[jss::result];
2564
2565 BEAST_EXPECT(jrr.isMember(jss::node));
2566 auto r = jrr[jss::node];
2567 // std::cout << to_string(r) << '\n';
2568
2569 BEAST_EXPECT(r.isMember(jss::Account));
2570 BEAST_EXPECT(r[jss::Account] == scBob.human());
2571 BEAST_EXPECT(
2572 r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
2573 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
2574 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
2575 }
2576 }
2577
2578 void
2580 {
2581 testcase("ledger_entry: xchain_create_account_claim_id");
2582 using namespace test::jtx;
2583
2584 Env mcEnv{*this, features};
2585 Env scEnv(*this, envconfig(), features);
2586
2587 // note: signers.size() and quorum are both 5 in createBridgeObjects
2588 createBridgeObjects(mcEnv, scEnv);
2589
2590 auto scCarol =
2591 Account("scCarol"); // Don't fund it - it will be created with the
2592 // xchain transaction
2593 auto const amt = XRP(1000);
2595 mcAlice, jvb, scCarol, amt, reward));
2596 mcEnv.close();
2597
2598 // send less than quorum of attestations (otherwise funds are
2599 // immediately transferred and no "claim" object is created)
2600 size_t constexpr num_attest = 3;
2601 auto attestations = create_account_attestations(
2602 scAttester,
2603 jvb,
2604 mcAlice,
2605 amt,
2606 reward,
2607 payee,
2608 /*wasLockingChainSend*/ true,
2609 1,
2610 scCarol,
2611 signers,
2613 for (size_t i = 0; i < num_attest; ++i)
2614 {
2615 scEnv(attestations[i]);
2616 }
2617 scEnv.close();
2618
2619 {
2620 // request the create account claim_id via RPC
2621 Json::Value jvParams;
2622 jvParams[jss::xchain_owned_create_account_claim_id] =
2624 jvParams[jss::xchain_owned_create_account_claim_id]
2625 [jss::xchain_owned_create_account_claim_id] = 1;
2626 // std::cout << to_string(jvParams) << '\n';
2627 Json::Value const jrr = scEnv.rpc(
2628 "json", "ledger_entry", to_string(jvParams))[jss::result];
2629 // std::cout << to_string(jrr) << '\n';
2630
2631 BEAST_EXPECT(jrr.isMember(jss::node));
2632 auto r = jrr[jss::node];
2633
2634 BEAST_EXPECT(r.isMember(jss::Account));
2635 BEAST_EXPECT(r[jss::Account] == Account::master.human());
2636
2637 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
2638 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 1);
2639
2640 BEAST_EXPECT(
2641 r.isMember(sfXChainCreateAccountAttestations.jsonName));
2642 auto attest = r[sfXChainCreateAccountAttestations.jsonName];
2643 BEAST_EXPECT(attest.isArray());
2644 BEAST_EXPECT(attest.size() == 3);
2645 BEAST_EXPECT(attest[Json::Value::UInt(0)].isMember(
2646 sfXChainCreateAccountProofSig.jsonName));
2647 Json::Value a[num_attest];
2648 for (size_t i = 0; i < num_attest; ++i)
2649 {
2650 a[i] = attest[Json::Value::UInt(0)]
2651 [sfXChainCreateAccountProofSig.jsonName];
2652 BEAST_EXPECT(
2653 a[i].isMember(jss::Amount) &&
2654 a[i][jss::Amount].asInt() == 1000 * drop_per_xrp);
2655 BEAST_EXPECT(
2656 a[i].isMember(jss::Destination) &&
2657 a[i][jss::Destination] == scCarol.human());
2658 BEAST_EXPECT(
2659 a[i].isMember(sfAttestationSignerAccount.jsonName) &&
2661 signers.begin(), signers.end(), [&](signer const& s) {
2662 return a[i][sfAttestationSignerAccount.jsonName] ==
2663 s.account.human();
2664 }));
2665 BEAST_EXPECT(
2666 a[i].isMember(sfAttestationRewardAccount.jsonName) &&
2668 payee.begin(),
2669 payee.end(),
2670 [&](Account const& account) {
2671 return a[i][sfAttestationRewardAccount.jsonName] ==
2672 account.human();
2673 }));
2674 BEAST_EXPECT(
2675 a[i].isMember(sfWasLockingChainSend.jsonName) &&
2676 a[i][sfWasLockingChainSend.jsonName] == 1);
2677 BEAST_EXPECT(
2678 a[i].isMember(sfSignatureReward.jsonName) &&
2679 a[i][sfSignatureReward.jsonName].asInt() ==
2680 1 * drop_per_xrp);
2681 }
2682 }
2683
2684 // complete attestations quorum - CreateAccountClaimID should not be
2685 // present anymore
2686 for (size_t i = num_attest; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS; ++i)
2687 {
2688 scEnv(attestations[i]);
2689 }
2690 scEnv.close();
2691 {
2692 // request the create account claim_id via RPC
2693 Json::Value jvParams;
2694 jvParams[jss::xchain_owned_create_account_claim_id] =
2696 jvParams[jss::xchain_owned_create_account_claim_id]
2697 [jss::xchain_owned_create_account_claim_id] = 1;
2698 // std::cout << to_string(jvParams) << '\n';
2699 Json::Value const jrr = scEnv.rpc(
2700 "json", "ledger_entry", to_string(jvParams))[jss::result];
2701 checkErrorValue(jrr, "entryNotFound", "");
2702 }
2703 }
2704
2705public:
2706 void
2707 run() override
2708 {
2712 }
2713};
2714
2715BEAST_DEFINE_TESTSUITE(LedgerEntry, app, ripple);
2716BEAST_DEFINE_TESTSUITE(LedgerEntry_XChain, app, ripple);
2717
2718} // namespace test
2719} // namespace ripple
T any_of(T... args)
T bind_front(T... args)
Represents a JSON value.
Definition: json_value.h:150
Json::UInt UInt
Definition: json_value.h:157
Value & append(Value const &value)
Append value to array at the end.
Definition: json_value.cpp:910
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:719
void clear()
Remove all object members and array elements.
Definition: json_value.cpp:766
Int asInt() const
Definition: json_value.cpp:516
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:482
bool isMember(char const *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:962
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition: suite.h:533
A public key.
Definition: PublicKey.h:62
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
void run() override
Runs the suite.
void testLedgerEntryInvalidParams(unsigned int apiVersion)
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
std::string makeBadAddress(std::string good)
void run() override
Runs the suite.
Immutable cryptographic account descriptor.
Definition: Account.h:39
AccountID id() const
Returns the Account ID.
Definition: Account.h:107
static Account const master
The master account.
Definition: Account.h:48
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:114
A transaction testing environment.
Definition: Env.h:121
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition: Env.cpp:212
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:331
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:117
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:770
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:233
void create(MPTCreate const &arg=MPTCreate{})
Definition: mpt.cpp:86
Oracle class facilitates unit-testing of the Price Oracle feature.
Definition: Oracle.h:120
T empty(T... args)
@ nullValue
'null' value
Definition: json_value.h:39
@ arrayValue
array value (ordered list)
Definition: json_value.h:45
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:46
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition: Indexes.cpp:563
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:177
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition: Indexes.cpp:329
Keylet payChan(AccountID const &src, AccountID const &dst, std::uint32_t seq) noexcept
A PaymentChannel.
Definition: Indexes.cpp:388
Json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
Definition: TestHelpers.h:441
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition: creds.cpp:32
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition: creds.cpp:83
Json::Value set(jtx::Account const &account, jtx::Account const &authorize, std::vector< std::string > const &permissions)
Definition: delegate.cpp:31
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition: deposit.cpp:32
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition: deposit.cpp:54
Json::Value setTx(AccountID const &account, Credentials const &credentials, std::optional< uint256 > domain)
std::map< uint256, Json::Value > getObjects(Account const &account, Env &env, bool withType)
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition: ticket.cpp:31
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:29
Json::Value sidechain_xchain_account_create(Account const &acc, Json::Value const &bridge, Account const &dst, AnyAmount const &amt, AnyAmount const &reward)
constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS
Definition: xchain_bridge.h:37
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:30
JValueVec create_account_attestations(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::AnyAmount const &rewardAmount, std::vector< jtx::Account > const &rewardAccounts, bool wasLockingChainSend, std::uint64_t createCount, jtx::Account const &dst, std::vector< jtx::signer > const &signers, std::size_t const numAtts, std::size_t const fromIdx)
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition: offer.cpp:29
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
FeatureBitset supported_amendments()
Definition: Env.h:74
static uint256 ledgerHash(LedgerInfo const &info)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
constexpr std::uint32_t asfDepositAuth
Definition: TxFlags.h:84
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:178
constexpr std::uint32_t const tfMPTCanTransfer
Definition: TxFlags.h:152
constexpr std::uint32_t const tfMPTCanTrade
Definition: TxFlags.h:151
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition: Protocol.h:107
@ invalid
Timely, but invalid signature.
void forAllApiVersions(Fn const &fn, Args &&... args)
Definition: ApiVersion.h:102
constexpr std::uint32_t tfUniversal
Definition: TxFlags.h:61
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition: Indexes.cpp:149
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
constexpr std::uint32_t const tfMPTCanEscrow
Definition: TxFlags.h:150
constexpr std::uint32_t const tfMPTRequireAuth
Definition: TxFlags.h:149
constexpr std::uint32_t const tfMPTCanLock
Definition: TxFlags.h:148
constexpr std::uint32_t const tfMPTCanClawback
Definition: TxFlags.h:153
T push_back(T... args)
T replace(T... args)
T size(T... args)
uint256 key
Definition: Keylet.h:40
std::vector< Account > const payee
std::vector< signer > const signers
void createBridgeObjects(Env &mcEnv, Env &scEnv)
Set the sequence number on a JTx.
Definition: seq.h:34
A signer in a SignerList.
Definition: multisign.h:38
T to_string(T... args)