diff --git a/src/random.cpp b/src/random.cpp index 69adcddc7..0265c9522 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -1,598 +1,660 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #ifdef WIN32 #include // for Windows API #include #endif #include #include // for LogPrint() #include #include // for WAIT_LOCK #include // for GetTime() #include #include #include #include #include #include #include #include #ifndef WIN32 #include #include #endif #ifdef HAVE_SYS_GETRANDOM #include #include #endif #if defined(HAVE_GETENTROPY) || \ (defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX)) #include #endif #if defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) #include #endif #ifdef HAVE_SYSCTL_ARND #include #include // for ARRAYLEN #endif #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) #include #endif [[noreturn]] static void RandFailure() { LogPrintf("Failed to read randomness, aborting\n"); std::abort(); } static inline int64_t GetPerformanceCounter() { // Read the hardware time stamp counter when available. // See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information. #if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) return __rdtsc(); #elif !defined(_MSC_VER) && defined(__i386__) uint64_t r = 0; // Constrain the r variable to the eax:edx pair. __asm__ volatile("rdtsc" : "=A"(r)); return r; #elif !defined(_MSC_VER) && (defined(__x86_64__) || defined(__amd64__)) uint64_t r1 = 0, r2 = 0; // Constrain r1 to rax and r2 to rdx. __asm__ volatile("rdtsc" : "=a"(r1), "=d"(r2)); return (r2 << 32) | r1; #else // Fall back to using C++11 clock (usually microsecond or nanosecond // precision) return std::chrono::high_resolution_clock::now().time_since_epoch().count(); #endif } #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) static std::atomic hwrand_initialized{false}; static bool rdrand_supported = false; static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; static void InitHardwareRand() { uint32_t eax, ebx, ecx, edx; if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { rdrand_supported = true; } hwrand_initialized.store(true); } static void ReportHardwareRand() { assert(hwrand_initialized.load(std::memory_order_relaxed)); if (rdrand_supported) { // This must be done in a separate function, as HWRandInit() may be // indirectly called from global constructors, before logging is // initialized. LogPrintf("Using RdRand as an additional entropy source\n"); } } #else /** * Access to other hardware random number generators could be added here later, * assuming it is sufficiently fast (in the order of a few hundred CPU cycles). * Slower sources should probably be invoked separately, and/or only from * RandAddSeedSleep (which is called during idle background operation). */ static void InitHardwareRand() {} static void ReportHardwareRand() {} #endif static bool GetHardwareRand(uint8_t *ent32) { #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) assert(hwrand_initialized.load(std::memory_order_relaxed)); if (rdrand_supported) { uint8_t ok; // Not all assemblers support the rdrand instruction, write it in hex. #ifdef __i386__ for (int iter = 0; iter < 4; ++iter) { uint32_t r1, r2; __asm__ volatile(".byte 0x0f, 0xc7, 0xf0;" // rdrand %eax ".byte 0x0f, 0xc7, 0xf2;" // rdrand %edx "setc %2" : "=a"(r1), "=d"(r2), "=q"(ok)::"cc"); if (!ok) { return false; } WriteLE32(ent32 + 8 * iter, r1); WriteLE32(ent32 + 8 * iter + 4, r2); } #else uint64_t r1, r2, r3, r4; __asm__ volatile(".byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax "0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx "0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx "0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx "setc %4" : "=a"(r1), "=b"(r2), "=c"(r3), "=d"(r4), "=q"(ok)::"cc"); if (!ok) { return false; } WriteLE64(ent32, r1); WriteLE64(ent32 + 8, r2); WriteLE64(ent32 + 16, r3); WriteLE64(ent32 + 24, r4); #endif return true; } #endif return false; } -void RandAddSeed() { - // Seed with CPU performance counter - int64_t nCounter = GetPerformanceCounter(); - RAND_add(&nCounter, sizeof(nCounter), 1.5); - memory_cleanse((void *)&nCounter, sizeof(nCounter)); -} - -static void RandAddSeedPerfmon() { - RandAddSeed(); - +static void RandAddSeedPerfmon(CSHA512 &hasher) { #ifdef WIN32 // Don't need this on Linux, OpenSSL automatically uses /dev/urandom // Seed with the entire set of perfmon data // This can take up to 2 seconds, so only do it every 10 minutes static int64_t nLastPerfmon; if (GetTime() < nLastPerfmon + 10 * 60) { return; } nLastPerfmon = GetTime(); std::vector vData(250000, 0); long ret = 0; unsigned long nSize = 0; // Bail out at more than 10MB of performance data const size_t nMaxSize = 10000000; while (true) { nSize = vData.size(); ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize); if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) { break; } // Grow size of buffer exponentially vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); } RegCloseKey(HKEY_PERFORMANCE_DATA); if (ret == ERROR_SUCCESS) { - RAND_add(vData.data(), nSize, nSize / 100.0); + hasher.Write(vData.data(), nSize); memory_cleanse(vData.data(), nSize); } else { // Performance data is only a best-effort attempt at improving the // situation when the OS randomness (and other sources) aren't // adequate. As a result, failure to read it is isn't considered // critical, so we don't call RandFailure(). // TODO: Add logging when the logger is made functional before global // constructors have been invoked. } #endif } #ifndef WIN32 /** * Fallback: get 32 bytes of system entropy from /dev/urandom. The most * compatible way to get cryptographic randomness on UNIX-ish platforms. */ static void GetDevURandom(uint8_t *ent32) { int f = open("/dev/urandom", O_RDONLY); if (f == -1) { RandFailure(); } int have = 0; do { ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { close(f); RandFailure(); } have += n; } while (have < NUM_OS_RANDOM_BYTES); close(f); } #endif /** Get 32 bytes of system entropy. */ void GetOSRand(uint8_t *ent32) { #if defined(WIN32) HCRYPTPROV hProvider; int ret = CryptAcquireContextW(&hProvider, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); if (!ret) { RandFailure(); } ret = CryptGenRandom(hProvider, NUM_OS_RANDOM_BYTES, ent32); if (!ret) { RandFailure(); } CryptReleaseContext(hProvider, 0); #elif defined(HAVE_SYS_GETRANDOM) /** * Linux. From the getrandom(2) man page: * "If the urandom source has been initialized, reads of up to 256 bytes * will always return as many bytes as requested and will not be interrupted * by signals." */ int rv = syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0); if (rv != NUM_OS_RANDOM_BYTES) { if (rv < 0 && errno == ENOSYS) { /* Fallback for kernel <3.17: the return value will be -1 and errno * ENOSYS if the syscall is not available, in that case fall back * to /dev/urandom. */ GetDevURandom(ent32); } else { RandFailure(); } } #elif defined(HAVE_GETENTROPY) && defined(__OpenBSD__) /** * On OpenBSD this can return up to 256 bytes of entropy, will return an * error if more are requested. * The call cannot return less than the requested number of bytes. * getentropy is explicitly limited to openbsd here, as a similar (but not * the same) function may exist on other platforms via glibc. */ if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { RandFailure(); } #elif defined(HAVE_GETENTROPY_RAND) && defined(MAC_OSX) // We need a fallback for OSX < 10.12 if (&getentropy != nullptr) { if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { RandFailure(); } } else { GetDevURandom(ent32); } #elif defined(HAVE_SYSCTL_ARND) /** * FreeBSD and similar. It is possible for the call to return less bytes * than requested, so need to read in a loop. */ static const int name[2] = {CTL_KERN, KERN_ARND}; int have = 0; do { size_t len = NUM_OS_RANDOM_BYTES - have; if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, nullptr, 0) != 0) { RandFailure(); } have += len; } while (have < NUM_OS_RANDOM_BYTES); #else /** * Fall back to /dev/urandom if there is no specific method implemented to * get system entropy for this OS. */ GetDevURandom(ent32); #endif } -void GetRandBytes(uint8_t *buf, int num) { - if (RAND_bytes(buf, num) != 1) { - RandFailure(); - } -} - void LockingCallbackOpenSSL(int mode, int i, const char *file, int line); namespace { struct RNGState { Mutex m_mutex; uint8_t m_state[32] GUARDED_BY(m_mutex) = {0}; uint64_t m_counter GUARDED_BY(m_mutex) = 0; + bool m_strongly_seeded GUARDED_BY(m_mutex) = false; std::unique_ptr m_mutex_openssl; RNGState() { InitHardwareRand(); // Init OpenSSL library multithreading support m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]); CRYPTO_set_locking_callback(LockingCallbackOpenSSL); // OpenSSL can optionally load a config file which lists optional // loadable modules and engines. We don't use them so we don't require // the config. However some of our libs may call functions which attempt // to load the config file, possibly resulting in an exit() or crash if // it is missing or corrupt. Explicitly tell OpenSSL not to try to load // the file. The result for our libs will be that the config appears to // have been loaded and there are no modules/engines available. OPENSSL_no_config(); - -#ifdef WIN32 - // Seed OpenSSL PRNG with current contents of the screen - RAND_screen(); -#endif - - // Seed OpenSSL PRNG with performance counter - RandAddSeed(); } ~RNGState() { // Securely erase the memory used by the OpenSSL PRNG RAND_cleanup(); // Shutdown OpenSSL library multithreading support CRYPTO_set_locking_callback(nullptr); } /** * Extract up to 32 bytes of entropy from the RNG state, mixing in new * entropy from hasher. + * + * If this function has never been called with strong_seed = true, false is + * returned. */ - void MixExtract(uint8_t *out, size_t num, CSHA512 &&hasher) { + bool MixExtract(uint8_t *out, size_t num, CSHA512 &&hasher, + bool strong_seed) { assert(num <= 32); uint8_t buf[64]; static_assert(sizeof(buf) == CSHA512::OUTPUT_SIZE, "Buffer needs to have hasher's output size"); + bool ret; { LOCK(m_mutex); + ret = (m_strongly_seeded |= strong_seed); // Write the current state of the RNG into the hasher hasher.Write(m_state, 32); // Write a new counter number into the state hasher.Write((const uint8_t *)&m_counter, sizeof(m_counter)); ++m_counter; // Finalize the hasher hasher.Finalize(buf); // Store the last 32 bytes of the hash output as new RNG state. memcpy(m_state, buf + 32, 32); } // If desired, copy (up to) the first 32 bytes of the hash output as // output. if (num) { assert(out != nullptr); memcpy(out, buf, num); } // Best effort cleanup of internal state hasher.Reset(); memory_cleanse(buf, 64); + return ret; } }; RNGState &GetRNGState() { // This C++11 idiom relies on the guarantee that static variable are // initialized on first call, even when multiple parallel calls are // permitted. static std::unique_ptr g_rng{new RNGState()}; return *g_rng; } } // namespace void LockingCallbackOpenSSL(int mode, int i, const char *file, int line) NO_THREAD_SAFETY_ANALYSIS { RNGState &rng = GetRNGState(); if (mode & CRYPTO_LOCK) { rng.m_mutex_openssl[i].lock(); } else { rng.m_mutex_openssl[i].unlock(); } } -static void AddDataToRng(void *data, size_t len, RNGState &rng); +static void SeedTimestamp(CSHA512 &hasher) { + int64_t perfcounter = GetPerformanceCounter(); + hasher.Write((const uint8_t *)&perfcounter, sizeof(perfcounter)); +} -void RandAddSeedSleep() { - RNGState &rng = GetRNGState(); +static void SeedFast(CSHA512 &hasher) { + uint8_t buffer[32]; - int64_t nPerfCounter1 = GetPerformanceCounter(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - int64_t nPerfCounter2 = GetPerformanceCounter(); + // Stack pointer to indirectly commit to thread/callstack + const uint8_t *ptr = buffer; + hasher.Write((const uint8_t *)&ptr, sizeof(ptr)); - // Combine with and update state - AddDataToRng(&nPerfCounter1, sizeof(nPerfCounter1), rng); - AddDataToRng(&nPerfCounter2, sizeof(nPerfCounter2), rng); + // Hardware randomness is very fast when available; use it always. + bool have_hw_rand = GetHardwareRand(buffer); + if (have_hw_rand) { + hasher.Write(buffer, sizeof(buffer)); + } - memory_cleanse(&nPerfCounter1, sizeof(nPerfCounter1)); - memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); + // High-precision timestamp + SeedTimestamp(hasher); } -static void AddDataToRng(void *data, size_t len, RNGState &rng) { - CSHA512 hasher; - hasher.Write((const uint8_t *)&len, sizeof(len)); - hasher.Write((const uint8_t *)data, len); - rng.MixExtract(nullptr, 0, std::move(hasher)); +static void SeedSlow(CSHA512 &hasher) { + uint8_t buffer[32]; + + // Everything that the 'fast' seeder includes + SeedFast(hasher); + + // OS randomness + GetOSRand(buffer); + hasher.Write(buffer, sizeof(buffer)); + + // OpenSSL RNG (for now) + RAND_bytes(buffer, sizeof(buffer)); + hasher.Write(buffer, sizeof(buffer)); + + // High-precision timestamp. + // + // Note that we also commit to a timestamp in the Fast seeder, so we + // indirectly commit to a benchmark of all the entropy gathering sources in + // this function). + SeedTimestamp(hasher); } -void GetStrongRandBytes(uint8_t *out, int num) { - RNGState &rng = GetRNGState(); +static void SeedSleep(CSHA512 &hasher) { + // Everything that the 'fast' seeder includes + SeedFast(hasher); - assert(num <= 32); - CSHA512 hasher; - uint8_t buf[64]; + // High-precision timestamp + SeedTimestamp(hasher); + + // Sleep for 1ms + MilliSleep(1); - // First source: OpenSSL's RNG - RandAddSeedPerfmon(); - GetRandBytes(buf, 32); - hasher.Write(buf, 32); + // High-precision timestamp after sleeping (as we commit to both the time + // before and after, this measures the delay) + SeedTimestamp(hasher); - // Second source: OS RNG - GetOSRand(buf); - hasher.Write(buf, 32); + // Windows performance monitor data (once every 10 minutes) + RandAddSeedPerfmon(hasher); +} + +static void SeedStartup(CSHA512 &hasher) { +#ifdef WIN32 + RAND_screen(); +#endif + + // Everything that the 'slow' seeder includes. + SeedSlow(hasher); + + // Windows performance monitor data. + RandAddSeedPerfmon(hasher); +} - // Third source: HW RNG, if available. - if (GetHardwareRand(buf)) { - hasher.Write(buf, 32); +enum class RNGLevel { + FAST, //!< Automatically called by GetRandBytes + SLOW, //!< Automatically called by GetStrongRandBytes + SLEEP, //!< Called by RandAddSeedSleep() +}; + +static void ProcRand(uint8_t *out, int num, RNGLevel level) { + // Make sure the RNG is initialized first (as all Seed* function possibly + // need hwrand to be available). + RNGState &rng = GetRNGState(); + + assert(num <= 32); + + CSHA512 hasher; + switch (level) { + case RNGLevel::FAST: + SeedFast(hasher); + break; + case RNGLevel::SLOW: + SeedSlow(hasher); + break; + case RNGLevel::SLEEP: + SeedSleep(hasher); + break; } // Combine with and update state - rng.MixExtract(out, num, std::move(hasher)); + if (!rng.MixExtract(out, num, std::move(hasher), false)) { + // On the first invocation, also seed with SeedStartup(). + CSHA512 startup_hasher; + SeedStartup(startup_hasher); + rng.MixExtract(out, num, std::move(startup_hasher), true); + } - // Produce output - memcpy(out, buf, num); - memory_cleanse(buf, 64); + // For anything but the 'fast' level, feed the resulting RNG output (after + // an additional hashing step) back into OpenSSL. + if (level != RNGLevel::FAST) { + uint8_t buf[64]; + CSHA512().Write(out, num).Finalize(buf); + RAND_add(buf, sizeof(buf), num); + memory_cleanse(buf, 64); + } +} + +void GetRandBytes(uint8_t *buf, int num) { + ProcRand(buf, num, RNGLevel::FAST); +} +void GetStrongRandBytes(uint8_t *buf, int num) { + ProcRand(buf, num, RNGLevel::SLOW); +} +void RandAddSeedSleep() { + ProcRand(nullptr, 0, RNGLevel::SLEEP); } uint64_t GetRand(uint64_t nMax) { if (nMax == 0) { return 0; } // The range of the random source must be a multiple of the modulus to give // every possible output value an equal possibility uint64_t nRange = (std::numeric_limits::max() / nMax) * nMax; uint64_t nRand = 0; do { GetRandBytes((uint8_t *)&nRand, sizeof(nRand)); } while (nRand >= nRange); return (nRand % nMax); } int GetRandInt(int nMax) { return GetRand(nMax); } uint256 GetRandHash() { uint256 hash; GetRandBytes((uint8_t *)&hash, sizeof(hash)); return hash; } void FastRandomContext::RandomSeed() { uint256 seed = GetRandHash(); rng.SetKey(seed.begin(), 32); requires_seed = false; } uint256 FastRandomContext::rand256() { if (bytebuf_size < 32) { FillByteBuffer(); } uint256 ret; memcpy(ret.begin(), bytebuf + 64 - bytebuf_size, 32); bytebuf_size -= 32; return ret; } std::vector FastRandomContext::randbytes(size_t len) { if (requires_seed) { RandomSeed(); } std::vector ret(len); if (len > 0) { rng.Output(&ret[0], len); } return ret; } FastRandomContext::FastRandomContext(const uint256 &seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0) { rng.SetKey(seed.begin(), 32); } bool Random_SanityCheck() { uint64_t start = GetPerformanceCounter(); /** * This does not measure the quality of randomness, but it does test that * OSRandom() overwrites all 32 bytes of the output given a maximum number * of tries. */ static const ssize_t MAX_TRIES = 1024; uint8_t data[NUM_OS_RANDOM_BYTES]; /* Tracks which bytes have been overwritten at least once */ bool overwritten[NUM_OS_RANDOM_BYTES] = {}; int num_overwritten; int tries = 0; /** * Loop until all bytes have been overwritten at least once, or max number * tries reached. */ do { memset(data, 0, NUM_OS_RANDOM_BYTES); GetOSRand(data); for (int x = 0; x < NUM_OS_RANDOM_BYTES; ++x) { overwritten[x] |= (data[x] != 0); } num_overwritten = 0; for (int x = 0; x < NUM_OS_RANDOM_BYTES; ++x) { if (overwritten[x]) { num_overwritten += 1; } } tries += 1; } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); /* If this failed, bailed out after too many tries */ if (num_overwritten != NUM_OS_RANDOM_BYTES) { return false; } // Check that GetPerformanceCounter increases at least during a GetOSRand() // call + 1ms sleep. std::this_thread::sleep_for(std::chrono::milliseconds(1)); uint64_t stop = GetPerformanceCounter(); if (stop == start) { return false; } // We called GetPerformanceCounter. Use it as entropy. - RAND_add((const uint8_t *)&start, sizeof(start), 1); - RAND_add((const uint8_t *)&stop, sizeof(stop), 1); + CSHA512 to_add; + to_add.Write((const uint8_t *)&start, sizeof(start)); + to_add.Write((const uint8_t *)&stop, sizeof(stop)); + GetRNGState().MixExtract(nullptr, 0, std::move(to_add), false); return true; } FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) { if (!fDeterministic) { return; } uint256 seed; rng.SetKey(seed.begin(), 32); } FastRandomContext &FastRandomContext:: operator=(FastRandomContext &&from) noexcept { requires_seed = from.requires_seed; rng = from.rng; std::copy(std::begin(from.bytebuf), std::end(from.bytebuf), std::begin(bytebuf)); bytebuf_size = from.bytebuf_size; bitbuf = from.bitbuf; bitbuf_size = from.bitbuf_size; from.requires_seed = true; from.bytebuf_size = 0; from.bitbuf_size = 0; return *this; } void RandomInit() { // Invoke RNG code to trigger initialization (if not already performed) - GetRNGState(); + ProcRand(nullptr, 0, RNGLevel::FAST); ReportHardwareRand(); } diff --git a/src/random.h b/src/random.h index f36d239dd..9e8338599 100644 --- a/src/random.h +++ b/src/random.h @@ -1,201 +1,207 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_RANDOM_H #define BITCOIN_RANDOM_H #include #include #include #include #include /** - * Seed OpenSSL PRNG with additional entropy data. - */ -void RandAddSeed(); - -/** - * Functions to gather random data via the OpenSSL PRNG + * Generate random data via the internal PRNG. + * + * These functions are designed to be fast (sub microsecond), but do not + * necessarily meaningfully add entropy to the PRNG state. + * + * Thread-safe. */ void GetRandBytes(uint8_t *buf, int num); uint64_t GetRand(uint64_t nMax); int GetRandInt(int nMax); uint256 GetRandHash(); /** - * Add a little bit of randomness to the output of GetStrongRangBytes. - * This sleeps for a millisecond, so should only be called when there is no - * other work to be done. + * Gather entropy from various sources, feed it into the internal PRNG, and + * generate random data using it. + * + * This function will cause failure whenever the OS RNG fails. + * + * Thread-safe. */ -void RandAddSeedSleep(); +void GetStrongRandBytes(uint8_t *buf, int num); /** - * Function to gather random data from multiple sources, failing whenever any of - * those sources fail to provide a result. + * Sleep for 1ms, gather entropy from various sources, and feed them to the PRNG + * state. + * + * Thread-safe. */ -void GetStrongRandBytes(uint8_t *buf, int num); +void RandAddSeedSleep(); /** - * Fast randomness source. This is seeded once with secure random data, but is - * completely deterministic and insecure after that. + * Fast randomness source. This is seeded once with secure random data, but + * is completely deterministic and does not gather more entropy after that. + * * This class is not thread-safe. */ class FastRandomContext { private: bool requires_seed; ChaCha20 rng; uint8_t bytebuf[64]; int bytebuf_size; uint64_t bitbuf; int bitbuf_size; void RandomSeed(); void FillByteBuffer() { if (requires_seed) { RandomSeed(); } rng.Output(bytebuf, sizeof(bytebuf)); bytebuf_size = sizeof(bytebuf); } void FillBitBuffer() { bitbuf = rand64(); bitbuf_size = 64; } public: explicit FastRandomContext(bool fDeterministic = false); /** Initialize with explicit seed (only for testing) */ explicit FastRandomContext(const uint256 &seed); // Do not permit copying a FastRandomContext (move it, or create a new one // to get reseeded). FastRandomContext(const FastRandomContext &) = delete; FastRandomContext(FastRandomContext &&) = delete; FastRandomContext &operator=(const FastRandomContext &) = delete; /** * Move a FastRandomContext. If the original one is used again, it will be * reseeded. */ FastRandomContext &operator=(FastRandomContext &&from) noexcept; /** Generate a random 64-bit integer. */ uint64_t rand64() { if (bytebuf_size < 8) { FillByteBuffer(); } uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size); bytebuf_size -= 8; return ret; } /** Generate a random (bits)-bit integer. */ uint64_t randbits(int bits) { if (bits == 0) { return 0; } else if (bits > 32) { return rand64() >> (64 - bits); } else { if (bitbuf_size < bits) { FillBitBuffer(); } uint64_t ret = bitbuf & (~uint64_t(0) >> (64 - bits)); bitbuf >>= bits; bitbuf_size -= bits; return ret; } } /** Generate a random integer in the range [0..range). */ uint64_t randrange(uint64_t range) { --range; int bits = CountBits(range); while (true) { uint64_t ret = randbits(bits); if (ret <= range) { return ret; } } } /** Generate random bytes. */ std::vector randbytes(size_t len); /** Generate a random 32-bit integer. */ uint32_t rand32() { return randbits(32); } /** generate a random uint256. */ uint256 rand256(); /** Generate a random boolean. */ bool randbool() { return randbits(1); } // Compatibility with the C++11 UniformRandomBitGenerator concept typedef uint64_t result_type; static constexpr uint64_t min() { return 0; } static constexpr uint64_t max() { return std::numeric_limits::max(); } inline uint64_t operator()() { return rand64(); } }; /** * More efficient than using std::shuffle on a FastRandomContext. * * This is more efficient as std::shuffle will consume entropy in groups of * 64 bits at the time and throw away most. * * This also works around a bug in libstdc++ std::shuffle that may cause * type::operator=(type&&) to be invoked on itself, which the library's * debug mode detects and panics on. This is a known issue, see * https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle */ template void Shuffle(I first, I last, R &&rng) { while (first != last) { size_t j = rng.randrange(last - first); if (j) { using std::swap; swap(*first, *(first + j)); } ++first; } } /** * Number of random bytes returned by GetOSRand. * When changing this constant make sure to change all call sites, and make * sure that the underlying OS APIs for all platforms support the number. * (many cap out at 256 bytes). */ static const ssize_t NUM_OS_RANDOM_BYTES = 32; /** * Get 32 bytes of system entropy. Do not use this in application code: use * GetStrongRandBytes instead. */ void GetOSRand(uint8_t *ent32); /** * Check that OS randomness is available and returning the requested number of * bytes. */ bool Random_SanityCheck(); /** * Initialize global RNG state and log any CPU features that are used. * * Calling this function is optional. RNG state will be initialized when first * needed if it is not called. */ void RandomInit(); #endif // BITCOIN_RANDOM_H diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 7141143d1..bd6141b2a 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -1,212 +1,212 @@ // Copyright (c) 2015-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false) {} CScheduler::~CScheduler() { assert(nThreadsServicingQueue == 0); } void CScheduler::serviceQueue() { boost::unique_lock lock(newTaskMutex); ++nThreadsServicingQueue; // newTaskMutex is locked throughout this loop EXCEPT when the thread is // waiting or when the user's function is called. while (!shouldStop()) { try { if (!shouldStop() && taskQueue.empty()) { reverse_lock> rlock(lock); - // Use this chance to get a tiny bit more entropy + // Use this chance to get more entropy RandAddSeedSleep(); } while (!shouldStop() && taskQueue.empty()) { // Wait until there is something to do. newTaskScheduled.wait(lock); } // Wait until either there is a new task, or until the time of the // first item on the queue. // Some boost versions have a conflicting overload of wait_until // that returns void. Explicitly use a template here to avoid // hitting that overload. while (!shouldStop() && !taskQueue.empty()) { boost::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first; if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) == boost::cv_status::timeout) { // Exit loop after timeout, it means we reached the time of // the event break; } } // If there are multiple threads, the queue can empty while we're // waiting (another thread may service the task we were waiting on). if (shouldStop() || taskQueue.empty()) { continue; } Function f = taskQueue.begin()->second; taskQueue.erase(taskQueue.begin()); { // Unlock before calling f, so it can reschedule itself or // another task without deadlocking: reverse_lock> rlock(lock); f(); } } catch (...) { --nThreadsServicingQueue; throw; } } --nThreadsServicingQueue; newTaskScheduled.notify_one(); } void CScheduler::stop(bool drain) { { boost::unique_lock lock(newTaskMutex); if (drain) { stopWhenEmpty = true; } else { stopRequested = true; } } newTaskScheduled.notify_all(); } void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t) { { boost::unique_lock lock(newTaskMutex); taskQueue.insert(std::make_pair(t, f)); } newTaskScheduled.notify_one(); } void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSeconds) { schedule(f, boost::chrono::system_clock::now() + boost::chrono::milliseconds(deltaMilliSeconds)); } static void Repeat(CScheduler *s, CScheduler::Predicate p, int64_t deltaMilliSeconds) { if (p()) { s->scheduleFromNow(std::bind(&Repeat, s, p, deltaMilliSeconds), deltaMilliSeconds); } } void CScheduler::scheduleEvery(CScheduler::Predicate p, int64_t deltaMilliSeconds) { scheduleFromNow(std::bind(&Repeat, this, p, deltaMilliSeconds), deltaMilliSeconds); } size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, boost::chrono::system_clock::time_point &last) const { boost::unique_lock lock(newTaskMutex); size_t result = taskQueue.size(); if (!taskQueue.empty()) { first = taskQueue.begin()->first; last = taskQueue.rbegin()->first; } return result; } bool CScheduler::AreThreadsServicingQueue() const { boost::unique_lock lock(newTaskMutex); return nThreadsServicingQueue; } void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() { { LOCK(m_cs_callbacks_pending); // Try to avoid scheduling too many copies here, but if we // accidentally have two ProcessQueue's scheduled at once its // not a big deal. if (m_are_callbacks_running) { return; } if (m_callbacks_pending.empty()) { return; } } m_pscheduler->schedule( std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this)); } void SingleThreadedSchedulerClient::ProcessQueue() { std::function callback; { LOCK(m_cs_callbacks_pending); if (m_are_callbacks_running) { return; } if (m_callbacks_pending.empty()) { return; } m_are_callbacks_running = true; callback = std::move(m_callbacks_pending.front()); m_callbacks_pending.pop_front(); } // RAII the setting of fCallbacksRunning and calling // MaybeScheduleProcessQueue to ensure both happen safely even if callback() // throws. struct RAIICallbacksRunning { SingleThreadedSchedulerClient *instance; explicit RAIICallbacksRunning(SingleThreadedSchedulerClient *_instance) : instance(_instance) {} ~RAIICallbacksRunning() { { LOCK(instance->m_cs_callbacks_pending); instance->m_are_callbacks_running = false; } instance->MaybeScheduleProcessQueue(); } } raiicallbacksrunning(this); callback(); } void SingleThreadedSchedulerClient::AddToProcessQueue( std::function func) { assert(m_pscheduler); { LOCK(m_cs_callbacks_pending); m_callbacks_pending.emplace_back(std::move(func)); } MaybeScheduleProcessQueue(); } void SingleThreadedSchedulerClient::EmptyQueue() { assert(!m_pscheduler->AreThreadsServicingQueue()); bool should_continue = true; while (should_continue) { ProcessQueue(); LOCK(m_cs_callbacks_pending); should_continue = !m_callbacks_pending.empty(); } } size_t SingleThreadedSchedulerClient::CallbacksPending() { LOCK(m_cs_callbacks_pending); return m_callbacks_pending.size(); }