rippled
ShardArchiveHandler_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2020 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/beast/utility/temp_dir.h>
21 #include <ripple/core/ConfigSections.h>
22 #include <ripple/nodestore/DummyScheduler.h>
23 #include <ripple/nodestore/Manager.h>
24 #include <ripple/nodestore/impl/DecodedBlob.h>
25 #include <ripple/protocol/jss.h>
26 #include <ripple/rpc/ShardArchiveHandler.h>
27 #include <test/jtx/Env.h>
28 #include <test/jtx/TrustedPublisherServer.h>
29 #include <test/jtx/envconfig.h>
30 #include <test/nodestore/TestBase.h>
31 
32 namespace ripple {
33 namespace test {
34 
35 class ShardArchiveHandler_test : public beast::unit_test::suite
36 {
38 
40  createServer(jtx::Env& env, bool ssl = true)
41  {
45  env.app().getIOService(),
46  list,
47  env.timeKeeper().now() + std::chrono::seconds{3600},
48  ssl);
49  }
50 
51 public:
52  // Test the shard downloading module by initiating
53  // and completing a download and verifying the
54  // contents of the state database.
55  void
57  {
58  testcase("testSingleDownloadAndStateDB");
59 
60  beast::temp_dir tempDir;
61 
62  auto c = jtx::envconfig();
63  auto& section = c->section(ConfigSection::shardDatabase());
64  section.set("path", tempDir.path());
65  section.set("max_historical_shards", "20");
66  c->setupControl(true, true, true);
67 
68  jtx::Env env(*this, std::move(c));
69  auto handler = env.app().getShardArchiveHandler();
70  BEAST_EXPECT(handler);
71  BEAST_EXPECT(dynamic_cast<RPC::RecoveryHandler*>(handler) == nullptr);
72 
73  std::string const rawUrl = "https://foo:443/1.tar.lz4";
74  parsedURL url;
75 
76  parseUrl(url, rawUrl);
77  handler->add(1, {url, rawUrl});
78 
79  {
80  std::lock_guard<std::mutex> lock(handler->m_);
81 
82  auto& session{handler->sqliteDB_->getSession()};
83 
84  soci::rowset<soci::row> rs =
85  (session.prepare << "SELECT * FROM State;");
86 
87  std::uint64_t rowCount = 0;
88 
89  for (auto it = rs.begin(); it != rs.end(); ++it, ++rowCount)
90  {
91  BEAST_EXPECT(it->get<int>(0) == 1);
92  BEAST_EXPECT(it->get<std::string>(1) == rawUrl);
93  }
94 
95  BEAST_EXPECT(rowCount == 1);
96  }
97 
98  handler->release();
99  }
100 
101  // Test the shard downloading module by initiating
102  // and completing three downloads and verifying
103  // the contents of the state database.
104  void
106  {
107  testcase("testDownloadsAndStateDB");
108 
109  beast::temp_dir tempDir;
110 
111  auto c = jtx::envconfig();
112  auto& section = c->section(ConfigSection::shardDatabase());
113  section.set("path", tempDir.path());
114  section.set("max_historical_shards", "20");
115  c->setupControl(true, true, true);
116 
117  jtx::Env env(*this, std::move(c));
118  auto handler = env.app().getShardArchiveHandler();
119  BEAST_EXPECT(handler);
120  BEAST_EXPECT(dynamic_cast<RPC::RecoveryHandler*>(handler) == nullptr);
121 
122  Downloads const dl = {
123  {1, "https://foo:443/1.tar.lz4"},
124  {2, "https://foo:443/2.tar.lz4"},
125  {3, "https://foo:443/3.tar.lz4"}};
126 
127  for (auto const& entry : dl)
128  {
129  parsedURL url;
130  parseUrl(url, entry.second);
131  handler->add(entry.first, {url, entry.second});
132  }
133 
134  {
135  std::lock_guard<std::mutex> lock(handler->m_);
136 
137  auto& session{handler->sqliteDB_->getSession()};
138  soci::rowset<soci::row> rs =
139  (session.prepare << "SELECT * FROM State;");
140 
141  std::uint64_t pos = 0;
142  for (auto it = rs.begin(); it != rs.end(); ++it, ++pos)
143  {
144  BEAST_EXPECT(it->get<int>(0) == dl[pos].first);
145  BEAST_EXPECT(it->get<std::string>(1) == dl[pos].second);
146  }
147 
148  BEAST_EXPECT(pos == dl.size());
149  }
150 
151  handler->release();
152  }
153 
154  // Test the shard downloading module by initiating
155  // and completing ten downloads and verifying the
156  // contents of the filesystem and the handler's
157  // archives.
158  void
160  {
161  testcase("testDownloadsAndFileSystem");
162 
163  beast::temp_dir tempDir;
164 
165  auto c = jtx::envconfig();
166  auto& section = c->section(ConfigSection::shardDatabase());
167  section.set("path", tempDir.path());
168  section.set("max_historical_shards", "20");
169  section.set("ledgers_per_shard", "256");
170  section.set("earliest_seq", "257");
171  auto& sectionNode = c->section(ConfigSection::nodeDatabase());
172  sectionNode.set("earliest_seq", "257");
173  c->setupControl(true, true, true);
174 
175  jtx::Env env(*this, std::move(c));
176 
177  std::uint8_t const numberOfDownloads = 10;
178 
179  // Create some ledgers so that the ShardArchiveHandler
180  // can verify the last ledger hash for the shard
181  // downloads.
182  for (int i = 0; i < env.app().getShardStore()->ledgersPerShard() *
183  (numberOfDownloads + 1);
184  ++i)
185  {
186  env.close();
187  }
188 
189  auto handler = env.app().getShardArchiveHandler();
190  BEAST_EXPECT(handler);
191  BEAST_EXPECT(dynamic_cast<RPC::RecoveryHandler*>(handler) == nullptr);
192 
193  auto server = createServer(env);
194  auto host = server->local_endpoint().address().to_string();
195  auto port = std::to_string(server->local_endpoint().port());
196  server->stop();
197 
198  Downloads const dl = [count = numberOfDownloads, &host, &port] {
199  Downloads ret;
200 
201  for (int i = 1; i <= count; ++i)
202  {
203  ret.push_back(
204  {i,
205  (boost::format("https://%s:%d/%d.tar.lz4") % host % port %
206  i)
207  .str()});
208  }
209 
210  return ret;
211  }();
212 
213  for (auto const& entry : dl)
214  {
215  parsedURL url;
216  parseUrl(url, entry.second);
217  handler->add(entry.first, {url, entry.second});
218  }
219 
220  BEAST_EXPECT(handler->start());
221 
222  auto stateDir =
224 
225  std::unique_lock<std::mutex> lock(handler->m_);
226 
227  BEAST_EXPECT(
228  boost::filesystem::exists(stateDir) || handler->archives_.empty());
229 
230  using namespace std::chrono_literals;
231  auto waitMax = 60s;
232 
233  while (!handler->archives_.empty())
234  {
235  lock.unlock();
237 
238  if (waitMax -= 1s; waitMax <= 0s)
239  {
240  BEAST_EXPECT(false);
241  break;
242  }
243 
244  lock.lock();
245  }
246 
247  BEAST_EXPECT(!boost::filesystem::exists(stateDir));
248  }
249 
250  // Test the shard downloading module by initiating
251  // and completing ten downloads and verifying the
252  // contents of the filesystem and the handler's
253  // archives. Then restart the application and ensure
254  // that the handler is created and started automatically.
255  void
257  {
258  testcase("testDownloadsAndRestart");
259 
260  beast::temp_dir tempDir;
261 
262  {
263  auto c = jtx::envconfig();
264  auto& section = c->section(ConfigSection::shardDatabase());
265  section.set("path", tempDir.path());
266  section.set("max_historical_shards", "20");
267  section.set("ledgers_per_shard", "256");
268  section.set("earliest_seq", "257");
269  auto& sectionNode = c->section(ConfigSection::nodeDatabase());
270  sectionNode.set("earliest_seq", "257");
271  c->setupControl(true, true, true);
272 
273  jtx::Env env(*this, std::move(c));
274 
275  std::uint8_t const numberOfDownloads = 10;
276 
277  // Create some ledgers so that the ShardArchiveHandler
278  // can verify the last ledger hash for the shard
279  // downloads.
280  for (int i = 0; i < env.app().getShardStore()->ledgersPerShard() *
281  (numberOfDownloads + 1);
282  ++i)
283  {
284  env.close();
285  }
286 
287  auto handler = env.app().getShardArchiveHandler();
288  BEAST_EXPECT(handler);
289  BEAST_EXPECT(
290  dynamic_cast<RPC::RecoveryHandler*>(handler) == nullptr);
291 
292  auto server = createServer(env);
293  auto host = server->local_endpoint().address().to_string();
294  auto port = std::to_string(server->local_endpoint().port());
295  server->stop();
296 
297  Downloads const dl = [count = numberOfDownloads, &host, &port] {
298  Downloads ret;
299 
300  for (int i = 1; i <= count; ++i)
301  {
302  ret.push_back(
303  {i,
304  (boost::format("https://%s:%d/%d.tar.lz4") % host %
305  port % i)
306  .str()});
307  }
308 
309  return ret;
310  }();
311 
312  for (auto const& entry : dl)
313  {
314  parsedURL url;
315  parseUrl(url, entry.second);
316  handler->add(entry.first, {url, entry.second});
317  }
318 
320  env.app().config());
321 
322  boost::filesystem::copy_file(
323  stateDir / stateDBName,
324  boost::filesystem::path(tempDir.path()) / stateDBName);
325 
326  BEAST_EXPECT(handler->start());
327 
328  std::unique_lock<std::mutex> lock(handler->m_);
329 
330  BEAST_EXPECT(
331  boost::filesystem::exists(stateDir) ||
332  handler->archives_.empty());
333 
334  using namespace std::chrono_literals;
335  auto waitMax = 60s;
336 
337  while (!handler->archives_.empty())
338  {
339  lock.unlock();
341 
342  if (waitMax -= 1s; waitMax <= 0s)
343  {
344  BEAST_EXPECT(false);
345  break;
346  }
347 
348  lock.lock();
349  }
350 
351  BEAST_EXPECT(!boost::filesystem::exists(stateDir));
352 
353  boost::filesystem::create_directory(stateDir);
354 
355  boost::filesystem::copy_file(
356  boost::filesystem::path(tempDir.path()) / stateDBName,
357  stateDir / stateDBName);
358  }
359 
360  auto c = jtx::envconfig();
361  auto& section = c->section(ConfigSection::shardDatabase());
362  section.set("path", tempDir.path());
363  section.set("max_historical_shards", "20");
364  section.set("ledgers_per_shard", "256");
365  section.set("shard_verification_retry_interval", "1");
366  section.set("shard_verification_max_attempts", "10000");
367  section.set("earliest_seq", "257");
368  auto& sectionNode = c->section(ConfigSection::nodeDatabase());
369  sectionNode.set("earliest_seq", "257");
370  c->setupControl(true, true, true);
371 
372  jtx::Env env(*this, std::move(c));
373 
374  std::uint8_t const numberOfDownloads = 10;
375 
376  // Create some ledgers so that the ShardArchiveHandler
377  // can verify the last ledger hash for the shard
378  // downloads.
379  for (int i = 0; i < env.app().getShardStore()->ledgersPerShard() *
380  (numberOfDownloads + 1);
381  ++i)
382  {
383  env.close();
384  }
385 
386  auto handler = env.app().getShardArchiveHandler();
387  BEAST_EXPECT(dynamic_cast<RPC::RecoveryHandler*>(handler) != nullptr);
388 
389  auto stateDir =
391 
392  std::unique_lock<std::mutex> lock(handler->m_);
393 
394  BEAST_EXPECT(
395  boost::filesystem::exists(stateDir) || handler->archives_.empty());
396 
397  using namespace std::chrono_literals;
398  auto waitMax = 60s;
399 
400  while (!handler->archives_.empty())
401  {
402  lock.unlock();
404 
405  if (waitMax -= 1s; waitMax <= 0s)
406  {
407  BEAST_EXPECT(false);
408  break;
409  }
410 
411  lock.lock();
412  }
413 
414  BEAST_EXPECT(!boost::filesystem::exists(stateDir));
415  }
416 
417  void
418  run() override
419  {
424  }
425 };
426 
427 BEAST_DEFINE_TESTSUITE(ShardArchiveHandler, app, ripple);
428 
429 } // namespace test
430 } // namespace ripple
ripple::RPC::ShardArchiveHandler::getDownloadDirectory
static boost::filesystem::path getDownloadDirectory(Config const &config)
Definition: ShardArchiveHandler.cpp:37
std::this_thread::sleep_for
T sleep_for(T... args)
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountDelete, app, ripple)
std::string
STL class.
std::shared_ptr
STL class.
ripple::parsedURL
Definition: StringUtilities.h:123
std::vector
STL class.
ripple::ConfigSection::shardDatabase
static std::string shardDatabase()
Definition: ConfigSections.h:38
std::chrono::seconds
std::lock_guard
STL class.
ripple::test::jtx::Env::timeKeeper
ManualTimeKeeper & timeKeeper()
Definition: Env.h:252
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::test::ShardArchiveHandler_test::testDownloadsAndRestart
void testDownloadsAndRestart()
Definition: ShardArchiveHandler_test.cpp:256
ripple::test::ShardArchiveHandler_test::createServer
std::shared_ptr< TrustedPublisherServer > createServer(jtx::Env &env, bool ssl=true)
Definition: ShardArchiveHandler_test.cpp:40
std::vector::push_back
T push_back(T... args)
ripple::test::make_TrustedPublisherServer
std::shared_ptr< TrustedPublisherServer > make_TrustedPublisherServer(boost::asio::io_context &ioc, std::vector< TrustedPublisherServer::Validator > const &validators, NetClock::time_point expiration, bool useSSL=false, int version=1, bool immediateStart=true, int sequence=1)
Definition: TrustedPublisherServer.h:618
ripple::test::ShardArchiveHandler_test::testDownloadsAndFileSystem
void testDownloadsAndFileSystem()
Definition: ShardArchiveHandler_test.cpp:159
ripple::Application::config
virtual Config & config()=0
std::unique_lock
STL class.
std::to_string
T to_string(T... args)
ripple::parseUrl
bool parseUrl(parsedURL &pUrl, std::string const &strUrl)
Definition: StringUtilities.cpp:55
std::uint64_t
ripple::test::TrustedPublisherServer::randomValidator
static Validator randomValidator()
Definition: TrustedPublisherServer.h:131
beast::temp_dir::path
std::string path() const
Get the native path for the temporary directory.
Definition: temp_dir.h:66
ripple::Application::getIOService
virtual boost::asio::io_service & getIOService()=0
ripple::RPC::RecoveryHandler
Definition: ShardArchiveHandler.h:161
ripple::stateDBName
static constexpr auto stateDBName
Definition: DBInit.h:174
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::ShardArchiveHandler_test::testDownloadsAndStateDB
void testDownloadsAndStateDB()
Definition: ShardArchiveHandler_test.cpp:105
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::ShardArchiveHandler_test::run
void run() override
Definition: ShardArchiveHandler_test.cpp:418
ripple::test::ShardArchiveHandler_test
Definition: ShardArchiveHandler_test.cpp:35
ripple::test::ManualTimeKeeper::now
time_point now() const override
Returns the estimate of wall time, in network time.
Definition: ManualTimeKeeper.cpp:37
ripple::test::ShardArchiveHandler_test::testSingleDownloadAndStateDB
void testSingleDownloadAndStateDB()
Definition: ShardArchiveHandler_test.cpp:56
ripple::getShardStore
static NodeStore::Database & getShardStore(Application &app)
Definition: ShardFamily.cpp:29
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:115
ripple::ConfigSection::nodeDatabase
static std::string nodeDatabase()
Definition: ConfigSections.h:33
beast::temp_dir
RAII temporary directory.
Definition: temp_dir.h:33
ripple::Application::getShardArchiveHandler
virtual RPC::ShardArchiveHandler * getShardArchiveHandler(bool tryRecovery=false)=0