// Copyright (c) 2018-2022, The Monero Project // // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "gtest/gtest.h" #include "misc_log_ex.h" #include "cryptonote_config.h" #include "common/pruning.h" #define ASSERT_EX(x) do { bool ex = false; try { x; } catch(...) { ex = true; } ASSERT_TRUE(ex); } while(0) TEST(pruning, parts) { ASSERT_EQ(tools::get_pruning_stripe(tools::make_pruning_seed(3, 2)), 3); ASSERT_EQ(tools::get_pruning_stripe(tools::make_pruning_seed(1, 2)), 1); ASSERT_EQ(tools::get_pruning_stripe(tools::make_pruning_seed(7, 7)), 7); ASSERT_EQ(tools::get_pruning_log_stripes(tools::make_pruning_seed(1, 2)), 2); ASSERT_EQ(tools::get_pruning_log_stripes(tools::make_pruning_seed(1, 0)), 0); ASSERT_EQ(tools::get_pruning_log_stripes(tools::make_pruning_seed(1, 7)), 7); ASSERT_EQ(tools::get_pruning_log_stripes(tools::make_pruning_seed(7, 7)), 7); for (uint32_t log_stripes = 1; log_stripes <= tools::PRUNING_SEED_LOG_STRIPES_MASK; ++log_stripes) { for (uint32_t stripe = 1; stripe <= (1 << log_stripes); ++stripe) { ASSERT_EQ(tools::get_pruning_log_stripes(tools::make_pruning_seed(stripe, log_stripes)), log_stripes); ASSERT_EQ(tools::get_pruning_stripe(tools::make_pruning_seed(stripe, log_stripes)), stripe); } } } TEST(pruning, out_of_range) { ASSERT_EX(tools::make_pruning_seed(5, 2)); ASSERT_EX(tools::make_pruning_seed(0, 2)); } TEST(pruning, fits) { const uint32_t log_stripes = 3; const uint32_t num_stripes = 1 << log_stripes; for (uint32_t stripe = 1; stripe <= num_stripes; ++stripe) { const uint32_t seed = tools::make_pruning_seed(stripe, log_stripes); ASSERT_NE(seed, 0); ASSERT_EQ(tools::get_pruning_log_stripes(seed), log_stripes); ASSERT_EQ(tools::get_pruning_stripe(seed), stripe); } } TEST(pruning, tip) { static constexpr uint64_t H = CRYPTONOTE_PRUNING_TIP_BLOCKS + 1000; static_assert(H >= CRYPTONOTE_PRUNING_TIP_BLOCKS, "H must be >= CRYPTONOTE_PRUNING_TIP_BLOCKS"); for (uint64_t h = H - CRYPTONOTE_PRUNING_TIP_BLOCKS; h < H; ++h) { uint32_t pruning_seed = tools::get_pruning_seed(h, H, CRYPTONOTE_PRUNING_LOG_STRIPES); ASSERT_EQ(pruning_seed, 0); for (pruning_seed = 0; pruning_seed <= (1 << CRYPTONOTE_PRUNING_LOG_STRIPES); ++pruning_seed) ASSERT_TRUE(tools::has_unpruned_block(h, H, pruning_seed)); } } TEST(pruning, seed) { const uint64_t SS = CRYPTONOTE_PRUNING_STRIPE_SIZE; const uint64_t NS = 1 << CRYPTONOTE_PRUNING_LOG_STRIPES; const uint64_t TB = NS * SS; for (uint64_t cycle = 0; cycle < 10; ++cycle) { const uint64_t O = TB * cycle; ASSERT_EQ(tools::get_pruning_stripe(O + 0, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 1); ASSERT_EQ(tools::get_pruning_stripe(O + 1, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 1); ASSERT_EQ(tools::get_pruning_stripe(O + SS-1, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 1); ASSERT_EQ(tools::get_pruning_stripe(O + SS, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 2); ASSERT_EQ(tools::get_pruning_stripe(O + SS*2-1, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 2); ASSERT_EQ(tools::get_pruning_stripe(O + SS*2, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 3); ASSERT_EQ(tools::get_pruning_stripe(O + SS*NS-1, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), NS); ASSERT_EQ(tools::get_pruning_stripe(O + SS*NS, 10000000, CRYPTONOTE_PRUNING_LOG_STRIPES), 1); } } TEST(pruning, match) { static constexpr uint64_t H = CRYPTONOTE_PRUNING_TIP_BLOCKS + 1000; static_assert(H >= CRYPTONOTE_PRUNING_TIP_BLOCKS, "H must be >= CRYPTONOTE_PRUNING_TIP_BLOCKS"); for (uint64_t h = 0; h < H - CRYPTONOTE_PRUNING_TIP_BLOCKS; ++h) { uint32_t pruning_seed = tools::get_pruning_seed(h, H, CRYPTONOTE_PRUNING_LOG_STRIPES); uint32_t pruning_stripe = tools::get_pruning_stripe(pruning_seed); ASSERT_TRUE(pruning_stripe > 0 && pruning_stripe <= (1 << CRYPTONOTE_PRUNING_LOG_STRIPES)); for (uint32_t other_pruning_stripe = 1; other_pruning_stripe <= (1 << CRYPTONOTE_PRUNING_LOG_STRIPES); ++other_pruning_stripe) { uint32_t other_pruning_seed = tools::make_pruning_seed(other_pruning_stripe, CRYPTONOTE_PRUNING_LOG_STRIPES); ASSERT_TRUE(tools::has_unpruned_block(h, H, other_pruning_seed) == (other_pruning_seed == pruning_seed)); } } } TEST(pruning, stripe_size) { static constexpr uint64_t H = CRYPTONOTE_PRUNING_TIP_BLOCKS + CRYPTONOTE_PRUNING_STRIPE_SIZE * (1 << CRYPTONOTE_PRUNING_LOG_STRIPES) + 1000; static_assert(H >= CRYPTONOTE_PRUNING_TIP_BLOCKS + CRYPTONOTE_PRUNING_STRIPE_SIZE * (1 << CRYPTONOTE_PRUNING_LOG_STRIPES), "H must be >= that stuff in front"); for (uint32_t pruning_stripe = 1; pruning_stripe <= (1 << CRYPTONOTE_PRUNING_LOG_STRIPES); ++pruning_stripe) { uint32_t pruning_seed = tools::make_pruning_seed(pruning_stripe, CRYPTONOTE_PRUNING_LOG_STRIPES); unsigned int current_run = 0, best_run = 0; for (uint64_t h = 0; h < H - CRYPTONOTE_PRUNING_TIP_BLOCKS; ++h) { if (tools::has_unpruned_block(h, H, pruning_seed)) { ++current_run; } else if (current_run) { ASSERT_EQ(current_run, CRYPTONOTE_PRUNING_STRIPE_SIZE); best_run = std::max(best_run, current_run); current_run = 0; } } ASSERT_EQ(best_run, CRYPTONOTE_PRUNING_STRIPE_SIZE); } } TEST(pruning, next_unpruned) { static_assert((1 << CRYPTONOTE_PRUNING_LOG_STRIPES) >= 4, "CRYPTONOTE_PRUNING_LOG_STRIPES too low"); const uint64_t SS = CRYPTONOTE_PRUNING_STRIPE_SIZE; const uint64_t NS = 1 << CRYPTONOTE_PRUNING_LOG_STRIPES; const uint64_t TB = NS * SS; for (uint64_t h = 0; h < 100; ++h) ASSERT_EQ(tools::get_next_unpruned_block_height(h, 10000000, 0), h); const uint32_t seed1 = tools::make_pruning_seed(1, CRYPTONOTE_PRUNING_LOG_STRIPES); const uint32_t seed2 = tools::make_pruning_seed(2, CRYPTONOTE_PRUNING_LOG_STRIPES); const uint32_t seed3 = tools::make_pruning_seed(3, CRYPTONOTE_PRUNING_LOG_STRIPES); const uint32_t seed4 = tools::make_pruning_seed(4, CRYPTONOTE_PRUNING_LOG_STRIPES); const uint32_t seedNS = tools::make_pruning_seed(NS, CRYPTONOTE_PRUNING_LOG_STRIPES); ASSERT_EQ(tools::get_next_unpruned_block_height(0, 10000000, seed1), 0); ASSERT_EQ(tools::get_next_unpruned_block_height(1, 10000000, seed1), 1); ASSERT_EQ(tools::get_next_unpruned_block_height(SS-1, 10000000, seed1), SS-1); ASSERT_EQ(tools::get_next_unpruned_block_height(SS, 10000000, seed1), TB); ASSERT_EQ(tools::get_next_unpruned_block_height(TB, 10000000, seed1), TB); ASSERT_EQ(tools::get_next_unpruned_block_height(TB-1, 10000000, seed1), TB); ASSERT_EQ(tools::get_next_unpruned_block_height(0, 10000000, seed2), SS); ASSERT_EQ(tools::get_next_unpruned_block_height(1, 10000000, seed2), SS); ASSERT_EQ(tools::get_next_unpruned_block_height(SS-1, 10000000, seed2), SS); ASSERT_EQ(tools::get_next_unpruned_block_height(SS, 10000000, seed2), SS); ASSERT_EQ(tools::get_next_unpruned_block_height(2*SS-1, 10000000, seed2), 2*SS-1); ASSERT_EQ(tools::get_next_unpruned_block_height(2*SS, 10000000, seed2), TB+SS); ASSERT_EQ(tools::get_next_unpruned_block_height(TB+2*SS,10000000, seed2), TB*2+SS); ASSERT_EQ(tools::get_next_unpruned_block_height(0, 10000000, seed3), SS*2); ASSERT_EQ(tools::get_next_unpruned_block_height(SS, 10000000, seed3), SS*2); ASSERT_EQ(tools::get_next_unpruned_block_height(2*SS, 10000000, seed3), SS*2); ASSERT_EQ(tools::get_next_unpruned_block_height(3*SS-1, 10000000, seed3), SS*3-1); ASSERT_EQ(tools::get_next_unpruned_block_height(3*SS, 10000000, seed3), TB+SS*2); ASSERT_EQ(tools::get_next_unpruned_block_height(TB+3*SS,10000000, seed3), TB*2+SS*2); ASSERT_EQ(tools::get_next_unpruned_block_height(SS, 10000000, seed4), 3*SS); ASSERT_EQ(tools::get_next_unpruned_block_height(4*SS-1, 10000000, seed4), 4*SS-1); ASSERT_EQ(tools::get_next_unpruned_block_height(4*SS, 10000000, seed4), TB+3*SS); ASSERT_EQ(tools::get_next_unpruned_block_height(TB+4*SS,10000000, seed4), TB*2+3*SS); ASSERT_EQ(tools::get_next_unpruned_block_height(SS, 10000000, seedNS), (NS-1)*SS); ASSERT_EQ(tools::get_next_unpruned_block_height(NS*SS-1,10000000, seedNS), NS*SS-1); ASSERT_EQ(tools::get_next_unpruned_block_height(NS*SS, 10000000, seedNS), TB+(NS-1)*SS); ASSERT_EQ(tools::get_next_unpruned_block_height(TB+NS*SS, 10000000, seedNS), TB*2+(NS-1)*SS); } TEST(pruning, next_pruned) { static_assert((1 << CRYPTONOTE_PRUNING_LOG_STRIPES) >= 4, "CRYPTONOTE_PRUNING_LOG_STRIPES too low"); const uint64_t SS = CRYPTONOTE_PRUNING_STRIPE_SIZE; const uint64_t NS = 1 << CRYPTONOTE_PRUNING_LOG_STRIPES; const uint64_t TB = NS * SS; const uint32_t seed1 = tools::make_pruning_seed(1, CRYPTONOTE_PRUNING_LOG_STRIPES); const uint32_t seed2 = tools::make_pruning_seed(2, CRYPTONOTE_PRUNING_LOG_STRIPES); const uint32_t seedNS_1 = tools::make_pruning_seed(NS-1, CRYPTONOTE_PRUNING_LOG_STRIPES); const uint32_t seedNS = tools::make_pruning_seed(NS, CRYPTONOTE_PRUNING_LOG_STRIPES); for (uint64_t h = 0; h < 100; ++h) ASSERT_EQ(tools::get_next_pruned_block_height(h, 10000000, 0), 10000000); for (uint64_t h = 10000000 - 1 - CRYPTONOTE_PRUNING_TIP_BLOCKS; h < 10000000; ++h) ASSERT_EQ(tools::get_next_pruned_block_height(h, 10000000, 0), 10000000); ASSERT_EQ(tools::get_next_pruned_block_height(1, 10000000, seed1), SS); ASSERT_EQ(tools::get_next_pruned_block_height(SS-1, 10000000, seed1), SS); ASSERT_EQ(tools::get_next_pruned_block_height(SS, 10000000, seed1), SS); ASSERT_EQ(tools::get_next_pruned_block_height(TB-1, 10000000, seed1), TB-1); ASSERT_EQ(tools::get_next_pruned_block_height(TB, 10000000, seed1), TB+SS); ASSERT_EQ(tools::get_next_pruned_block_height(1, 10000000, seed2), 1); ASSERT_EQ(tools::get_next_pruned_block_height(SS-1, 10000000, seed2), SS-1); ASSERT_EQ(tools::get_next_pruned_block_height(SS, 10000000, seed2), 2*SS); ASSERT_EQ(tools::get_next_pruned_block_height(TB-1, 10000000, seed2), TB-1); ASSERT_EQ(tools::get_next_pruned_block_height(1, 10000000, seedNS_1), 1); ASSERT_EQ(tools::get_next_pruned_block_height(SS-1, 10000000, seedNS_1), SS-1); ASSERT_EQ(tools::get_next_pruned_block_height(SS, 10000000, seedNS_1), SS); ASSERT_EQ(tools::get_next_pruned_block_height(TB-1, 10000000, seedNS_1), TB-1); ASSERT_EQ(tools::get_next_pruned_block_height(1, 10000000, seedNS), 1); ASSERT_EQ(tools::get_next_pruned_block_height(SS-1, 10000000, seedNS), SS-1); ASSERT_EQ(tools::get_next_pruned_block_height(SS, 10000000, seedNS), SS); ASSERT_EQ(tools::get_next_pruned_block_height(TB-1, 10000000, seedNS), TB); }