Merge bitcoin/bitcoin#27189: util: Use steady clock in SeedStrengthen, FindBestImplementation, FlushStateToDisk

fa1b4e5c32 Use steady clock in FlushStateToDisk (MarcoFalke)
1111e2f8b4 Use steady clock in SeedStrengthen and FindBestImplementation (MarcoFalke)

Pull request description:

  There may be a theoretical deadlock for the duration of the offset when the system clock is adjusted into a past time while executing `SeedStrengthen`.

  Fix this by using steady clock.

  Do the same in `FindBestImplementation`, which shouldn't be affected, because it discards outlier measurements. However, doing the same there for consistency seems fine.

  Do the same in `FlushStateToDisk`, which should make the flushes more steady, if the system clock is adjusted by a large offset.

ACKs for top commit:
  john-moffett:
    ACK fa1b4e5c32
  willcl-ark:
    ACK fa1b4e5c3

Tree-SHA512: cc625e796b186accd53222bd64eb57d0512bc7e588312d254349b542bbc5e5daac348ff2b3b3f7dc5ae0bbbae2ec11fdbf3022cf2164211633765a4b0108e83e
This commit is contained in:
fanquake
2023-03-08 08:40:21 +01:00
6 changed files with 20 additions and 18 deletions

View File

@@ -51,12 +51,14 @@ if [ "${RUN_TIDY}" = "true" ]; then
" src/node/chainstate.cpp"\ " src/node/chainstate.cpp"\
" src/node/chainstatemanager_args.cpp"\ " src/node/chainstatemanager_args.cpp"\
" src/node/mempool_args.cpp"\ " src/node/mempool_args.cpp"\
" src/node/minisketchwrapper.cpp"\
" src/node/utxo_snapshot.cpp"\ " src/node/utxo_snapshot.cpp"\
" src/node/validation_cache_args.cpp"\ " src/node/validation_cache_args.cpp"\
" src/policy/feerate.cpp"\ " src/policy/feerate.cpp"\
" src/policy/packages.cpp"\ " src/policy/packages.cpp"\
" src/policy/settings.cpp"\ " src/policy/settings.cpp"\
" src/primitives/transaction.cpp"\ " src/primitives/transaction.cpp"\
" src/random.cpp"\
" src/rpc/fees.cpp"\ " src/rpc/fees.cpp"\
" src/rpc/signmessage.cpp"\ " src/rpc/signmessage.cpp"\
" src/test/fuzz/txorphan.cpp"\ " src/test/fuzz/txorphan.cpp"\

View File

@@ -23,17 +23,17 @@ static constexpr uint32_t BITS = 32;
uint32_t FindBestImplementation() uint32_t FindBestImplementation()
{ {
std::optional<std::pair<int64_t, uint32_t>> best; std::optional<std::pair<SteadyClock::duration, uint32_t>> best;
uint32_t max_impl = Minisketch::MaxImplementation(); uint32_t max_impl = Minisketch::MaxImplementation();
for (uint32_t impl = 0; impl <= max_impl; ++impl) { for (uint32_t impl = 0; impl <= max_impl; ++impl) {
std::vector<int64_t> benches; std::vector<SteadyClock::duration> benches;
uint64_t offset = 0; uint64_t offset = 0;
/* Run a little benchmark with capacity 32, adding 184 entries, and decoding 11 of them once. */ /* Run a little benchmark with capacity 32, adding 184 entries, and decoding 11 of them once. */
for (int b = 0; b < 11; ++b) { for (int b = 0; b < 11; ++b) {
if (!Minisketch::ImplementationSupported(BITS, impl)) break; if (!Minisketch::ImplementationSupported(BITS, impl)) break;
Minisketch sketch(BITS, impl, 32); Minisketch sketch(BITS, impl, 32);
auto start = GetTimeMicros(); auto start = SteadyClock::now();
for (uint64_t e = 0; e < 100; ++e) { for (uint64_t e = 0; e < 100; ++e) {
sketch.Add(e*1337 + b*13337 + offset); sketch.Add(e*1337 + b*13337 + offset);
} }
@@ -41,7 +41,7 @@ uint32_t FindBestImplementation()
sketch.Add(e*1337 + b*13337 + offset); sketch.Add(e*1337 + b*13337 + offset);
} }
offset += (*sketch.Decode(32))[0]; offset += (*sketch.Decode(32))[0];
auto stop = GetTimeMicros(); auto stop = SteadyClock::now();
benches.push_back(stop - start); benches.push_back(stop - start);
} }
/* Remember which implementation has the best median benchmark time. */ /* Remember which implementation has the best median benchmark time. */

View File

@@ -221,14 +221,14 @@ static void SeedHardwareSlow(CSHA512& hasher) noexcept {
} }
/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */ /** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */
static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA512& hasher) noexcept static void Strengthen(const unsigned char (&seed)[32], SteadyClock::duration dur, CSHA512& hasher) noexcept
{ {
CSHA512 inner_hasher; CSHA512 inner_hasher;
inner_hasher.Write(seed, sizeof(seed)); inner_hasher.Write(seed, sizeof(seed));
// Hash loop // Hash loop
unsigned char buffer[64]; unsigned char buffer[64];
int64_t stop = GetTimeMicros() + microseconds; const auto stop{SteadyClock::now() + dur};
do { do {
for (int i = 0; i < 1000; ++i) { for (int i = 0; i < 1000; ++i) {
inner_hasher.Finalize(buffer); inner_hasher.Finalize(buffer);
@@ -238,7 +238,7 @@ static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA51
// Benchmark operation and feed it into outer hasher. // Benchmark operation and feed it into outer hasher.
int64_t perf = GetPerformanceCounter(); int64_t perf = GetPerformanceCounter();
hasher.Write((const unsigned char*)&perf, sizeof(perf)); hasher.Write((const unsigned char*)&perf, sizeof(perf));
} while (GetTimeMicros() < stop); } while (SteadyClock::now() < stop);
// Produce output from inner state and feed it to outer hasher. // Produce output from inner state and feed it to outer hasher.
inner_hasher.Finalize(buffer); inner_hasher.Finalize(buffer);
@@ -492,13 +492,13 @@ static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept
} }
/** Extract entropy from rng, strengthen it, and feed it into hasher. */ /** Extract entropy from rng, strengthen it, and feed it into hasher. */
static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noexcept static void SeedStrengthen(CSHA512& hasher, RNGState& rng, SteadyClock::duration dur) noexcept
{ {
// Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher. // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
unsigned char strengthen_seed[32]; unsigned char strengthen_seed[32];
rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false); rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
// Strengthen the seed, and feed it into hasher. // Strengthen the seed, and feed it into hasher.
Strengthen(strengthen_seed, microseconds, hasher); Strengthen(strengthen_seed, dur, hasher);
} }
static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept
@@ -518,7 +518,7 @@ static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept
LogPrint(BCLog::RAND, "Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size); LogPrint(BCLog::RAND, "Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size);
// Strengthen for 10 ms // Strengthen for 10 ms
SeedStrengthen(hasher, rng, 10000); SeedStrengthen(hasher, rng, 10ms);
} }
static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
@@ -538,7 +538,7 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
LogPrint(BCLog::RAND, "Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size); LogPrint(BCLog::RAND, "Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size);
// Strengthen for 100 ms // Strengthen for 100 ms
SeedStrengthen(hasher, rng, 100000); SeedStrengthen(hasher, rng, 100ms);
} }
enum class RNGLevel { enum class RNGLevel {

View File

@@ -8,7 +8,7 @@
#include <compat/compat.h> #include <compat/compat.h>
#include <chrono> #include <chrono> // IWYU pragma: export
#include <cstdint> #include <cstdint>
#include <string> #include <string>

View File

@@ -2478,12 +2478,12 @@ bool Chainstate::FlushStateToDisk(
} }
} }
} }
const auto nNow = GetTime<std::chrono::microseconds>(); const auto nNow{SteadyClock::now()};
// Avoid writing/flushing immediately after startup. // Avoid writing/flushing immediately after startup.
if (m_last_write.count() == 0) { if (m_last_write == decltype(m_last_write){}) {
m_last_write = nNow; m_last_write = nNow;
} }
if (m_last_flush.count() == 0) { if (m_last_flush == decltype(m_last_flush){}) {
m_last_flush = nNow; m_last_flush = nNow;
} }
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing). // The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
@@ -2544,7 +2544,7 @@ bool Chainstate::FlushStateToDisk(
m_last_flush = nNow; m_last_flush = nNow;
full_flush_completed = true; full_flush_completed = true;
TRACE5(utxocache, flush, TRACE5(utxocache, flush,
(int64_t)(GetTimeMicros() - nNow.count()), // in microseconds (µs) int64_t{Ticks<std::chrono::microseconds>(SteadyClock::now() - nNow)},
(uint32_t)mode, (uint32_t)mode,
(uint64_t)coins_count, (uint64_t)coins_count,
(uint64_t)coins_mem_usage, (uint64_t)coins_mem_usage,

View File

@@ -808,8 +808,8 @@ private:
void UpdateTip(const CBlockIndex* pindexNew) void UpdateTip(const CBlockIndex* pindexNew)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main); EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
std::chrono::microseconds m_last_write{0}; SteadyClock::time_point m_last_write{};
std::chrono::microseconds m_last_flush{0}; SteadyClock::time_point m_last_flush{};
/** /**
* In case of an invalid snapshot, rename the coins leveldb directory so * In case of an invalid snapshot, rename the coins leveldb directory so