text: // Copyright (c) 2009-2018 Satoshi Nakamoto
  // Copyright (c) 2009-2018 The Bitcoin Developers
  // Copyright (c) 2014-2018 The Dash Core Developers
  // Copyright (c) 2017-2018 Zumy Developers
  // Distributed under the MIT/X11 software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "random.h"
  
  #include "support/cleanse.h"
  #ifdef WIN32
  #include "compat.h" // for Windows API
  #endif
  #include "util.h"             // for LogPrint()
  #include "utilstrencodings.h" // for GetTime()
  
  #include <stdlib.h>
  #include <limits>
  
  #ifndef WIN32
  #include <sys/time.h>
  #endif
  
  #include <openssl/err.h>
  #include <openssl/rand.h>
  
  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)
      return __rdtsc();
  #elif defined(__i386__)
      uint64_t r;
      __asm__ volatile ("rdtsc" : "=A"(r)); // Constrain the r variable to the eax:edx pair.
      return r;
  #elif defined(__x86_64__) || defined(__amd64__)
      uint64_t r1, r2;
      __asm__ volatile ("rdtsc" : "=a"(r1), "=d"(r2)); // Constrain r1 to rax and r2 to rdx.
      return (r2 << 32) | r1;
  #else
      // Fall back to using gettimeofday (with microsecond precision)
      struct timeval tv;
      gettimeofday(&tv, NULL);
      return ((uint64_t)tv.tv_sec) * 1000000 + tv.tv_usec;
  #endif
  }
  
  void RandAddSeed()
  {
      // Seed with CPU performance counter
      int64_t nCounter = GetPerformanceCounter();
      RAND_add(&nCounter, sizeof(nCounter), 1.5);
      memory_cleanse((void*)&nCounter, sizeof(nCounter));
  }
  
  void RandAddSeedPerfmon()
  {
      RandAddSeed();
  
  #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<unsigned char> vData(250000, 0);
      long ret = 0;
      unsigned long nSize = 0;
      const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
      while (true) {
          nSize = vData.size();
          ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, vData.data(), &nSize);
          if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
              break;
          vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
      }
      RegCloseKey(HKEY_PERFORMANCE_DATA);
      if (ret == ERROR_SUCCESS) {
          RAND_add(vData.data(), nSize, nSize / 100.0);
          memory_cleanse(vData.data(), nSize);
          LogPrint("rand", "%s: %lu bytes\n", __func__, nSize);
      } else {
          static bool warned = false; // Warn only once
          if (!warned) {
              LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret);
              warned = true;
          }
      }
  #endif
  }
  
  void GetRandBytes(unsigned char* buf, int num)
  {
      if (RAND_bytes(buf, num) != 1) {
          LogPrintf("%s: OpenSSL RAND_bytes() failed with error: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL));
          assert(false);
      }
  }
  
  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<uint64_t>::max() / nMax) * nMax;
      uint64_t nRand = 0;
      do {
          GetRandBytes((unsigned char*)&nRand, sizeof(nRand));
      } while (nRand >= nRange);
      return (nRand % nMax);
  }
  
  int GetRandInt(int nMax)
  {
      return GetRand(nMax);
  }
  
  uint256 GetRandHash()
  {
      uint256 hash;
      GetRandBytes((unsigned char*)&hash, sizeof(hash));
      return hash;
  }
  
  FastRandomContext::FastRandomContext(bool fDeterministic)
  {
      // The seed values have some unlikely fixed points which we avoid.
      if (fDeterministic) {
          Rz = Rw = 11;
      } else {
          uint32_t tmp;
          do {
              GetRandBytes((unsigned char*)&tmp, 4);
          } while (tmp == 0 || tmp == 0x9068ffffU);
          Rz = tmp;
          do {
              GetRandBytes((unsigned char*)&tmp, 4);
          } while (tmp == 0 || tmp == 0x464fffffU);
          Rw = tmp;
      }
  }
  
  InsecureRand::InsecureRand(bool _fDeterministic)
      : nRz(11),
        nRw(11),
        fDeterministic(_fDeterministic)
  {
      // The seed values have some unlikely fixed points which we avoid.
      if(fDeterministic) return;
      uint32_t nTmp;
      do {
          GetRandBytes((unsigned char*)&nTmp, 4);
      } while (nTmp == 0 || nTmp == 0x9068ffffU);
      nRz = nTmp;
      do {
          GetRandBytes((unsigned char*)&nTmp, 4);
      } while (nTmp == 0 || nTmp == 0x464fffffU);
      nRw = nTmp;
  }
  