From 18308657b70ffab91480af11ce68142baddb81d4 Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Thu, 27 Oct 2022 13:27:54 +0200 Subject: [PATCH] Fix: Allow block count > UINT32_MAX (#3979) The method `ledger::hash_root_random()` required the block count to fit into a uint32_t. This limit is too small to be future-proof. This commit increases the limit to UINT64_MAX, so that it matches the other places where block count is a uint64_t. --- nano/core_test/uint256_union.cpp | 26 ++++++++++++++++++++++++++ nano/crypto_lib/random_pool.cpp | 19 +++++++++++++++++++ nano/crypto_lib/random_pool.hpp | 2 ++ nano/secure/ledger.cpp | 3 +-- 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/nano/core_test/uint256_union.cpp b/nano/core_test/uint256_union.cpp index 975ba865..c85c6127 100644 --- a/nano/core_test/uint256_union.cpp +++ b/nano/core_test/uint256_union.cpp @@ -600,3 +600,29 @@ TEST (random_pool, multithreading) i.join (); } } + +// Test that random 64bit numbers are within the given range +TEST (random_pool, generate_word64) +{ + int occurrences[10] = { 0 }; + for (auto i = 0; i < 1000; ++i) + { + auto random = nano::random_pool::generate_word64 (1, 9); + ASSERT_TRUE (random >= 1 && random <= 9); + occurrences[random] += 1; + } + + for (auto i = 1; i < 10; ++i) + { + ASSERT_TRUE (occurrences[i] > 0); + } +} + +// Test random numbers > uint32 max +TEST (random_pool, generate_word64_big_number) +{ + uint64_t min = static_cast (std::numeric_limits::max ()) + 1; + uint64_t max = std::numeric_limits::max (); + auto big_random = nano::random_pool::generate_word64 (min, max); + ASSERT_TRUE (big_random >= min); +} diff --git a/nano/crypto_lib/random_pool.cpp b/nano/crypto_lib/random_pool.cpp index 398466dc..aad17177 100644 --- a/nano/crypto_lib/random_pool.cpp +++ b/nano/crypto_lib/random_pool.cpp @@ -1,5 +1,6 @@ #include +#include #include void nano::random_pool::generate_block (unsigned char * output, size_t size) @@ -14,6 +15,24 @@ unsigned nano::random_pool::generate_word32 (unsigned min, unsigned max) return pool.GenerateWord32 (min, max); } +uint64_t nano::random_pool::generate_word64 (uint64_t min, uint64_t max) +{ + auto & pool = get_pool (); + + const auto range = max - min; + const auto max_bits = CryptoPP::BitPrecision (range); + + uint64_t value; + + do + { + pool.GenerateBlock ((unsigned char *)&value, sizeof (value)); + value = CryptoPP::Crop (value, max_bits); + } while (value > range); + + return value + min; +} + unsigned char nano::random_pool::generate_byte () { auto & pool = get_pool (); diff --git a/nano/crypto_lib/random_pool.hpp b/nano/crypto_lib/random_pool.hpp index eddfcc0c..e3b8b6fe 100644 --- a/nano/crypto_lib/random_pool.hpp +++ b/nano/crypto_lib/random_pool.hpp @@ -15,6 +15,8 @@ class random_pool public: static void generate_block (unsigned char * output, size_t size); static unsigned generate_word32 (unsigned min, unsigned max); + /** Generates a random uint64_t in the range min to max. min and max are inclusive. */ + static uint64_t generate_word64 (uint64_t min, uint64_t max); static unsigned char generate_byte (); random_pool () = delete; diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 11544e11..fdaff753 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1004,8 +1004,7 @@ std::pair nano::ledger::hash_root_random (na else { uint64_t count (cache.block_count); - release_assert (std::numeric_limits::max () > count); - auto region = static_cast (nano::random_pool::generate_word32 (0, static_cast (count - 1))); + auto region = nano::random_pool::generate_word64 (0, count - 1); // Pruned cache cannot guarantee that pruned blocks are already commited if (region < cache.pruned_count) {