0e4b317555
The new class is accessed via the Params() method and holds most things that vary between main, test and regtest networks. The regtest mode has two purposes, one is to run the bitcoind/bitcoinj comparison tool which compares two separate implementations of the Bitcoin protocol looking for divergence. The other is that when run, you get a local node which can mine a single block instantly, which is highly convenient for testing apps during development as there's no need to wait 10 minutes for a block on the testnet.
1471 lines
41 KiB
C++
1471 lines
41 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2012 The Bitcoin developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef WIN32
|
|
// for posix_fallocate
|
|
#ifdef __linux__
|
|
#define _POSIX_C_SOURCE 200112L
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/resource.h>
|
|
#endif
|
|
|
|
#include "chainparams.h"
|
|
#include "util.h"
|
|
#include "sync.h"
|
|
#include "version.h"
|
|
#include "ui_interface.h"
|
|
#include <boost/algorithm/string/join.hpp>
|
|
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
|
|
#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
|
|
|
|
// Work around clang compilation problem in Boost 1.46:
|
|
// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup
|
|
// See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options
|
|
// http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION
|
|
namespace boost {
|
|
namespace program_options {
|
|
std::string to_internal(const std::string&);
|
|
}
|
|
}
|
|
|
|
#include <boost/program_options/detail/config_file.hpp>
|
|
#include <boost/program_options/parsers.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/filesystem/fstream.hpp>
|
|
#include <boost/foreach.hpp>
|
|
#include <boost/thread.hpp>
|
|
#include <openssl/crypto.h>
|
|
#include <openssl/rand.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifdef WIN32
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable:4786)
|
|
#pragma warning(disable:4804)
|
|
#pragma warning(disable:4805)
|
|
#pragma warning(disable:4717)
|
|
#endif
|
|
#ifdef _WIN32_WINNT
|
|
#undef _WIN32_WINNT
|
|
#endif
|
|
#define _WIN32_WINNT 0x0501
|
|
#ifdef _WIN32_IE
|
|
#undef _WIN32_IE
|
|
#endif
|
|
#define _WIN32_IE 0x0501
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <io.h> /* for _commit */
|
|
#include "shlobj.h"
|
|
#elif defined(__linux__)
|
|
# include <sys/prctl.h>
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
map<string, string> mapArgs;
|
|
map<string, vector<string> > mapMultiArgs;
|
|
bool fDebug = false;
|
|
bool fDebugNet = false;
|
|
bool fPrintToConsole = false;
|
|
bool fPrintToDebugger = false;
|
|
bool fDaemon = false;
|
|
bool fServer = false;
|
|
bool fCommandLine = false;
|
|
string strMiscWarning;
|
|
bool fNoListen = false;
|
|
bool fLogTimestamps = false;
|
|
CMedianFilter<int64> vTimeOffsets(200,0);
|
|
volatile bool fReopenDebugLog = false;
|
|
bool fCachedPath[2] = {false, false};
|
|
|
|
// Init OpenSSL library multithreading support
|
|
static CCriticalSection** ppmutexOpenSSL;
|
|
void locking_callback(int mode, int i, const char* file, int line)
|
|
{
|
|
if (mode & CRYPTO_LOCK) {
|
|
ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]);
|
|
} else {
|
|
LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]);
|
|
}
|
|
}
|
|
|
|
LockedPageManager LockedPageManager::instance;
|
|
|
|
// Init
|
|
class CInit
|
|
{
|
|
public:
|
|
CInit()
|
|
{
|
|
// Init OpenSSL library multithreading support
|
|
ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*));
|
|
for (int i = 0; i < CRYPTO_num_locks(); i++)
|
|
ppmutexOpenSSL[i] = new CCriticalSection();
|
|
CRYPTO_set_locking_callback(locking_callback);
|
|
|
|
#ifdef WIN32
|
|
// Seed random number generator with screen scrape and other hardware sources
|
|
RAND_screen();
|
|
#endif
|
|
|
|
// Seed random number generator with performance counter
|
|
RandAddSeed();
|
|
}
|
|
~CInit()
|
|
{
|
|
// Shutdown OpenSSL library multithreading support
|
|
CRYPTO_set_locking_callback(NULL);
|
|
for (int i = 0; i < CRYPTO_num_locks(); i++)
|
|
delete ppmutexOpenSSL[i];
|
|
OPENSSL_free(ppmutexOpenSSL);
|
|
}
|
|
}
|
|
instance_of_cinit;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void RandAddSeed()
|
|
{
|
|
// Seed with CPU performance counter
|
|
int64 nCounter = GetPerformanceCounter();
|
|
RAND_add(&nCounter, sizeof(nCounter), 1.5);
|
|
memset(&nCounter, 0, sizeof(nCounter));
|
|
}
|
|
|
|
void RandAddSeedPerfmon()
|
|
{
|
|
RandAddSeed();
|
|
|
|
// This can take up to 2 seconds, so only do it every 10 minutes
|
|
static int64 nLastPerfmon;
|
|
if (GetTime() < nLastPerfmon + 10 * 60)
|
|
return;
|
|
nLastPerfmon = GetTime();
|
|
|
|
#ifdef WIN32
|
|
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
|
|
// Seed with the entire set of perfmon data
|
|
unsigned char pdata[250000];
|
|
memset(pdata, 0, sizeof(pdata));
|
|
unsigned long nSize = sizeof(pdata);
|
|
long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize);
|
|
RegCloseKey(HKEY_PERFORMANCE_DATA);
|
|
if (ret == ERROR_SUCCESS)
|
|
{
|
|
RAND_add(pdata, nSize, nSize/100.0);
|
|
OPENSSL_cleanse(pdata, nSize);
|
|
printf("RandAddSeed() %lu bytes\n", nSize);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uint64 GetRand(uint64 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 nRange = (std::numeric_limits<uint64>::max() / nMax) * nMax;
|
|
uint64 nRand = 0;
|
|
do
|
|
RAND_bytes((unsigned char*)&nRand, sizeof(nRand));
|
|
while (nRand >= nRange);
|
|
return (nRand % nMax);
|
|
}
|
|
|
|
int GetRandInt(int nMax)
|
|
{
|
|
return GetRand(nMax);
|
|
}
|
|
|
|
uint256 GetRandHash()
|
|
{
|
|
uint256 hash;
|
|
RAND_bytes((unsigned char*)&hash, sizeof(hash));
|
|
return hash;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// OutputDebugStringF (aka printf -- there is a #define that we really
|
|
// should get rid of one day) has been broken a couple of times now
|
|
// by well-meaning people adding mutexes in the most straightforward way.
|
|
// It breaks because it may be called by global destructors during shutdown.
|
|
// Since the order of destruction of static/global objects is undefined,
|
|
// defining a mutex as a global object doesn't work (the mutex gets
|
|
// destroyed, and then some later destructor calls OutputDebugStringF,
|
|
// maybe indirectly, and you get a core dump at shutdown trying to lock
|
|
// the mutex).
|
|
|
|
static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT;
|
|
// We use boost::call_once() to make sure these are initialized in
|
|
// in a thread-safe manner the first time it is called:
|
|
static FILE* fileout = NULL;
|
|
static boost::mutex* mutexDebugLog = NULL;
|
|
|
|
static void DebugPrintInit()
|
|
{
|
|
assert(fileout == NULL);
|
|
assert(mutexDebugLog == NULL);
|
|
|
|
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
|
|
fileout = fopen(pathDebug.string().c_str(), "a");
|
|
if (fileout) setbuf(fileout, NULL); // unbuffered
|
|
|
|
mutexDebugLog = new boost::mutex();
|
|
}
|
|
|
|
int OutputDebugStringF(const char* pszFormat, ...)
|
|
{
|
|
int ret = 0; // Returns total number of characters written
|
|
if (fPrintToConsole)
|
|
{
|
|
// print to console
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, pszFormat);
|
|
ret += vprintf(pszFormat, arg_ptr);
|
|
va_end(arg_ptr);
|
|
}
|
|
else if (!fPrintToDebugger)
|
|
{
|
|
static bool fStartedNewLine = true;
|
|
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
|
|
|
|
if (fileout == NULL)
|
|
return ret;
|
|
|
|
boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
|
|
|
|
// reopen the log file, if requested
|
|
if (fReopenDebugLog) {
|
|
fReopenDebugLog = false;
|
|
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
|
|
if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL)
|
|
setbuf(fileout, NULL); // unbuffered
|
|
}
|
|
|
|
// Debug print useful for profiling
|
|
if (fLogTimestamps && fStartedNewLine)
|
|
ret += fprintf(fileout, "%s ", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str());
|
|
if (pszFormat[strlen(pszFormat) - 1] == '\n')
|
|
fStartedNewLine = true;
|
|
else
|
|
fStartedNewLine = false;
|
|
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, pszFormat);
|
|
ret += vfprintf(fileout, pszFormat, arg_ptr);
|
|
va_end(arg_ptr);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
if (fPrintToDebugger)
|
|
{
|
|
static CCriticalSection cs_OutputDebugStringF;
|
|
|
|
// accumulate and output a line at a time
|
|
{
|
|
LOCK(cs_OutputDebugStringF);
|
|
static std::string buffer;
|
|
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, pszFormat);
|
|
buffer += vstrprintf(pszFormat, arg_ptr);
|
|
va_end(arg_ptr);
|
|
|
|
int line_start = 0, line_end;
|
|
while((line_end = buffer.find('\n', line_start)) != -1)
|
|
{
|
|
OutputDebugStringA(buffer.substr(line_start, line_end - line_start).c_str());
|
|
line_start = line_end + 1;
|
|
ret += line_end-line_start;
|
|
}
|
|
buffer.erase(0, line_start);
|
|
}
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
string vstrprintf(const char *format, va_list ap)
|
|
{
|
|
char buffer[50000];
|
|
char* p = buffer;
|
|
int limit = sizeof(buffer);
|
|
int ret;
|
|
loop
|
|
{
|
|
va_list arg_ptr;
|
|
va_copy(arg_ptr, ap);
|
|
#ifdef WIN32
|
|
ret = _vsnprintf(p, limit, format, arg_ptr);
|
|
#else
|
|
ret = vsnprintf(p, limit, format, arg_ptr);
|
|
#endif
|
|
va_end(arg_ptr);
|
|
if (ret >= 0 && ret < limit)
|
|
break;
|
|
if (p != buffer)
|
|
delete[] p;
|
|
limit *= 2;
|
|
p = new char[limit];
|
|
if (p == NULL)
|
|
throw std::bad_alloc();
|
|
}
|
|
string str(p, p+ret);
|
|
if (p != buffer)
|
|
delete[] p;
|
|
return str;
|
|
}
|
|
|
|
string real_strprintf(const char *format, int dummy, ...)
|
|
{
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, dummy);
|
|
string str = vstrprintf(format, arg_ptr);
|
|
va_end(arg_ptr);
|
|
return str;
|
|
}
|
|
|
|
string real_strprintf(const std::string &format, int dummy, ...)
|
|
{
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, dummy);
|
|
string str = vstrprintf(format.c_str(), arg_ptr);
|
|
va_end(arg_ptr);
|
|
return str;
|
|
}
|
|
|
|
bool error(const char *format, ...)
|
|
{
|
|
va_list arg_ptr;
|
|
va_start(arg_ptr, format);
|
|
std::string str = vstrprintf(format, arg_ptr);
|
|
va_end(arg_ptr);
|
|
printf("ERROR: %s\n", str.c_str());
|
|
return false;
|
|
}
|
|
|
|
|
|
void ParseString(const string& str, char c, vector<string>& v)
|
|
{
|
|
if (str.empty())
|
|
return;
|
|
string::size_type i1 = 0;
|
|
string::size_type i2;
|
|
loop
|
|
{
|
|
i2 = str.find(c, i1);
|
|
if (i2 == str.npos)
|
|
{
|
|
v.push_back(str.substr(i1));
|
|
return;
|
|
}
|
|
v.push_back(str.substr(i1, i2-i1));
|
|
i1 = i2+1;
|
|
}
|
|
}
|
|
|
|
|
|
string FormatMoney(int64 n, bool fPlus)
|
|
{
|
|
// Note: not using straight sprintf here because we do NOT want
|
|
// localized number formatting.
|
|
int64 n_abs = (n > 0 ? n : -n);
|
|
int64 quotient = n_abs/COIN;
|
|
int64 remainder = n_abs%COIN;
|
|
string str = strprintf("%"PRI64d".%08"PRI64d, quotient, remainder);
|
|
|
|
// Right-trim excess zeros before the decimal point:
|
|
int nTrim = 0;
|
|
for (int i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i)
|
|
++nTrim;
|
|
if (nTrim)
|
|
str.erase(str.size()-nTrim, nTrim);
|
|
|
|
if (n < 0)
|
|
str.insert((unsigned int)0, 1, '-');
|
|
else if (fPlus && n > 0)
|
|
str.insert((unsigned int)0, 1, '+');
|
|
return str;
|
|
}
|
|
|
|
|
|
bool ParseMoney(const string& str, int64& nRet)
|
|
{
|
|
return ParseMoney(str.c_str(), nRet);
|
|
}
|
|
|
|
bool ParseMoney(const char* pszIn, int64& nRet)
|
|
{
|
|
string strWhole;
|
|
int64 nUnits = 0;
|
|
const char* p = pszIn;
|
|
while (isspace(*p))
|
|
p++;
|
|
for (; *p; p++)
|
|
{
|
|
if (*p == '.')
|
|
{
|
|
p++;
|
|
int64 nMult = CENT*10;
|
|
while (isdigit(*p) && (nMult > 0))
|
|
{
|
|
nUnits += nMult * (*p++ - '0');
|
|
nMult /= 10;
|
|
}
|
|
break;
|
|
}
|
|
if (isspace(*p))
|
|
break;
|
|
if (!isdigit(*p))
|
|
return false;
|
|
strWhole.insert(strWhole.end(), *p);
|
|
}
|
|
for (; *p; p++)
|
|
if (!isspace(*p))
|
|
return false;
|
|
if (strWhole.size() > 10) // guard against 63 bit overflow
|
|
return false;
|
|
if (nUnits < 0 || nUnits > COIN)
|
|
return false;
|
|
int64 nWhole = atoi64(strWhole);
|
|
int64 nValue = nWhole*COIN + nUnits;
|
|
|
|
nRet = nValue;
|
|
return true;
|
|
}
|
|
|
|
|
|
static const signed char phexdigit[256] =
|
|
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,
|
|
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, };
|
|
|
|
bool IsHex(const string& str)
|
|
{
|
|
BOOST_FOREACH(unsigned char c, str)
|
|
{
|
|
if (phexdigit[c] < 0)
|
|
return false;
|
|
}
|
|
return (str.size() > 0) && (str.size()%2 == 0);
|
|
}
|
|
|
|
vector<unsigned char> ParseHex(const char* psz)
|
|
{
|
|
// convert hex dump to vector
|
|
vector<unsigned char> vch;
|
|
loop
|
|
{
|
|
while (isspace(*psz))
|
|
psz++;
|
|
signed char c = phexdigit[(unsigned char)*psz++];
|
|
if (c == (signed char)-1)
|
|
break;
|
|
unsigned char n = (c << 4);
|
|
c = phexdigit[(unsigned char)*psz++];
|
|
if (c == (signed char)-1)
|
|
break;
|
|
n |= c;
|
|
vch.push_back(n);
|
|
}
|
|
return vch;
|
|
}
|
|
|
|
vector<unsigned char> ParseHex(const string& str)
|
|
{
|
|
return ParseHex(str.c_str());
|
|
}
|
|
|
|
static void InterpretNegativeSetting(string name, map<string, string>& mapSettingsRet)
|
|
{
|
|
// interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set
|
|
if (name.find("-no") == 0)
|
|
{
|
|
std::string positive("-");
|
|
positive.append(name.begin()+3, name.end());
|
|
if (mapSettingsRet.count(positive) == 0)
|
|
{
|
|
bool value = !GetBoolArg(name, false);
|
|
mapSettingsRet[positive] = (value ? "1" : "0");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParseParameters(int argc, const char* const argv[])
|
|
{
|
|
mapArgs.clear();
|
|
mapMultiArgs.clear();
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
std::string str(argv[i]);
|
|
std::string strValue;
|
|
size_t is_index = str.find('=');
|
|
if (is_index != std::string::npos)
|
|
{
|
|
strValue = str.substr(is_index+1);
|
|
str = str.substr(0, is_index);
|
|
}
|
|
#ifdef WIN32
|
|
boost::to_lower(str);
|
|
if (boost::algorithm::starts_with(str, "/"))
|
|
str = "-" + str.substr(1);
|
|
#endif
|
|
if (str[0] != '-')
|
|
break;
|
|
|
|
mapArgs[str] = strValue;
|
|
mapMultiArgs[str].push_back(strValue);
|
|
}
|
|
|
|
// New 0.6 features:
|
|
BOOST_FOREACH(const PAIRTYPE(string,string)& entry, mapArgs)
|
|
{
|
|
string name = entry.first;
|
|
|
|
// interpret --foo as -foo (as long as both are not set)
|
|
if (name.find("--") == 0)
|
|
{
|
|
std::string singleDash(name.begin()+1, name.end());
|
|
if (mapArgs.count(singleDash) == 0)
|
|
mapArgs[singleDash] = entry.second;
|
|
name = singleDash;
|
|
}
|
|
|
|
// interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set
|
|
InterpretNegativeSetting(name, mapArgs);
|
|
}
|
|
}
|
|
|
|
std::string GetArg(const std::string& strArg, const std::string& strDefault)
|
|
{
|
|
if (mapArgs.count(strArg))
|
|
return mapArgs[strArg];
|
|
return strDefault;
|
|
}
|
|
|
|
int64 GetArg(const std::string& strArg, int64 nDefault)
|
|
{
|
|
if (mapArgs.count(strArg))
|
|
return atoi64(mapArgs[strArg]);
|
|
return nDefault;
|
|
}
|
|
|
|
bool GetBoolArg(const std::string& strArg, bool fDefault)
|
|
{
|
|
if (mapArgs.count(strArg))
|
|
{
|
|
if (mapArgs[strArg].empty())
|
|
return true;
|
|
return (atoi(mapArgs[strArg]) != 0);
|
|
}
|
|
return fDefault;
|
|
}
|
|
|
|
bool SoftSetArg(const std::string& strArg, const std::string& strValue)
|
|
{
|
|
if (mapArgs.count(strArg))
|
|
return false;
|
|
mapArgs[strArg] = strValue;
|
|
return true;
|
|
}
|
|
|
|
bool SoftSetBoolArg(const std::string& strArg, bool fValue)
|
|
{
|
|
if (fValue)
|
|
return SoftSetArg(strArg, std::string("1"));
|
|
else
|
|
return SoftSetArg(strArg, std::string("0"));
|
|
}
|
|
|
|
|
|
string EncodeBase64(const unsigned char* pch, size_t len)
|
|
{
|
|
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
string strRet="";
|
|
strRet.reserve((len+2)/3*4);
|
|
|
|
int mode=0, left=0;
|
|
const unsigned char *pchEnd = pch+len;
|
|
|
|
while (pch<pchEnd)
|
|
{
|
|
int enc = *(pch++);
|
|
switch (mode)
|
|
{
|
|
case 0: // we have no bits
|
|
strRet += pbase64[enc >> 2];
|
|
left = (enc & 3) << 4;
|
|
mode = 1;
|
|
break;
|
|
|
|
case 1: // we have two bits
|
|
strRet += pbase64[left | (enc >> 4)];
|
|
left = (enc & 15) << 2;
|
|
mode = 2;
|
|
break;
|
|
|
|
case 2: // we have four bits
|
|
strRet += pbase64[left | (enc >> 6)];
|
|
strRet += pbase64[enc & 63];
|
|
mode = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mode)
|
|
{
|
|
strRet += pbase64[left];
|
|
strRet += '=';
|
|
if (mode == 1)
|
|
strRet += '=';
|
|
}
|
|
|
|
return strRet;
|
|
}
|
|
|
|
string EncodeBase64(const string& str)
|
|
{
|
|
return EncodeBase64((const unsigned char*)str.c_str(), str.size());
|
|
}
|
|
|
|
vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
|
|
{
|
|
static const int decode64_table[256] =
|
|
{
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
|
|
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
|
|
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
|
49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
|
};
|
|
|
|
if (pfInvalid)
|
|
*pfInvalid = false;
|
|
|
|
vector<unsigned char> vchRet;
|
|
vchRet.reserve(strlen(p)*3/4);
|
|
|
|
int mode = 0;
|
|
int left = 0;
|
|
|
|
while (1)
|
|
{
|
|
int dec = decode64_table[(unsigned char)*p];
|
|
if (dec == -1) break;
|
|
p++;
|
|
switch (mode)
|
|
{
|
|
case 0: // we have no bits and get 6
|
|
left = dec;
|
|
mode = 1;
|
|
break;
|
|
|
|
case 1: // we have 6 bits and keep 4
|
|
vchRet.push_back((left<<2) | (dec>>4));
|
|
left = dec & 15;
|
|
mode = 2;
|
|
break;
|
|
|
|
case 2: // we have 4 bits and get 6, we keep 2
|
|
vchRet.push_back((left<<4) | (dec>>2));
|
|
left = dec & 3;
|
|
mode = 3;
|
|
break;
|
|
|
|
case 3: // we have 2 bits and get 6
|
|
vchRet.push_back((left<<6) | dec);
|
|
mode = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pfInvalid)
|
|
switch (mode)
|
|
{
|
|
case 0: // 4n base64 characters processed: ok
|
|
break;
|
|
|
|
case 1: // 4n+1 base64 character processed: impossible
|
|
*pfInvalid = true;
|
|
break;
|
|
|
|
case 2: // 4n+2 base64 characters processed: require '=='
|
|
if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1)
|
|
*pfInvalid = true;
|
|
break;
|
|
|
|
case 3: // 4n+3 base64 characters processed: require '='
|
|
if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1)
|
|
*pfInvalid = true;
|
|
break;
|
|
}
|
|
|
|
return vchRet;
|
|
}
|
|
|
|
string DecodeBase64(const string& str)
|
|
{
|
|
vector<unsigned char> vchRet = DecodeBase64(str.c_str());
|
|
return string((const char*)&vchRet[0], vchRet.size());
|
|
}
|
|
|
|
string EncodeBase32(const unsigned char* pch, size_t len)
|
|
{
|
|
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
|
|
|
|
string strRet="";
|
|
strRet.reserve((len+4)/5*8);
|
|
|
|
int mode=0, left=0;
|
|
const unsigned char *pchEnd = pch+len;
|
|
|
|
while (pch<pchEnd)
|
|
{
|
|
int enc = *(pch++);
|
|
switch (mode)
|
|
{
|
|
case 0: // we have no bits
|
|
strRet += pbase32[enc >> 3];
|
|
left = (enc & 7) << 2;
|
|
mode = 1;
|
|
break;
|
|
|
|
case 1: // we have three bits
|
|
strRet += pbase32[left | (enc >> 6)];
|
|
strRet += pbase32[(enc >> 1) & 31];
|
|
left = (enc & 1) << 4;
|
|
mode = 2;
|
|
break;
|
|
|
|
case 2: // we have one bit
|
|
strRet += pbase32[left | (enc >> 4)];
|
|
left = (enc & 15) << 1;
|
|
mode = 3;
|
|
break;
|
|
|
|
case 3: // we have four bits
|
|
strRet += pbase32[left | (enc >> 7)];
|
|
strRet += pbase32[(enc >> 2) & 31];
|
|
left = (enc & 3) << 3;
|
|
mode = 4;
|
|
break;
|
|
|
|
case 4: // we have two bits
|
|
strRet += pbase32[left | (enc >> 5)];
|
|
strRet += pbase32[enc & 31];
|
|
mode = 0;
|
|
}
|
|
}
|
|
|
|
static const int nPadding[5] = {0, 6, 4, 3, 1};
|
|
if (mode)
|
|
{
|
|
strRet += pbase32[left];
|
|
for (int n=0; n<nPadding[mode]; n++)
|
|
strRet += '=';
|
|
}
|
|
|
|
return strRet;
|
|
}
|
|
|
|
string EncodeBase32(const string& str)
|
|
{
|
|
return EncodeBase32((const unsigned char*)str.c_str(), str.size());
|
|
}
|
|
|
|
vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid)
|
|
{
|
|
static const int decode32_table[256] =
|
|
{
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2,
|
|
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
|
|
23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
|
};
|
|
|
|
if (pfInvalid)
|
|
*pfInvalid = false;
|
|
|
|
vector<unsigned char> vchRet;
|
|
vchRet.reserve((strlen(p))*5/8);
|
|
|
|
int mode = 0;
|
|
int left = 0;
|
|
|
|
while (1)
|
|
{
|
|
int dec = decode32_table[(unsigned char)*p];
|
|
if (dec == -1) break;
|
|
p++;
|
|
switch (mode)
|
|
{
|
|
case 0: // we have no bits and get 5
|
|
left = dec;
|
|
mode = 1;
|
|
break;
|
|
|
|
case 1: // we have 5 bits and keep 2
|
|
vchRet.push_back((left<<3) | (dec>>2));
|
|
left = dec & 3;
|
|
mode = 2;
|
|
break;
|
|
|
|
case 2: // we have 2 bits and keep 7
|
|
left = left << 5 | dec;
|
|
mode = 3;
|
|
break;
|
|
|
|
case 3: // we have 7 bits and keep 4
|
|
vchRet.push_back((left<<1) | (dec>>4));
|
|
left = dec & 15;
|
|
mode = 4;
|
|
break;
|
|
|
|
case 4: // we have 4 bits, and keep 1
|
|
vchRet.push_back((left<<4) | (dec>>1));
|
|
left = dec & 1;
|
|
mode = 5;
|
|
break;
|
|
|
|
case 5: // we have 1 bit, and keep 6
|
|
left = left << 5 | dec;
|
|
mode = 6;
|
|
break;
|
|
|
|
case 6: // we have 6 bits, and keep 3
|
|
vchRet.push_back((left<<2) | (dec>>3));
|
|
left = dec & 7;
|
|
mode = 7;
|
|
break;
|
|
|
|
case 7: // we have 3 bits, and keep 0
|
|
vchRet.push_back((left<<5) | dec);
|
|
mode = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pfInvalid)
|
|
switch (mode)
|
|
{
|
|
case 0: // 8n base32 characters processed: ok
|
|
break;
|
|
|
|
case 1: // 8n+1 base32 characters processed: impossible
|
|
case 3: // +3
|
|
case 6: // +6
|
|
*pfInvalid = true;
|
|
break;
|
|
|
|
case 2: // 8n+2 base32 characters processed: require '======'
|
|
if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1)
|
|
*pfInvalid = true;
|
|
break;
|
|
|
|
case 4: // 8n+4 base32 characters processed: require '===='
|
|
if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1)
|
|
*pfInvalid = true;
|
|
break;
|
|
|
|
case 5: // 8n+5 base32 characters processed: require '==='
|
|
if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1)
|
|
*pfInvalid = true;
|
|
break;
|
|
|
|
case 7: // 8n+7 base32 characters processed: require '='
|
|
if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1)
|
|
*pfInvalid = true;
|
|
break;
|
|
}
|
|
|
|
return vchRet;
|
|
}
|
|
|
|
string DecodeBase32(const string& str)
|
|
{
|
|
vector<unsigned char> vchRet = DecodeBase32(str.c_str());
|
|
return string((const char*)&vchRet[0], vchRet.size());
|
|
}
|
|
|
|
|
|
bool WildcardMatch(const char* psz, const char* mask)
|
|
{
|
|
loop
|
|
{
|
|
switch (*mask)
|
|
{
|
|
case '\0':
|
|
return (*psz == '\0');
|
|
case '*':
|
|
return WildcardMatch(psz, mask+1) || (*psz && WildcardMatch(psz+1, mask));
|
|
case '?':
|
|
if (*psz == '\0')
|
|
return false;
|
|
break;
|
|
default:
|
|
if (*psz != *mask)
|
|
return false;
|
|
break;
|
|
}
|
|
psz++;
|
|
mask++;
|
|
}
|
|
}
|
|
|
|
bool WildcardMatch(const string& str, const string& mask)
|
|
{
|
|
return WildcardMatch(str.c_str(), mask.c_str());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static std::string FormatException(std::exception* pex, const char* pszThread)
|
|
{
|
|
#ifdef WIN32
|
|
char pszModule[MAX_PATH] = "";
|
|
GetModuleFileNameA(NULL, pszModule, sizeof(pszModule));
|
|
#else
|
|
const char* pszModule = "bitcoin";
|
|
#endif
|
|
if (pex)
|
|
return strprintf(
|
|
"EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
|
|
else
|
|
return strprintf(
|
|
"UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread);
|
|
}
|
|
|
|
void LogException(std::exception* pex, const char* pszThread)
|
|
{
|
|
std::string message = FormatException(pex, pszThread);
|
|
printf("\n%s", message.c_str());
|
|
}
|
|
|
|
void PrintException(std::exception* pex, const char* pszThread)
|
|
{
|
|
std::string message = FormatException(pex, pszThread);
|
|
printf("\n\n************************\n%s\n", message.c_str());
|
|
fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
|
|
strMiscWarning = message;
|
|
throw;
|
|
}
|
|
|
|
void PrintExceptionContinue(std::exception* pex, const char* pszThread)
|
|
{
|
|
std::string message = FormatException(pex, pszThread);
|
|
printf("\n\n************************\n%s\n", message.c_str());
|
|
fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
|
|
strMiscWarning = message;
|
|
}
|
|
|
|
boost::filesystem::path GetDefaultDataDir()
|
|
{
|
|
namespace fs = boost::filesystem;
|
|
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin
|
|
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin
|
|
// Mac: ~/Library/Application Support/Bitcoin
|
|
// Unix: ~/.bitcoin
|
|
#ifdef WIN32
|
|
// Windows
|
|
return GetSpecialFolderPath(CSIDL_APPDATA) / "Bitcoin";
|
|
#else
|
|
fs::path pathRet;
|
|
char* pszHome = getenv("HOME");
|
|
if (pszHome == NULL || strlen(pszHome) == 0)
|
|
pathRet = fs::path("/");
|
|
else
|
|
pathRet = fs::path(pszHome);
|
|
#ifdef MAC_OSX
|
|
// Mac
|
|
pathRet /= "Library/Application Support";
|
|
fs::create_directory(pathRet);
|
|
return pathRet / "Bitcoin";
|
|
#else
|
|
// Unix
|
|
return pathRet / ".bitcoin";
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
const boost::filesystem::path &GetDataDir(bool fNetSpecific)
|
|
{
|
|
namespace fs = boost::filesystem;
|
|
|
|
static fs::path pathCached[2];
|
|
static CCriticalSection csPathCached;
|
|
|
|
fs::path &path = pathCached[fNetSpecific];
|
|
|
|
// This can be called during exceptions by printf, so we cache the
|
|
// value so we don't have to do memory allocations after that.
|
|
if (fCachedPath[fNetSpecific])
|
|
return path;
|
|
|
|
LOCK(csPathCached);
|
|
|
|
if (mapArgs.count("-datadir")) {
|
|
path = fs::system_complete(mapArgs["-datadir"]);
|
|
if (!fs::is_directory(path)) {
|
|
path = "";
|
|
return path;
|
|
}
|
|
} else {
|
|
path = GetDefaultDataDir();
|
|
}
|
|
if (fNetSpecific)
|
|
path /= Params().DataDir();
|
|
|
|
fs::create_directories(path);
|
|
|
|
fCachedPath[fNetSpecific] = true;
|
|
return path;
|
|
}
|
|
|
|
boost::filesystem::path GetConfigFile()
|
|
{
|
|
boost::filesystem::path pathConfigFile(GetArg("-conf", "bitcoin.conf"));
|
|
if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile;
|
|
return pathConfigFile;
|
|
}
|
|
|
|
void ReadConfigFile(map<string, string>& mapSettingsRet,
|
|
map<string, vector<string> >& mapMultiSettingsRet)
|
|
{
|
|
boost::filesystem::ifstream streamConfig(GetConfigFile());
|
|
if (!streamConfig.good())
|
|
return; // No bitcoin.conf file is OK
|
|
|
|
// clear path cache after loading config file
|
|
fCachedPath[0] = fCachedPath[1] = false;
|
|
|
|
set<string> setOptions;
|
|
setOptions.insert("*");
|
|
|
|
for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it)
|
|
{
|
|
// Don't overwrite existing settings so command line settings override bitcoin.conf
|
|
string strKey = string("-") + it->string_key;
|
|
if (mapSettingsRet.count(strKey) == 0)
|
|
{
|
|
mapSettingsRet[strKey] = it->value[0];
|
|
// interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set)
|
|
InterpretNegativeSetting(strKey, mapSettingsRet);
|
|
}
|
|
mapMultiSettingsRet[strKey].push_back(it->value[0]);
|
|
}
|
|
}
|
|
|
|
boost::filesystem::path GetPidFile()
|
|
{
|
|
boost::filesystem::path pathPidFile(GetArg("-pid", "bitcoind.pid"));
|
|
if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile;
|
|
return pathPidFile;
|
|
}
|
|
|
|
void CreatePidFile(const boost::filesystem::path &path, pid_t pid)
|
|
{
|
|
FILE* file = fopen(path.string().c_str(), "w");
|
|
if (file)
|
|
{
|
|
fprintf(file, "%d\n", pid);
|
|
fclose(file);
|
|
}
|
|
}
|
|
|
|
bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest)
|
|
{
|
|
#ifdef WIN32
|
|
return MoveFileExA(src.string().c_str(), dest.string().c_str(),
|
|
MOVEFILE_REPLACE_EXISTING);
|
|
#else
|
|
int rc = std::rename(src.string().c_str(), dest.string().c_str());
|
|
return (rc == 0);
|
|
#endif /* WIN32 */
|
|
}
|
|
|
|
void FileCommit(FILE *fileout)
|
|
{
|
|
fflush(fileout); // harmless if redundantly called
|
|
#ifdef WIN32
|
|
_commit(_fileno(fileout));
|
|
#else
|
|
#if defined(__linux__) || defined(__NetBSD__)
|
|
fdatasync(fileno(fileout));
|
|
#else
|
|
fsync(fileno(fileout));
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
int GetFilesize(FILE* file)
|
|
{
|
|
int nSavePos = ftell(file);
|
|
int nFilesize = -1;
|
|
if (fseek(file, 0, SEEK_END) == 0)
|
|
nFilesize = ftell(file);
|
|
fseek(file, nSavePos, SEEK_SET);
|
|
return nFilesize;
|
|
}
|
|
|
|
bool TruncateFile(FILE *file, unsigned int length) {
|
|
#if defined(WIN32)
|
|
return _chsize(_fileno(file), length) == 0;
|
|
#else
|
|
return ftruncate(fileno(file), length) == 0;
|
|
#endif
|
|
}
|
|
|
|
// this function tries to raise the file descriptor limit to the requested number.
|
|
// It returns the actual file descriptor limit (which may be more or less than nMinFD)
|
|
int RaiseFileDescriptorLimit(int nMinFD) {
|
|
#if defined(WIN32)
|
|
return 2048;
|
|
#else
|
|
struct rlimit limitFD;
|
|
if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
|
|
if (limitFD.rlim_cur < (rlim_t)nMinFD) {
|
|
limitFD.rlim_cur = nMinFD;
|
|
if (limitFD.rlim_cur > limitFD.rlim_max)
|
|
limitFD.rlim_cur = limitFD.rlim_max;
|
|
setrlimit(RLIMIT_NOFILE, &limitFD);
|
|
getrlimit(RLIMIT_NOFILE, &limitFD);
|
|
}
|
|
return limitFD.rlim_cur;
|
|
}
|
|
return nMinFD; // getrlimit failed, assume it's fine
|
|
#endif
|
|
}
|
|
|
|
// this function tries to make a particular range of a file allocated (corresponding to disk space)
|
|
// it is advisory, and the range specified in the arguments will never contain live data
|
|
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
|
|
#if defined(WIN32)
|
|
// Windows-specific version
|
|
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
|
|
LARGE_INTEGER nFileSize;
|
|
int64 nEndPos = (int64)offset + length;
|
|
nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
|
|
nFileSize.u.HighPart = nEndPos >> 32;
|
|
SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
|
|
SetEndOfFile(hFile);
|
|
#elif defined(MAC_OSX)
|
|
// OSX specific version
|
|
fstore_t fst;
|
|
fst.fst_flags = F_ALLOCATECONTIG;
|
|
fst.fst_posmode = F_PEOFPOSMODE;
|
|
fst.fst_offset = 0;
|
|
fst.fst_length = (off_t)offset + length;
|
|
fst.fst_bytesalloc = 0;
|
|
if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
|
|
fst.fst_flags = F_ALLOCATEALL;
|
|
fcntl(fileno(file), F_PREALLOCATE, &fst);
|
|
}
|
|
ftruncate(fileno(file), fst.fst_length);
|
|
#elif defined(__linux__)
|
|
// Version using posix_fallocate
|
|
off_t nEndPos = (off_t)offset + length;
|
|
posix_fallocate(fileno(file), 0, nEndPos);
|
|
#else
|
|
// Fallback version
|
|
// TODO: just write one byte per block
|
|
static const char buf[65536] = {};
|
|
fseek(file, offset, SEEK_SET);
|
|
while (length > 0) {
|
|
unsigned int now = 65536;
|
|
if (length < now)
|
|
now = length;
|
|
fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
|
|
length -= now;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ShrinkDebugFile()
|
|
{
|
|
// Scroll debug.log if it's getting too big
|
|
boost::filesystem::path pathLog = GetDataDir() / "debug.log";
|
|
FILE* file = fopen(pathLog.string().c_str(), "r");
|
|
if (file && GetFilesize(file) > 10 * 1000000)
|
|
{
|
|
// Restart the file with some of the end
|
|
char pch[200000];
|
|
fseek(file, -sizeof(pch), SEEK_END);
|
|
int nBytes = fread(pch, 1, sizeof(pch), file);
|
|
fclose(file);
|
|
|
|
file = fopen(pathLog.string().c_str(), "w");
|
|
if (file)
|
|
{
|
|
fwrite(pch, 1, nBytes, file);
|
|
fclose(file);
|
|
}
|
|
}
|
|
else if (file != NULL)
|
|
fclose(file);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// "Never go to sea with two chronometers; take one or three."
|
|
// Our three time sources are:
|
|
// - System clock
|
|
// - Median of other nodes clocks
|
|
// - The user (asking the user to fix the system clock if the first two disagree)
|
|
//
|
|
static int64 nMockTime = 0; // For unit testing
|
|
|
|
int64 GetTime()
|
|
{
|
|
if (nMockTime) return nMockTime;
|
|
|
|
return time(NULL);
|
|
}
|
|
|
|
void SetMockTime(int64 nMockTimeIn)
|
|
{
|
|
nMockTime = nMockTimeIn;
|
|
}
|
|
|
|
static int64 nTimeOffset = 0;
|
|
|
|
int64 GetTimeOffset()
|
|
{
|
|
return nTimeOffset;
|
|
}
|
|
|
|
int64 GetAdjustedTime()
|
|
{
|
|
return GetTime() + GetTimeOffset();
|
|
}
|
|
|
|
void AddTimeData(const CNetAddr& ip, int64 nTime)
|
|
{
|
|
int64 nOffsetSample = nTime - GetTime();
|
|
|
|
// Ignore duplicates
|
|
static set<CNetAddr> setKnown;
|
|
if (!setKnown.insert(ip).second)
|
|
return;
|
|
|
|
// Add data
|
|
vTimeOffsets.input(nOffsetSample);
|
|
printf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);
|
|
if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
|
|
{
|
|
int64 nMedian = vTimeOffsets.median();
|
|
std::vector<int64> vSorted = vTimeOffsets.sorted();
|
|
// Only let other nodes change our time by so much
|
|
if (abs64(nMedian) < 70 * 60)
|
|
{
|
|
nTimeOffset = nMedian;
|
|
}
|
|
else
|
|
{
|
|
nTimeOffset = 0;
|
|
|
|
static bool fDone;
|
|
if (!fDone)
|
|
{
|
|
// If nobody has a time different than ours but within 5 minutes of ours, give a warning
|
|
bool fMatch = false;
|
|
BOOST_FOREACH(int64 nOffset, vSorted)
|
|
if (nOffset != 0 && abs64(nOffset) < 5 * 60)
|
|
fMatch = true;
|
|
|
|
if (!fMatch)
|
|
{
|
|
fDone = true;
|
|
string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin will not work properly.");
|
|
strMiscWarning = strMessage;
|
|
printf("*** %s\n", strMessage.c_str());
|
|
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
|
|
}
|
|
}
|
|
}
|
|
if (fDebug) {
|
|
BOOST_FOREACH(int64 n, vSorted)
|
|
printf("%+"PRI64d" ", n);
|
|
printf("| ");
|
|
}
|
|
printf("nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60);
|
|
}
|
|
}
|
|
|
|
uint32_t insecure_rand_Rz = 11;
|
|
uint32_t insecure_rand_Rw = 11;
|
|
void seed_insecure_rand(bool fDeterministic)
|
|
{
|
|
//The seed values have some unlikely fixed points which we avoid.
|
|
if(fDeterministic)
|
|
{
|
|
insecure_rand_Rz = insecure_rand_Rw = 11;
|
|
} else {
|
|
uint32_t tmp;
|
|
do {
|
|
RAND_bytes((unsigned char*)&tmp, 4);
|
|
} while(tmp == 0 || tmp == 0x9068ffffU);
|
|
insecure_rand_Rz = tmp;
|
|
do {
|
|
RAND_bytes((unsigned char*)&tmp, 4);
|
|
} while(tmp == 0 || tmp == 0x464fffffU);
|
|
insecure_rand_Rw = tmp;
|
|
}
|
|
}
|
|
|
|
string FormatVersion(int nVersion)
|
|
{
|
|
if (nVersion%100 == 0)
|
|
return strprintf("%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100);
|
|
else
|
|
return strprintf("%d.%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100, nVersion%100);
|
|
}
|
|
|
|
string FormatFullVersion()
|
|
{
|
|
return CLIENT_BUILD;
|
|
}
|
|
|
|
// Format the subversion field according to BIP 14 spec (https://en.bitcoin.it/wiki/BIP_0014)
|
|
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments)
|
|
{
|
|
std::ostringstream ss;
|
|
ss << "/";
|
|
ss << name << ":" << FormatVersion(nClientVersion);
|
|
if (!comments.empty())
|
|
ss << "(" << boost::algorithm::join(comments, "; ") << ")";
|
|
ss << "/";
|
|
return ss.str();
|
|
}
|
|
|
|
#ifdef WIN32
|
|
boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate)
|
|
{
|
|
namespace fs = boost::filesystem;
|
|
|
|
char pszPath[MAX_PATH] = "";
|
|
|
|
if(SHGetSpecialFolderPathA(NULL, pszPath, nFolder, fCreate))
|
|
{
|
|
return fs::path(pszPath);
|
|
}
|
|
|
|
printf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n");
|
|
return fs::path("");
|
|
}
|
|
#endif
|
|
|
|
boost::filesystem::path GetTempPath() {
|
|
#if BOOST_FILESYSTEM_VERSION == 3
|
|
return boost::filesystem::temp_directory_path();
|
|
#else
|
|
// TODO: remove when we don't support filesystem v2 anymore
|
|
boost::filesystem::path path;
|
|
#ifdef WIN32
|
|
char pszPath[MAX_PATH] = "";
|
|
|
|
if (GetTempPathA(MAX_PATH, pszPath))
|
|
path = boost::filesystem::path(pszPath);
|
|
#else
|
|
path = boost::filesystem::path("/tmp");
|
|
#endif
|
|
if (path.empty() || !boost::filesystem::is_directory(path)) {
|
|
printf("GetTempPath(): failed to find temp path\n");
|
|
return boost::filesystem::path("");
|
|
}
|
|
return path;
|
|
#endif
|
|
}
|
|
|
|
void runCommand(std::string strCommand)
|
|
{
|
|
int nErr = ::system(strCommand.c_str());
|
|
if (nErr)
|
|
printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr);
|
|
}
|
|
|
|
void RenameThread(const char* name)
|
|
{
|
|
#if defined(PR_SET_NAME)
|
|
// Only the first 15 characters are used (16 - NUL terminator)
|
|
::prctl(PR_SET_NAME, name, 0, 0, 0);
|
|
#elif 0 && (defined(__FreeBSD__) || defined(__OpenBSD__))
|
|
// TODO: This is currently disabled because it needs to be verified to work
|
|
// on FreeBSD or OpenBSD first. When verified the '0 &&' part can be
|
|
// removed.
|
|
pthread_set_name_np(pthread_self(), name);
|
|
|
|
#elif defined(MAC_OSX) && defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
|
|
|
|
// pthread_setname_np is XCode 10.6-and-later
|
|
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
|
|
pthread_setname_np(name);
|
|
#endif
|
|
|
|
#else
|
|
// Prevent warnings for unused parameters...
|
|
(void)name;
|
|
#endif
|
|
}
|