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::Account] = account.human();
1263 jv[jss::Destination] = to.human();
1264 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1265 jv[sfFinishAfter.jsonName] =
1266 cancelAfter.time_since_epoch().count() + 2;
1267 return jv;
1268 };
1269
1270 using namespace std::chrono_literals;
1271 env(escrowCreate(alice, alice, XRP(333), env.now() + 2s));
1272 env.close();
1273
1274 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1275 std::string escrowIndex;
1276 {
1277 // Request the escrow using owner and sequence.
1278 Json::Value jvParams;
1279 jvParams[jss::escrow] = Json::objectValue;
1280 jvParams[jss::escrow][jss::owner] = alice.human();
1281 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1282 Json::Value const jrr = env.rpc(
1283 "json", "ledger_entry", to_string(jvParams))[jss::result];
1284 BEAST_EXPECT(
1285 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1286 escrowIndex = jrr[jss::index].asString();
1287 }
1288 {
1289 // Request the escrow by index.
1290 Json::Value jvParams;
1291 jvParams[jss::escrow] = escrowIndex;
1292 jvParams[jss::ledger_hash] = ledgerHash;
1293 Json::Value const jrr = env.rpc(
1294 "json", "ledger_entry", to_string(jvParams))[jss::result];
1295 BEAST_EXPECT(
1296 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1297 }
1298 {
1299 // Malformed owner entry.
1300 Json::Value jvParams;
1301 jvParams[jss::escrow] = Json::objectValue;
1302
1303 std::string const badAddress = makeBadAddress(alice.human());
1304 jvParams[jss::escrow][jss::owner] = badAddress;
1305 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1306 jvParams[jss::ledger_hash] = ledgerHash;
1307 Json::Value const jrr = env.rpc(
1308 "json", "ledger_entry", to_string(jvParams))[jss::result];
1309 checkErrorValue(jrr, "malformedOwner", "");
1310 }
1311 {
1312 // Missing owner.
1313 Json::Value jvParams;
1314 jvParams[jss::escrow] = Json::objectValue;
1315 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1316 jvParams[jss::ledger_hash] = ledgerHash;
1317 Json::Value const jrr = env.rpc(
1318 "json", "ledger_entry", to_string(jvParams))[jss::result];
1319 checkErrorValue(jrr, "malformedRequest", "");
1320 }
1321 {
1322 // Missing sequence.
1323 Json::Value jvParams;
1324 jvParams[jss::escrow] = Json::objectValue;
1325 jvParams[jss::escrow][jss::owner] = alice.human();
1326 jvParams[jss::ledger_hash] = ledgerHash;
1327 Json::Value const jrr = env.rpc(
1328 "json", "ledger_entry", to_string(jvParams))[jss::result];
1329 checkErrorValue(jrr, "malformedRequest", "");
1330 }
1331 {
1332 // Non-integer sequence.
1333 Json::Value jvParams;
1334 jvParams[jss::escrow] = Json::objectValue;
1335 jvParams[jss::escrow][jss::owner] = alice.human();
1336 jvParams[jss::escrow][jss::seq] =
1337 std::to_string(env.seq(alice) - 1);
1338 jvParams[jss::ledger_hash] = ledgerHash;
1339 Json::Value const jrr = env.rpc(
1340 "json", "ledger_entry", to_string(jvParams))[jss::result];
1341 checkErrorValue(jrr, "malformedRequest", "");
1342 }
1343 }
1344
1345 void
1347 {
1348 testcase("ledger_entry Request Offer");
1349 using namespace test::jtx;
1350 Env env{*this};
1351 Account const alice{"alice"};
1352 Account const gw{"gateway"};
1353 auto const USD = gw["USD"];
1354 env.fund(XRP(10000), alice, gw);
1355 env.close();
1356
1357 env(offer(alice, USD(321), XRP(322)));
1358 env.close();
1359
1360 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1361 std::string offerIndex;
1362 {
1363 // Request the offer using owner and sequence.
1364 Json::Value jvParams;
1365 jvParams[jss::offer] = Json::objectValue;
1366 jvParams[jss::offer][jss::account] = alice.human();
1367 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1368 jvParams[jss::ledger_hash] = ledgerHash;
1369 Json::Value const jrr = env.rpc(
1370 "json", "ledger_entry", to_string(jvParams))[jss::result];
1371 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1372 offerIndex = jrr[jss::index].asString();
1373 }
1374 {
1375 // Request the offer using its index.
1376 Json::Value jvParams;
1377 jvParams[jss::offer] = offerIndex;
1378 Json::Value const jrr = env.rpc(
1379 "json", "ledger_entry", to_string(jvParams))[jss::result];
1380 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1381 }
1382 {
1383 // Malformed account entry.
1384 Json::Value jvParams;
1385 jvParams[jss::offer] = Json::objectValue;
1386
1387 std::string const badAddress = makeBadAddress(alice.human());
1388 jvParams[jss::offer][jss::account] = badAddress;
1389 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1390 jvParams[jss::ledger_hash] = ledgerHash;
1391 Json::Value const jrr = env.rpc(
1392 "json", "ledger_entry", to_string(jvParams))[jss::result];
1393 checkErrorValue(jrr, "malformedAddress", "");
1394 }
1395 {
1396 // Malformed offer object. Missing account member.
1397 Json::Value jvParams;
1398 jvParams[jss::offer] = Json::objectValue;
1399 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1400 jvParams[jss::ledger_hash] = ledgerHash;
1401 Json::Value const jrr = env.rpc(
1402 "json", "ledger_entry", to_string(jvParams))[jss::result];
1403 checkErrorValue(jrr, "malformedRequest", "");
1404 }
1405 {
1406 // Malformed offer object. Missing seq member.
1407 Json::Value jvParams;
1408 jvParams[jss::offer] = Json::objectValue;
1409 jvParams[jss::offer][jss::account] = alice.human();
1410 jvParams[jss::ledger_hash] = ledgerHash;
1411 Json::Value const jrr = env.rpc(
1412 "json", "ledger_entry", to_string(jvParams))[jss::result];
1413 checkErrorValue(jrr, "malformedRequest", "");
1414 }
1415 {
1416 // Malformed offer object. Non-integral seq member.
1417 Json::Value jvParams;
1418 jvParams[jss::offer] = Json::objectValue;
1419 jvParams[jss::offer][jss::account] = alice.human();
1420 jvParams[jss::offer][jss::seq] = std::to_string(env.seq(alice) - 1);
1421 jvParams[jss::ledger_hash] = ledgerHash;
1422 Json::Value const jrr = env.rpc(
1423 "json", "ledger_entry", to_string(jvParams))[jss::result];
1424 checkErrorValue(jrr, "malformedRequest", "");
1425 }
1426 }
1427
1428 void
1430 {
1431 testcase("ledger_entry Request Pay Chan");
1432 using namespace test::jtx;
1433 using namespace std::literals::chrono_literals;
1434 Env env{*this};
1435 Account const alice{"alice"};
1436
1437 env.fund(XRP(10000), alice);
1438 env.close();
1439
1440 // Lambda to create a PayChan.
1441 auto payChanCreate = [](test::jtx::Account const& account,
1442 test::jtx::Account const& to,
1443 STAmount const& amount,
1444 NetClock::duration const& settleDelay,
1445 PublicKey const& pk) {
1446 Json::Value jv;
1447 jv[jss::TransactionType] = jss::PaymentChannelCreate;
1448 jv[jss::Account] = account.human();
1449 jv[jss::Destination] = to.human();
1450 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1451 jv[sfSettleDelay.jsonName] = settleDelay.count();
1452 jv[sfPublicKey.jsonName] = strHex(pk.slice());
1453 return jv;
1454 };
1455
1456 env(payChanCreate(alice, env.master, XRP(57), 18s, alice.pk()));
1457 env.close();
1458
1459 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1460
1461 uint256 const payChanIndex{
1462 keylet::payChan(alice, env.master, env.seq(alice) - 1).key};
1463 {
1464 // Request the payment channel using its index.
1465 Json::Value jvParams;
1466 jvParams[jss::payment_channel] = to_string(payChanIndex);
1467 jvParams[jss::ledger_hash] = ledgerHash;
1468 Json::Value const jrr = env.rpc(
1469 "json", "ledger_entry", to_string(jvParams))[jss::result];
1470 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
1471 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
1472 BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
1473 }
1474 {
1475 // Request an index that is not a payment channel.
1476 Json::Value jvParams;
1477 jvParams[jss::payment_channel] = ledgerHash;
1478 jvParams[jss::ledger_hash] = ledgerHash;
1479 Json::Value const jrr = env.rpc(
1480 "json", "ledger_entry", to_string(jvParams))[jss::result];
1481 checkErrorValue(jrr, "entryNotFound", "");
1482 }
1483 }
1484
1485 void
1487 {
1488 testcase("ledger_entry Request RippleState");
1489 using namespace test::jtx;
1490 Env env{*this};
1491 Account const alice{"alice"};
1492 Account const gw{"gateway"};
1493 auto const USD = gw["USD"];
1494 env.fund(XRP(10000), alice, gw);
1495 env.close();
1496
1497 env.trust(USD(999), alice);
1498 env.close();
1499
1500 env(pay(gw, alice, USD(97)));
1501 env.close();
1502
1503 // check both aliases
1504 for (auto const& fieldName : {jss::ripple_state, jss::state})
1505 {
1506 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1507 {
1508 // Request the trust line using the accounts and currency.
1509 Json::Value jvParams;
1510 jvParams[fieldName] = Json::objectValue;
1511 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1512 jvParams[fieldName][jss::accounts][0u] = alice.human();
1513 jvParams[fieldName][jss::accounts][1u] = gw.human();
1514 jvParams[fieldName][jss::currency] = "USD";
1515 jvParams[jss::ledger_hash] = ledgerHash;
1516 Json::Value const jrr = env.rpc(
1517 "json", "ledger_entry", to_string(jvParams))[jss::result];
1518 BEAST_EXPECT(
1519 jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
1520 BEAST_EXPECT(
1521 jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
1522 }
1523 {
1524 // ripple_state is not an object.
1525 Json::Value jvParams;
1526 jvParams[fieldName] = "ripple_state";
1527 jvParams[jss::ledger_hash] = ledgerHash;
1528 Json::Value const jrr = env.rpc(
1529 "json", "ledger_entry", to_string(jvParams))[jss::result];
1530 checkErrorValue(jrr, "malformedRequest", "");
1531 }
1532 {
1533 // ripple_state.currency is missing.
1534 Json::Value jvParams;
1535 jvParams[fieldName] = Json::objectValue;
1536 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1537 jvParams[fieldName][jss::accounts][0u] = alice.human();
1538 jvParams[fieldName][jss::accounts][1u] = gw.human();
1539 jvParams[jss::ledger_hash] = ledgerHash;
1540 Json::Value const jrr = env.rpc(
1541 "json", "ledger_entry", to_string(jvParams))[jss::result];
1542 checkErrorValue(jrr, "malformedRequest", "");
1543 }
1544 {
1545 // ripple_state accounts is not an array.
1546 Json::Value jvParams;
1547 jvParams[fieldName] = Json::objectValue;
1548 jvParams[fieldName][jss::accounts] = 2;
1549 jvParams[fieldName][jss::currency] = "USD";
1550 jvParams[jss::ledger_hash] = ledgerHash;
1551 Json::Value const jrr = env.rpc(
1552 "json", "ledger_entry", to_string(jvParams))[jss::result];
1553 checkErrorValue(jrr, "malformedRequest", "");
1554 }
1555 {
1556 // ripple_state one of the accounts is missing.
1557 Json::Value jvParams;
1558 jvParams[fieldName] = Json::objectValue;
1559 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1560 jvParams[fieldName][jss::accounts][0u] = alice.human();
1561 jvParams[fieldName][jss::currency] = "USD";
1562 jvParams[jss::ledger_hash] = ledgerHash;
1563 Json::Value const jrr = env.rpc(
1564 "json", "ledger_entry", to_string(jvParams))[jss::result];
1565 checkErrorValue(jrr, "malformedRequest", "");
1566 }
1567 {
1568 // ripple_state more than 2 accounts.
1569 Json::Value jvParams;
1570 jvParams[fieldName] = Json::objectValue;
1571 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1572 jvParams[fieldName][jss::accounts][0u] = alice.human();
1573 jvParams[fieldName][jss::accounts][1u] = gw.human();
1574 jvParams[fieldName][jss::accounts][2u] = alice.human();
1575 jvParams[fieldName][jss::currency] = "USD";
1576 jvParams[jss::ledger_hash] = ledgerHash;
1577 Json::Value const jrr = env.rpc(
1578 "json", "ledger_entry", to_string(jvParams))[jss::result];
1579 checkErrorValue(jrr, "malformedRequest", "");
1580 }
1581 {
1582 // ripple_state account[0] is not a string.
1583 Json::Value jvParams;
1584 jvParams[fieldName] = Json::objectValue;
1585 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1586 jvParams[fieldName][jss::accounts][0u] = 44;
1587 jvParams[fieldName][jss::accounts][1u] = gw.human();
1588 jvParams[fieldName][jss::currency] = "USD";
1589 jvParams[jss::ledger_hash] = ledgerHash;
1590 Json::Value const jrr = env.rpc(
1591 "json", "ledger_entry", to_string(jvParams))[jss::result];
1592 checkErrorValue(jrr, "malformedRequest", "");
1593 }
1594 {
1595 // ripple_state account[1] is not a string.
1596 Json::Value jvParams;
1597 jvParams[fieldName] = Json::objectValue;
1598 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1599 jvParams[fieldName][jss::accounts][0u] = alice.human();
1600 jvParams[fieldName][jss::accounts][1u] = 21;
1601 jvParams[fieldName][jss::currency] = "USD";
1602 jvParams[jss::ledger_hash] = ledgerHash;
1603 Json::Value const jrr = env.rpc(
1604 "json", "ledger_entry", to_string(jvParams))[jss::result];
1605 checkErrorValue(jrr, "malformedRequest", "");
1606 }
1607 {
1608 // ripple_state account[0] == account[1].
1609 Json::Value jvParams;
1610 jvParams[fieldName] = Json::objectValue;
1611 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1612 jvParams[fieldName][jss::accounts][0u] = alice.human();
1613 jvParams[fieldName][jss::accounts][1u] = alice.human();
1614 jvParams[fieldName][jss::currency] = "USD";
1615 jvParams[jss::ledger_hash] = ledgerHash;
1616 Json::Value const jrr = env.rpc(
1617 "json", "ledger_entry", to_string(jvParams))[jss::result];
1618 checkErrorValue(jrr, "malformedRequest", "");
1619 }
1620 {
1621 // ripple_state malformed account[0].
1622 Json::Value jvParams;
1623 jvParams[fieldName] = Json::objectValue;
1624 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1625 jvParams[fieldName][jss::accounts][0u] =
1626 makeBadAddress(alice.human());
1627 jvParams[fieldName][jss::accounts][1u] = gw.human();
1628 jvParams[fieldName][jss::currency] = "USD";
1629 jvParams[jss::ledger_hash] = ledgerHash;
1630 Json::Value const jrr = env.rpc(
1631 "json", "ledger_entry", to_string(jvParams))[jss::result];
1632 checkErrorValue(jrr, "malformedAddress", "");
1633 }
1634 {
1635 // ripple_state malformed account[1].
1636 Json::Value jvParams;
1637 jvParams[fieldName] = Json::objectValue;
1638 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1639 jvParams[fieldName][jss::accounts][0u] = alice.human();
1640 jvParams[fieldName][jss::accounts][1u] =
1641 makeBadAddress(gw.human());
1642 jvParams[fieldName][jss::currency] = "USD";
1643 jvParams[jss::ledger_hash] = ledgerHash;
1644 Json::Value const jrr = env.rpc(
1645 "json", "ledger_entry", to_string(jvParams))[jss::result];
1646 checkErrorValue(jrr, "malformedAddress", "");
1647 }
1648 {
1649 // ripple_state malformed currency.
1650 Json::Value jvParams;
1651 jvParams[fieldName] = Json::objectValue;
1652 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1653 jvParams[fieldName][jss::accounts][0u] = alice.human();
1654 jvParams[fieldName][jss::accounts][1u] = gw.human();
1655 jvParams[fieldName][jss::currency] = "USDollars";
1656 jvParams[jss::ledger_hash] = ledgerHash;
1657 Json::Value const jrr = env.rpc(
1658 "json", "ledger_entry", to_string(jvParams))[jss::result];
1659 checkErrorValue(jrr, "malformedCurrency", "");
1660 }
1661 }
1662 }
1663
1664 void
1666 {
1667 testcase("ledger_entry Request Ticket");
1668 using namespace test::jtx;
1669 Env env{*this};
1670 env.close();
1671
1672 // Create two tickets.
1673 std::uint32_t const tkt1{env.seq(env.master) + 1};
1674 env(ticket::create(env.master, 2));
1675 env.close();
1676
1677 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1678 // Request four tickets: one before the first one we created, the
1679 // two created tickets, and the ticket that would come after the
1680 // last created ticket.
1681 {
1682 // Not a valid ticket requested by index.
1683 Json::Value jvParams;
1684 jvParams[jss::ticket] =
1685 to_string(getTicketIndex(env.master, tkt1 - 1));
1686 jvParams[jss::ledger_hash] = ledgerHash;
1687 Json::Value const jrr = env.rpc(
1688 "json", "ledger_entry", to_string(jvParams))[jss::result];
1689 checkErrorValue(jrr, "entryNotFound", "");
1690 }
1691 {
1692 // First real ticket requested by index.
1693 Json::Value jvParams;
1694 jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1));
1695 jvParams[jss::ledger_hash] = ledgerHash;
1696 Json::Value const jrr = env.rpc(
1697 "json", "ledger_entry", to_string(jvParams))[jss::result];
1698 BEAST_EXPECT(
1699 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Ticket);
1700 BEAST_EXPECT(jrr[jss::node][sfTicketSequence.jsonName] == tkt1);
1701 }
1702 {
1703 // Second real ticket requested by account and sequence.
1704 Json::Value jvParams;
1705 jvParams[jss::ticket] = Json::objectValue;
1706 jvParams[jss::ticket][jss::account] = env.master.human();
1707 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 1;
1708 jvParams[jss::ledger_hash] = ledgerHash;
1709 Json::Value const jrr = env.rpc(
1710 "json", "ledger_entry", to_string(jvParams))[jss::result];
1711 BEAST_EXPECT(
1712 jrr[jss::node][jss::index] ==
1713 to_string(getTicketIndex(env.master, tkt1 + 1)));
1714 }
1715 {
1716 // Not a valid ticket requested by account and sequence.
1717 Json::Value jvParams;
1718 jvParams[jss::ticket] = Json::objectValue;
1719 jvParams[jss::ticket][jss::account] = env.master.human();
1720 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 2;
1721 jvParams[jss::ledger_hash] = ledgerHash;
1722 Json::Value const jrr = env.rpc(
1723 "json", "ledger_entry", to_string(jvParams))[jss::result];
1724 checkErrorValue(jrr, "entryNotFound", "");
1725 }
1726 {
1727 // Request a ticket using an account root entry.
1728 Json::Value jvParams;
1729 jvParams[jss::ticket] = to_string(keylet::account(env.master).key);
1730 jvParams[jss::ledger_hash] = ledgerHash;
1731 Json::Value const jrr = env.rpc(
1732 "json", "ledger_entry", to_string(jvParams))[jss::result];
1733 checkErrorValue(jrr, "unexpectedLedgerType", "");
1734 }
1735 {
1736 // Malformed account entry.
1737 Json::Value jvParams;
1738 jvParams[jss::ticket] = Json::objectValue;
1739
1740 std::string const badAddress = makeBadAddress(env.master.human());
1741 jvParams[jss::ticket][jss::account] = badAddress;
1742 jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
1743 jvParams[jss::ledger_hash] = ledgerHash;
1744 Json::Value const jrr = env.rpc(
1745 "json", "ledger_entry", to_string(jvParams))[jss::result];
1746 checkErrorValue(jrr, "malformedAddress", "");
1747 }
1748 {
1749 // Malformed ticket object. Missing account member.
1750 Json::Value jvParams;
1751 jvParams[jss::ticket] = Json::objectValue;
1752 jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1;
1753 jvParams[jss::ledger_hash] = ledgerHash;
1754 Json::Value const jrr = env.rpc(
1755 "json", "ledger_entry", to_string(jvParams))[jss::result];
1756 checkErrorValue(jrr, "malformedRequest", "");
1757 }
1758 {
1759 // Malformed ticket object. Missing seq member.
1760 Json::Value jvParams;
1761 jvParams[jss::ticket] = Json::objectValue;
1762 jvParams[jss::ticket][jss::account] = env.master.human();
1763 jvParams[jss::ledger_hash] = ledgerHash;
1764 Json::Value const jrr = env.rpc(
1765 "json", "ledger_entry", to_string(jvParams))[jss::result];
1766 checkErrorValue(jrr, "malformedRequest", "");
1767 }
1768 {
1769 // Malformed ticket object. Non-integral seq member.
1770 Json::Value jvParams;
1771 jvParams[jss::ticket] = Json::objectValue;
1772 jvParams[jss::ticket][jss::account] = env.master.human();
1773 jvParams[jss::ticket][jss::ticket_seq] =
1774 std::to_string(env.seq(env.master) - 1);
1775 jvParams[jss::ledger_hash] = ledgerHash;
1776 Json::Value const jrr = env.rpc(
1777 "json", "ledger_entry", to_string(jvParams))[jss::result];
1778 checkErrorValue(jrr, "malformedRequest", "");
1779 }
1780 }
1781
1782 void
1784 {
1785 testcase("ledger_entry Request DID");
1786 using namespace test::jtx;
1787 using namespace std::literals::chrono_literals;
1788 Env env{*this};
1789 Account const alice{"alice"};
1790
1791 env.fund(XRP(10000), alice);
1792 env.close();
1793
1794 // Lambda to create a DID.
1795 auto didCreate = [](test::jtx::Account const& account) {
1796 Json::Value jv;
1797 jv[jss::TransactionType] = jss::DIDSet;
1798 jv[jss::Account] = account.human();
1799 jv[sfDIDDocument.jsonName] = strHex(std::string{"data"});
1800 jv[sfURI.jsonName] = strHex(std::string{"uri"});
1801 return jv;
1802 };
1803
1804 env(didCreate(alice));
1805 env.close();
1806
1807 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1808
1809 {
1810 // Request the DID using its index.
1811 Json::Value jvParams;
1812 jvParams[jss::did] = alice.human();
1813 jvParams[jss::ledger_hash] = ledgerHash;
1814 Json::Value const jrr = env.rpc(
1815 "json", "ledger_entry", to_string(jvParams))[jss::result];
1816 BEAST_EXPECT(
1817 jrr[jss::node][sfDIDDocument.jsonName] ==
1818 strHex(std::string{"data"}));
1819 BEAST_EXPECT(
1820 jrr[jss::node][sfURI.jsonName] == strHex(std::string{"uri"}));
1821 }
1822 {
1823 // Request an index that is not a DID.
1824 Json::Value jvParams;
1825 jvParams[jss::did] = env.master.human();
1826 jvParams[jss::ledger_hash] = ledgerHash;
1827 Json::Value const jrr = env.rpc(
1828 "json", "ledger_entry", to_string(jvParams))[jss::result];
1829 checkErrorValue(jrr, "entryNotFound", "");
1830 }
1831 }
1832
1833 void
1834 testLedgerEntryInvalidParams(unsigned int apiVersion)
1835 {
1836 testcase(
1837 "ledger_entry Request With Invalid Parameters v" +
1838 std::to_string(apiVersion));
1839 using namespace test::jtx;
1840 Env env{*this};
1841
1842 std::string const ledgerHash{to_string(env.closed()->info().hash)};
1843
1844 auto makeParams = [&apiVersion](std::function<void(Json::Value&)> f) {
1845 Json::Value params;
1846 params[jss::api_version] = apiVersion;
1847 f(params);
1848 return params;
1849 };
1850 // "features" is not an option supported by ledger_entry.
1851 {
1852 auto const jvParams =
1853 makeParams([&ledgerHash](Json::Value& jvParams) {
1854 jvParams[jss::features] = ledgerHash;
1855 jvParams[jss::ledger_hash] = ledgerHash;
1856 });
1857 Json::Value const jrr = env.rpc(
1858 "json", "ledger_entry", to_string(jvParams))[jss::result];
1859
1860 if (apiVersion < 2u)
1861 checkErrorValue(jrr, "unknownOption", "");
1862 else
1863 checkErrorValue(jrr, "invalidParams", "");
1864 }
1865 Json::Value const injectObject = []() {
1867 obj[jss::account] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
1868 obj[jss::ledger_index] = "validated";
1869 return obj;
1870 }();
1871 Json::Value const injectArray = []() {
1873 arr[0u] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
1874 arr[1u] = "validated";
1875 return arr;
1876 }();
1877
1878 // invalid input for fields that can handle an object, but can't handle
1879 // an array
1880 for (auto const& field :
1881 {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm})
1882 {
1883 auto const jvParams =
1884 makeParams([&field, &injectArray](Json::Value& jvParams) {
1885 jvParams[field] = injectArray;
1886 });
1887
1888 Json::Value const jrr = env.rpc(
1889 "json", "ledger_entry", to_string(jvParams))[jss::result];
1890
1891 if (apiVersion < 2u)
1892 checkErrorValue(jrr, "internal", "Internal error.");
1893 else
1894 checkErrorValue(jrr, "invalidParams", "");
1895 }
1896 // Fields that can handle objects just fine
1897 for (auto const& field :
1898 {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm})
1899 {
1900 auto const jvParams =
1901 makeParams([&field, &injectObject](Json::Value& jvParams) {
1902 jvParams[field] = injectObject;
1903 });
1904
1905 Json::Value const jrr = env.rpc(
1906 "json", "ledger_entry", to_string(jvParams))[jss::result];
1907
1908 checkErrorValue(jrr, "malformedRequest", "");
1909 }
1910
1911 for (auto const& inject : {injectObject, injectArray})
1912 {
1913 // invalid input for fields that can't handle an object or an array
1914 for (auto const& field :
1915 {jss::index,
1916 jss::account_root,
1917 jss::check,
1918 jss::payment_channel})
1919 {
1920 auto const jvParams =
1921 makeParams([&field, &inject](Json::Value& jvParams) {
1922 jvParams[field] = inject;
1923 });
1924
1925 Json::Value const jrr = env.rpc(
1926 "json", "ledger_entry", to_string(jvParams))[jss::result];
1927
1928 if (apiVersion < 2u)
1929 checkErrorValue(jrr, "internal", "Internal error.");
1930 else
1931 checkErrorValue(jrr, "invalidParams", "");
1932 }
1933 // directory sub-fields
1934 for (auto const& field : {jss::dir_root, jss::owner})
1935 {
1936 auto const jvParams =
1937 makeParams([&field, &inject](Json::Value& jvParams) {
1938 jvParams[jss::directory][field] = inject;
1939 });
1940
1941 Json::Value const jrr = env.rpc(
1942 "json", "ledger_entry", to_string(jvParams))[jss::result];
1943
1944 if (apiVersion < 2u)
1945 checkErrorValue(jrr, "internal", "Internal error.");
1946 else
1947 checkErrorValue(jrr, "invalidParams", "");
1948 }
1949 // escrow sub-fields
1950 {
1951 auto const jvParams =
1952 makeParams([&inject](Json::Value& jvParams) {
1953 jvParams[jss::escrow][jss::owner] = inject;
1954 jvParams[jss::escrow][jss::seq] = 99;
1955 });
1956
1957 Json::Value const jrr = env.rpc(
1958 "json", "ledger_entry", to_string(jvParams))[jss::result];
1959
1960 if (apiVersion < 2u)
1961 checkErrorValue(jrr, "internal", "Internal error.");
1962 else
1963 checkErrorValue(jrr, "invalidParams", "");
1964 }
1965 // offer sub-fields
1966 {
1967 auto const jvParams =
1968 makeParams([&inject](Json::Value& jvParams) {
1969 jvParams[jss::offer][jss::account] = inject;
1970 jvParams[jss::offer][jss::seq] = 99;
1971 });
1972
1973 Json::Value const jrr = env.rpc(
1974 "json", "ledger_entry", to_string(jvParams))[jss::result];
1975
1976 if (apiVersion < 2u)
1977 checkErrorValue(jrr, "internal", "Internal error.");
1978 else
1979 checkErrorValue(jrr, "invalidParams", "");
1980 }
1981 // ripple_state sub-fields
1982 {
1983 auto const jvParams =
1984 makeParams([&inject](Json::Value& jvParams) {
1986 rs[jss::currency] = "FOO";
1987 rs[jss::accounts] = Json::Value(Json::arrayValue);
1988 rs[jss::accounts][0u] =
1989 "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
1990 rs[jss::accounts][1u] =
1991 "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv";
1992 rs[jss::currency] = inject;
1993 jvParams[jss::ripple_state] = std::move(rs);
1994 });
1995
1996 Json::Value const jrr = env.rpc(
1997 "json", "ledger_entry", to_string(jvParams))[jss::result];
1998
1999 if (apiVersion < 2u)
2000 checkErrorValue(jrr, "internal", "Internal error.");
2001 else
2002 checkErrorValue(jrr, "invalidParams", "");
2003 }
2004 // ticket sub-fields
2005 {
2006 auto const jvParams =
2007 makeParams([&inject](Json::Value& jvParams) {
2008 jvParams[jss::ticket][jss::account] = inject;
2009 jvParams[jss::ticket][jss::ticket_seq] = 99;
2010 });
2011
2012 Json::Value const jrr = env.rpc(
2013 "json", "ledger_entry", to_string(jvParams))[jss::result];
2014
2015 if (apiVersion < 2u)
2016 checkErrorValue(jrr, "internal", "Internal error.");
2017 else
2018 checkErrorValue(jrr, "invalidParams", "");
2019 }
2020
2021 // Fields that can handle malformed inputs just fine
2022 for (auto const& field : {jss::nft_page, jss::deposit_preauth})
2023 {
2024 auto const jvParams =
2025 makeParams([&field, &inject](Json::Value& jvParams) {
2026 jvParams[field] = inject;
2027 });
2028
2029 Json::Value const jrr = env.rpc(
2030 "json", "ledger_entry", to_string(jvParams))[jss::result];
2031
2032 checkErrorValue(jrr, "malformedRequest", "");
2033 }
2034 // Subfields of deposit_preauth that can handle malformed inputs
2035 // fine
2036 for (auto const& field : {jss::owner, jss::authorized})
2037 {
2038 auto const jvParams =
2039 makeParams([&field, &inject](Json::Value& jvParams) {
2040 auto pa = Json::Value(Json::objectValue);
2041 pa[jss::owner] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
2042 pa[jss::authorized] =
2043 "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv";
2044 pa[field] = inject;
2045 jvParams[jss::deposit_preauth] = std::move(pa);
2046 });
2047
2048 Json::Value const jrr = env.rpc(
2049 "json", "ledger_entry", to_string(jvParams))[jss::result];
2050
2051 checkErrorValue(jrr, "malformedRequest", "");
2052 }
2053 }
2054 }
2055
2056 void
2058 {
2059 testcase("Invalid Oracle Ledger Entry");
2060 using namespace ripple::test::jtx;
2061 using namespace ripple::test::jtx::oracle;
2062
2063 Env env(*this);
2064 Account const owner("owner");
2065 env.fund(XRP(1'000), owner);
2066 Oracle oracle(
2067 env,
2068 {.owner = owner,
2069 .fee = static_cast<int>(env.current()->fees().base.drops())});
2070
2071 // Malformed document id
2072 auto res = Oracle::ledgerEntry(env, owner, NoneTag);
2073 BEAST_EXPECT(res[jss::error].asString() == "invalidParams");
2074 std::vector<AnyValue> invalid = {-1, 1.2, "", "Invalid"};
2075 for (auto const& v : invalid)
2076 {
2077 auto const res = Oracle::ledgerEntry(env, owner, v);
2078 BEAST_EXPECT(res[jss::error].asString() == "malformedDocumentID");
2079 }
2080 // Missing document id
2081 res = Oracle::ledgerEntry(env, owner, std::nullopt);
2082 BEAST_EXPECT(res[jss::error].asString() == "malformedRequest");
2083
2084 // Missing account
2085 res = Oracle::ledgerEntry(env, std::nullopt, 1);
2086 BEAST_EXPECT(res[jss::error].asString() == "malformedRequest");
2087
2088 // Malformed account
2089 std::string malfAccount = to_string(owner.id());
2090 malfAccount.replace(10, 1, 1, '!');
2091 res = Oracle::ledgerEntry(env, malfAccount, 1);
2092 BEAST_EXPECT(res[jss::error].asString() == "malformedAddress");
2093 }
2094
2095 void
2097 {
2098 testcase("Oracle Ledger Entry");
2099 using namespace ripple::test::jtx;
2100 using namespace ripple::test::jtx::oracle;
2101
2102 Env env(*this);
2103 auto const baseFee =
2104 static_cast<int>(env.current()->fees().base.drops());
2105 std::vector<AccountID> accounts;
2107 for (int i = 0; i < 10; ++i)
2108 {
2109 Account const owner(std::string("owner") + std::to_string(i));
2110 env.fund(XRP(1'000), owner);
2111 // different accounts can have the same asset pair
2112 Oracle oracle(
2113 env, {.owner = owner, .documentID = i, .fee = baseFee});
2114 accounts.push_back(owner.id());
2115 oracles.push_back(oracle.documentID());
2116 // same account can have different asset pair
2117 Oracle oracle1(
2118 env, {.owner = owner, .documentID = i + 10, .fee = baseFee});
2119 accounts.push_back(owner.id());
2120 oracles.push_back(oracle1.documentID());
2121 }
2122 for (int i = 0; i < accounts.size(); ++i)
2123 {
2124 auto const jv = [&]() {
2125 // document id is uint32
2126 if (i % 2)
2127 return Oracle::ledgerEntry(env, accounts[i], oracles[i]);
2128 // document id is string
2129 return Oracle::ledgerEntry(
2130 env, accounts[i], std::to_string(oracles[i]));
2131 }();
2132 try
2133 {
2134 BEAST_EXPECT(
2135 jv[jss::node][jss::Owner] == to_string(accounts[i]));
2136 }
2137 catch (...)
2138 {
2139 fail();
2140 }
2141 }
2142 }
2143
2144 void
2146 {
2147 testcase("ledger_entry Request MPT");
2148 using namespace test::jtx;
2149 using namespace std::literals::chrono_literals;
2150 Env env{*this};
2151 Account const alice{"alice"};
2152 Account const bob("bob");
2153
2154 MPTTester mptAlice(env, alice, {.holders = {bob}});
2155 mptAlice.create(
2156 {.transferFee = 10,
2157 .metadata = "123",
2158 .ownerCount = 1,
2161 mptAlice.authorize({.account = bob, .holderCount = 1});
2162
2163 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2164
2165 std::string const badMptID =
2166 "00000193B9DDCAF401B5B3B26875986043F82CD0D13B4315";
2167 {
2168 // Request the MPTIssuance using its MPTIssuanceID.
2169 Json::Value jvParams;
2170 jvParams[jss::mpt_issuance] = strHex(mptAlice.issuanceID());
2171 jvParams[jss::ledger_hash] = ledgerHash;
2172 Json::Value const jrr = env.rpc(
2173 "json", "ledger_entry", to_string(jvParams))[jss::result];
2174 BEAST_EXPECT(
2175 jrr[jss::node][sfMPTokenMetadata.jsonName] ==
2176 strHex(std::string{"123"}));
2177 BEAST_EXPECT(
2178 jrr[jss::node][jss::mpt_issuance_id] ==
2179 strHex(mptAlice.issuanceID()));
2180 }
2181 {
2182 // Request an index that is not a MPTIssuance.
2183 Json::Value jvParams;
2184 jvParams[jss::mpt_issuance] = badMptID;
2185 jvParams[jss::ledger_hash] = ledgerHash;
2186 Json::Value const jrr = env.rpc(
2187 "json", "ledger_entry", to_string(jvParams))[jss::result];
2188 checkErrorValue(jrr, "entryNotFound", "");
2189 }
2190 {
2191 // Request the MPToken using its owner + mptIssuanceID.
2192 Json::Value jvParams;
2193 jvParams[jss::mptoken] = Json::objectValue;
2194 jvParams[jss::mptoken][jss::account] = bob.human();
2195 jvParams[jss::mptoken][jss::mpt_issuance_id] =
2196 strHex(mptAlice.issuanceID());
2197 jvParams[jss::ledger_hash] = ledgerHash;
2198 Json::Value const jrr = env.rpc(
2199 "json", "ledger_entry", to_string(jvParams))[jss::result];
2200 BEAST_EXPECT(
2201 jrr[jss::node][sfMPTokenIssuanceID.jsonName] ==
2202 strHex(mptAlice.issuanceID()));
2203 }
2204 {
2205 // Request the MPToken using a bad mptIssuanceID.
2206 Json::Value jvParams;
2207 jvParams[jss::mptoken] = Json::objectValue;
2208 jvParams[jss::mptoken][jss::account] = bob.human();
2209 jvParams[jss::mptoken][jss::mpt_issuance_id] = badMptID;
2210 jvParams[jss::ledger_hash] = ledgerHash;
2211 Json::Value const jrr = env.rpc(
2212 "json", "ledger_entry", to_string(jvParams))[jss::result];
2213 checkErrorValue(jrr, "entryNotFound", "");
2214 }
2215 }
2216
2217 void
2219 {
2220 testcase("ledger_entry PermissionedDomain");
2221
2222 using namespace test::jtx;
2223
2224 Env env(*this, testable_amendments() | featurePermissionedDomains);
2225 Account const issuer{"issuer"};
2226 Account const alice{"alice"};
2227 Account const bob{"bob"};
2228
2229 env.fund(XRP(5000), issuer, alice, bob);
2230 env.close();
2231
2232 auto const seq = env.seq(alice);
2233 env(pdomain::setTx(alice, {{alice, "first credential"}}));
2234 env.close();
2235 auto const objects = pdomain::getObjects(alice, env);
2236 if (!BEAST_EXPECT(objects.size() == 1))
2237 return;
2238
2239 {
2240 // Succeed
2241 Json::Value params;
2242 params[jss::ledger_index] = jss::validated;
2243 params[jss::permissioned_domain][jss::account] = alice.human();
2244 params[jss::permissioned_domain][jss::seq] = seq;
2245 auto jv = env.rpc("json", "ledger_entry", to_string(params));
2246 BEAST_EXPECT(
2247 jv.isObject() && jv.isMember(jss::result) &&
2248 !jv[jss::result].isMember(jss::error) &&
2249 jv[jss::result].isMember(jss::node) &&
2250 jv[jss::result][jss::node].isMember(
2251 sfLedgerEntryType.jsonName) &&
2252 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
2253 jss::PermissionedDomain);
2254
2255 std::string const pdIdx = jv[jss::result][jss::index].asString();
2256 BEAST_EXPECT(
2257 strHex(keylet::permissionedDomain(alice, seq).key) == pdIdx);
2258
2259 params.clear();
2260 params[jss::ledger_index] = jss::validated;
2261 params[jss::permissioned_domain] = pdIdx;
2262 jv = env.rpc("json", "ledger_entry", to_string(params));
2263 BEAST_EXPECT(
2264 jv.isObject() && jv.isMember(jss::result) &&
2265 !jv[jss::result].isMember(jss::error) &&
2266 jv[jss::result].isMember(jss::node) &&
2267 jv[jss::result][jss::node].isMember(
2268 sfLedgerEntryType.jsonName) &&
2269 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
2270 jss::PermissionedDomain);
2271 }
2272
2273 {
2274 // Fail, invalid permissioned domain index
2275 Json::Value params;
2276 params[jss::ledger_index] = jss::validated;
2277 params[jss::permissioned_domain] =
2278 "12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A"
2279 "DE";
2280 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2281 checkErrorValue(jrr[jss::result], "entryNotFound", "");
2282 }
2283
2284 {
2285 // Fail, invalid permissioned domain index
2286 Json::Value params;
2287 params[jss::ledger_index] = jss::validated;
2288 params[jss::permissioned_domain] = "NotAHexString";
2289 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2290 checkErrorValue(jrr[jss::result], "malformedRequest", "");
2291 }
2292
2293 {
2294 // Fail, permissioned domain is not an object
2295 Json::Value params;
2296 params[jss::ledger_index] = jss::validated;
2297 params[jss::permissioned_domain] = 10;
2298 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2299 checkErrorValue(jrr[jss::result], "malformedRequest", "");
2300 }
2301
2302 {
2303 // Fail, invalid account
2304 Json::Value params;
2305 params[jss::ledger_index] = jss::validated;
2306 params[jss::permissioned_domain][jss::account] = 1;
2307 params[jss::permissioned_domain][jss::seq] = seq;
2308 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2309 checkErrorValue(jrr[jss::result], "malformedAddress", "");
2310 }
2311
2312 {
2313 // Fail, account is an object
2314 Json::Value params;
2315 params[jss::ledger_index] = jss::validated;
2316 params[jss::permissioned_domain][jss::account] =
2318 params[jss::permissioned_domain][jss::seq] = seq;
2319 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2320 checkErrorValue(jrr[jss::result], "malformedAddress", "");
2321 }
2322
2323 {
2324 // Fail, no account
2325 Json::Value params;
2326 params[jss::ledger_index] = jss::validated;
2327 params[jss::permissioned_domain][jss::account] = "";
2328 params[jss::permissioned_domain][jss::seq] = seq;
2329 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2330 checkErrorValue(jrr[jss::result], "malformedAddress", "");
2331 }
2332
2333 {
2334 // Fail, invalid sequence
2335 Json::Value params;
2336 params[jss::ledger_index] = jss::validated;
2337 params[jss::permissioned_domain][jss::account] = alice.human();
2338 params[jss::permissioned_domain][jss::seq] = "12g";
2339 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2340 checkErrorValue(jrr[jss::result], "malformedRequest", "");
2341 }
2342 }
2343
2344 void
2346 {
2347 testcase("ledger_entry command-line");
2348 using namespace test::jtx;
2349
2350 Env env{*this};
2351 Account const alice{"alice"};
2352 env.fund(XRP(10000), alice);
2353 env.close();
2354
2355 auto const checkId = keylet::check(env.master, env.seq(env.master));
2356
2357 env(check::create(env.master, alice, XRP(100)));
2358 env.close();
2359
2360 std::string const ledgerHash{to_string(env.closed()->info().hash)};
2361 {
2362 // Request a check.
2363 Json::Value const jrr =
2364 env.rpc("ledger_entry", to_string(checkId.key))[jss::result];
2365 BEAST_EXPECT(
2366 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
2367 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
2368 }
2369 }
2370
2371public:
2372 void
2398};
2399
2402{
2403 void
2405 Json::Value const& jv,
2406 std::string const& err,
2407 std::string const& msg)
2408 {
2409 if (BEAST_EXPECT(jv.isMember(jss::status)))
2410 BEAST_EXPECT(jv[jss::status] == "error");
2411 if (BEAST_EXPECT(jv.isMember(jss::error)))
2412 BEAST_EXPECT(jv[jss::error] == err);
2413 if (msg.empty())
2414 {
2415 BEAST_EXPECT(
2416 jv[jss::error_message] == Json::nullValue ||
2417 jv[jss::error_message] == "");
2418 }
2419 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
2420 BEAST_EXPECT(jv[jss::error_message] == msg);
2421 }
2422
2423 void
2425 {
2426 testcase("ledger_entry: bridge");
2427 using namespace test::jtx;
2428
2429 Env mcEnv{*this, features};
2430 Env scEnv(*this, envconfig(), features);
2431
2432 createBridgeObjects(mcEnv, scEnv);
2433
2434 std::string const ledgerHash{to_string(mcEnv.closed()->info().hash)};
2435 std::string bridge_index;
2436 Json::Value mcBridge;
2437 {
2438 // request the bridge via RPC
2439 Json::Value jvParams;
2440 jvParams[jss::bridge_account] = mcDoor.human();
2441 jvParams[jss::bridge] = jvb;
2442 Json::Value const jrr = mcEnv.rpc(
2443 "json", "ledger_entry", to_string(jvParams))[jss::result];
2444
2445 BEAST_EXPECT(jrr.isMember(jss::node));
2446 auto r = jrr[jss::node];
2447 // std::cout << to_string(r) << '\n';
2448
2449 BEAST_EXPECT(r.isMember(jss::Account));
2450 BEAST_EXPECT(r[jss::Account] == mcDoor.human());
2451
2452 BEAST_EXPECT(r.isMember(jss::Flags));
2453
2454 BEAST_EXPECT(r.isMember(sfLedgerEntryType.jsonName));
2455 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::Bridge);
2456
2457 // we not created an account yet
2458 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
2459 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 0);
2460
2461 // we have not claimed a locking chain tx yet
2462 BEAST_EXPECT(r.isMember(sfXChainAccountClaimCount.jsonName));
2463 BEAST_EXPECT(r[sfXChainAccountClaimCount.jsonName].asInt() == 0);
2464
2465 BEAST_EXPECT(r.isMember(jss::index));
2466 bridge_index = r[jss::index].asString();
2467 mcBridge = r;
2468 }
2469 {
2470 // request the bridge via RPC by index
2471 Json::Value jvParams;
2472 jvParams[jss::index] = bridge_index;
2473 Json::Value const jrr = mcEnv.rpc(
2474 "json", "ledger_entry", to_string(jvParams))[jss::result];
2475
2476 BEAST_EXPECT(jrr.isMember(jss::node));
2477 BEAST_EXPECT(jrr[jss::node] == mcBridge);
2478 }
2479 {
2480 // swap door accounts and make sure we get an error value
2481 Json::Value jvParams;
2482 // Sidechain door account is "master", not scDoor
2483 jvParams[jss::bridge_account] = Account::master.human();
2484 jvParams[jss::bridge] = jvb;
2485 jvParams[jss::ledger_hash] = ledgerHash;
2486 Json::Value const jrr = mcEnv.rpc(
2487 "json", "ledger_entry", to_string(jvParams))[jss::result];
2488
2489 checkErrorValue(jrr, "entryNotFound", "");
2490 }
2491 {
2492 // create two claim ids and verify that the bridge counter was
2493 // incremented
2495 mcEnv.close();
2497 mcEnv.close();
2498
2499 // request the bridge via RPC
2500 Json::Value jvParams;
2501 jvParams[jss::bridge_account] = mcDoor.human();
2502 jvParams[jss::bridge] = jvb;
2503 // std::cout << to_string(jvParams) << '\n';
2504 Json::Value const jrr = mcEnv.rpc(
2505 "json", "ledger_entry", to_string(jvParams))[jss::result];
2506
2507 BEAST_EXPECT(jrr.isMember(jss::node));
2508 auto r = jrr[jss::node];
2509
2510 // we executed two create claim id txs
2511 BEAST_EXPECT(r.isMember(sfXChainClaimID.jsonName));
2512 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
2513 }
2514 }
2515
2516 void
2518 {
2519 testcase("ledger_entry: xchain_claim_id");
2520 using namespace test::jtx;
2521
2522 Env mcEnv{*this, features};
2523 Env scEnv(*this, envconfig(), features);
2524
2525 createBridgeObjects(mcEnv, scEnv);
2526
2528 scEnv.close();
2530 scEnv.close();
2531
2532 std::string bridge_index;
2533 {
2534 // request the xchain_claim_id via RPC
2535 Json::Value jvParams;
2536 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
2537 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
2538 1;
2539 // std::cout << to_string(jvParams) << '\n';
2540 Json::Value const jrr = scEnv.rpc(
2541 "json", "ledger_entry", to_string(jvParams))[jss::result];
2542
2543 BEAST_EXPECT(jrr.isMember(jss::node));
2544 auto r = jrr[jss::node];
2545 // std::cout << to_string(r) << '\n';
2546
2547 BEAST_EXPECT(r.isMember(jss::Account));
2548 BEAST_EXPECT(r[jss::Account] == scAlice.human());
2549 BEAST_EXPECT(
2550 r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
2551 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 1);
2552 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
2553 }
2554
2555 {
2556 // request the xchain_claim_id via RPC
2557 Json::Value jvParams;
2558 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
2559 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
2560 2;
2561 Json::Value const jrr = scEnv.rpc(
2562 "json", "ledger_entry", to_string(jvParams))[jss::result];
2563
2564 BEAST_EXPECT(jrr.isMember(jss::node));
2565 auto r = jrr[jss::node];
2566 // std::cout << to_string(r) << '\n';
2567
2568 BEAST_EXPECT(r.isMember(jss::Account));
2569 BEAST_EXPECT(r[jss::Account] == scBob.human());
2570 BEAST_EXPECT(
2571 r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
2572 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
2573 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
2574 }
2575 }
2576
2577 void
2579 {
2580 testcase("ledger_entry: xchain_create_account_claim_id");
2581 using namespace test::jtx;
2582
2583 Env mcEnv{*this, features};
2584 Env scEnv(*this, envconfig(), features);
2585
2586 // note: signers.size() and quorum are both 5 in createBridgeObjects
2587 createBridgeObjects(mcEnv, scEnv);
2588
2589 auto scCarol =
2590 Account("scCarol"); // Don't fund it - it will be created with the
2591 // xchain transaction
2592 auto const amt = XRP(1000);
2594 mcAlice, jvb, scCarol, amt, reward));
2595 mcEnv.close();
2596
2597 // send less than quorum of attestations (otherwise funds are
2598 // immediately transferred and no "claim" object is created)
2599 size_t constexpr num_attest = 3;
2600 auto attestations = create_account_attestations(
2601 scAttester,
2602 jvb,
2603 mcAlice,
2604 amt,
2605 reward,
2606 payee,
2607 /*wasLockingChainSend*/ true,
2608 1,
2609 scCarol,
2610 signers,
2612 for (size_t i = 0; i < num_attest; ++i)
2613 {
2614 scEnv(attestations[i]);
2615 }
2616 scEnv.close();
2617
2618 {
2619 // request the create account claim_id via RPC
2620 Json::Value jvParams;
2621 jvParams[jss::xchain_owned_create_account_claim_id] =
2623 jvParams[jss::xchain_owned_create_account_claim_id]
2624 [jss::xchain_owned_create_account_claim_id] = 1;
2625 // std::cout << to_string(jvParams) << '\n';
2626 Json::Value const jrr = scEnv.rpc(
2627 "json", "ledger_entry", to_string(jvParams))[jss::result];
2628 // std::cout << to_string(jrr) << '\n';
2629
2630 BEAST_EXPECT(jrr.isMember(jss::node));
2631 auto r = jrr[jss::node];
2632
2633 BEAST_EXPECT(r.isMember(jss::Account));
2634 BEAST_EXPECT(r[jss::Account] == Account::master.human());
2635
2636 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
2637 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 1);
2638
2639 BEAST_EXPECT(
2640 r.isMember(sfXChainCreateAccountAttestations.jsonName));
2641 auto attest = r[sfXChainCreateAccountAttestations.jsonName];
2642 BEAST_EXPECT(attest.isArray());
2643 BEAST_EXPECT(attest.size() == 3);
2644 BEAST_EXPECT(attest[Json::Value::UInt(0)].isMember(
2645 sfXChainCreateAccountProofSig.jsonName));
2646 Json::Value a[num_attest];
2647 for (size_t i = 0; i < num_attest; ++i)
2648 {
2649 a[i] = attest[Json::Value::UInt(0)]
2650 [sfXChainCreateAccountProofSig.jsonName];
2651 BEAST_EXPECT(
2652 a[i].isMember(jss::Amount) &&
2653 a[i][jss::Amount].asInt() == 1000 * drop_per_xrp);
2654 BEAST_EXPECT(
2655 a[i].isMember(jss::Destination) &&
2656 a[i][jss::Destination] == scCarol.human());
2657 BEAST_EXPECT(
2658 a[i].isMember(sfAttestationSignerAccount.jsonName) &&
2660 signers.begin(), signers.end(), [&](signer const& s) {
2661 return a[i][sfAttestationSignerAccount.jsonName] ==
2662 s.account.human();
2663 }));
2664 BEAST_EXPECT(
2665 a[i].isMember(sfAttestationRewardAccount.jsonName) &&
2667 payee.begin(),
2668 payee.end(),
2669 [&](Account const& account) {
2670 return a[i][sfAttestationRewardAccount.jsonName] ==
2671 account.human();
2672 }));
2673 BEAST_EXPECT(
2674 a[i].isMember(sfWasLockingChainSend.jsonName) &&
2675 a[i][sfWasLockingChainSend.jsonName] == 1);
2676 BEAST_EXPECT(
2677 a[i].isMember(sfSignatureReward.jsonName) &&
2678 a[i][sfSignatureReward.jsonName].asInt() ==
2679 1 * drop_per_xrp);
2680 }
2681 }
2682
2683 // complete attestations quorum - CreateAccountClaimID should not be
2684 // present anymore
2685 for (size_t i = num_attest; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS; ++i)
2686 {
2687 scEnv(attestations[i]);
2688 }
2689 scEnv.close();
2690 {
2691 // request the create account claim_id via RPC
2692 Json::Value jvParams;
2693 jvParams[jss::xchain_owned_create_account_claim_id] =
2695 jvParams[jss::xchain_owned_create_account_claim_id]
2696 [jss::xchain_owned_create_account_claim_id] = 1;
2697 // std::cout << to_string(jvParams) << '\n';
2698 Json::Value const jrr = scEnv.rpc(
2699 "json", "ledger_entry", to_string(jvParams))[jss::result];
2700 checkErrorValue(jrr, "entryNotFound", "");
2701 }
2702 }
2703
2704public:
2705 void
2712};
2713
2714BEAST_DEFINE_TESTSUITE(LedgerEntry, rpc, ripple);
2715BEAST_DEFINE_TESTSUITE(LedgerEntry_XChain, rpc, ripple);
2716
2717} // namespace test
2718} // namespace ripple
T any_of(T... args)
T bind_front(T... args)
Represents a JSON value.
Definition json_value.h:149
Json::UInt UInt
Definition json_value.h:156
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
void clear()
Remove all object members and array elements.
Int asInt() const
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
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:61
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:258
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:121
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:788
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:279
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:87
Oracle class facilitates unit-testing of the Price Oracle feature.
Definition Oracle.h:120
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:35
T empty(T... args)
T is_same_v
@ nullValue
'null' value
Definition json_value.h:38
@ arrayValue
array value (ordered list)
Definition json_value.h:44
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:45
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:570
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition Indexes.cpp:336
Keylet payChan(AccountID const &src, AccountID const &dst, std::uint32_t seq) noexcept
A PaymentChannel.
Definition Indexes.cpp:395
Json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
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:78
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
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
FeatureBitset testable_amendments()
Definition Env.h:74
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
static uint256 ledgerHash(LedgerInfo const &info)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:85
AccountID const & xrpAccount()
Compute AccountID from public key.
constexpr std::uint32_t const tfMPTCanTransfer
Definition TxFlags.h:149
constexpr std::uint32_t const tfMPTCanTrade
Definition TxFlags.h:148
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:101
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition Indexes.cpp:156
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
constexpr std::uint32_t const tfMPTCanEscrow
Definition TxFlags.h:147
constexpr std::uint32_t const tfMPTRequireAuth
Definition TxFlags.h:146
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:145
constexpr std::uint32_t const tfMPTCanClawback
Definition TxFlags.h:150
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:39
T to_string(T... args)