rippled
AccountInfo_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/protocol/Feature.h>
21 #include <ripple/protocol/jss.h>
22 #include <test/jtx.h>
23 
24 #include <ripple/resource/Charge.h>
25 #include <ripple/resource/Fees.h>
26 #include <ripple/rpc/GRPCHandlers.h>
27 #include <test/jtx/WSClient.h>
28 #include <test/rpc/GRPCTestClientBase.h>
29 
30 namespace ripple {
31 namespace test {
32 
33 class AccountInfo_test : public beast::unit_test::suite
34 {
35 public:
36  void
38  {
39  using namespace jtx;
40  Env env(*this);
41  {
42  // account_info with no account.
43  auto const info = env.rpc("json", "account_info", "{ }");
44  BEAST_EXPECT(
45  info[jss::result][jss::error_message] ==
46  "Missing field 'account'.");
47  }
48  {
49  // account_info with a malformed account sting.
50  auto const info = env.rpc(
51  "json",
52  "account_info",
53  "{\"account\": "
54  "\"n94JNrQYkDrpt62bbSR7nVEhdyAvcJXRAsjEkFYyqRkh9SUTYEqV\"}");
55  BEAST_EXPECT(
56  info[jss::result][jss::error_message] == "Disallowed seed.");
57  }
58  {
59  // account_info with an account that's not in the ledger.
60  Account const bogie{"bogie"};
61  auto const info = env.rpc(
62  "json",
63  "account_info",
64  std::string("{ ") + "\"account\": \"" + bogie.human() + "\"}");
65  BEAST_EXPECT(
66  info[jss::result][jss::error_message] == "Account not found.");
67  }
68  }
69 
70  // Test the "signer_lists" argument in account_info.
71  void
73  {
74  using namespace jtx;
75  Env env(*this);
76  Account const alice{"alice"};
77  env.fund(XRP(1000), alice);
78 
79  auto const withoutSigners =
80  std::string("{ ") + "\"account\": \"" + alice.human() + "\"}";
81 
82  auto const withSigners = std::string("{ ") + "\"account\": \"" +
83  alice.human() + "\", " + "\"signer_lists\": true }";
84 
85  // Alice has no SignerList yet.
86  {
87  // account_info without the "signer_lists" argument.
88  auto const info = env.rpc("json", "account_info", withoutSigners);
89  BEAST_EXPECT(
90  info.isMember(jss::result) &&
91  info[jss::result].isMember(jss::account_data));
92  BEAST_EXPECT(!info[jss::result][jss::account_data].isMember(
93  jss::signer_lists));
94  }
95  {
96  // account_info with the "signer_lists" argument.
97  auto const info = env.rpc("json", "account_info", withSigners);
98  BEAST_EXPECT(
99  info.isMember(jss::result) &&
100  info[jss::result].isMember(jss::account_data));
101  auto const& data = info[jss::result][jss::account_data];
102  BEAST_EXPECT(data.isMember(jss::signer_lists));
103  auto const& signerLists = data[jss::signer_lists];
104  BEAST_EXPECT(signerLists.isArray());
105  BEAST_EXPECT(signerLists.size() == 0);
106  }
107 
108  // Give alice a SignerList.
109  Account const bogie{"bogie"};
110 
111  Json::Value const smallSigners = signers(alice, 2, {{bogie, 3}});
112  env(smallSigners);
113  {
114  // account_info without the "signer_lists" argument.
115  auto const info = env.rpc("json", "account_info", withoutSigners);
116  BEAST_EXPECT(
117  info.isMember(jss::result) &&
118  info[jss::result].isMember(jss::account_data));
119  BEAST_EXPECT(!info[jss::result][jss::account_data].isMember(
120  jss::signer_lists));
121  }
122  {
123  // account_info with the "signer_lists" argument.
124  auto const info = env.rpc("json", "account_info", withSigners);
125  BEAST_EXPECT(
126  info.isMember(jss::result) &&
127  info[jss::result].isMember(jss::account_data));
128  auto const& data = info[jss::result][jss::account_data];
129  BEAST_EXPECT(data.isMember(jss::signer_lists));
130  auto const& signerLists = data[jss::signer_lists];
131  BEAST_EXPECT(signerLists.isArray());
132  BEAST_EXPECT(signerLists.size() == 1);
133  auto const& signers = signerLists[0u];
134  BEAST_EXPECT(signers.isObject());
135  BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 2);
136  auto const& signerEntries = signers[sfSignerEntries.jsonName];
137  BEAST_EXPECT(signerEntries.size() == 1);
138  auto const& entry0 = signerEntries[0u][sfSignerEntry.jsonName];
139  BEAST_EXPECT(entry0[sfSignerWeight.jsonName] == 3);
140  }
141 
142  // Give alice a big signer list
143  Account const demon{"demon"};
144  Account const ghost{"ghost"};
145  Account const haunt{"haunt"};
146  Account const jinni{"jinni"};
147  Account const phase{"phase"};
148  Account const shade{"shade"};
149  Account const spook{"spook"};
150 
151  Json::Value const bigSigners = signers(
152  alice,
153  4,
154  {
155  {bogie, 1},
156  {demon, 1},
157  {ghost, 1},
158  {haunt, 1},
159  {jinni, 1},
160  {phase, 1},
161  {shade, 1},
162  {spook, 1},
163  });
164  env(bigSigners);
165  {
166  // account_info with the "signer_lists" argument.
167  auto const info = env.rpc("json", "account_info", withSigners);
168  BEAST_EXPECT(
169  info.isMember(jss::result) &&
170  info[jss::result].isMember(jss::account_data));
171  auto const& data = info[jss::result][jss::account_data];
172  BEAST_EXPECT(data.isMember(jss::signer_lists));
173  auto const& signerLists = data[jss::signer_lists];
174  BEAST_EXPECT(signerLists.isArray());
175  BEAST_EXPECT(signerLists.size() == 1);
176  auto const& signers = signerLists[0u];
177  BEAST_EXPECT(signers.isObject());
178  BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 4);
179  auto const& signerEntries = signers[sfSignerEntries.jsonName];
180  BEAST_EXPECT(signerEntries.size() == 8);
181  for (unsigned i = 0u; i < 8; ++i)
182  {
183  auto const& entry = signerEntries[i][sfSignerEntry.jsonName];
184  BEAST_EXPECT(entry.size() == 2);
185  BEAST_EXPECT(entry.isMember(sfAccount.jsonName));
186  BEAST_EXPECT(entry[sfSignerWeight.jsonName] == 1);
187  }
188  }
189  }
190 
191  // Test the "signer_lists" argument in account_info, with api_version 2.
192  void
194  {
195  using namespace jtx;
196  Env env{*this, envconfig([](std::unique_ptr<Config> c) {
197  c->loadFromString("\n[beta_rpc_api]\n1\n");
198  return c;
199  })};
200  Account const alice{"alice"};
201  env.fund(XRP(1000), alice);
202 
203  auto const withoutSigners = std::string("{ ") +
204  "\"api_version\": 2, \"account\": \"" + alice.human() + "\"}";
205 
206  auto const withSigners = std::string("{ ") +
207  "\"api_version\": 2, \"account\": \"" + alice.human() + "\", " +
208  "\"signer_lists\": true }";
209 
210  // Alice has no SignerList yet.
211  {
212  // account_info without the "signer_lists" argument.
213  auto const info = env.rpc("json", "account_info", withoutSigners);
214  BEAST_EXPECT(info.isMember(jss::result));
215  BEAST_EXPECT(!info[jss::result].isMember(jss::signer_lists));
216  }
217  {
218  // account_info with the "signer_lists" argument.
219  auto const info = env.rpc("json", "account_info", withSigners);
220  BEAST_EXPECT(info.isMember(jss::result));
221  auto const& data = info[jss::result];
222  BEAST_EXPECT(data.isMember(jss::signer_lists));
223  auto const& signerLists = data[jss::signer_lists];
224  BEAST_EXPECT(signerLists.isArray());
225  BEAST_EXPECT(signerLists.size() == 0);
226  }
227 
228  // Give alice a SignerList.
229  Account const bogie{"bogie"};
230 
231  Json::Value const smallSigners = signers(alice, 2, {{bogie, 3}});
232  env(smallSigners);
233  {
234  // account_info without the "signer_lists" argument.
235  auto const info = env.rpc("json", "account_info", withoutSigners);
236  BEAST_EXPECT(info.isMember(jss::result));
237  BEAST_EXPECT(!info[jss::result].isMember(jss::signer_lists));
238  }
239  {
240  // account_info with the "signer_lists" argument.
241  auto const info = env.rpc("json", "account_info", withSigners);
242  BEAST_EXPECT(info.isMember(jss::result));
243  auto const& data = info[jss::result];
244  BEAST_EXPECT(data.isMember(jss::signer_lists));
245  auto const& signerLists = data[jss::signer_lists];
246  BEAST_EXPECT(signerLists.isArray());
247  BEAST_EXPECT(signerLists.size() == 1);
248  auto const& signers = signerLists[0u];
249  BEAST_EXPECT(signers.isObject());
250  BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 2);
251  auto const& signerEntries = signers[sfSignerEntries.jsonName];
252  BEAST_EXPECT(signerEntries.size() == 1);
253  auto const& entry0 = signerEntries[0u][sfSignerEntry.jsonName];
254  BEAST_EXPECT(entry0[sfSignerWeight.jsonName] == 3);
255  }
256 
257  // Give alice a big signer list
258  Account const demon{"demon"};
259  Account const ghost{"ghost"};
260  Account const haunt{"haunt"};
261  Account const jinni{"jinni"};
262  Account const phase{"phase"};
263  Account const shade{"shade"};
264  Account const spook{"spook"};
265 
266  Json::Value const bigSigners = signers(
267  alice,
268  4,
269  {
270  {bogie, 1},
271  {demon, 1},
272  {ghost, 1},
273  {haunt, 1},
274  {jinni, 1},
275  {phase, 1},
276  {shade, 1},
277  {spook, 1},
278  });
279  env(bigSigners);
280  {
281  // account_info with the "signer_lists" argument.
282  auto const info = env.rpc("json", "account_info", withSigners);
283  BEAST_EXPECT(info.isMember(jss::result));
284  auto const& data = info[jss::result];
285  BEAST_EXPECT(data.isMember(jss::signer_lists));
286  auto const& signerLists = data[jss::signer_lists];
287  BEAST_EXPECT(signerLists.isArray());
288  BEAST_EXPECT(signerLists.size() == 1);
289  auto const& signers = signerLists[0u];
290  BEAST_EXPECT(signers.isObject());
291  BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 4);
292  auto const& signerEntries = signers[sfSignerEntries.jsonName];
293  BEAST_EXPECT(signerEntries.size() == 8);
294  for (unsigned i = 0u; i < 8; ++i)
295  {
296  auto const& entry = signerEntries[i][sfSignerEntry.jsonName];
297  BEAST_EXPECT(entry.size() == 2);
298  BEAST_EXPECT(entry.isMember(sfAccount.jsonName));
299  BEAST_EXPECT(entry[sfSignerWeight.jsonName] == 1);
300  }
301  }
302  }
303 
304  // Test the "signer_lists" argument in account_info, version 2 API.
305  void
307  {
308  using namespace jtx;
309  Env env(*this);
310  Account const alice{"alice"};
311  env.fund(XRP(1000), alice);
312 
313  auto const withoutSigners = std::string("{ ") +
314  "\"jsonrpc\": \"2.0\", "
315  "\"ripplerpc\": \"2.0\", "
316  "\"id\": 5, "
317  "\"method\": \"account_info\", "
318  "\"params\": { "
319  "\"account\": \"" +
320  alice.human() + "\"}}";
321 
322  auto const withSigners = std::string("{ ") +
323  "\"jsonrpc\": \"2.0\", "
324  "\"ripplerpc\": \"2.0\", "
325  "\"id\": 6, "
326  "\"method\": \"account_info\", "
327  "\"params\": { "
328  "\"account\": \"" +
329  alice.human() + "\", " + "\"signer_lists\": true }}";
330  // Alice has no SignerList yet.
331  {
332  // account_info without the "signer_lists" argument.
333  auto const info = env.rpc("json2", withoutSigners);
334  BEAST_EXPECT(
335  info.isMember(jss::result) &&
336  info[jss::result].isMember(jss::account_data));
337  BEAST_EXPECT(!info[jss::result][jss::account_data].isMember(
338  jss::signer_lists));
339  BEAST_EXPECT(
340  info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0");
341  BEAST_EXPECT(
342  info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0");
343  BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 5);
344  }
345  {
346  // account_info with the "signer_lists" argument.
347  auto const info = env.rpc("json2", withSigners);
348  BEAST_EXPECT(
349  info.isMember(jss::result) &&
350  info[jss::result].isMember(jss::account_data));
351  auto const& data = info[jss::result][jss::account_data];
352  BEAST_EXPECT(data.isMember(jss::signer_lists));
353  auto const& signerLists = data[jss::signer_lists];
354  BEAST_EXPECT(signerLists.isArray());
355  BEAST_EXPECT(signerLists.size() == 0);
356  BEAST_EXPECT(
357  info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0");
358  BEAST_EXPECT(
359  info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0");
360  BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 6);
361  }
362  {
363  // Do both of the above as a batch job
364  auto const info = env.rpc(
365  "json2", '[' + withoutSigners + ", " + withSigners + ']');
366  BEAST_EXPECT(
367  info[0u].isMember(jss::result) &&
368  info[0u][jss::result].isMember(jss::account_data));
369  BEAST_EXPECT(!info[0u][jss::result][jss::account_data].isMember(
370  jss::signer_lists));
371  BEAST_EXPECT(
372  info[0u].isMember(jss::jsonrpc) &&
373  info[0u][jss::jsonrpc] == "2.0");
374  BEAST_EXPECT(
375  info[0u].isMember(jss::ripplerpc) &&
376  info[0u][jss::ripplerpc] == "2.0");
377  BEAST_EXPECT(info[0u].isMember(jss::id) && info[0u][jss::id] == 5);
378 
379  BEAST_EXPECT(
380  info[1u].isMember(jss::result) &&
381  info[1u][jss::result].isMember(jss::account_data));
382  auto const& data = info[1u][jss::result][jss::account_data];
383  BEAST_EXPECT(data.isMember(jss::signer_lists));
384  auto const& signerLists = data[jss::signer_lists];
385  BEAST_EXPECT(signerLists.isArray());
386  BEAST_EXPECT(signerLists.size() == 0);
387  BEAST_EXPECT(
388  info[1u].isMember(jss::jsonrpc) &&
389  info[1u][jss::jsonrpc] == "2.0");
390  BEAST_EXPECT(
391  info[1u].isMember(jss::ripplerpc) &&
392  info[1u][jss::ripplerpc] == "2.0");
393  BEAST_EXPECT(info[1u].isMember(jss::id) && info[1u][jss::id] == 6);
394  }
395 
396  // Give alice a SignerList.
397  Account const bogie{"bogie"};
398 
399  Json::Value const smallSigners = signers(alice, 2, {{bogie, 3}});
400  env(smallSigners);
401  {
402  // account_info without the "signer_lists" argument.
403  auto const info = env.rpc("json2", withoutSigners);
404  BEAST_EXPECT(
405  info.isMember(jss::result) &&
406  info[jss::result].isMember(jss::account_data));
407  BEAST_EXPECT(!info[jss::result][jss::account_data].isMember(
408  jss::signer_lists));
409  BEAST_EXPECT(
410  info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0");
411  BEAST_EXPECT(
412  info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0");
413  BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 5);
414  }
415  {
416  // account_info with the "signer_lists" argument.
417  auto const info = env.rpc("json2", withSigners);
418  BEAST_EXPECT(
419  info.isMember(jss::result) &&
420  info[jss::result].isMember(jss::account_data));
421  auto const& data = info[jss::result][jss::account_data];
422  BEAST_EXPECT(data.isMember(jss::signer_lists));
423  auto const& signerLists = data[jss::signer_lists];
424  BEAST_EXPECT(signerLists.isArray());
425  BEAST_EXPECT(signerLists.size() == 1);
426  auto const& signers = signerLists[0u];
427  BEAST_EXPECT(signers.isObject());
428  BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 2);
429  auto const& signerEntries = signers[sfSignerEntries.jsonName];
430  BEAST_EXPECT(signerEntries.size() == 1);
431  auto const& entry0 = signerEntries[0u][sfSignerEntry.jsonName];
432  BEAST_EXPECT(entry0[sfSignerWeight.jsonName] == 3);
433  BEAST_EXPECT(
434  info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0");
435  BEAST_EXPECT(
436  info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0");
437  BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 6);
438  }
439 
440  // Give alice a big signer list
441  Account const demon{"demon"};
442  Account const ghost{"ghost"};
443  Account const haunt{"haunt"};
444  Account const jinni{"jinni"};
445  Account const phase{"phase"};
446  Account const shade{"shade"};
447  Account const spook{"spook"};
448 
449  Json::Value const bigSigners = signers(
450  alice,
451  4,
452  {
453  {bogie, 1},
454  {demon, 1},
455  {ghost, 1},
456  {haunt, 1},
457  {jinni, 1},
458  {phase, 1},
459  {shade, 1},
460  {spook, 1},
461  });
462  env(bigSigners);
463  {
464  // account_info with the "signer_lists" argument.
465  auto const info = env.rpc("json2", withSigners);
466  BEAST_EXPECT(
467  info.isMember(jss::result) &&
468  info[jss::result].isMember(jss::account_data));
469  auto const& data = info[jss::result][jss::account_data];
470  BEAST_EXPECT(data.isMember(jss::signer_lists));
471  auto const& signerLists = data[jss::signer_lists];
472  BEAST_EXPECT(signerLists.isArray());
473  BEAST_EXPECT(signerLists.size() == 1);
474  auto const& signers = signerLists[0u];
475  BEAST_EXPECT(signers.isObject());
476  BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 4);
477  auto const& signerEntries = signers[sfSignerEntries.jsonName];
478  BEAST_EXPECT(signerEntries.size() == 8);
479  for (unsigned i = 0u; i < 8; ++i)
480  {
481  auto const& entry = signerEntries[i][sfSignerEntry.jsonName];
482  BEAST_EXPECT(entry.size() == 2);
483  BEAST_EXPECT(entry.isMember(sfAccount.jsonName));
484  BEAST_EXPECT(entry[sfSignerWeight.jsonName] == 1);
485  }
486  BEAST_EXPECT(
487  info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0");
488  BEAST_EXPECT(
489  info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0");
490  BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 6);
491  }
492  }
493 
494  void
495  run() override
496  {
497  testErrors();
498  testSignerLists();
501  }
502 };
503 
504 BEAST_DEFINE_TESTSUITE(AccountInfo, app, ripple);
505 
506 } // namespace test
507 } // namespace ripple
ripple::sfSignerWeight
const SF_UINT16 sfSignerWeight
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
std::string
STL class.
ripple::test::AccountInfo_test::run
void run() override
Definition: AccountInfo_test.cpp:495
ripple::test::AccountInfo_test::testSignerListsV2
void testSignerListsV2()
Definition: AccountInfo_test.cpp:306
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::SField::jsonName
const Json::StaticString jsonName
Definition: SField.h:136
ripple::sfSignerQuorum
const SF_UINT32 sfSignerQuorum
ripple::test::AccountInfo_test
Definition: AccountInfo_test.cpp:33
ripple::test::AccountInfo_test::testSignerLists
void testSignerLists()
Definition: AccountInfo_test.cpp:72
ripple::sfSignerEntry
const SField sfSignerEntry
ripple::sfSignerEntries
const SField sfSignerEntries
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:225
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::sfAccount
const SF_ACCOUNT sfAccount
std::unique_ptr
STL class.
ripple::test::AccountInfo_test::testErrors
void testErrors()
Definition: AccountInfo_test.cpp:37
ripple::test::AccountInfo_test::testSignerListsApiVersion2
void testSignerListsApiVersion2()
Definition: AccountInfo_test.cpp:193
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
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:684
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)