Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
test_trace_file.cpp
Go to the documentation of this file.
1#define BOOST_TEST_MODULE trace_trace_file
2#include <boost/test/included/unit_test.hpp>
3#include <fc/io/cfile.hpp>
6#include <boost/filesystem.hpp>
7
8using namespace sysio;
9using namespace sysio::trace_api;
10using namespace sysio::trace_api::test_common;
11namespace bfs = boost::filesystem;
13
14namespace {
15 struct test_fixture {
16
17 std::vector<action_trace_v1> actions = {
18 {
19 {
20 1,
21 "receiver"_n, "contract"_n, "action"_n,
22 {{ "alice"_n, "active"_n }},
23 { 0x01, 0x01, 0x01, 0x01 }
24 },
25 { 0x05, 0x05, 0x05, 0x05 }
26 },
27 {
28 {
29 0,
30 "receiver"_n, "contract"_n, "action"_n,
31 {{ "alice"_n, "active"_n }},
32 { 0x00, 0x00, 0x00, 0x00 }
33 },
34 { 0x04, 0x04, 0x04, 0x04}
35 },
36 {
37 {
38 2,
39 "receiver"_n, "contract"_n, "action"_n,
40 {{ "alice"_n, "active"_n }},
41 { 0x02, 0x02, 0x02, 0x02 }
42 },
43 { 0x06, 0x06, 0x06, 0x06 }
44 }
45 };
46
48 "0000000000000000000000000000000000000000000000000000000000000001"_h,
49 actions,
50 fc::enum_type<uint8_t, chain::transaction_receipt_header::status_enum>{chain::transaction_receipt_header::status_enum::executed},
51 10,
52 5,
54 { chain::time_point(), 1, 0, 100, 50, 0 }
55 };
56
57 block_trace_v2 block_trace1_v2 {
58 "b000000000000000000000000000000000000000000000000000000000000001"_h,
59 1,
60 "0000000000000000000000000000000000000000000000000000000000000000"_h,
62 "bp.one"_n,
63 "0000000000000000000000000000000000000000000000000000000000000000"_h,
64 "0000000000000000000000000000000000000000000000000000000000000000"_h,
65 0,
66 std::vector<transaction_trace_v2> {
68 }
69 };
70
71 block_trace_v2 block_trace2_v2 {
72 "b000000000000000000000000000000000000000000000000000000000000001"_h,
73 5,
74 "0000000000000000000000000000000000000000000000000000000000000000"_h,
76 "bp.one"_n,
77 "0000000000000000000000000000000000000000000000000000000000000000"_h,
78 "0000000000000000000000000000000000000000000000000000000000000000"_h,
79 0,
80 std::vector<transaction_trace_v2> {
82 }
83 };
84
85 const block_trace_v1 bt_v1 {
86 {
87 "0000000000000000000000000000000000000000000000000000000000000001"_h,
88 1,
89 "0000000000000000000000000000000000000000000000000000000000000003"_h,
91 "bp.one"_n,
92 {}
93 },
94 "0000000000000000000000000000000000000000000000000000000000000000"_h,
95 "0000000000000000000000000000000000000000000000000000000000000000"_h,
96 0,
97 {
98 {
99 {
100 "0000000000000000000000000000000000000000000000000000000000000001"_h,
101 {
102 {
103 0,
104 "sysio.token"_n, "sysio.token"_n, "transfer"_n,
105 {{ "alice"_n, "active"_n }},
106 make_transfer_data( "alice"_n, "bob"_n, "0.0001 SYS"_t, "Memo!" )
107 },
108 {
109 1,
110 "alice"_n, "sysio.token"_n, "transfer"_n,
111 {{ "alice"_n, "active"_n }},
112 make_transfer_data( "alice"_n, "bob"_n, "0.0001 SYS"_t, "Memo!" )
113 },
114 {
115 2,
116 "bob"_n, "sysio.token"_n, "transfer"_n,
117 {{ "alice"_n, "active"_n }},
118 make_transfer_data( "alice"_n, "bob"_n, "0.0001 SYS"_t, "Memo!" )
119 }
120 }
121 },
122 fc::enum_type<uint8_t, chain::transaction_receipt_header::status_enum>{chain::transaction_receipt_header::status_enum::executed},
123 10,
124 5,
125 std::vector<chain::signature_type>{chain::signature_type()},
127 }
128 }
129 };
130
131 const block_trace_v1 bt2_v1 {
132 {
133 "0000000000000000000000000000000000000000000000000000000000000002"_h,
134 5,
135 "0000000000000000000000000000000000000000000000000000000000000005"_h,
137 "bp.two"_n
138 },
139 "0000000000000000000000000000000000000000000000000000000000000000"_h,
140 "0000000000000000000000000000000000000000000000000000000000000000"_h,
141 0,
142 {
143 {
144 {
145 "f000000000000000000000000000000000000000000000000000000000000004"_h,
146 {}
147 },
148 fc::enum_type<uint8_t, chain::transaction_receipt_header::status_enum>{chain::transaction_receipt_header::status_enum::executed},
149 10,
150 5,
151 std::vector<chain::signature_type>{chain::signature_type()},
153 }
154 }
155 };
156
157 const block_trace_v0 bt_v0 {
158 "0000000000000000000000000000000000000000000000000000000000000001"_h,
159 1,
160 "0000000000000000000000000000000000000000000000000000000000000003"_h,
162 "bp.one"_n,
163 {
164 {
165 "0000000000000000000000000000000000000000000000000000000000000001"_h,
166 {
167 {
168 0,
169 "sysio.token"_n, "sysio.token"_n, "transfer"_n,
170 {{ "alice"_n, "active"_n }},
171 make_transfer_data( "alice"_n, "bob"_n, "0.0001 SYS"_t, "Memo!" )
172 },
173 {
174 1,
175 "alice"_n, "sysio.token"_n, "transfer"_n,
176 {{ "alice"_n, "active"_n }},
177 make_transfer_data( "alice"_n, "bob"_n, "0.0001 SYS"_t, "Memo!" )
178 },
179 {
180 2,
181 "bob"_n, "sysio.token"_n, "transfer"_n,
182 {{ "alice"_n, "active"_n }},
183 make_transfer_data( "alice"_n, "bob"_n, "0.0001 SYS"_t, "Memo!" )
184 }
185 }
186 }
187 }
188 };
189
191 "b000000000000000000000000000000000000000000000000000000000000001"_h, 5, 0
192 } };
193 const metadata_log_entry le1 { lib_entry_v0 { 4 } };
195 "b000000000000000000000000000000000000000000000000000000000000002"_h, 7, 0
196 } };
197 const metadata_log_entry le2 { lib_entry_v0 { 5 } };
198
199 bool create_non_empty_trace_slice( slice_directory& sd, uint32_t slice_number, fc::cfile& file) {
200 const uint8_t bad_which = 0x7F;
201 if (!sd.find_or_create_trace_slice(slice_number, open_state::write, file)) {
202 file.write(reinterpret_cast<const char*>(&bad_which), sizeof(uint8_t));
203 file.close();
204 return sd.find_or_create_trace_slice(slice_number, open_state::read, file);
205 }
206 return false;
207 }
208 };
209
210 struct test_store_provider : public store_provider {
211 test_store_provider(const bfs::path& slice_dir, uint32_t width, std::optional<uint32_t> minimum_irreversible_history_blocks = std::optional<uint32_t>(), std::optional<uint32_t> minimum_uncompressed_irreversible_history_blocks = std::optional<uint32_t>(), size_t compression_seek_point_stride = 0)
212 : store_provider(slice_dir, width, minimum_irreversible_history_blocks, minimum_uncompressed_irreversible_history_blocks, compression_seek_point_stride) {
213 }
216 };
217
218 class vslice_datastream;
219
220 struct vslice {
221 enum mode { read_mode, write_mode};
222 vslice(mode m = write_mode) : _mode(m) {}
223 unsigned long tellp() const {
224 return _pos;
225 }
226
227 void seek( unsigned long loc ) {
228 if (_mode == read_mode) {
229 if (loc > _buffer.size()) {
230 throw std::ios_base::failure( "read vslice unable to seek to: " + std::to_string(loc) + ", end is at: " + std::to_string(_buffer.size()));
231 }
232 }
233 _pos = loc;
234 }
235
236 void seek_end( long loc ) {
237 _pos = _buffer.size();
238 }
239
240 void read( char* d, size_t n ) {
241 if( _pos + n > _buffer.size() ) {
242 throw std::ios_base::failure( "vslice unable to read " + std::to_string( n ) + " bytes; only can read " + std::to_string( _buffer.size() - _pos ) );
243 }
244 std::memcpy( d, _buffer.data() + _pos, n);
245 _pos += n;
246 }
247
248 void write( const char* d, size_t n ) {
249 if (_mode == read_mode) {
250 throw std::ios_base::failure( "read vslice should not have write called" );
251 }
252 if (_buffer.size() < _pos + n) {
253 _buffer.resize(_pos + n);
254 }
255 std::memcpy( _buffer.data() + _pos, d, n);
256 _pos += n;
257 }
258
259 void flush() {
260 _flush = true;
261 }
262
263 void sync() {
264 _sync = true;
265 }
266
267 vslice_datastream create_datastream();
268
269 std::vector<char> _buffer;
270 mode _mode = write_mode;
271 unsigned long _pos = 0lu;
272 bool _flush = false;
273 bool _sync = false;
274 };
275
276 class vslice_datastream {
277 public:
278 explicit vslice_datastream( vslice& vs ) : _vs(vs) {}
279
280 void skip( size_t s ) {
281 std::vector<char> d( s );
282 read( &d[0], s );
283 }
284
285 bool read( char* d, size_t s ) {
286 _vs.read( d, s );
287 return true;
288 }
289
290 bool get( unsigned char& c ) { return get( *(char*)&c ); }
291
292 bool get( char& c ) { return read(&c, 1); }
293
294 private:
295 vslice& _vs;
296 };
297
298 inline vslice_datastream vslice::create_datastream() {
299 return vslice_datastream(*this);
300 }
301}
302
303BOOST_AUTO_TEST_SUITE(slice_tests)
304 BOOST_FIXTURE_TEST_CASE(write_data_trace, test_fixture)
305 {
306 vslice vs;
307 const auto offset = append_store(bt_v1, vs );
308 BOOST_REQUIRE_EQUAL(offset,0);
309
310 const auto offset2 = append_store(bt2_v1, vs );
311 BOOST_REQUIRE(offset < offset2);
312
313 vs._pos = offset;
314 const auto bt_returned = extract_store<block_trace_v1>( vs );
315 BOOST_REQUIRE(bt_returned == bt_v1);
316
317 vs._pos = offset2;
318 const auto bt_returned2 = extract_store<block_trace_v1>( vs );
319 BOOST_REQUIRE(bt_returned2 == bt2_v1);
320 }
321
322 BOOST_FIXTURE_TEST_CASE(write_data_multi_trace_version, test_fixture)
323 {
324 vslice vs;
325 const auto offset = append_store(bt_v0, vs );
326 BOOST_REQUIRE_EQUAL(offset,0);
327
328 const auto offset2 = append_store(bt_v1, vs );
329 BOOST_REQUIRE(offset < offset2);
330
331 const auto offset3 = append_store(block_trace1_v2, vs );
332 BOOST_REQUIRE(offset2 < offset3);
333
334 vs._pos = offset;
335 const auto bt_returned = extract_store<block_trace_v0>( vs );
336 BOOST_REQUIRE(bt_returned == bt_v0);
337
338 vs._pos = offset2;
339 const auto bt_returned2 = extract_store<block_trace_v1>( vs );
340 BOOST_REQUIRE(bt_returned2 == bt_v1);
341
342 vs._pos = offset3;
343 const auto bt_returned3 = extract_store<block_trace_v2>( vs );
344 BOOST_REQUIRE(bt_returned3 == block_trace1_v2);
345 }
346
347 BOOST_FIXTURE_TEST_CASE(write_metadata_trace, test_fixture)
348 {
349 vslice vs;
350 const auto offset = append_store( be1, vs );
351 auto next_offset = vs._pos;
352 BOOST_REQUIRE(offset < next_offset);
353 const auto offset2 = append_store( le1, vs );
354 BOOST_REQUIRE(next_offset <= offset2);
355 BOOST_REQUIRE(offset2 < vs._pos);
356 next_offset = vs._pos;
357 const auto offset3 = append_store( be2, vs );
358 BOOST_REQUIRE(next_offset <= offset3);
359 BOOST_REQUIRE(offset3 < vs._pos);
360 next_offset = vs._pos;
361 const auto offset4 = append_store( le2, vs );
362 BOOST_REQUIRE(next_offset <= offset4);
363 BOOST_REQUIRE(offset4 < vs._pos);
364
365 vs._pos = offset;
366 const auto be_returned1 = extract_store<metadata_log_entry>( vs );
367 BOOST_REQUIRE(std::holds_alternative<block_entry_v0>(be_returned1));
368 const auto real_be_returned1 = std::get<block_entry_v0>(be_returned1);
369 const auto real_be1 = std::get<block_entry_v0>(be1);
370 BOOST_REQUIRE(real_be_returned1 == real_be1);
371
372 vs._pos = offset2;
373 const auto le_returned1 = extract_store<metadata_log_entry>( vs );
374 BOOST_REQUIRE(std::holds_alternative<lib_entry_v0>(le_returned1));
375 const auto real_le_returned1 = std::get<lib_entry_v0>(le_returned1);
376 const auto real_le1 = std::get<lib_entry_v0>(le1);
377 BOOST_REQUIRE(real_le_returned1 == real_le1);
378
379 vs._pos = offset3;
380 const auto be_returned2 = extract_store<metadata_log_entry>( vs );
381 BOOST_REQUIRE(std::holds_alternative<block_entry_v0>(be_returned2));
382 const auto real_be_returned2 = std::get<block_entry_v0>(be_returned2);
383 const auto real_be2 = std::get<block_entry_v0>(be2);
384 BOOST_REQUIRE(real_be_returned2 == real_be2);
385
386 vs._pos = offset4;
387 const auto le_returned2 = extract_store<metadata_log_entry>( vs );
388 BOOST_REQUIRE(std::holds_alternative<lib_entry_v0>(le_returned2));
389 const auto real_le_returned2 = std::get<lib_entry_v0>(le_returned2);
390 const auto real_le2 = std::get<lib_entry_v0>(le2);
391 BOOST_REQUIRE(real_le_returned2 == real_le2);
392 }
393
394 BOOST_FIXTURE_TEST_CASE(slice_number, test_fixture)
395 {
396 fc::temp_directory tempdir;
397 slice_directory sd(tempdir.path(), 100, std::optional<uint32_t>(), std::optional<uint32_t>(), 0);
398 BOOST_REQUIRE_EQUAL(sd.slice_number(99), 0);
399 BOOST_REQUIRE_EQUAL(sd.slice_number(100), 1);
400 BOOST_REQUIRE_EQUAL(sd.slice_number(1599), 15);
401 slice_directory sd2(tempdir.path(), 0x10, std::optional<uint32_t>(), std::optional<uint32_t>(), 0);
402 BOOST_REQUIRE_EQUAL(sd2.slice_number(0xf), 0);
403 BOOST_REQUIRE_EQUAL(sd2.slice_number(0x100), 0x10);
404 BOOST_REQUIRE_EQUAL(sd2.slice_number(0x233), 0x23);
405 }
406
407 BOOST_FIXTURE_TEST_CASE(slice_file, test_fixture)
408 {
409 fc::temp_directory tempdir;
410 slice_directory sd(tempdir.path(), 100, std::optional<uint32_t>(), std::optional<uint32_t>(), 0);
411 fc::cfile slice;
412
413 // create trace slices
414 for (uint i = 0; i < 9; ++i) {
415 bool found = sd.find_or_create_trace_slice(i, open_state::write, slice);
416 BOOST_REQUIRE(!found);
417 bfs::path fp = slice.get_file_path();
418 BOOST_REQUIRE_EQUAL(fp.parent_path().generic_string(), tempdir.path().generic_string());
419 const std::string expected_filename = "trace_0000000" + std::to_string(i) + "00-0000000" + std::to_string(i+1) + "00.log";
420 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
421 BOOST_REQUIRE(slice.is_open());
422 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), 0);
423 BOOST_REQUIRE_EQUAL(slice.tellp(), 0);
424 slice.close();
425 }
426
427 // create trace index slices
428 for (uint i = 0; i < 9; ++i) {
429 bool found = sd.find_or_create_index_slice(i, open_state::write, slice);
430 BOOST_REQUIRE(!found);
431 fc::path fp = slice.get_file_path();
432 BOOST_REQUIRE_EQUAL(fp.parent_path().generic_string(), tempdir.path().generic_string());
433 const std::string expected_filename = "trace_index_0000000" + std::to_string(i) + "00-0000000" + std::to_string(i+1) + "00.log";
434 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
435 BOOST_REQUIRE(slice.is_open());
437 const auto data = fc::raw::pack(h);
438 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), data.size());
439 BOOST_REQUIRE_EQUAL(slice.tellp(), data.size());
440 slice.close();
441 }
442
443 // reopen trace slice for append
444 bool found = sd.find_or_create_trace_slice(0, open_state::write, slice);
445 BOOST_REQUIRE(found);
446 fc::path fp = slice.get_file_path();
447 BOOST_REQUIRE_EQUAL(fp.parent_path().generic_string(), tempdir.path().generic_string());
448 std::string expected_filename = "trace_0000000000-0000000100.log";
449 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
450 BOOST_REQUIRE(slice.is_open());
451 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), 0);
452 BOOST_REQUIRE_EQUAL(slice.tellp(), 0);
453 uint64_t offset = append_store(bt_v1, slice);
454 BOOST_REQUIRE_EQUAL(offset, 0);
455 auto data = fc::raw::pack(bt_v1);
456 BOOST_REQUIRE(slice.tellp() > 0);
457 BOOST_REQUIRE_EQUAL(data.size(), slice.tellp());
458 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), slice.tellp());
459 uint64_t trace_file_size = bfs::file_size(fp);
460 slice.close();
461
462 // open same file for read
463 found = sd.find_or_create_trace_slice(0, open_state::read, slice);
464 BOOST_REQUIRE(found);
465 fp = slice.get_file_path();
466 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
467 BOOST_REQUIRE(slice.is_open());
468 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), trace_file_size);
469 BOOST_REQUIRE_EQUAL(slice.tellp(), 0);
470 slice.close();
471
472 // open same file for append again
473 found = sd.find_or_create_trace_slice(0, open_state::write, slice);
474 BOOST_REQUIRE(found);
475 fp = slice.get_file_path();
476 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
477 BOOST_REQUIRE(slice.is_open());
478 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), trace_file_size);
479 BOOST_REQUIRE_EQUAL(slice.tellp(), trace_file_size);
480 slice.close();
481
482 // reopen trace index slice for append
483 found = sd.find_or_create_index_slice(1, open_state::write, slice);
484 BOOST_REQUIRE(found);
485 fp = slice.get_file_path();
486 BOOST_REQUIRE_EQUAL(fp.parent_path().generic_string(), tempdir.path().generic_string());
487 expected_filename = "trace_index_0000000100-0000000200.log";
488 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
489 BOOST_REQUIRE(slice.is_open());
491 data = fc::raw::pack(h);
492 const uint64_t header_size = data.size();
493 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), header_size);
494 BOOST_REQUIRE_EQUAL(slice.tellp(), header_size);
495 offset = append_store(be1, slice);
496 BOOST_REQUIRE_EQUAL(offset, header_size);
497 data = fc::raw::pack(be1);
498 const auto be1_size = data.size();
499 BOOST_REQUIRE_EQUAL(header_size + be1_size, slice.tellp());
500 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), slice.tellp());
501 slice.close();
502
503 found = sd.find_or_create_index_slice(1, open_state::read, slice);
504 BOOST_REQUIRE(found);
505 fp = slice.get_file_path();
506 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
507 BOOST_REQUIRE(slice.is_open());
508 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), header_size + be1_size);
509 BOOST_REQUIRE_EQUAL(slice.tellp(), header_size);
510 slice.close();
511
512 found = sd.find_or_create_index_slice(1, open_state::write, slice);
513 BOOST_REQUIRE(found);
514 fp = slice.get_file_path();
515 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
516 BOOST_REQUIRE(slice.is_open());
517 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), header_size + be1_size);
518 BOOST_REQUIRE_EQUAL(slice.tellp(), header_size + be1_size);
519 offset = append_store(le1, slice);
520 BOOST_REQUIRE_EQUAL(offset, header_size + be1_size);
521 data = fc::raw::pack(le1);
522 const auto le1_size = data.size();
523 BOOST_REQUIRE_EQUAL(header_size + be1_size + le1_size, slice.tellp());
524 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), slice.tellp());
525 slice.close();
526
527 found = sd.find_or_create_index_slice(1, open_state::read, slice);
528 BOOST_REQUIRE(found);
529 fp = slice.get_file_path();
530 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
531 BOOST_REQUIRE(slice.is_open());
532 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), header_size + be1_size + le1_size);
533 BOOST_REQUIRE_EQUAL(slice.tellp(), header_size);
534 slice.close();
535 }
536
537 BOOST_FIXTURE_TEST_CASE(slice_file_find_test, test_fixture)
538 {
539 fc::temp_directory tempdir;
540 slice_directory sd(tempdir.path(), 100, std::optional<uint32_t>(), std::optional<uint32_t>(), 0);
541 fc::cfile slice;
542
543 // create trace slice
544 bool found = sd.find_or_create_trace_slice(1, open_state::write, slice);
545 BOOST_REQUIRE(!found);
546 bfs::path fp = slice.get_file_path();
547 BOOST_REQUIRE_EQUAL(fp.parent_path().generic_string(), tempdir.path().generic_string());
548 const std::string expected_filename = "trace_0000000100-0000000200.log";
549 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
550 BOOST_REQUIRE(slice.is_open());
551 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), 0);
552 BOOST_REQUIRE_EQUAL(slice.tellp(), 0);
553 slice.close();
554
555 // find trace slice (and open)
556 found = sd.find_trace_slice(1, open_state::write, slice);
557 BOOST_REQUIRE(found);
558 fp = slice.get_file_path();
559 BOOST_REQUIRE_EQUAL(fp.parent_path().generic_string(), tempdir.path().generic_string());
560 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
561 BOOST_REQUIRE(slice.is_open());
562 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), 0);
563 slice.close();
564
565 // find trace slice (and don't open)
566 found = sd.find_trace_slice(1, open_state::write, slice, false);
567 BOOST_REQUIRE(found);
568 fp = slice.get_file_path();
569 BOOST_REQUIRE_EQUAL(fp.parent_path().generic_string(), tempdir.path().generic_string());
570 BOOST_REQUIRE_EQUAL(fp.filename().generic_string(), expected_filename);
571 BOOST_REQUIRE(!slice.is_open());
572 BOOST_REQUIRE_EQUAL(bfs::file_size(fp), 0);
573 slice.close();
574 }
575
576 void verify_directory_contents(const bfs::path& tempdir, std::set<bfs::path> expected_files) {
577 std::set<bfs::path> unexpected_files;
578 for (bfs::directory_iterator itr(tempdir); itr != directory_iterator(); ++itr) {
579 const auto filename = itr->path().filename();
580 if (expected_files.erase(filename) < 1) {
581 unexpected_files.insert(filename);
582 }
583 }
584 if (expected_files.size() + unexpected_files.size() == 0)
585 return;
586
587 std::string msg;
588 if (expected_files.size()) {
589 msg += " Expected the following files to be present, but were not:";
590 }
591 bool comma = false;
592 for(auto file : expected_files) {
593 if (comma)
594 msg += ",";
595 msg += " " + file.generic_string();
596 }
597 if (unexpected_files.size()) {
598 msg += " Did not expect the following files to be present, but they were:";
599 }
600 for(auto file : expected_files) {
601 if (comma)
602 msg += ",";
603 msg += " " + file.generic_string();
604 }
605 BOOST_FAIL(msg);
606 }
607
608 BOOST_FIXTURE_TEST_CASE(slice_dir_cleanup_height_less_than_width, test_fixture)
609 {
610 fc::temp_directory tempdir;
611 const uint32_t width = 10;
612 const uint32_t min_saved_blocks = 5;
613 slice_directory sd(tempdir.path(), width, std::optional<uint32_t>(min_saved_blocks), std::optional<uint32_t>(), 0);
614 fc::cfile file;
615
616 // verify it cleans up when there is just an index file, just a trace file, or when both are there
617 // verify it cleans up all slices that need to be cleaned
618 std::set<bfs::path> files;
619 BOOST_REQUIRE(!sd.find_or_create_index_slice(0, open_state::read, file));
620 files.insert(file.get_file_path().filename());
621 verify_directory_contents(tempdir.path(), files);
622 BOOST_REQUIRE(!sd.find_or_create_trace_slice(0, open_state::read, file));
623 files.insert(file.get_file_path().filename());
624 BOOST_REQUIRE(!sd.find_or_create_index_slice(1, open_state::read, file));
625 files.insert(file.get_file_path().filename());
626 BOOST_REQUIRE(!sd.find_or_create_trace_slice(2, open_state::read, file));
627 files.insert(file.get_file_path().filename());
628 BOOST_REQUIRE(!sd.find_or_create_index_slice(3, open_state::read, file));
629 files.insert(file.get_file_path().filename());
630 BOOST_REQUIRE(!sd.find_or_create_index_slice(4, open_state::read, file));
631 const auto index4 = file.get_file_path().filename();
632 files.insert(index4);
633 BOOST_REQUIRE(!sd.find_or_create_trace_slice(4, open_state::read, file));
634 const auto trace4 = file.get_file_path().filename();
635 files.insert(trace4);
636 BOOST_REQUIRE(!sd.find_or_create_index_slice(5, open_state::read, file));
637 const auto index5 = file.get_file_path().filename();
638 files.insert(index5);
639 BOOST_REQUIRE(!sd.find_or_create_trace_slice(6, open_state::read, file));
640 const auto trace6 = file.get_file_path().filename();
641 files.insert(trace6);
642 verify_directory_contents(tempdir.path(), files);
643
644 // verify that the current_slice and the previous are maintained as long as lib - min_saved_blocks is part of previous slice
645 uint32_t current_slice = 6;
646 uint32_t lib = current_slice * width;
647 sd.run_maintenance_tasks(lib, {});
648 std::set<bfs::path> files2;
649 files2.insert(index5);
650 files2.insert(trace6);
651 verify_directory_contents(tempdir.path(), files2);
652
653 // saved blocks still in previous slice
654 lib += min_saved_blocks - 1; // current_slice * width + min_saved_blocks - 1
655 sd.run_maintenance_tasks(lib, {});
656 verify_directory_contents(tempdir.path(), files2);
657
658 // now all saved blocks in current slice
659 lib += 1; // current_slice * width + min_saved_blocks
660 sd.run_maintenance_tasks(lib, {});
661 std::set<bfs::path> files3;
662 files3.insert(trace6);
663 verify_directory_contents(tempdir.path(), files3);
664
665 // moving lib into next slice, so 1 saved blocks still in 6th slice
666 lib += width - 1;
667 sd.run_maintenance_tasks(lib, {});
668 verify_directory_contents(tempdir.path(), files3);
669
670 // moved last saved block out of 6th slice, so 6th slice is cleaned up
671 lib += 1;
672 sd.run_maintenance_tasks(lib, {});
673 verify_directory_contents(tempdir.path(), std::set<bfs::path>());
674 }
675
676 BOOST_FIXTURE_TEST_CASE(slice_dir_compress, test_fixture)
677 {
678 fc::temp_directory tempdir;
679 const uint32_t width = 10;
680 const uint32_t min_uncompressed_blocks = 5;
681 slice_directory sd(tempdir.path(), width, std::optional<uint32_t>(), std::optional<uint32_t>(min_uncompressed_blocks), 8);
682 fc::cfile file;
683
684 using file_vector_t = std::vector<std::tuple<bfs::path, bfs::path, bfs::path>>;
685 file_vector_t file_paths;
686 for (int i = 0; i < 7 ; i++) {
687 BOOST_REQUIRE(!sd.find_or_create_index_slice(i, open_state::read, file));
688 auto index_name = file.get_file_path().filename();
689 BOOST_REQUIRE(create_non_empty_trace_slice(sd, i, file));
690 auto trace_name = file.get_file_path().filename();
691 auto compressed_trace_name = trace_name;
692 compressed_trace_name.replace_extension(".clog");
693 file_paths.emplace_back(index_name, trace_name, compressed_trace_name);
694 }
695
696 // initial set is only indices and uncompressed traces
697 std::set<bfs::path> files;
698 for (const auto& e: file_paths) {
699 files.insert(std::get<0>(e));
700 files.insert(std::get<1>(e));
701 }
702 verify_directory_contents(tempdir.path(), files);
703
704 // verify no change up to the last block before a slice becomes compressible
705 sd.run_maintenance_tasks(14, {});
706 verify_directory_contents(tempdir.path(), files);
707
708 for (std::size_t reps = 0; reps < file_paths.size(); reps++) {
709 // leading edge,
710 // compresses one slice
711 files.erase(std::get<1>(file_paths.at(reps)));
712 files.insert(std::get<2>(file_paths.at(reps)));
713
714 sd.run_maintenance_tasks(15 + (reps * width), {});
715 verify_directory_contents(tempdir.path(), files);
716
717 // trailing edge, no change
718 sd.run_maintenance_tasks(24 + (reps * width), {});
719 verify_directory_contents(tempdir.path(), files);
720 }
721
722 // make sure the test is correct and and no uncompressed files remain
723 for (const auto& e: file_paths) {
724 BOOST_REQUIRE_EQUAL(files.count(std::get<0>(e)), 1);
725 BOOST_REQUIRE_EQUAL(files.count(std::get<1>(e)), 0);
726 BOOST_REQUIRE_EQUAL(files.count(std::get<2>(e)), 1);
727 }
728 }
729
730 BOOST_FIXTURE_TEST_CASE(slice_dir_compress_and_delete, test_fixture)
731 {
732 fc::temp_directory tempdir;
733 const uint32_t width = 10;
734 const uint32_t min_uncompressed_blocks = 5;
735 const uint32_t min_saved_blocks = min_uncompressed_blocks + width;
736 slice_directory sd(tempdir.path(), width, std::optional<uint32_t>(min_saved_blocks), std::optional<uint32_t>(min_uncompressed_blocks), 8);
737 fc::cfile file;
738
739 using file_vector_t = std::vector<std::tuple<bfs::path, bfs::path, bfs::path>>;
740 file_vector_t file_paths;
741 for (int i = 0; i < 7 ; i++) {
742 BOOST_REQUIRE(!sd.find_or_create_index_slice(i, open_state::read, file));
743 auto index_name = file.get_file_path().filename();
744 BOOST_REQUIRE(create_non_empty_trace_slice(sd, i, file));
745 auto trace_name = file.get_file_path().filename();
746 auto compressed_trace_name = trace_name;
747 compressed_trace_name.replace_extension(".clog");
748 file_paths.emplace_back(index_name, trace_name, compressed_trace_name);
749 }
750
751 // initial set is only indices and uncompressed traces
752 std::set<bfs::path> files;
753 for (const auto& e: file_paths) {
754 files.insert(std::get<0>(e));
755 files.insert(std::get<1>(e));
756 }
757 verify_directory_contents(tempdir.path(), files);
758
759 // verify no change up to the last block before a slice becomes compressible
760 sd.run_maintenance_tasks(14, {});
761 verify_directory_contents(tempdir.path(), files);
762
763 for (std::size_t reps = 0; reps < file_paths.size() + 1; reps++) {
764 // leading edge,
765 // compresses one slice IF its not past the end of our test,
766 if (reps < file_paths.size()) {
767 files.erase(std::get<1>(file_paths.at(reps)));
768 files.insert(std::get<2>(file_paths.at(reps)));
769 }
770
771 // removes one IF its not the first
772 if (reps > 0) {
773 files.erase(std::get<0>(file_paths.at(reps-1)));
774 files.erase(std::get<2>(file_paths.at(reps-1)));
775 }
776 sd.run_maintenance_tasks(15 + (reps * width), {});
777 verify_directory_contents(tempdir.path(), files);
778
779 // trailing edge, no change
780 sd.run_maintenance_tasks(24 + (reps * width), {});
781 verify_directory_contents(tempdir.path(), files);
782 }
783
784 // make sure the test is correct and ran through the permutations
785 BOOST_REQUIRE_EQUAL(files.size(), 0);
786 }
787
788 BOOST_FIXTURE_TEST_CASE(store_provider_write_read_v1, test_fixture)
789 {
790 fc::temp_directory tempdir;
791 test_store_provider sp(tempdir.path(), 100);
792 sp.append(bt_v1);
793 sp.append_lib(54);
794 sp.append(bt2_v1);
795 const uint32_t bt_bn = bt_v1.number;
796 bool found_block = false;
797 bool lib_seen = false;
798 const uint64_t first_offset = sp.scan_metadata_log_from(9, 0, [&](const metadata_log_entry& e) -> bool {
799 if (std::holds_alternative<block_entry_v0>(e)) {
800 const auto& block = std::get<block_entry_v0>(e);
801 if (block.number == bt_bn) {
802 BOOST_REQUIRE(!found_block);
803 found_block = true;
804 }
805 } else if (std::holds_alternative<lib_entry_v0>(e)) {
806 auto best_lib = std::get<lib_entry_v0>(e);
807 BOOST_REQUIRE(!lib_seen);
808 BOOST_REQUIRE_EQUAL(best_lib.lib, 54);
809 lib_seen = true;
810 return false;
811 }
812 return true;
813 }, []() {});
814 BOOST_REQUIRE(found_block);
815 BOOST_REQUIRE(lib_seen);
816
817 std::vector<uint32_t> block_nums;
818 std::vector<uint64_t> block_offsets;
819 lib_seen = false;
820 uint64_t offset = sp.scan_metadata_log_from(9, 0, [&](const metadata_log_entry& e) -> bool {
821 if (std::holds_alternative<block_entry_v0>(e)) {
822 const auto& block = std::get<block_entry_v0>(e);
823 block_nums.push_back(block.number);
824 block_offsets.push_back(block.offset);
825 } else if (std::holds_alternative<lib_entry_v0>(e)) {
826 auto best_lib = std::get<lib_entry_v0>(e);
827 BOOST_REQUIRE(!lib_seen);
828 BOOST_REQUIRE_EQUAL(best_lib.lib, 54);
829 lib_seen = true;
830 }
831 return true;
832 }, []() {});
833 BOOST_REQUIRE(lib_seen);
834 BOOST_REQUIRE_EQUAL(block_nums.size(), 2);
835 BOOST_REQUIRE_EQUAL(block_nums[0], bt_v1.number);
836 BOOST_REQUIRE_EQUAL(block_nums[1], bt2_v1.number);
837 BOOST_REQUIRE_EQUAL(block_offsets.size(), 2);
838 BOOST_REQUIRE(block_offsets[0] < block_offsets[1]);
839 BOOST_REQUIRE(first_offset < offset);
840
841 std::optional<data_log_entry> bt_data = sp.read_data_log(block_nums[0], block_offsets[0]);
842 BOOST_REQUIRE_EQUAL(std::get<block_trace_v1>(*bt_data), bt_v1);
843
844 bt_data = sp.read_data_log(block_nums[1], block_offsets[1]);
845 BOOST_REQUIRE(bt_data);
846 auto v = std::variant<block_trace_v0, block_trace_v1, block_trace_v2>(*bt_data);
847 BOOST_REQUIRE_EQUAL(std::get<block_trace_v1>(v), bt2_v1);
848
849 block_nums.clear();
850 block_offsets.clear();
851 lib_seen = false;
852 int counter = 0;
853 try {
854 offset = sp.scan_metadata_log_from(9, 0, [&](const metadata_log_entry& e) -> bool {
855 if (std::holds_alternative<block_entry_v0>(e)) {
856 const auto& block = std::get<block_entry_v0>(e);
857 block_nums.push_back(block.number);
858 block_offsets.push_back(block.offset);
859 } else if (std::holds_alternative<lib_entry_v0>(e)) {
860 auto best_lib = std::get<lib_entry_v0>(e);
861 BOOST_REQUIRE(!lib_seen);
862 BOOST_REQUIRE_EQUAL(best_lib.lib, 54);
863 lib_seen = true;
864 }
865 return true;
866 }, [&counter]() {
867 if( ++counter == 3 ) {
868 throw yield_exception("");
869 }
870 });
871 BOOST_FAIL("Should not have completed scan");
872 } catch (const yield_exception& ex) {
873 }
874 BOOST_REQUIRE(lib_seen);
875 BOOST_REQUIRE_EQUAL(block_nums.size(), 1);
876 BOOST_REQUIRE_EQUAL(block_nums[0], bt_v1.number);
877 BOOST_REQUIRE_EQUAL(block_offsets.size(), 1);
878 BOOST_REQUIRE(first_offset < offset);
879 }
880
881 BOOST_FIXTURE_TEST_CASE(store_provider_write_read_v2, test_fixture)
882 {
883 fc::temp_directory tempdir;
884 test_store_provider sp(tempdir.path(), 100);
885 sp.append(block_trace1_v2);
886 sp.append_lib(54);
887 sp.append(block_trace2_v2);
888 const uint32_t bt_bn = block_trace1_v2.number;
889 bool found_block = false;
890 bool lib_seen = false;
891 const uint64_t first_offset = sp.scan_metadata_log_from(9, 0, [&](const metadata_log_entry& e) -> bool {
892 if (std::holds_alternative<block_entry_v0>(e)) {
893 const auto& block = std::get<block_entry_v0>(e);
894 if (block.number == bt_bn) {
895 BOOST_REQUIRE(!found_block);
896 found_block = true;
897 }
898 } else if (std::holds_alternative<lib_entry_v0>(e)) {
899 auto best_lib = std::get<lib_entry_v0>(e);
900 BOOST_REQUIRE(!lib_seen);
901 BOOST_REQUIRE_EQUAL(best_lib.lib, 54);
902 lib_seen = true;
903 return false;
904 }
905 return true;
906 }, []() {});
907 BOOST_REQUIRE(found_block);
908 BOOST_REQUIRE(lib_seen);
909
910 std::vector<uint32_t> block_nums;
911 std::vector<uint64_t> block_offsets;
912 lib_seen = false;
913 uint64_t offset = sp.scan_metadata_log_from(9, 0, [&](const metadata_log_entry& e) -> bool {
914 if (std::holds_alternative<block_entry_v0>(e)) {
915 const auto& block = std::get<block_entry_v0>(e);
916 block_nums.push_back(block.number);
917 block_offsets.push_back(block.offset);
918 } else if (std::holds_alternative<lib_entry_v0>(e)) {
919 auto best_lib = std::get<lib_entry_v0>(e);
920 BOOST_REQUIRE(!lib_seen);
921 BOOST_REQUIRE_EQUAL(best_lib.lib, 54);
922 lib_seen = true;
923 }
924 return true;
925 }, []() {});
926 BOOST_REQUIRE(lib_seen);
927 BOOST_REQUIRE_EQUAL(block_nums.size(), 2);
928 BOOST_REQUIRE_EQUAL(block_nums[0], block_trace1_v2.number);
929 BOOST_REQUIRE_EQUAL(block_nums[1], block_trace2_v2.number);
930 BOOST_REQUIRE_EQUAL(block_offsets.size(), 2);
931 BOOST_REQUIRE(block_offsets[0] < block_offsets[1]);
932 BOOST_REQUIRE(first_offset < offset);
933
934 std::optional<data_log_entry> bt_data = sp.read_data_log(block_nums[0], block_offsets[0]);
935 BOOST_REQUIRE_EQUAL(std::get<block_trace_v2>(*bt_data), block_trace1_v2);
936
937 bt_data = sp.read_data_log(block_nums[1], block_offsets[1]);
938 BOOST_REQUIRE(bt_data);
939 auto v = data_log_entry(*bt_data);
940 BOOST_REQUIRE_EQUAL(std::get<block_trace_v2>(v), block_trace2_v2);
941
942 block_nums.clear();
943 block_offsets.clear();
944 lib_seen = false;
945 int counter = 0;
946 try {
947 offset = sp.scan_metadata_log_from(9, 0, [&](const metadata_log_entry& e) -> bool {
948 if (std::holds_alternative<block_entry_v0>(e)) {
949 const auto& block = std::get<block_entry_v0>(e);
950 block_nums.push_back(block.number);
951 block_offsets.push_back(block.offset);
952 } else if (std::holds_alternative<lib_entry_v0>(e)) {
953 auto best_lib = std::get<lib_entry_v0>(e);
954 BOOST_REQUIRE(!lib_seen);
955 BOOST_REQUIRE_EQUAL(best_lib.lib, 54);
956 lib_seen = true;
957 }
958 return true;
959 }, [&counter]() {
960 if( ++counter == 3 ) {
961 throw yield_exception("");
962 }
963 });
964 BOOST_FAIL("Should not have completed scan");
965 } catch (const yield_exception& ex) {
966 }
967 BOOST_REQUIRE(lib_seen);
968 BOOST_REQUIRE_EQUAL(block_nums.size(), 1);
969 BOOST_REQUIRE_EQUAL(block_nums[0], block_trace1_v2.number);
970 BOOST_REQUIRE_EQUAL(block_offsets.size(), 1);
971 BOOST_REQUIRE(first_offset < offset);
972 }
973
974 BOOST_FIXTURE_TEST_CASE(test_get_block_v1, test_fixture)
975 {
976 fc::temp_directory tempdir;
977 store_provider sp(tempdir.path(), 100, std::optional<uint32_t>(), std::optional<uint32_t>(), 0);
978 sp.append(bt_v1);
979 sp.append_lib(1);
980 sp.append(bt2_v1);
981 int count = 0;
982 get_block_t block1 = sp.get_block(1, [&count]() {
983 if (++count >= 3) {
984 throw yield_exception("");
985 }
986 });
987 BOOST_REQUIRE(block1);
988 BOOST_REQUIRE(std::get<1>(*block1));
989 const auto block1_bt = std::get<0>(*block1);
990 BOOST_REQUIRE_EQUAL(std::get<block_trace_v1>(block1_bt), bt_v1);
991
992 count = 0;
993 get_block_t block2 = sp.get_block(5, [&count]() {
994 if (++count >= 4) {
995 throw yield_exception("");
996 }
997 });
998 BOOST_REQUIRE(block2);
999 BOOST_REQUIRE(!std::get<1>(*block2));
1000 const auto block2_bt = std::get<0>(*block2);
1001 BOOST_REQUIRE_EQUAL(std::get<block_trace_v1>(block2_bt), bt2_v1);
1002
1003 count = 0;
1004 try {
1005 sp.get_block(5,[&count]() {
1006 if (++count >= 3) {
1007 throw yield_exception("");
1008 }
1009 });
1010 BOOST_FAIL("Should not have completed scan");
1011 } catch (const yield_exception& ex) {
1012 }
1013
1014 count = 0;
1015 block2 = sp.get_block(2,[&count]() {
1016 if (++count >= 4) {
1017 throw yield_exception("");
1018 }
1019 });
1020 BOOST_REQUIRE(!block2);
1021 }
1022
1023 BOOST_FIXTURE_TEST_CASE(test_get_block_v2, test_fixture)
1024 {
1025 fc::temp_directory tempdir;
1026 store_provider sp(tempdir.path(), 100, std::optional<uint32_t>(), std::optional<uint32_t>(), 0);
1027 sp.append(block_trace1_v2);
1028 sp.append_lib(1);
1029 sp.append(block_trace2_v2);
1030 int count = 0;
1031 get_block_t block1 = sp.get_block(1, [&count]() {
1032 if (++count >= 3) {
1033 throw yield_exception("");
1034 }
1035 });
1036 BOOST_REQUIRE(block1);
1037 BOOST_REQUIRE(std::get<1>(*block1));
1038 const auto block1_bt = std::get<0>(*block1);
1039 BOOST_REQUIRE_EQUAL(std::get<block_trace_v2>(block1_bt), block_trace1_v2);
1040
1041 count = 0;
1042 get_block_t block2 = sp.get_block(5, [&count]() {
1043 if (++count >= 4) {
1044 throw yield_exception("");
1045 }
1046 });
1047 BOOST_REQUIRE(block2);
1048 BOOST_REQUIRE(!std::get<1>(*block2));
1049 const auto block2_bt = std::get<0>(*block2);
1050 BOOST_REQUIRE_EQUAL(std::get<block_trace_v2>(block2_bt), block_trace2_v2);
1051
1052 count = 0;
1053 try {
1054 sp.get_block(5,[&count]() {
1055 if (++count >= 3) {
1056 throw yield_exception("");
1057 }
1058 });
1059 BOOST_FAIL("Should not have completed scan");
1060 } catch (const yield_exception& ex) {
1061 }
1062
1063 count = 0;
1064 block2 = sp.get_block(2,[&count]() {
1065 if (++count >= 4) {
1066 throw yield_exception("");
1067 }
1068 });
1069 BOOST_REQUIRE(!block2);
1070 }
1071
1072
1073BOOST_AUTO_TEST_SUITE_END()
void close()
Definition cfile.hpp:202
fc::path get_file_path() const
Definition cfile.hpp:41
size_t tellp() const
Definition cfile.hpp:79
bool is_open() const
Definition cfile.hpp:45
wraps boost::filesystem::path to provide platform independent path manipulation.
fc::path parent_path() const
fc::path filename() const
std::string generic_string() const
const fc::path & path() const
bool find_or_create_index_slice(uint32_t slice_number, open_state state, fc::cfile &index_file) const
bool find_trace_slice(uint32_t slice_number, open_state state, fc::cfile &trace_file, bool open_file=true) const
bool find_or_create_trace_slice(uint32_t slice_number, open_state state, fc::cfile &trace_file) const
uint32_t slice_number(uint32_t block_height) const
void run_maintenance_tasks(uint32_t lib, const log_handler &log)
uint64_t scan_metadata_log_from(uint32_t block_height, uint64_t offset, Fn &&fn, const yield_function &yield)
std::optional< data_log_entry > read_data_log(uint32_t block_height, uint64_t offset)
int * count
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
chain::bytes make_transfer_data(chain::name from, chain::name to, chain::asset quantity, std::string &&memo)
std::optional< std::tuple< data_log_entry, bool > > get_block_t
Definition common.hpp:49
std::variant< block_entry_v0, lib_entry_v0, block_trxs_entry > metadata_log_entry
std::variant< block_trace_v0, block_trace_v1, block_trace_v2 > data_log_entry
Definition data_log.hpp:9
unsigned int uint32_t
Definition stdint.h:126
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
BOOST_FIXTURE_TEST_CASE(write_data_trace, test_fixture)
void verify_directory_contents(const bfs::path &tempdir, std::set< bfs::path > expected_files)
CK_ULONG d
char * s