rippled
SHAMapStore_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2015 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/app/main/Application.h>
21 #include <ripple/app/misc/SHAMapStore.h>
22 #include <ripple/core/ConfigSections.h>
23 #include <ripple/core/DatabaseCon.h>
24 #include <ripple/core/SociDB.h>
25 #include <ripple/protocol/jss.h>
26 #include <test/jtx.h>
27 #include <test/jtx/envconfig.h>
28 
29 namespace ripple {
30 namespace test {
31 
32 class SHAMapStore_test : public beast::unit_test::suite
33 {
34  static auto const deleteInterval = 8;
35 
36  static auto
38  {
39  cfg->LEDGER_HISTORY = deleteInterval;
40  auto& section = cfg->section(ConfigSection::nodeDatabase());
41  section.set("online_delete", std::to_string(deleteInterval));
42  return cfg;
43  }
44 
45  static auto
47  {
48  cfg = onlineDelete(std::move(cfg));
49  cfg->section(ConfigSection::nodeDatabase()).set("advisory_delete", "1");
50  return cfg;
51  }
52 
53  bool
55  jtx::Env& env,
56  Json::Value const& json,
57  std::string ledgerID,
58  bool checkDB = false)
59  {
60  auto good = json.isMember(jss::result) &&
61  !RPC::contains_error(json[jss::result]) &&
62  json[jss::result][jss::ledger][jss::ledger_index] == ledgerID;
63  if (!good || !checkDB)
64  return good;
65 
66  auto const seq = json[jss::result][jss::ledger_index].asUInt();
67  std::string outHash;
68  LedgerIndex outSeq;
69  std::string outParentHash;
70  std::string outDrops;
71  std::uint64_t outCloseTime;
72  std::uint64_t outParentCloseTime;
73  std::uint64_t outCloseTimeResolution;
74  std::uint64_t outCloseFlags;
75  std::string outAccountHash;
76  std::string outTxHash;
77 
78  {
79  auto db = env.app().getLedgerDB().checkoutDb();
80 
81  *db << "SELECT LedgerHash,LedgerSeq,PrevHash,TotalCoins, "
82  "ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags, "
83  "AccountSetHash,TransSetHash "
84  "FROM Ledgers "
85  "WHERE LedgerSeq = :seq",
86  soci::use(seq), soci::into(outHash), soci::into(outSeq),
87  soci::into(outParentHash), soci::into(outDrops),
88  soci::into(outCloseTime), soci::into(outParentCloseTime),
89  soci::into(outCloseTimeResolution), soci::into(outCloseFlags),
90  soci::into(outAccountHash), soci::into(outTxHash);
91  }
92 
93  auto const& ledger = json[jss::result][jss::ledger];
94  return outHash == ledger[jss::hash].asString() && outSeq == seq &&
95  outParentHash == ledger[jss::parent_hash].asString() &&
96  outDrops == ledger[jss::total_coins].asString() &&
97  outCloseTime == ledger[jss::close_time].asUInt() &&
98  outParentCloseTime == ledger[jss::parent_close_time].asUInt() &&
99  outCloseTimeResolution ==
100  ledger[jss::close_time_resolution].asUInt() &&
101  outCloseFlags == ledger[jss::close_flags].asUInt() &&
102  outAccountHash == ledger[jss::account_hash].asString() &&
103  outTxHash == ledger[jss::transaction_hash].asString();
104  }
105 
106  bool
108  {
109  return json.isMember(jss::result) &&
110  RPC::contains_error(json[jss::result]) &&
111  json[jss::result][jss::error_code] == error;
112  }
113 
116  {
117  BEAST_EXPECT(
118  json.isMember(jss::result) &&
119  json[jss::result].isMember(jss::ledger) &&
120  json[jss::result][jss::ledger].isMember(jss::hash) &&
121  json[jss::result][jss::ledger][jss::hash].isString());
122  return json[jss::result][jss::ledger][jss::hash].asString();
123  }
124 
125  void
126  ledgerCheck(jtx::Env& env, int const rows, int const first)
127  {
128  auto db = env.app().getLedgerDB().checkoutDb();
129 
130  int actualRows, actualFirst, actualLast;
131  *db << "SELECT count(*) AS rows, "
132  "min(LedgerSeq) as first, "
133  "max(LedgerSeq) as last "
134  "FROM Ledgers;",
135  soci::into(actualRows), soci::into(actualFirst),
136  soci::into(actualLast);
137 
138  BEAST_EXPECT(actualRows == rows);
139  BEAST_EXPECT(actualFirst == first);
140  BEAST_EXPECT(actualLast == first + rows - 1);
141  }
142 
143  void
144  transactionCheck(jtx::Env& env, int const rows)
145  {
146  auto db = env.app().getTxnDB().checkoutDb();
147 
148  int actualRows;
149  *db << "SELECT count(*) AS rows "
150  "FROM Transactions;",
151  soci::into(actualRows);
152 
153  BEAST_EXPECT(actualRows == rows);
154  }
155 
156  void
157  accountTransactionCheck(jtx::Env& env, int const rows)
158  {
159  auto db = env.app().getTxnDB().checkoutDb();
160 
161  int actualRows;
162  *db << "SELECT count(*) AS rows "
163  "FROM AccountTransactions;",
164  soci::into(actualRows);
165 
166  BEAST_EXPECT(actualRows == rows);
167  }
168 
169  int
171  {
172  using namespace std::chrono_literals;
173 
174  auto& store = env.app().getSHAMapStore();
175 
176  int ledgerSeq = 3;
177  store.rendezvous();
178  BEAST_EXPECT(!store.getLastRotated());
179 
180  env.close();
181  store.rendezvous();
182 
183  auto ledger = env.rpc("ledger", "validated");
184  BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++)));
185 
186  BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
187  return ledgerSeq;
188  }
189 
190 public:
191  void
193  {
194  using namespace std::chrono_literals;
195 
196  testcase("clearPrior");
197  using namespace jtx;
198 
199  Env env(*this, envconfig(onlineDelete));
200 
201  auto& store = env.app().getSHAMapStore();
202  env.fund(XRP(10000), noripple("alice"));
203 
204  ledgerCheck(env, 1, 2);
205  transactionCheck(env, 0);
206  accountTransactionCheck(env, 0);
207 
209 
210  auto ledgerTmp = env.rpc("ledger", "0");
211  BEAST_EXPECT(bad(ledgerTmp));
212 
213  ledgers.emplace(std::make_pair(1, env.rpc("ledger", "1")));
214  BEAST_EXPECT(goodLedger(env, ledgers[1], "1"));
215 
216  ledgers.emplace(std::make_pair(2, env.rpc("ledger", "2")));
217  BEAST_EXPECT(goodLedger(env, ledgers[2], "2"));
218 
219  ledgerTmp = env.rpc("ledger", "current");
220  BEAST_EXPECT(goodLedger(env, ledgerTmp, "3"));
221 
222  ledgerTmp = env.rpc("ledger", "4");
223  BEAST_EXPECT(bad(ledgerTmp));
224 
225  ledgerTmp = env.rpc("ledger", "100");
226  BEAST_EXPECT(bad(ledgerTmp));
227 
228  auto const firstSeq = waitForReady(env);
229  auto lastRotated = firstSeq - 1;
230 
231  for (auto i = firstSeq + 1; i < deleteInterval + firstSeq; ++i)
232  {
233  env.fund(XRP(10000), noripple("test" + std::to_string(i)));
234  env.close();
235 
236  ledgerTmp = env.rpc("ledger", "current");
237  BEAST_EXPECT(goodLedger(env, ledgerTmp, std::to_string(i)));
238  }
239  BEAST_EXPECT(store.getLastRotated() == lastRotated);
240 
241  for (auto i = 3; i < deleteInterval + lastRotated; ++i)
242  {
243  ledgers.emplace(
244  std::make_pair(i, env.rpc("ledger", std::to_string(i))));
245  BEAST_EXPECT(
246  goodLedger(env, ledgers[i], std::to_string(i), true) &&
247  getHash(ledgers[i]).length());
248  }
249 
250  ledgerCheck(env, deleteInterval + 1, 2);
253 
254  {
255  // Closing one more ledger triggers a rotate
256  env.close();
257 
258  auto ledger = env.rpc("ledger", "current");
259  BEAST_EXPECT(
260  goodLedger(env, ledger, std::to_string(deleteInterval + 4)));
261  }
262 
263  store.rendezvous();
264 
265  BEAST_EXPECT(store.getLastRotated() == deleteInterval + 3);
266  lastRotated = store.getLastRotated();
267  BEAST_EXPECT(lastRotated == 11);
268 
269  // That took care of the fake hashes
270  ledgerCheck(env, deleteInterval + 1, 3);
273 
274  // The last iteration of this loop should trigger a rotate
275  for (auto i = lastRotated - 1; i < lastRotated + deleteInterval - 1;
276  ++i)
277  {
278  env.close();
279 
280  ledgerTmp = env.rpc("ledger", "current");
281  BEAST_EXPECT(goodLedger(env, ledgerTmp, std::to_string(i + 3)));
282 
283  ledgers.emplace(
284  std::make_pair(i, env.rpc("ledger", std::to_string(i))));
285  BEAST_EXPECT(
286  store.getLastRotated() == lastRotated ||
287  i == lastRotated + deleteInterval - 2);
288  BEAST_EXPECT(
289  goodLedger(env, ledgers[i], std::to_string(i), true) &&
290  getHash(ledgers[i]).length());
291  }
292 
293  store.rendezvous();
294 
295  BEAST_EXPECT(store.getLastRotated() == deleteInterval + lastRotated);
296 
297  ledgerCheck(env, deleteInterval + 1, lastRotated);
298  transactionCheck(env, 0);
299  accountTransactionCheck(env, 0);
300  }
301 
302  void
304  {
305  testcase("automatic online_delete");
306  using namespace jtx;
307  using namespace std::chrono_literals;
308 
309  Env env(*this, envconfig(onlineDelete));
310  auto& store = env.app().getSHAMapStore();
311 
312  auto ledgerSeq = waitForReady(env);
313  auto lastRotated = ledgerSeq - 1;
314  BEAST_EXPECT(store.getLastRotated() == lastRotated);
315  BEAST_EXPECT(lastRotated != 2);
316 
317  // Because advisory_delete is unset,
318  // "can_delete" is disabled.
319  auto const canDelete = env.rpc("can_delete");
320  BEAST_EXPECT(bad(canDelete, rpcNOT_ENABLED));
321 
322  // Close ledgers without triggering a rotate
323  for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
324  {
325  env.close();
326 
327  auto ledger = env.rpc("ledger", "validated");
328  BEAST_EXPECT(
329  goodLedger(env, ledger, std::to_string(ledgerSeq), true));
330  }
331 
332  store.rendezvous();
333 
334  // The database will always have back to ledger 2,
335  // regardless of lastRotated.
336  ledgerCheck(env, ledgerSeq - 2, 2);
337  BEAST_EXPECT(lastRotated == store.getLastRotated());
338 
339  {
340  // Closing one more ledger triggers a rotate
341  env.close();
342 
343  auto ledger = env.rpc("ledger", "validated");
344  BEAST_EXPECT(
345  goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
346  }
347 
348  store.rendezvous();
349 
350  ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
351  BEAST_EXPECT(lastRotated != store.getLastRotated());
352 
353  lastRotated = store.getLastRotated();
354 
355  // Close enough ledgers to trigger another rotate
356  for (; ledgerSeq < lastRotated + deleteInterval + 1; ++ledgerSeq)
357  {
358  env.close();
359 
360  auto ledger = env.rpc("ledger", "validated");
361  BEAST_EXPECT(
362  goodLedger(env, ledger, std::to_string(ledgerSeq), true));
363  }
364 
365  store.rendezvous();
366 
367  ledgerCheck(env, deleteInterval + 1, lastRotated);
368  BEAST_EXPECT(lastRotated != store.getLastRotated());
369  }
370 
371  void
373  {
374  testcase("online_delete with advisory_delete");
375  using namespace jtx;
376  using namespace std::chrono_literals;
377 
378  // Same config with advisory_delete enabled
379  Env env(*this, envconfig(advisoryDelete));
380  auto& store = env.app().getSHAMapStore();
381 
382  auto ledgerSeq = waitForReady(env);
383  auto lastRotated = ledgerSeq - 1;
384  BEAST_EXPECT(store.getLastRotated() == lastRotated);
385  BEAST_EXPECT(lastRotated != 2);
386 
387  auto canDelete = env.rpc("can_delete");
388  BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
389  BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == 0);
390 
391  canDelete = env.rpc("can_delete", "never");
392  BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
393  BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == 0);
394 
395  auto const firstBatch = deleteInterval + ledgerSeq;
396  for (; ledgerSeq < firstBatch; ++ledgerSeq)
397  {
398  env.close();
399 
400  auto ledger = env.rpc("ledger", "validated");
401  BEAST_EXPECT(
402  goodLedger(env, ledger, std::to_string(ledgerSeq), true));
403  }
404 
405  store.rendezvous();
406 
407  ledgerCheck(env, ledgerSeq - 2, 2);
408  BEAST_EXPECT(lastRotated == store.getLastRotated());
409 
410  // This does not kick off a cleanup
411  canDelete = env.rpc(
412  "can_delete", std::to_string(ledgerSeq + deleteInterval / 2));
413  BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
414  BEAST_EXPECT(
415  canDelete[jss::result][jss::can_delete] ==
416  ledgerSeq + deleteInterval / 2);
417 
418  store.rendezvous();
419 
420  ledgerCheck(env, ledgerSeq - 2, 2);
421  BEAST_EXPECT(store.getLastRotated() == lastRotated);
422 
423  {
424  // This kicks off a cleanup, but it stays small.
425  env.close();
426 
427  auto ledger = env.rpc("ledger", "validated");
428  BEAST_EXPECT(
429  goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
430  }
431 
432  store.rendezvous();
433 
434  ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
435 
436  BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
437  lastRotated = ledgerSeq - 1;
438 
439  for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
440  {
441  // No cleanups in this loop.
442  env.close();
443 
444  auto ledger = env.rpc("ledger", "validated");
445  BEAST_EXPECT(
446  goodLedger(env, ledger, std::to_string(ledgerSeq), true));
447  }
448 
449  store.rendezvous();
450 
451  BEAST_EXPECT(store.getLastRotated() == lastRotated);
452 
453  {
454  // This kicks off another cleanup.
455  env.close();
456 
457  auto ledger = env.rpc("ledger", "validated");
458  BEAST_EXPECT(
459  goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
460  }
461 
462  store.rendezvous();
463 
464  ledgerCheck(env, ledgerSeq - firstBatch, firstBatch);
465 
466  BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
467  lastRotated = ledgerSeq - 1;
468 
469  // This does not kick off a cleanup
470  canDelete = env.rpc("can_delete", "always");
471  BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
472  BEAST_EXPECT(
473  canDelete[jss::result][jss::can_delete] ==
475 
476  for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
477  {
478  // No cleanups in this loop.
479  env.close();
480 
481  auto ledger = env.rpc("ledger", "validated");
482  BEAST_EXPECT(
483  goodLedger(env, ledger, std::to_string(ledgerSeq), true));
484  }
485 
486  store.rendezvous();
487 
488  BEAST_EXPECT(store.getLastRotated() == lastRotated);
489 
490  {
491  // This kicks off another cleanup.
492  env.close();
493 
494  auto ledger = env.rpc("ledger", "validated");
495  BEAST_EXPECT(
496  goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
497  }
498 
499  store.rendezvous();
500 
501  ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
502 
503  BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
504  lastRotated = ledgerSeq - 1;
505 
506  // This does not kick off a cleanup
507  canDelete = env.rpc("can_delete", "now");
508  BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
509  BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == ledgerSeq - 1);
510 
511  for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
512  {
513  // No cleanups in this loop.
514  env.close();
515 
516  auto ledger = env.rpc("ledger", "validated");
517  BEAST_EXPECT(
518  goodLedger(env, ledger, std::to_string(ledgerSeq), true));
519  }
520 
521  store.rendezvous();
522 
523  BEAST_EXPECT(store.getLastRotated() == lastRotated);
524 
525  {
526  // This kicks off another cleanup.
527  env.close();
528 
529  auto ledger = env.rpc("ledger", "validated");
530  BEAST_EXPECT(
531  goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
532  }
533 
534  store.rendezvous();
535 
536  ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
537 
538  BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
539  lastRotated = ledgerSeq - 1;
540  }
541 
542  void
543  run() override
544  {
545  testClear();
546  testAutomatic();
547  testCanDelete();
548  }
549 };
550 
551 // VFALCO This test fails because of thread asynchronous issues
553 
554 } // namespace test
555 } // namespace ripple
ripple::test::jtx::json
Inject raw JSON.
Definition: jtx_json.h:31
ripple::test::SHAMapStore_test::run
void run() override
Definition: SHAMapStore_test.cpp:543
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountDelete, app, ripple)
std::string
STL class.
ripple::test::SHAMapStore_test::testAutomatic
void testAutomatic()
Definition: SHAMapStore_test.cpp:303
ripple::SHAMapStore
class to create database, launch online delete thread, and related SQLite database
Definition: SHAMapStore.h:37
ripple::test::SHAMapStore_test::deleteInterval
static const auto deleteInterval
Definition: SHAMapStore_test.cpp:34
std::map::emplace
T emplace(T... args)
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:240
ripple::test::jtx::envconfig
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:49
ripple::rpcLGR_NOT_FOUND
@ rpcLGR_NOT_FOUND
Definition: ErrorCodes.h:72
ripple::SHAMapStore::rendezvous
virtual void rendezvous() const =0
ripple::error_code_i
error_code_i
Definition: ErrorCodes.h:40
ripple::test::SHAMapStore_test::advisoryDelete
static auto advisoryDelete(std::unique_ptr< Config > cfg)
Definition: SHAMapStore_test.cpp:46
ripple::DatabaseCon::checkoutDb
LockedSociSession checkoutDb()
Definition: DatabaseCon.h:176
ripple::RPC::contains_error
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Definition: ErrorCodes.cpp:219
std::to_string
T to_string(T... args)
ripple::rpcNOT_ENABLED
@ rpcNOT_ENABLED
Definition: ErrorCodes.h:59
ripple::Application::getLedgerDB
virtual DatabaseCon & getLedgerDB()=0
std::uint32_t
std::map
STL class.
ripple::test::SHAMapStore_test::transactionCheck
void transactionCheck(jtx::Env &env, int const rows)
Definition: SHAMapStore_test.cpp:144
ripple::test::jtx::seq
Set the sequence number on a JTx.
Definition: seq.h:32
ripple::test::SHAMapStore_test::testCanDelete
void testCanDelete()
Definition: SHAMapStore_test.cpp:372
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::jtx::noripple
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
Definition: Env.h:64
ripple::test::jtx::Env::close
bool close(NetClock::time_point closeTime, boost::optional< std::chrono::milliseconds > consensusDelay=boost::none)
Close and advance the ledger.
Definition: Env.cpp:121
ripple::test::SHAMapStore_test::testClear
void testClear()
Definition: SHAMapStore_test.cpp:192
ripple::test::SHAMapStore_test::ledgerCheck
void ledgerCheck(jtx::Env &env, int const rows, int const first)
Definition: SHAMapStore_test.cpp:126
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:219
ripple::test::SHAMapStore_test::accountTransactionCheck
void accountTransactionCheck(jtx::Env &env, int const rows)
Definition: SHAMapStore_test.cpp:157
ripple::test::SHAMapStore_test::waitForReady
int waitForReady(jtx::Env &env)
Definition: SHAMapStore_test.cpp:170
ripple::test::SHAMapStore_test::goodLedger
bool goodLedger(jtx::Env &env, Json::Value const &json, std::string ledgerID, bool checkDB=false)
Definition: SHAMapStore_test.cpp:54
std::make_pair
T make_pair(T... args)
ripple::Application::getSHAMapStore
virtual SHAMapStore & getSHAMapStore()=0
ripple::test::SHAMapStore_test
Definition: SHAMapStore_test.cpp:32
ripple::test::SHAMapStore_test::onlineDelete
static auto onlineDelete(std::unique_ptr< Config > cfg)
Definition: SHAMapStore_test.cpp:37
std::unique_ptr
STL class.
std::numeric_limits
ripple::test::SHAMapStore_test::bad
bool bad(Json::Value const &json, error_code_i error=rpcLGR_NOT_FOUND)
Definition: SHAMapStore_test.cpp:107
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:115
ripple::ConfigSection::nodeDatabase
static std::string nodeDatabase()
Definition: ConfigSections.h:33
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::Application::getTxnDB
virtual DatabaseCon & getTxnDB()=0
ripple::test::SHAMapStore_test::getHash
std::string getHash(Json::Value const &json)
Definition: SHAMapStore_test.cpp:115