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 
603  // gw mints an NFT so we can find it.
604  uint256 const nftID{token::getNextID(env, gw, 0u, tfTransferable)};
605  env(token::mint(gw, 0u), txflags(tfTransferable));
606  env.close();
607  {
608  // Find the NFToken page and make sure it's the right one.
609  Json::Value const resp = acct_objs(gw, jss::nft_page);
610  BEAST_EXPECT(acct_objs_is_size(resp, 1));
611 
612  auto const& nftPage = resp[jss::result][jss::account_objects][0u];
613  BEAST_EXPECT(nftPage[sfNFTokens.jsonName].size() == 1);
614  BEAST_EXPECT(
615  nftPage[sfNFTokens.jsonName][0u][sfNFToken.jsonName]
616  [sfNFTokenID.jsonName] == to_string(nftID));
617  }
618 
619  // Set up a trust line so we can find it.
620  env.trust(USD(1000), alice);
621  env.close();
622  env(pay(gw, alice, USD(5)));
623  env.close();
624  {
625  // Find the trustline and make sure it's the right one.
626  Json::Value const resp = acct_objs(gw, jss::state);
627  BEAST_EXPECT(acct_objs_is_size(resp, 1));
628 
629  auto const& state = resp[jss::result][jss::account_objects][0u];
630  BEAST_EXPECT(state[sfBalance.jsonName][jss::value].asInt() == -5);
631  BEAST_EXPECT(
632  state[sfHighLimit.jsonName][jss::value].asUInt() == 1000);
633  }
634  // gw writes a check for USD(10) to alice.
635  env(check::create(gw, alice, USD(10)));
636  env.close();
637  {
638  // Find the check.
639  Json::Value const resp = acct_objs(gw, jss::check);
640  BEAST_EXPECT(acct_objs_is_size(resp, 1));
641 
642  auto const& check = resp[jss::result][jss::account_objects][0u];
643  BEAST_EXPECT(check[sfAccount.jsonName] == gw.human());
644  BEAST_EXPECT(check[sfDestination.jsonName] == alice.human());
645  BEAST_EXPECT(check[sfSendMax.jsonName][jss::value].asUInt() == 10);
646  }
647  // gw preauthorizes payments from alice.
648  env(deposit::auth(gw, alice));
649  env.close();
650  {
651  // Find the preauthorization.
652  Json::Value const resp = acct_objs(gw, jss::deposit_preauth);
653  BEAST_EXPECT(acct_objs_is_size(resp, 1));
654 
655  auto const& preauth = resp[jss::result][jss::account_objects][0u];
656  BEAST_EXPECT(preauth[sfAccount.jsonName] == gw.human());
657  BEAST_EXPECT(preauth[sfAuthorize.jsonName] == alice.human());
658  }
659  {
660  // gw creates an escrow that we can look for in the ledger.
661  Json::Value jvEscrow;
662  jvEscrow[jss::TransactionType] = jss::EscrowCreate;
663  jvEscrow[jss::Flags] = tfUniversal;
664  jvEscrow[jss::Account] = gw.human();
665  jvEscrow[jss::Destination] = gw.human();
666  jvEscrow[jss::Amount] = XRP(100).value().getJson(JsonOptions::none);
667  jvEscrow[sfFinishAfter.jsonName] =
668  env.now().time_since_epoch().count() + 1;
669  env(jvEscrow);
670  env.close();
671  }
672  {
673  // Find the escrow.
674  Json::Value const resp = acct_objs(gw, jss::escrow);
675  BEAST_EXPECT(acct_objs_is_size(resp, 1));
676 
677  auto const& escrow = resp[jss::result][jss::account_objects][0u];
678  BEAST_EXPECT(escrow[sfAccount.jsonName] == gw.human());
679  BEAST_EXPECT(escrow[sfDestination.jsonName] == gw.human());
680  BEAST_EXPECT(escrow[sfAmount.jsonName].asUInt() == 100'000'000);
681  }
682  {
683  // Create a bridge
685  Env scEnv(*this, envconfig(port_increment, 3), features);
686  x.createScBridgeObjects(scEnv);
687 
688  auto scenv_acct_objs = [&](Account const& acct, char const* type) {
689  Json::Value params;
690  params[jss::account] = acct.human();
691  params[jss::type] = type;
692  params[jss::ledger_index] = "validated";
693  return scEnv.rpc("json", "account_objects", to_string(params));
694  };
695 
696  Json::Value const resp =
697  scenv_acct_objs(Account::master, jss::bridge);
698 
699  BEAST_EXPECT(acct_objs_is_size(resp, 1));
700  auto const& acct_bridge =
701  resp[jss::result][jss::account_objects][0u];
702  BEAST_EXPECT(
703  acct_bridge[sfAccount.jsonName] == Account::master.human());
704  BEAST_EXPECT(
705  acct_bridge[sfLedgerEntryType.getJsonName()] == "Bridge");
706  BEAST_EXPECT(
707  acct_bridge[sfXChainClaimID.getJsonName()].asUInt() == 0);
708  BEAST_EXPECT(
709  acct_bridge[sfXChainAccountClaimCount.getJsonName()].asUInt() ==
710  0);
711  BEAST_EXPECT(
713  .asUInt() == 0);
714  BEAST_EXPECT(
715  acct_bridge[sfMinAccountCreateAmount.getJsonName()].asUInt() ==
716  20000000);
717  BEAST_EXPECT(
718  acct_bridge[sfSignatureReward.getJsonName()].asUInt() ==
719  1000000);
720  BEAST_EXPECT(acct_bridge[sfXChainBridge.getJsonName()] == x.jvb);
721  }
722  {
723  // Alice and Bob create a xchain sequence number that we can look
724  // for in the ledger.
726  Env scEnv(*this, envconfig(port_increment, 3), features);
727  x.createScBridgeObjects(scEnv);
728 
729  scEnv(
731  scEnv.close();
732  scEnv(xchain_create_claim_id(x.scBob, x.jvb, x.reward, x.mcBob));
733  scEnv.close();
734 
735  auto scenv_acct_objs = [&](Account const& acct, char const* type) {
736  Json::Value params;
737  params[jss::account] = acct.human();
738  params[jss::type] = type;
739  params[jss::ledger_index] = "validated";
740  return scEnv.rpc("json", "account_objects", to_string(params));
741  };
742 
743  {
744  // Find the xchain sequence number for Andrea.
745  Json::Value const resp =
746  scenv_acct_objs(x.scAlice, jss::xchain_owned_claim_id);
747  BEAST_EXPECT(acct_objs_is_size(resp, 1));
748 
749  auto const& xchain_seq =
750  resp[jss::result][jss::account_objects][0u];
751  BEAST_EXPECT(
752  xchain_seq[sfAccount.jsonName] == x.scAlice.human());
753  BEAST_EXPECT(
754  xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 1);
755  }
756  {
757  // and the one for Bob
758  Json::Value const resp =
759  scenv_acct_objs(x.scBob, jss::xchain_owned_claim_id);
760  BEAST_EXPECT(acct_objs_is_size(resp, 1));
761 
762  auto const& xchain_seq =
763  resp[jss::result][jss::account_objects][0u];
764  BEAST_EXPECT(xchain_seq[sfAccount.jsonName] == x.scBob.human());
765  BEAST_EXPECT(
766  xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 2);
767  }
768  }
769  {
771  Env scEnv(*this, envconfig(port_increment, 3), features);
772  x.createScBridgeObjects(scEnv);
773  auto const amt = XRP(1000);
774 
775  // send first batch of account create attestations, so the
776  // xchain_create_account_claim_id should be present on the door
777  // account (Account::master) to collect the signatures until a
778  // quorum is reached
780  x.scAttester,
781  x.jvb,
782  x.mcCarol,
783  amt,
784  x.reward,
785  x.payees[0],
786  true,
787  1,
788  x.scuAlice,
789  x.signers[0]));
790  scEnv.close();
791 
792  auto scenv_acct_objs = [&](Account const& acct, char const* type) {
793  Json::Value params;
794  params[jss::account] = acct.human();
795  params[jss::type] = type;
796  params[jss::ledger_index] = "validated";
797  return scEnv.rpc("json", "account_objects", to_string(params));
798  };
799 
800  {
801  // Find the xchain_create_account_claim_id
802  Json::Value const resp = scenv_acct_objs(
803  Account::master, jss::xchain_owned_create_account_claim_id);
804  BEAST_EXPECT(acct_objs_is_size(resp, 1));
805 
806  auto const& xchain_create_account_claim_id =
807  resp[jss::result][jss::account_objects][0u];
808  BEAST_EXPECT(
809  xchain_create_account_claim_id[sfAccount.jsonName] ==
811  BEAST_EXPECT(
812  xchain_create_account_claim_id[sfXChainAccountCreateCount
813  .getJsonName()]
814  .asUInt() == 1);
815  }
816  }
817 
818  // gw creates an offer that we can look for in the ledger.
819  env(offer(gw, USD(7), XRP(14)));
820  env.close();
821  {
822  // Find the offer.
823  Json::Value const resp = acct_objs(gw, jss::offer);
824  BEAST_EXPECT(acct_objs_is_size(resp, 1));
825 
826  auto const& offer = resp[jss::result][jss::account_objects][0u];
827  BEAST_EXPECT(offer[sfAccount.jsonName] == gw.human());
828  BEAST_EXPECT(offer[sfTakerGets.jsonName].asUInt() == 14'000'000);
829  BEAST_EXPECT(offer[sfTakerPays.jsonName][jss::value].asUInt() == 7);
830  }
831  {
832  // Create a payment channel from qw to alice that we can look
833  // for.
834  Json::Value jvPayChan;
835  jvPayChan[jss::TransactionType] = jss::PaymentChannelCreate;
836  jvPayChan[jss::Flags] = tfUniversal;
837  jvPayChan[jss::Account] = gw.human();
838  jvPayChan[jss::Destination] = alice.human();
839  jvPayChan[jss::Amount] =
840  XRP(300).value().getJson(JsonOptions::none);
841  jvPayChan[sfSettleDelay.jsonName] = 24 * 60 * 60;
842  jvPayChan[sfPublicKey.jsonName] = strHex(gw.pk().slice());
843  env(jvPayChan);
844  env.close();
845  }
846  {
847  // Find the payment channel.
848  Json::Value const resp = acct_objs(gw, jss::payment_channel);
849  BEAST_EXPECT(acct_objs_is_size(resp, 1));
850 
851  auto const& payChan = resp[jss::result][jss::account_objects][0u];
852  BEAST_EXPECT(payChan[sfAccount.jsonName] == gw.human());
853  BEAST_EXPECT(payChan[sfAmount.jsonName].asUInt() == 300'000'000);
854  BEAST_EXPECT(
855  payChan[sfSettleDelay.jsonName].asUInt() == 24 * 60 * 60);
856  }
857  // Make gw multisigning by adding a signerList.
858  env(jtx::signers(gw, 6, {{alice, 7}}));
859  env.close();
860  {
861  // Find the signer list.
862  Json::Value const resp = acct_objs(gw, jss::signer_list);
863  BEAST_EXPECT(acct_objs_is_size(resp, 1));
864 
865  auto const& signerList =
866  resp[jss::result][jss::account_objects][0u];
867  BEAST_EXPECT(signerList[sfSignerQuorum.jsonName] == 6);
868  auto const& entry = signerList[sfSignerEntries.jsonName][0u]
870  BEAST_EXPECT(entry[sfAccount.jsonName] == alice.human());
871  BEAST_EXPECT(entry[sfSignerWeight.jsonName].asUInt() == 7);
872  }
873  // Create a Ticket for gw.
874  env(ticket::create(gw, 1));
875  env.close();
876  {
877  // Find the ticket.
878  Json::Value const resp = acct_objs(gw, jss::ticket);
879  BEAST_EXPECT(acct_objs_is_size(resp, 1));
880 
881  auto const& ticket = resp[jss::result][jss::account_objects][0u];
882  BEAST_EXPECT(ticket[sfAccount.jsonName] == gw.human());
883  BEAST_EXPECT(ticket[sfLedgerEntryType.jsonName] == jss::Ticket);
884  BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == 13);
885  }
886  {
887  // See how "deletion_blockers_only" handles gw's directory.
888  Json::Value params;
889  params[jss::account] = gw.human();
890  params[jss::deletion_blockers_only] = true;
891  auto resp = env.rpc("json", "account_objects", to_string(params));
892 
893  std::vector<std::string> const expectedLedgerTypes = [] {
895  jss::Escrow.c_str(),
896  jss::Check.c_str(),
897  jss::NFTokenPage.c_str(),
898  jss::RippleState.c_str(),
899  jss::PayChannel.c_str()};
900  std::sort(v.begin(), v.end());
901  return v;
902  }();
903 
904  std::uint32_t const expectedAccountObjects{
905  static_cast<std::uint32_t>(std::size(expectedLedgerTypes))};
906 
907  if (BEAST_EXPECT(acct_objs_is_size(resp, expectedAccountObjects)))
908  {
909  auto const& aobjs = resp[jss::result][jss::account_objects];
910  std::vector<std::string> gotLedgerTypes;
911  gotLedgerTypes.reserve(expectedAccountObjects);
912  for (std::uint32_t i = 0; i < expectedAccountObjects; ++i)
913  {
914  gotLedgerTypes.push_back(
915  aobjs[i]["LedgerEntryType"].asString());
916  }
917  std::sort(gotLedgerTypes.begin(), gotLedgerTypes.end());
918  BEAST_EXPECT(gotLedgerTypes == expectedLedgerTypes);
919  }
920  }
921  {
922  // See how "deletion_blockers_only" with `type` handles gw's
923  // directory.
924  Json::Value params;
925  params[jss::account] = gw.human();
926  params[jss::deletion_blockers_only] = true;
927  params[jss::type] = jss::escrow;
928  auto resp = env.rpc("json", "account_objects", to_string(params));
929 
930  if (BEAST_EXPECT(acct_objs_is_size(resp, 1u)))
931  {
932  auto const& aobjs = resp[jss::result][jss::account_objects];
933  BEAST_EXPECT(aobjs[0u]["LedgerEntryType"] == jss::Escrow);
934  }
935  }
936  {
937  // Make a lambda to get the types
938  auto getTypes = [&](Json::Value const& resp,
939  std::vector<std::string>& typesOut) {
940  auto const objs = resp[jss::result][jss::account_objects];
941  for (auto const& obj : resp[jss::result][jss::account_objects])
942  typesOut.push_back(
943  obj[sfLedgerEntryType.fieldName].asString());
944  std::sort(typesOut.begin(), typesOut.end());
945  };
946  // Make a lambda we can use to check the number of fetched
947  // account objects and their ledger type
948  auto expectObjects =
949  [&](Json::Value const& resp,
950  std::vector<std::string> const& types) -> bool {
951  if (!acct_objs_is_size(resp, types.size()))
952  return false;
953  std::vector<std::string> typesOut;
954  getTypes(resp, typesOut);
955  return types == typesOut;
956  };
957  // Find AMM objects
958  AMM amm(env, gw, XRP(1'000), USD(1'000));
959  amm.deposit(alice, USD(1));
960  // AMM account has 4 objects: AMM object and 3 trustlines
961  auto const lines = getAccountLines(env, amm.ammAccount());
962  BEAST_EXPECT(lines[jss::lines].size() == 3);
963  // request AMM only, doesn't depend on the limit
964  BEAST_EXPECT(
965  acct_objs_is_size(acct_objs(amm.ammAccount(), jss::amm), 1));
966  // request first two objects
967  auto resp = acct_objs(amm.ammAccount(), std::nullopt, 2);
968  std::vector<std::string> typesOut;
969  getTypes(resp, typesOut);
970  // request next two objects
971  resp = acct_objs(
972  amm.ammAccount(),
973  std::nullopt,
974  10,
975  resp[jss::result][jss::marker].asString());
976  getTypes(resp, typesOut);
977  BEAST_EXPECT(
978  (typesOut ==
980  jss::AMM.c_str(),
981  jss::RippleState.c_str(),
982  jss::RippleState.c_str(),
983  jss::RippleState.c_str()}));
984  // filter by state: there are three trustlines
985  resp = acct_objs(amm.ammAccount(), jss::state, 10);
986  BEAST_EXPECT(expectObjects(
987  resp,
988  {jss::RippleState.c_str(),
989  jss::RippleState.c_str(),
990  jss::RippleState.c_str()}));
991  // AMM account doesn't own offers
992  BEAST_EXPECT(
993  acct_objs_is_size(acct_objs(amm.ammAccount(), jss::offer), 0));
994  // gw account doesn't own AMM object
995  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::amm), 0));
996  }
997 
998  // Run up the number of directory entries so gw has two
999  // directory nodes.
1000  for (int d = 1'000'032; d >= 1'000'000; --d)
1001  {
1002  env(offer(gw, USD(1), drops(d)));
1003  env.close();
1004  }
1005 
1006  // Verify that the non-returning types still don't return anything.
1007  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::account), 0));
1008  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::amendments), 0));
1009  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::directory), 0));
1010  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::fee), 0));
1011  BEAST_EXPECT(acct_objs_is_size(acct_objs(gw, jss::hashes), 0));
1012  }
1013 
1014  void
1015  run() override
1016  {
1017  testErrors();
1020  testObjectTypes();
1021  }
1022 };
1023 
1024 BEAST_DEFINE_TESTSUITE(AccountObjects, app, ripple);
1025 
1026 } // namespace test
1027 } // 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:137
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:192
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:141
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::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:1015
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)