15#include <boost/test/unit_test.hpp>
17using namespace std::string_literals;
26 auto apply_random_xor_chunks{[&](std::span<std::byte> target,
const Obfuscation& obfuscation) {
27 for (
size_t offset{0}; offset < target.size();) {
28 const size_t chunk_size{1 + m_rng.randrange(target.size() - offset)};
29 obfuscation(target.subspan(offset, chunk_size), offset);
34 for (
size_t test{0}; test < 100; ++test) {
35 const size_t write_size{1 + m_rng.randrange(100U)};
36 const std::vector original{m_rng.randbytes<std::byte>(write_size)};
37 std::vector roundtrip{original};
39 const auto key_bytes{m_rng.randbool() ? m_rng.randbytes<
Obfuscation::KEY_SIZE>() : std::array<std::byte, Obfuscation::KEY_SIZE>{}};
41 apply_random_xor_chunks(roundtrip, obfuscation);
43 for (
size_t i{0}; i < original.size(); ++i) {
47 apply_random_xor_chunks(roundtrip, obfuscation);
48 BOOST_CHECK_EQUAL_COLLECTIONS(roundtrip.begin(), roundtrip.end(), original.begin(), original.end());
73 std::vector<std::byte> key_out;
75 ds_out << obfuscation;
79 BOOST_CHECK_EQUAL_COLLECTIONS(key_in.begin(), key_in.end(), key_out.begin(), key_out.end());
87 const Obfuscation non_null_obf{
"ff00ff00ff00ff00"_hex};
96 stream << uint8_t{42};
97 BOOST_CHECK_GT(stream.size(), 0U);
103 stream << uint16_t{42};
104 BOOST_CHECK_GT(stream.size(), 0U);
111 fs::path xor_path{m_args.GetDataDirBase() /
"test_xor.bin"};
112 auto raw_file{[&](
const auto& mode) {
return fsbridge::fopen(xor_path, mode); }};
113 const std::vector<uint8_t>
test1{1, 2, 3};
114 const std::vector<uint8_t> test2{4, 5};
115 const Obfuscation obfuscation{
"ff00ff00ff00ff00"_hex};
119 AutoFile xor_file{raw_file(
"rb"), obfuscation};
120 BOOST_CHECK_EXCEPTION(xor_file << std::byte{}, std::ios_base::failure,
HasReason{
"AutoFile::write: file handle is nullptr"});
121 BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure,
HasReason{
"AutoFile::read: file handle is nullptr"});
122 BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure,
HasReason{
"AutoFile::ignore: file handle is nullptr"});
123 BOOST_CHECK_EXCEPTION(xor_file.size(), std::ios_base::failure,
HasReason{
"AutoFile::size: file handle is nullptr"});
128 const char* mode =
"wb";
130 const char* mode =
"wbx";
132 AutoFile xor_file{raw_file(mode), obfuscation};
133 xor_file <<
test1 << test2;
135 BOOST_REQUIRE_EQUAL(xor_file.fclose(), 0);
139 AutoFile non_xor_file{raw_file(
"rb")};
140 std::vector<std::byte> raw(7);
141 non_xor_file >> std::span{raw};
144 BOOST_CHECK_EXCEPTION(non_xor_file.ignore(1), std::ios_base::failure,
HasReason{
"AutoFile::ignore: end of file"});
148 AutoFile xor_file{raw_file(
"rb"), obfuscation};
149 std::vector<std::byte> read1, read2;
150 xor_file >> read1 >> read2;
154 BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure,
HasReason{
"AutoFile::read: end of file"});
158 AutoFile xor_file{raw_file(
"rb"), obfuscation};
159 std::vector<std::byte> read2;
165 BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure,
HasReason{
"AutoFile::ignore: end of file"});
166 BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure,
HasReason{
"AutoFile::read: end of file"});
175 unsigned char bytes[] = {3, 4, 5, 6};
176 std::vector<unsigned char> vch;
183 BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
185 BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
189 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
191 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
196 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
198 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
203 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
205 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
210 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
212 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
216 BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
218 BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
223 BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
225 BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
233 unsigned char bytes[] = {3, 4, 5, 6};
234 std::array<std::byte, 8> arr{};
242 SpanWriter{std::span{arr}.subspan(2), a, bytes, b};
246 std::array<std::byte, 1> small{};
253 std::vector<unsigned char> vch = {1, 255, 3, 4, 5, 6};
298 std::vector<uint8_t>
data{0x82, 0xa7, 0x31};
312 bit_writer.Write(0, 1);
313 bit_writer.Write(2, 2);
314 bit_writer.Write(6, 3);
315 bit_writer.Write(11, 4);
316 bit_writer.Write(1, 5);
317 bit_writer.Write(32, 6);
318 bit_writer.Write(7, 7);
319 bit_writer.Write(30497, 16);
323 uint32_t serialized_int1;
324 data >> serialized_int1;
326 uint16_t serialized_int2;
327 data >> serialized_int2;
352 const Obfuscation obfuscation{
"ffffffffffffffff"_hex};
360 const Obfuscation obfuscation{
"ff0fff0fff0fff0f"_hex};
370 fs::path streams_test_filename = m_args.GetDataDirBase() /
"streams_test_tmp";
374 for (uint8_t j = 0; j < 40; ++j) {
377 file.seek(0, SEEK_SET);
384 }
catch (
const std::exception& e) {
386 "Rewind limit must be less than buffer size") !=
nullptr);
420 }
catch (
const std::exception& e) {
422 "Attempt to position past buffer limit") !=
nullptr);
430 for (uint8_t j = 3; j < 10; ++j) {
463 for (uint8_t j = 0; j <
sizeof(a); ++j) {
473 }
catch (
const std::exception& e) {
475 "BufferedFile::Fill: end of file") !=
nullptr);
493 BOOST_REQUIRE_EQUAL(file.fclose(), 0);
495 fs::remove(streams_test_filename);
500 fs::path streams_test_filename = m_args.GetDataDirBase() /
"streams_test_tmp";
503 for (uint8_t j = 0; j < 40; ++j) {
506 file.seek(0, SEEK_SET);
535 }
catch (
const std::exception& e) {
536 BOOST_CHECK(strstr(e.what(),
"Attempt to position past buffer limit") !=
nullptr);
543 BOOST_REQUIRE_EQUAL(file.fclose(), 0);
544 fs::remove(streams_test_filename);
552 fs::path streams_test_filename = m_args.GetDataDirBase() /
"streams_test_tmp";
553 for (
int rep = 0; rep < 50; ++rep) {
555 size_t fileSize = m_rng.randrange(256);
556 for (uint8_t i = 0; i < fileSize; ++i) {
559 file.seek(0, SEEK_SET);
561 size_t bufSize = m_rng.randrange(300) + 1;
562 size_t rewindSize = m_rng.randrange(bufSize);
564 size_t currentPos = 0;
566 for (
int step = 0; step < 100; ++step) {
567 if (currentPos >= fileSize)
578 switch (m_rng.randrange(6)) {
581 if (currentPos + 1 > fileSize)
583 bf.SetLimit(currentPos + 1);
585 for (uint8_t i = 0; i < 1; ++i) {
593 if (currentPos + 2 > fileSize)
595 bf.SetLimit(currentPos + 2);
597 for (uint8_t i = 0; i < 2; ++i) {
605 if (currentPos + 5 > fileSize)
607 bf.SetLimit(currentPos + 5);
609 for (uint8_t i = 0; i < 5; ++i) {
618 size_t skip_length{
static_cast<size_t>(m_rng.randrange(5))};
619 if (currentPos + skip_length > fileSize)
continue;
620 bf.SetLimit(currentPos + skip_length);
621 bf.SkipTo(currentPos + skip_length);
622 currentPos += skip_length;
627 size_t find = currentPos + m_rng.randrange(8);
628 if (find >= fileSize)
630 bf.FindByte(std::byte(find));
635 bf.SetLimit(currentPos + 1);
643 size_t requestPos = m_rng.randrange(maxPos + 4);
644 bool okay = bf.SetPos(requestPos);
649 currentPos = bf.GetPos();
652 if (requestPos <= maxPos &&
653 maxPos > rewindSize &&
654 requestPos >= maxPos - rewindSize) {
661 if (maxPos < currentPos)
664 BOOST_REQUIRE_EQUAL(file.fclose(), 0);
666 fs::remove(streams_test_filename);
671 const size_t file_size{1 + m_rng.randrange<
size_t>(1 << 17)};
672 const size_t buf_size{1 + m_rng.randrange(file_size)};
680 AutoFile f{test_file.Open(pos,
false), obfuscation};
681 f.
write(m_rng.randbytes<std::byte>(file_size));
682 BOOST_REQUIRE_EQUAL(f.fclose(), 0);
687 AutoFile direct_file{test_file.Open(pos,
true), obfuscation};
689 AutoFile buffered_file{test_file.Open(pos,
true), obfuscation};
690 BufferedReader buffered_reader{std::move(buffered_file), buf_size};
692 for (
size_t total_read{0}; total_read < file_size;) {
693 const size_t read{
Assert(std::min(1 + m_rng.randrange(m_rng.randbool() ? buf_size : 2 * buf_size), file_size - total_read))};
696 direct_file.read(direct_file_buffer);
699 buffered_reader.read(buffered_buffer);
701 BOOST_CHECK_EQUAL_COLLECTIONS(
702 direct_file_buffer.begin(), direct_file_buffer.end(),
703 buffered_buffer.begin(), buffered_buffer.end()
711 BOOST_CHECK_EXCEPTION(direct_file.read(excess_byte), std::ios_base::failure,
HasReason{
"end of file"});
716 BOOST_CHECK_EXCEPTION(buffered_reader.read(excess_byte), std::ios_base::failure,
HasReason{
"end of file"});
720 fs::remove(test_file.FileName(pos));
725 const size_t file_size{1 + m_rng.randrange<
size_t>(1 << 17)};
726 const size_t buf_size{1 + m_rng.randrange(file_size)};
734 DataBuffer test_data{m_rng.randbytes<std::byte>(file_size)};
736 AutoFile direct_file{test_direct.Open(pos,
false), obfuscation};
738 AutoFile buffered_file{test_buffered.Open(pos,
false), obfuscation};
742 for (
size_t total_written{0}; total_written < file_size;) {
743 const size_t write_size{
Assert(std::min(1 + m_rng.randrange(m_rng.randbool() ? buf_size : 2 * buf_size), file_size - total_written))};
745 auto current_span = std::span{test_data}.subspan(total_written, write_size);
746 direct_file.write(current_span);
747 buffered.write(current_span);
749 total_written += write_size;
752 BOOST_REQUIRE_EQUAL(buffered_file.fclose(), 0);
753 BOOST_REQUIRE_EQUAL(direct_file.fclose(), 0);
759 AutoFile verify_direct{test_direct.Open(pos,
true), obfuscation};
760 verify_direct.
read(direct_result);
763 BOOST_CHECK_EXCEPTION(verify_direct.read(excess_byte), std::ios_base::failure,
HasReason{
"end of file"});
768 AutoFile verify_buffered{test_buffered.Open(pos,
true), obfuscation};
769 verify_buffered.
read(buffered_result);
772 BOOST_CHECK_EXCEPTION(verify_buffered.read(excess_byte), std::ios_base::failure,
HasReason{
"end of file"});
775 BOOST_CHECK_EQUAL_COLLECTIONS(
776 direct_result.begin(), direct_result.end(),
777 buffered_result.begin(), buffered_result.end()
780 fs::remove(test_direct.FileName(pos));
781 fs::remove(test_buffered.FileName(pos));
786 const uint32_t v1{m_rng.rand32()}, v2{m_rng.rand32()}, v3{m_rng.rand32()};
787 const fs::path test_file{m_args.GetDataDirBase() /
"test_buffered_write_read.bin"};
794 f.
write(std::as_bytes(std::span{&v3, 1}));
796 BOOST_REQUIRE_EQUAL(file.fclose(), 0);
800 uint32_t _v1{0}, _v2{0}, _v3{0};
802 BufferedReader f(std::move(file),
sizeof(v1) +
sizeof(v2) +
sizeof(v3));
804 f.
read(std::as_writable_bytes(std::span{&_v3, 1}));
810 BOOST_CHECK_EXCEPTION(f.
read(excess_byte), std::ios_base::failure,
HasReason{
"end of file"});
813 fs::remove(test_file);
820 const std::string
data{
"bitcoin"};
825 hash_verifier >> result;
832 const fs::path path = m_args.GetDataDirBase() /
"size_pos_test.bin";
834 for (uint8_t j = 0; j < 10; ++j) {
861 BOOST_CHECK_EXCEPTION(f >> end, std::ios_base::failure,
HasReason{
"AutoFile::read: end of file"});
863 BOOST_REQUIRE_EQUAL(f.fclose(), 0);
#define Assert(val)
Identity function.
Non-refcounted RAII wrapper for FILE*.
void ignore(size_t nSize)
void write(std::span< const std::byte > src)
void read(std::span< std::byte > dst)
Wrapper around an AutoFile& that implements a ring buffer to deserialize from.
void SkipTo(const uint64_t file_pos)
Move the read position ahead in the stream to the given position.
Wrapper that buffers reads from an underlying stream.
void read(std::span< std::byte > dst)
Wrapper that buffers writes to an underlying stream.
void write(std::span< const std::byte > src)
Double ended buffer combining vector and stream-like interfaces.
FlatFileSeq represents a sequence of numbered files storing raw data.
BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
Reads data from an underlying stream, while hashing the read data.
Writes data to an underlying source stream, while hashing the written data.
static constexpr size_t KEY_SIZE
Minimal stream for reading from an existing byte array by std::span.
Minimal stream for writing to an existing span of bytes.
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
FILE * fopen(const fs::path &p, const char *mode)
static const unsigned int BLOCKFILE_CHUNK_SIZE
The pre-allocation chunk size for blk?????.dat files (since 0.8)
""_hex is a compile-time user-defined literal returning a std::array<std::byte>, equivalent to ParseH...
#define BOOST_CHECK_THROW(stmt, excMatch)
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
std::vector< std::byte > DataBuffer
BOOST_AUTO_TEST_CASE(xor_random_chunks)
@ ZEROS
Seed with a compile time constant of zeros.