rippled
AccountObjects_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2016 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 <ripple/json/json_reader.h>
21 #include <ripple/json/json_value.h>
22 #include <ripple/json/to_string.h>
23 #include <ripple/protocol/jss.h>
24 #include <test/jtx.h>
25 #include <test/jtx/AMM.h>
26 #include <test/jtx/xchain_bridge.h>
27 
28 #include <boost/utility/string_ref.hpp>
29 
30 #include <algorithm>
31 
32 namespace ripple {
33 namespace test {
34 
35 static char const* bobs_account_objects[] = {
36  R"json({
37  "Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
38  "BookDirectory" : "50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
39  "BookNode" : "0",
40  "Flags" : 65536,
41  "LedgerEntryType" : "Offer",
42  "OwnerNode" : "0",
43  "Sequence" : 6,
44  "TakerGets" : {
45  "currency" : "USD",
46  "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
47  "value" : "1"
48  },
49  "TakerPays" : "100000000",
50  "index" : "29665262716C19830E26AEEC0916E476FC7D8EF195FF3B4F06829E64F82A3B3E"
51 })json",
52  R"json({
53  "Balance" : {
54  "currency" : "USD",
55  "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
56  "value" : "-1000"
57  },
58  "Flags" : 131072,
59  "HighLimit" : {
60  "currency" : "USD",
61  "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
62  "value" : "1000"
63  },
64  "HighNode" : "0",
65  "LedgerEntryType" : "RippleState",
66  "LowLimit" : {
67  "currency" : "USD",
68  "issuer" : "r9cZvwKU3zzuZK9JFovGg1JC5n7QiqNL8L",
69  "value" : "0"
70  },
71  "LowNode" : "0",
72  "index" : "D13183BCFFC9AAC9F96AEBB5F66E4A652AD1F5D10273AEB615478302BEBFD4A4"
73 })json",
74  R"json({
75  "Balance" : {
76  "currency" : "USD",
77  "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
78  "value" : "-1000"
79  },
80  "Flags" : 131072,
81  "HighLimit" : {
82  "currency" : "USD",
83  "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
84  "value" : "1000"
85  },
86  "HighNode" : "0",
87  "LedgerEntryType" : "RippleState",
88  "LowLimit" : {
89  "currency" : "USD",
90  "issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
91  "value" : "0"
92  },
93  "LowNode" : "0",
94  "index" : "D89BC239086183EB9458C396E643795C1134963E6550E682A190A5F021766D43"
95 })json",
96  R"json({
97  "Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
98  "BookDirectory" : "B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
99  "BookNode" : "0",
100  "Flags" : 65536,
101  "LedgerEntryType" : "Offer",
102  "OwnerNode" : "0",
103  "Sequence" : 7,
104  "TakerGets" : {
105  "currency" : "USD",
106  "issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
107  "value" : "1"
108  },
109  "TakerPays" : "100000000",
110  "index" : "F03ABE26CB8C5F4AFB31A86590BD25C64C5756FCE5CE9704C27AFE291A4A29A1"
111 })json"};
112 
113 class AccountObjects_test : public beast::unit_test::suite
114 {
115 public:
116  void
118  {
119  testcase("error cases");
120 
121  using namespace jtx;
122  Env env(*this);
123 
124  // test error on no account
125  {
126  auto resp = env.rpc("json", "account_objects");
127  BEAST_EXPECT(resp[jss::error_message] == "Syntax error.");
128  }
129  // test error on malformed account string.
130  {
131  Json::Value params;
132  params[jss::account] =
133  "n94JNrQYkDrpt62bbSR7nVEhdyAvcJXRAsjEkFYyqRkh9SUTYEqV";
134  auto resp = env.rpc("json", "account_objects", to_string(params));
135  BEAST_EXPECT(
136  resp[jss::result][jss::error_message] == "Account malformed.");
137  }
138  // test error on account that's not in the ledger.
139  {
140  Json::Value params;
141  params[jss::account] = Account{"bogie"}.human();
142  auto resp = env.rpc("json", "account_objects", to_string(params));
143  BEAST_EXPECT(
144  resp[jss::result][jss::error_message] == "Account not found.");
145  }
146  Account const bob{"bob"};
147  // test error on large ledger_index.
148  {
149  Json::Value params;
150  params[jss::account] = bob.human();
151  params[jss::ledger_index] = 10;
152  auto resp = env.rpc("json", "account_objects", to_string(params));
153  BEAST_EXPECT(
154  resp[jss::result][jss::error_message] == "ledgerNotFound");
155  }
156 
157  env.fund(XRP(1000), bob);
158  // test error on type param not a string
159  {
160  Json::Value params;
161  params[jss::account] = bob.human();
162  params[jss::type] = 10;
163  auto resp = env.rpc("json", "account_objects", to_string(params));
164  BEAST_EXPECT(
165  resp[jss::result][jss::error_message] ==
166  "Invalid field 'type', not string.");
167  }
168  // test error on type param not a valid type
169  {
170  Json::Value params;
171  params[jss::account] = bob.human();
172  params[jss::type] = "expedited";
173  auto resp = env.rpc("json", "account_objects", to_string(params));
174  BEAST_EXPECT(
175  resp[jss::result][jss::error_message] ==
176  "Invalid field 'type'.");
177  }
178  // test error on limit -ve
179  {
180  Json::Value params;
181  params[jss::account] = bob.human();
182  params[jss::limit] = -1;
183  auto resp = env.rpc("json", "account_objects", to_string(params));
184  BEAST_EXPECT(
185  resp[jss::result][jss::error_message] ==
186  "Invalid field 'limit', not unsigned integer.");
187  }
188  // test errors on marker
189  {
190  Account const gw{"G"};
191  env.fund(XRP(1000), gw);
192  auto const USD = gw["USD"];
193  env.trust(USD(1000), bob);
194  env(pay(gw, bob, XRP(1)));
195  env(offer(bob, XRP(100), bob["USD"](1)), txflags(tfPassive));
196 
197  Json::Value params;
198  params[jss::account] = bob.human();
199  params[jss::limit] = 1;
200  auto resp = env.rpc("json", "account_objects", to_string(params));
201 
202  auto resume_marker = resp[jss::result][jss::marker];
203  std::string mark = to_string(resume_marker);
204  params[jss::marker] = 10;
205  resp = env.rpc("json", "account_objects", to_string(params));
206  BEAST_EXPECT(
207  resp[jss::result][jss::error_message] ==
208  "Invalid field 'marker', not string.");
209 
210  params[jss::marker] = "This is a string with no comma";
211  resp = env.rpc("json", "account_objects", to_string(params));
212  BEAST_EXPECT(
213  resp[jss::result][jss::error_message] ==
214  "Invalid field 'marker'.");
215 
216  params[jss::marker] = "This string has a comma, but is not hex";
217  resp = env.rpc("json", "account_objects", to_string(params));
218  BEAST_EXPECT(
219  resp[jss::result][jss::error_message] ==
220  "Invalid field 'marker'.");
221 
222  params[jss::marker] = std::string(&mark[1U], 64);
223  resp = env.rpc("json", "account_objects", to_string(params));
224  BEAST_EXPECT(
225  resp[jss::result][jss::error_message] ==
226  "Invalid field 'marker'.");
227 
228  params[jss::marker] = std::string(&mark[1U], 65);
229  resp = env.rpc("json", "account_objects", to_string(params));
230  BEAST_EXPECT(
231  resp[jss::result][jss::error_message] ==
232  "Invalid field 'marker'.");
233 
234  params[jss::marker] = std::string(&mark[1U], 65) + "not hex";
235  resp = env.rpc("json", "account_objects", to_string(params));
236  BEAST_EXPECT(
237  resp[jss::result][jss::error_message] ==
238  "Invalid field 'marker'.");
239 
240  // Should this be an error?
241  // A hex digit is absent from the end of marker.
242  // No account objects returned.
243  params[jss::marker] = std::string(&mark[1U], 128);
244  resp = env.rpc("json", "account_objects", to_string(params));
245  BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0);
246  }
247  }
248 
249  void
251  {
252  testcase("unsteppedThenStepped");
253 
254  using namespace jtx;
255  Env env(*this);
256 
257  Account const gw1{"G1"};
258  Account const gw2{"G2"};
259  Account const bob{"bob"};
260 
261  auto const USD1 = gw1["USD"];
262  auto const USD2 = gw2["USD"];
263 
264  env.fund(XRP(1000), gw1, gw2, bob);
265  env.trust(USD1(1000), bob);
266  env.trust(USD2(1000), bob);
267 
268  env(pay(gw1, bob, USD1(1000)));
269  env(pay(gw2, bob, USD2(1000)));
270 
271  env(offer(bob, XRP(100), bob["USD"](1)), txflags(tfPassive));
272  env(offer(bob, XRP(100), USD1(1)), txflags(tfPassive));
273 
274  Json::Value bobj[4];
275  for (int i = 0; i < 4; ++i)
276  Json::Reader{}.parse(bobs_account_objects[i], bobj[i]);
277 
278  // test 'unstepped'
279  // i.e. request account objects without explicit limit/marker paging
280  {
281  Json::Value params;
282  params[jss::account] = bob.human();
283  auto resp = env.rpc("json", "account_objects", to_string(params));
284  BEAST_EXPECT(!resp.isMember(jss::marker));
285 
286  BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 4);
287  for (int i = 0; i < 4; ++i)
288  {
289  auto& aobj = resp[jss::result][jss::account_objects][i];
290  aobj.removeMember("PreviousTxnID");
291  aobj.removeMember("PreviousTxnLgrSeq");
292  BEAST_EXPECT(aobj == bobj[i]);
293  }
294  }
295  // test request with type parameter as filter, unstepped
296  {
297  Json::Value params;
298  params[jss::account] = bob.human();
299  params[jss::type] = jss::state;
300  auto resp = env.rpc("json", "account_objects", to_string(params));
301  BEAST_EXPECT(!resp.isMember(jss::marker));
302 
303  BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 2);
304  for (int i = 0; i < 2; ++i)
305  {
306  auto& aobj = resp[jss::result][jss::account_objects][i];
307  aobj.removeMember("PreviousTxnID");
308  aobj.removeMember("PreviousTxnLgrSeq");
309  BEAST_EXPECT(aobj == bobj[i + 1]);
310  }
311  }
312  // test stepped one-at-a-time with limit=1, resume from prev marker
313  {
314  Json::Value params;
315  params[jss::account] = bob.human();
316  params[jss::limit] = 1;
317  for (int i = 0; i < 4; ++i)
318  {
319  auto resp =
320  env.rpc("json", "account_objects", to_string(params));
321  auto& aobjs = resp[jss::result][jss::account_objects];
322  BEAST_EXPECT(aobjs.size() == 1);
323  auto& aobj = aobjs[0U];
324  if (i < 3)
325  BEAST_EXPECT(resp[jss::result][jss::limit] == 1);
326  else
327  BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
328 
329  aobj.removeMember("PreviousTxnID");
330  aobj.removeMember("PreviousTxnLgrSeq");
331 
332  BEAST_EXPECT(aobj == bobj[i]);
333 
334  params[jss::marker] = resp[jss::result][jss::marker];
335  }
336  }
337  }
338 
339  void
341  {
342  // The preceding test case, unsteppedThenStepped(), found a bug in the
343  // support for NFToken Pages. So we're leaving that test alone when
344  // adding tests to exercise NFTokenPages.
345  testcase("unsteppedThenSteppedWithNFTs");
346 
347  using namespace jtx;
348  Env env(*this);
349 
350  Account const gw1{"G1"};
351  Account const gw2{"G2"};
352  Account const bob{"bob"};
353 
354  auto const USD1 = gw1["USD"];
355  auto const USD2 = gw2["USD"];
356 
357  env.fund(XRP(1000), gw1, gw2, bob);
358  env.close();
359 
360  // Check behavior if there are no account objects.
361  {
362  // Unpaged
363  Json::Value params;
364  params[jss::account] = bob.human();
365  auto resp = env.rpc("json", "account_objects", to_string(params));
366  BEAST_EXPECT(!resp.isMember(jss::marker));
367  BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0);
368 
369  // Limit == 1
370  params[jss::limit] = 1;
371  resp = env.rpc("json", "account_objects", to_string(params));
372  BEAST_EXPECT(!resp.isMember(jss::marker));
373  BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0);
374  }
375 
376  // Check behavior if there are only NFTokens.
377  env(token::mint(bob, 0u), txflags(tfTransferable));
378  env.close();
379 
380  // test 'unstepped'
381  // i.e. request account objects without explicit limit/marker paging
382  Json::Value unpaged;
383  {
384  Json::Value params;
385  params[jss::account] = bob.human();
386  auto resp = env.rpc("json", "account_objects", to_string(params));
387  BEAST_EXPECT(!resp.isMember(jss::marker));
388 
389  unpaged = resp[jss::result][jss::account_objects];
390  BEAST_EXPECT(unpaged.size() == 1);
391  }
392  // test request with type parameter as filter, unstepped
393  {
394  Json::Value params;
395  params[jss::account] = bob.human();
396  params[jss::type] = jss::nft_page;
397  auto resp = env.rpc("json", "account_objects", to_string(params));
398  BEAST_EXPECT(!resp.isMember(jss::marker));
399  Json::Value& aobjs = resp[jss::result][jss::account_objects];
400  BEAST_EXPECT(aobjs.size() == 1);
401  BEAST_EXPECT(
402  aobjs[0u][sfLedgerEntryType.jsonName] == jss::NFTokenPage);
403  BEAST_EXPECT(aobjs[0u][sfNFTokens.jsonName].size() == 1);
404  }
405  // test stepped one-at-a-time with limit=1, resume from prev marker
406  {
407  Json::Value params;
408  params[jss::account] = bob.human();
409  params[jss::limit] = 1;
410 
411  Json::Value resp =
412  env.rpc("json", "account_objects", to_string(params));
413  Json::Value& aobjs = resp[jss::result][jss::account_objects];
414  BEAST_EXPECT(aobjs.size() == 1);
415  auto& aobj = aobjs[0U];
416  BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
417  BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
418 
419  BEAST_EXPECT(aobj == unpaged[0u]);
420  }
421 
422  // Add more objects in addition to the NFToken Page.
423  env.trust(USD1(1000), bob);
424  env.trust(USD2(1000), bob);
425 
426  env(pay(gw1, bob, USD1(1000)));
427  env(pay(gw2, bob, USD2(1000)));
428 
429  env(offer(bob, XRP(100), bob["USD"](1)), txflags(tfPassive));
430  env(offer(bob, XRP(100), USD1(1)), txflags(tfPassive));
431  env.close();
432 
433  // test 'unstepped'
434  {
435  Json::Value params;
436  params[jss::account] = bob.human();
437  auto resp = env.rpc("json", "account_objects", to_string(params));
438  BEAST_EXPECT(!resp.isMember(jss::marker));
439 
440  unpaged = resp[jss::result][jss::account_objects];
441  BEAST_EXPECT(unpaged.size() == 5);
442  }
443  // test request with type parameter as filter, unstepped
444  {
445  Json::Value params;
446  params[jss::account] = bob.human();
447  params[jss::type] = jss::nft_page;
448  auto resp = env.rpc("json", "account_objects", to_string(params));
449  BEAST_EXPECT(!resp.isMember(jss::marker));
450  Json::Value& aobjs = resp[jss::result][jss::account_objects];
451  BEAST_EXPECT(aobjs.size() == 1);
452  BEAST_EXPECT(
453  aobjs[0u][sfLedgerEntryType.jsonName] == jss::NFTokenPage);
454  BEAST_EXPECT(aobjs[0u][sfNFTokens.jsonName].size() == 1);
455  }
456  // test stepped one-at-a-time with limit=1, resume from prev marker
457  {
458  Json::Value params;
459  params[jss::account] = bob.human();
460  params[jss::limit] = 1;
461  for (int i = 0; i < 5; ++i)
462  {
463  Json::Value resp =
464  env.rpc("json", "account_objects", to_string(params));
465  Json::Value& aobjs = resp[jss::result][jss::account_objects];
466  BEAST_EXPECT(aobjs.size() == 1);
467  auto& aobj = aobjs[0U];
468  if (i < 4)
469  {
470  BEAST_EXPECT(resp[jss::result][jss::limit] == 1);
471  BEAST_EXPECT(resp[jss::result].isMember(jss::marker));
472  }
473  else
474  {
475  BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
476  BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
477  }
478 
479  BEAST_EXPECT(aobj == unpaged[i]);
480 
481  params[jss::marker] = resp[jss::result][jss::marker];
482  }
483  }
484 
485  // Make sure things still work if there is more than 1 NFT Page.
486  for (int i = 0; i < 32; ++i)
487  {
488  env(token::mint(bob, 0u), txflags(tfTransferable));
489  env.close();
490  }
491  // test 'unstepped'
492  {
493  Json::Value params;
494  params[jss::account] = bob.human();
495  auto resp = env.rpc("json", "account_objects", to_string(params));
496  BEAST_EXPECT(!resp.isMember(jss::marker));
497 
498  unpaged = resp[jss::result][jss::account_objects];
499  BEAST_EXPECT(unpaged.size() == 6);
500  }
501  // test request with type parameter as filter, unstepped
502  {
503  Json::Value params;
504  params[jss::account] = bob.human();
505  params[jss::type] = jss::nft_page;
506  auto resp = env.rpc("json", "account_objects", to_string(params));
507  BEAST_EXPECT(!resp.isMember(jss::marker));
508  Json::Value& aobjs = resp[jss::result][jss::account_objects];
509  BEAST_EXPECT(aobjs.size() == 2);
510  }
511  // test stepped one-at-a-time with limit=1, resume from prev marker
512  {
513  Json::Value params;
514  params[jss::account] = bob.human();
515  params[jss::limit] = 1;
516  for (int i = 0; i < 6; ++i)
517  {
518  Json::Value resp =
519  env.rpc("json", "account_objects", to_string(params));
520  Json::Value& aobjs = resp[jss::result][jss::account_objects];
521  BEAST_EXPECT(aobjs.size() == 1);
522  auto& aobj = aobjs[0U];
523  if (i < 5)
524  {
525  BEAST_EXPECT(resp[jss::result][jss::limit] == 1);
526  BEAST_EXPECT(resp[jss::result].isMember(jss::marker));
527  }
528  else
529  {
530  BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
531  BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
532  }
533 
534  BEAST_EXPECT(aobj == unpaged[i]);
535 
536  params[jss::marker] = resp[jss::result][jss::marker];
537  }
538  }
539  }
540 
541  void
543  {
544  testcase("object types");
545 
546  // Give gw a bunch of ledger objects and make sure we can retrieve
547  // them by type.
548  using namespace jtx;
549 
550  Account const alice{"alice"};
551  Account const gw{"gateway"};
552  auto const USD = gw["USD"];
553 
554  auto const features =
556  Env env(*this, features);
557 
558  // Make a lambda we can use to get "account_objects" easily.
559  auto acct_objs = [&env](
560  AccountID const& acct,
562  std::optional<std::uint16_t> limit = std::nullopt,
563  std::optional<std::string> marker = std::nullopt) {
564  Json::Value params;
565  params[jss::account] = to_string(acct);
566  if (type)
567  params[jss::type] = *type;
568  if (limit)
569  params[jss::limit] = *limit;
570  if (marker)
571  params[jss::marker] = *marker;
572  params[jss::ledger_index] = "validated";
573  return env.rpc("json", "account_objects", to_string(params));
574  };
575 
576  // Make a lambda that easily identifies the size of account objects.
577  auto acct_objs_is_size = [](Json::Value const& resp, unsigned size) {
578  return resp[jss::result][jss::account_objects].isArray() &&
579  (resp[jss::result][jss::account_objects].size() == size);
580  };
581 
582  env.fund(XRP(10000), gw, alice);
583  env.close();
584 
585  // Since the account is empty now, all account objects should come
586  // back empty.
587  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::account), 0));
588  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::amendments), 0));
589  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::check), 0));
590  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::deposit_preauth), 0));
591  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::directory), 0));
592  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::escrow), 0));
593  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::fee), 0));
594  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::hashes), 0));
595  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::nft_page), 0));
596  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::offer), 0));
597  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::payment_channel), 0));
598  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::signer_list), 0));
599  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::state), 0));
600  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::ticket), 0));
601  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::amm), 0));
602  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::did), 0));
603 
604  // gw mints an NFT so we can find it.
605  uint256 const nftID{token::getNextID(env, gw, 0u, tfTransferable)};
606  env(token::mint(gw, 0u), txflags(tfTransferable));
607  env.close();
608  {
609  // Find the NFToken page and make sure it's the right one.
610  Json::Value const resp = acct_objs(gw, jss::nft_page);
611  BEAST_EXPECT(acct_objs_is_size(resp, 1));
612 
613  auto const& nftPage = resp[jss::result][jss::account_objects][0u];
614  BEAST_EXPECT(nftPage[sfNFTokens.jsonName].size() == 1);
615  BEAST_EXPECT(
616  nftPage[sfNFTokens.jsonName][0u][sfNFToken.jsonName]
617  [sfNFTokenID.jsonName] == to_string(nftID));
618  }
619 
620  // Set up a trust line so we can find it.
621  env.trust(USD(1000), alice);
622  env.close();
623  env(pay(gw, alice, USD(5)));
624  env.close();
625  {
626  // Find the trustline and make sure it's the right one.
627  Json::Value const resp = acct_objs(gw, jss::state);
628  BEAST_EXPECT(acct_objs_is_size(resp, 1));
629 
630  auto const& state = resp[jss::result][jss::account_objects][0u];
631  BEAST_EXPECT(state[sfBalance.jsonName][jss::value].asInt() == -5);
632  BEAST_EXPECT(
633  state[sfHighLimit.jsonName][jss::value].asUInt() == 1000);
634  }
635  // gw writes a check for USD(10) to alice.
636  env(check::create(gw, alice, USD(10)));
637  env.close();
638  {
639  // Find the check.
640  Json::Value const resp = acct_objs(gw, jss::check);
641  BEAST_EXPECT(acct_objs_is_size(resp, 1));
642 
643  auto const& check = resp[jss::result][jss::account_objects][0u];
644  BEAST_EXPECT(check[sfAccount.jsonName] == gw.human());
645  BEAST_EXPECT(check[sfDestination.jsonName] == alice.human());
646  BEAST_EXPECT(check[sfSendMax.jsonName][jss::value].asUInt() == 10);
647  }
648  // gw preauthorizes payments from alice.
649  env(deposit::auth(gw, alice));
650  env.close();
651  {
652  // Find the preauthorization.
653  Json::Value const resp = acct_objs(gw, jss::deposit_preauth);
654  BEAST_EXPECT(acct_objs_is_size(resp, 1));
655 
656  auto const& preauth = resp[jss::result][jss::account_objects][0u];
657  BEAST_EXPECT(preauth[sfAccount.jsonName] == gw.human());
658  BEAST_EXPECT(preauth[sfAuthorize.jsonName] == alice.human());
659  }
660  {
661  // gw creates an escrow that we can look for in the ledger.
662  Json::Value jvEscrow;
663  jvEscrow[jss::TransactionType] = jss::EscrowCreate;
664  jvEscrow[jss::Flags] = tfUniversal;
665  jvEscrow[jss::Account] = gw.human();
666  jvEscrow[jss::Destination] = gw.human();
667  jvEscrow[jss::Amount] = XRP(100).value().getJson(JsonOptions::none);
668  jvEscrow[sfFinishAfter.jsonName] =
669  env.now().time_since_epoch().count() + 1;
670  env(jvEscrow);
671  env.close();
672  }
673  {
674  // Find the escrow.
675  Json::Value const resp = acct_objs(gw, jss::escrow);
676  BEAST_EXPECT(acct_objs_is_size(resp, 1));
677 
678  auto const& escrow = resp[jss::result][jss::account_objects][0u];
679  BEAST_EXPECT(escrow[sfAccount.jsonName] == gw.human());
680  BEAST_EXPECT(escrow[sfDestination.jsonName] == gw.human());
681  BEAST_EXPECT(escrow[sfAmount.jsonName].asUInt() == 100'000'000);
682  }
683  {
684  // Create a bridge
686  Env scEnv(*this, envconfig(port_increment, 3), features);
687  x.createScBridgeObjects(scEnv);
688 
689  auto scenv_acct_objs = [&](Account const& acct, char const* type) {
690  Json::Value params;
691  params[jss::account] = acct.human();
692  params[jss::type] = type;
693  params[jss::ledger_index] = "validated";
694  return scEnv.rpc("json", "account_objects", to_string(params));
695  };
696 
697  Json::Value const resp =
698  scenv_acct_objs(Account::master, jss::bridge);
699 
700  BEAST_EXPECT(acct_objs_is_size(resp, 1));
701  auto const& acct_bridge =
702  resp[jss::result][jss::account_objects][0u];
703  BEAST_EXPECT(
704  acct_bridge[sfAccount.jsonName] == Account::master.human());
705  BEAST_EXPECT(
706  acct_bridge[sfLedgerEntryType.getJsonName()] == "Bridge");
707  BEAST_EXPECT(
708  acct_bridge[sfXChainClaimID.getJsonName()].asUInt() == 0);
709  BEAST_EXPECT(
710  acct_bridge[sfXChainAccountClaimCount.getJsonName()].asUInt() ==
711  0);
712  BEAST_EXPECT(
714  .asUInt() == 0);
715  BEAST_EXPECT(
716  acct_bridge[sfMinAccountCreateAmount.getJsonName()].asUInt() ==
717  20000000);
718  BEAST_EXPECT(
719  acct_bridge[sfSignatureReward.getJsonName()].asUInt() ==
720  1000000);
721  BEAST_EXPECT(acct_bridge[sfXChainBridge.getJsonName()] == x.jvb);
722  }
723  {
724  // Alice and Bob create a xchain sequence number that we can look
725  // for in the ledger.
727  Env scEnv(*this, envconfig(port_increment, 3), features);
728  x.createScBridgeObjects(scEnv);
729 
730  scEnv(
732  scEnv.close();
733  scEnv(xchain_create_claim_id(x.scBob, x.jvb, x.reward, x.mcBob));
734  scEnv.close();
735 
736  auto scenv_acct_objs = [&](Account const& acct, char const* type) {
737  Json::Value params;
738  params[jss::account] = acct.human();
739  params[jss::type] = type;
740  params[jss::ledger_index] = "validated";
741  return scEnv.rpc("json", "account_objects", to_string(params));
742  };
743 
744  {
745  // Find the xchain sequence number for Andrea.
746  Json::Value const resp =
747  scenv_acct_objs(x.scAlice, jss::xchain_owned_claim_id);
748  BEAST_EXPECT(acct_objs_is_size(resp, 1));
749 
750  auto const& xchain_seq =
751  resp[jss::result][jss::account_objects][0u];
752  BEAST_EXPECT(
753  xchain_seq[sfAccount.jsonName] == x.scAlice.human());
754  BEAST_EXPECT(
755  xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 1);
756  }
757  {
758  // and the one for Bob
759  Json::Value const resp =
760  scenv_acct_objs(x.scBob, jss::xchain_owned_claim_id);
761  BEAST_EXPECT(acct_objs_is_size(resp, 1));
762 
763  auto const& xchain_seq =
764  resp[jss::result][jss::account_objects][0u];
765  BEAST_EXPECT(xchain_seq[sfAccount.jsonName] == x.scBob.human());
766  BEAST_EXPECT(
767  xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 2);
768  }
769  }
770  {
772  Env scEnv(*this, envconfig(port_increment, 3), features);
773  x.createScBridgeObjects(scEnv);
774  auto const amt = XRP(1000);
775 
776  // send first batch of account create attestations, so the
777  // xchain_create_account_claim_id should be present on the door
778  // account (Account::master) to collect the signatures until a
779  // quorum is reached
781  x.scAttester,
782  x.jvb,
783  x.mcCarol,
784  amt,
785  x.reward,
786  x.payees[0],
787  true,
788  1,
789  x.scuAlice,
790  x.signers[0]));
791  scEnv.close();
792 
793  auto scenv_acct_objs = [&](Account const& acct, char const* type) {
794  Json::Value params;
795  params[jss::account] = acct.human();
796  params[jss::type] = type;
797  params[jss::ledger_index] = "validated";
798  return scEnv.rpc("json", "account_objects", to_string(params));
799  };
800 
801  {
802  // Find the xchain_create_account_claim_id
803  Json::Value const resp = scenv_acct_objs(
804  Account::master, jss::xchain_owned_create_account_claim_id);
805  BEAST_EXPECT(acct_objs_is_size(resp, 1));
806 
807  auto const& xchain_create_account_claim_id =
808  resp[jss::result][jss::account_objects][0u];
809  BEAST_EXPECT(
810  xchain_create_account_claim_id[sfAccount.jsonName] ==
812  BEAST_EXPECT(
813  xchain_create_account_claim_id[sfXChainAccountCreateCount
814  .getJsonName()]
815  .asUInt() == 1);
816  }
817  }
818 
819  // gw creates an offer that we can look for in the ledger.
820  env(offer(gw, USD(7), XRP(14)));
821  env.close();
822  {
823  // Find the offer.
824  Json::Value const resp = acct_objs(gw, jss::offer);
825  BEAST_EXPECT(acct_objs_is_size(resp, 1));
826 
827  auto const& offer = resp[jss::result][jss::account_objects][0u];
828  BEAST_EXPECT(offer[sfAccount.jsonName] == gw.human());
829  BEAST_EXPECT(offer[sfTakerGets.jsonName].asUInt() == 14'000'000);
830  BEAST_EXPECT(offer[sfTakerPays.jsonName][jss::value].asUInt() == 7);
831  }
832  {
833  // Create a payment channel from qw to alice that we can look
834  // for.
835  Json::Value jvPayChan;
836  jvPayChan[jss::TransactionType] = jss::PaymentChannelCreate;
837  jvPayChan[jss::Flags] = tfUniversal;
838  jvPayChan[jss::Account] = gw.human();
839  jvPayChan[jss::Destination] = alice.human();
840  jvPayChan[jss::Amount] =
841  XRP(300).value().getJson(JsonOptions::none);
842  jvPayChan[sfSettleDelay.jsonName] = 24 * 60 * 60;
843  jvPayChan[sfPublicKey.jsonName] = strHex(gw.pk().slice());
844  env(jvPayChan);
845  env.close();
846  }
847  {
848  // Find the payment channel.
849  Json::Value const resp = acct_objs(gw, jss::payment_channel);
850  BEAST_EXPECT(acct_objs_is_size(resp, 1));
851 
852  auto const& payChan = resp[jss::result][jss::account_objects][0u];
853  BEAST_EXPECT(payChan[sfAccount.jsonName] == gw.human());
854  BEAST_EXPECT(payChan[sfAmount.jsonName].asUInt() == 300'000'000);
855  BEAST_EXPECT(
856  payChan[sfSettleDelay.jsonName].asUInt() == 24 * 60 * 60);
857  }
858 
859  {
860  // gw creates a DID that we can look for in the ledger.
861  Json::Value jvDID;
862  jvDID[jss::TransactionType] = jss::DIDSet;
863  jvDID[jss::Flags] = tfUniversal;
864  jvDID[jss::Account] = gw.human();
865  jvDID[sfURI.jsonName] = strHex(std::string{"uri"});
866  env(jvDID);
867  env.close();
868  }
869  {
870  // Find the DID.
871  Json::Value const resp = acct_objs(gw, jss::did);
872  BEAST_EXPECT(acct_objs_is_size(resp, 1));
873 
874  auto const& did = resp[jss::result][jss::account_objects][0u];
875  BEAST_EXPECT(did[sfAccount.jsonName] == gw.human());
876  BEAST_EXPECT(did[sfURI.jsonName] == strHex(std::string{"uri"}));
877  }
878  // Make gw multisigning by adding a signerList.
879  env(jtx::signers(gw, 6, {{alice, 7}}));
880  env.close();
881  {
882  // Find the signer list.
883  Json::Value const resp = acct_objs(gw, jss::signer_list);
884  BEAST_EXPECT(acct_objs_is_size(resp, 1));
885 
886  auto const& signerList =
887  resp[jss::result][jss::account_objects][0u];
888  BEAST_EXPECT(signerList[sfSignerQuorum.jsonName] == 6);
889  auto const& entry = signerList[sfSignerEntries.jsonName][0u]
891  BEAST_EXPECT(entry[sfAccount.jsonName] == alice.human());
892  BEAST_EXPECT(entry[sfSignerWeight.jsonName].asUInt() == 7);
893  }
894  // Create a Ticket for gw.
895  env(ticket::create(gw, 1));
896  env.close();
897  {
898  // Find the ticket.
899  Json::Value const resp = acct_objs(gw, jss::ticket);
900  BEAST_EXPECT(acct_objs_is_size(resp, 1));
901 
902  auto const& ticket = resp[jss::result][jss::account_objects][0u];
903  BEAST_EXPECT(ticket[sfAccount.jsonName] == gw.human());
904  BEAST_EXPECT(ticket[sfLedgerEntryType.jsonName] == jss::Ticket);
905  BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == 14);
906  }
907  {
908  // See how "deletion_blockers_only" handles gw's directory.
909  Json::Value params;
910  params[jss::account] = gw.human();
911  params[jss::deletion_blockers_only] = true;
912  auto resp = env.rpc("json", "account_objects", to_string(params));
913 
914  std::vector<std::string> const expectedLedgerTypes = [] {
916  jss::Escrow.c_str(),
917  jss::Check.c_str(),
918  jss::NFTokenPage.c_str(),
919  jss::RippleState.c_str(),
920  jss::PayChannel.c_str()};
921  std::sort(v.begin(), v.end());
922  return v;
923  }();
924 
925  std::uint32_t const expectedAccountObjects{
926  static_cast<std::uint32_t>(std::size(expectedLedgerTypes))};
927 
928  if (BEAST_EXPECT(acct_objs_is_size(resp, expectedAccountObjects)))
929  {
930  auto const& aobjs = resp[jss::result][jss::account_objects];
931  std::vector<std::string> gotLedgerTypes;
932  gotLedgerTypes.reserve(expectedAccountObjects);
933  for (std::uint32_t i = 0; i < expectedAccountObjects; ++i)
934  {
935  gotLedgerTypes.push_back(
936  aobjs[i]["LedgerEntryType"].asString());
937  }
938  std::sort(gotLedgerTypes.begin(), gotLedgerTypes.end());
939  BEAST_EXPECT(gotLedgerTypes == expectedLedgerTypes);
940  }
941  }
942  {
943  // See how "deletion_blockers_only" with `type` handles gw's
944  // directory.
945  Json::Value params;
946  params[jss::account] = gw.human();
947  params[jss::deletion_blockers_only] = true;
948  params[jss::type] = jss::escrow;
949  auto resp = env.rpc("json", "account_objects", to_string(params));
950 
951  if (BEAST_EXPECT(acct_objs_is_size(resp, 1u)))
952  {
953  auto const& aobjs = resp[jss::result][jss::account_objects];
954  BEAST_EXPECT(aobjs[0u]["LedgerEntryType"] == jss::Escrow);
955  }
956  }
957  {
958  // Make a lambda to get the types
959  auto getTypes = [&](Json::Value const& resp,
960  std::vector<std::string>& typesOut) {
961  auto const objs = resp[jss::result][jss::account_objects];
962  for (auto const& obj : resp[jss::result][jss::account_objects])
963  typesOut.push_back(
964  obj[sfLedgerEntryType.fieldName].asString());
965  std::sort(typesOut.begin(), typesOut.end());
966  };
967  // Make a lambda we can use to check the number of fetched
968  // account objects and their ledger type
969  auto expectObjects =
970  [&](Json::Value const& resp,
971  std::vector<std::string> const& types) -> bool {
972  if (!acct_objs_is_size(resp, types.size()))
973  return false;
974  std::vector<std::string> typesOut;
975  getTypes(resp, typesOut);
976  return types == typesOut;
977  };
978  // Find AMM objects
979  AMM amm(env, gw, XRP(1'000), USD(1'000));
980  amm.deposit(alice, USD(1));
981  // AMM account has 4 objects: AMM object and 3 trustlines
982  auto const lines = getAccountLines(env, amm.ammAccount());
983  BEAST_EXPECT(lines[jss::lines].size() == 3);
984  // request AMM only, doesn't depend on the limit
985  BEAST_EXPECT(
986  acct_objs_is_size(acct_objs(amm.ammAccount(), jss::amm), 1));
987  // request first two objects
988  auto resp = acct_objs(amm.ammAccount(), std::nullopt, 2);
989  std::vector<std::string> typesOut;
990  getTypes(resp, typesOut);
991  // request next two objects
992  resp = acct_objs(
993  amm.ammAccount(),
994  std::nullopt,
995  10,
996  resp[jss::result][jss::marker].asString());
997  getTypes(resp, typesOut);
998  BEAST_EXPECT(
999  (typesOut ==
1001  jss::AMM.c_str(),
1002  jss::RippleState.c_str(),
1003  jss::RippleState.c_str(),
1004  jss::RippleState.c_str()}));
1005  // filter by state: there are three trustlines
1006  resp = acct_objs(amm.ammAccount(), jss::state, 10);
1007  BEAST_EXPECT(expectObjects(
1008  resp,
1009  {jss::RippleState.c_str(),
1010  jss::RippleState.c_str(),
1011  jss::RippleState.c_str()}));
1012  // AMM account doesn't own offers
1013  BEAST_EXPECT(
1014  acct_objs_is_size(acct_objs(amm.ammAccount(), jss::offer), 0));
1015  // gw account doesn't own AMM object
1016  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::amm), 0));
1017  }
1018 
1019  // Run up the number of directory entries so gw has two
1020  // directory nodes.
1021  for (int d = 1'000'032; d >= 1'000'000; --d)
1022  {
1023  env(offer(gw, USD(1), drops(d)));
1024  env.close();
1025  }
1026 
1027  // Verify that the non-returning types still don't return anything.
1028  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::account), 0));
1029  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::amendments), 0));
1030  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::directory), 0));
1031  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::fee), 0));
1032  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::hashes), 0));
1033  }
1034 
1035  void
1036  run() override
1037  {
1038  testErrors();
1041  testObjectTypes();
1042  }
1043 };
1044 
1045 BEAST_DEFINE_TESTSUITE(AccountObjects, app, ripple);
1046 
1047 } // namespace test
1048 } // namespace ripple
ripple::sfSignerWeight
const SF_UINT16 sfSignerWeight
ripple::test::AccountObjects_test::testObjectTypes
void testObjectTypes()
Definition: AccountObjects_test.cpp:542
ripple::tfTransferable
constexpr const std::uint32_t tfTransferable
Definition: TxFlags.h:131
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
ripple::test::jtx::XChainBridgeObjects::createScBridgeObjects
void createScBridgeObjects(Env &scEnv)
Definition: xchain_bridge.cpp:491
ripple::sfSendMax
const SF_AMOUNT sfSendMax
std::string
STL class.
ripple::test::jtx::drops
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Definition: amount.h:241
ripple::sfDestination
const SF_ACCOUNT sfDestination
ripple::sfAmount
const SF_AMOUNT sfAmount
ripple::sfNFTokenID
const SF_UINT256 sfNFTokenID
std::vector::reserve
T reserve(T... args)
ripple::test::AccountObjects_test
Definition: AccountObjects_test.cpp:113
std::vector< std::string >
std::size
T size(T... args)
ripple::SField::fieldName
const std::string fieldName
Definition: SField.h:159
ripple::test::jtx::XChainBridgeObjects::jvb
Json::Value jvb
Definition: xchain_bridge.h:184
ripple::test::jtx::port_increment
std::unique_ptr< Config > port_increment(std::unique_ptr< Config >, int)
adjust the default configured server ports by a specified value
Definition: envconfig.cpp:128
ripple::test::jtx::Account::human
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:113
ripple::sfTicketSequence
const SF_UINT32 sfTicketSequence
ripple::tfPassive
constexpr std::uint32_t tfPassive
Definition: TxFlags.h:94
ripple::test::jtx::AMM
Convenience class to test AMM functionality.
Definition: AMM.h:62
ripple::test::jtx::XChainBridgeObjects::mcCarol
const Account mcCarol
Definition: xchain_bridge.h:158
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::SField::getJsonName
Json::StaticString const & getJsonName() const
Definition: SField.h:214
ripple::test::jtx::create_account_attestation
Json::Value create_account_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::AnyAmount const &rewardAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, jtx::Account const &dst, jtx::signer const &signer)
Definition: xchain_bridge.cpp:251
ripple::sfXChainAccountClaimCount
const SF_UINT64 sfXChainAccountClaimCount
ripple::SField::jsonName
const Json::StaticString jsonName
Definition: SField.h:163
std::sort
T sort(T... args)
ripple::test::jtx::Env::trust
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:259
algorithm
ripple::sfXChainClaimID
const SF_UINT64 sfXChainClaimID
ripple::sfSignerQuorum
const SF_UINT32 sfSignerQuorum
ripple::test::AccountObjects_test::testUnsteppedThenStepped
void testUnsteppedThenStepped()
Definition: AccountObjects_test.cpp:250
std::vector::push_back
T push_back(T... args)
ripple::test::AccountObjects_test::testErrors
void testErrors()
Definition: AccountObjects_test.cpp:117
ripple::base_uint< 160, detail::AccountIDTag >
ripple::test::jtx::XChainBridgeObjects::reward
const STAmount reward
Definition: xchain_bridge.h:194
ripple::sfTakerPays
const SF_AMOUNT sfTakerPays
std::chrono::time_point::time_since_epoch
T time_since_epoch(T... args)
ripple::sfSettleDelay
const SF_UINT32 sfSettleDelay
ripple::sfSignatureReward
const SF_AMOUNT sfSignatureReward
ripple::test::jtx::signers
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition: multisign.cpp:35
ripple::JsonOptions::none
@ none
ripple::test::AccountObjects_test::testUnsteppedThenSteppedWithNFTs
void testUnsteppedThenSteppedWithNFTs()
Definition: AccountObjects_test.cpp:340
ripple::test::jtx::txflags
Set the flags on a JTx.
Definition: txflags.h:30
ripple::test::jtx::Env::close
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::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
ripple::sfTakerGets
const SF_AMOUNT sfTakerGets
ripple::test::jtx::supported_amendments
FeatureBitset supported_amendments()
Definition: Env.h:71
ripple::sfAuthorize
const SF_ACCOUNT sfAuthorize
std::uint32_t
ripple::sfHighLimit
const SF_AMOUNT sfHighLimit
ripple::test::jtx::XChainBridgeObjects::scAttester
const Account scAttester
Definition: xchain_bridge.h:165
ripple::test::jtx::Account::master
static const Account master
The master account.
Definition: Account.h:47
ripple::featureXChainBridge
const uint256 featureXChainBridge
ripple::sfSignerEntry
const SField sfSignerEntry
ripple::sfNFToken
const SField sfNFToken
Json::Value::isArray
bool isArray() const
Definition: json_value.cpp:1015
ripple::sfNFTokens
const SField sfNFTokens
ripple::sfSignerEntries
const SField sfSignerEntries
ripple::sfURI
const SF_VL sfURI
ripple::sfMinAccountCreateAmount
const SF_AMOUNT sfMinAccountCreateAmount
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::jtx::xchain_create_claim_id
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
Definition: xchain_bridge.cpp:115
ripple::sfLedgerEntryType
const SF_UINT16 sfLedgerEntryType
ripple::test::jtx::XChainBridgeObjects::scuAlice
const Account scuAlice
Definition: xchain_bridge.h:175
ripple::test::jtx::Env::now
NetClock::time_point now()
Returns the current network time.
Definition: Env.h:265
std::vector::begin
T begin(T... args)
ripple::test::jtx::XChainBridgeObjects
Definition: xchain_bridge.h:152
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:228
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:74
ripple::test::AccountObjects_test::run
void run() override
Definition: AccountObjects_test.cpp:1036
ripple::test::jtx::XChainBridgeObjects::scBob
const Account scBob
Definition: xchain_bridge.h:162
ripple::test::jtx::XChainBridgeObjects::payees
const std::vector< Account > payees
Definition: xchain_bridge.h:191
ripple::sfXChainAccountCreateCount
const SF_UINT64 sfXChainAccountCreateCount
ripple::test::jtx::XChainBridgeObjects::scAlice
const Account scAlice
Definition: xchain_bridge.h:161
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::FeatureBitset
Definition: Feature.h:113
std::optional
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::sfFinishAfter
const SF_UINT32 sfFinishAfter
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
std::vector::end
T end(T... args)
ripple::test::jtx::XChainBridgeObjects::mcBob
const Account mcBob
Definition: xchain_bridge.h:157
ripple::tfUniversal
constexpr std::uint32_t tfUniversal
Definition: TxFlags.h:59
ripple::test::jtx::XChainBridgeObjects::mcAlice
const Account mcAlice
Definition: xchain_bridge.h:156
ripple::test::bobs_account_objects
static char const * bobs_account_objects[]
Definition: AccountObjects_test.cpp:35
ripple::sfXChainBridge
const SF_XCHAIN_BRIDGE sfXChainBridge
ripple::sfPublicKey
const SF_VL sfPublicKey
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:117
ripple::test::jtx::getAccountLines
Json::Value getAccountLines(Env &env, AccountID const &acctId)
Definition: TestHelpers.cpp:40
ripple::test::jtx::Env::rpc
Json::Value rpc(std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:700
ripple::test::jtx::XChainBridgeObjects::signers
const std::vector< signer > signers
Definition: xchain_bridge.h:188
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::test::jtx::owner_count
Definition: owners.h:49
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)