Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/addrman.h b/src/addrman.h
index 081543ace..d703e1091 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -1,503 +1,503 @@
// Copyright (c) 2012 Pieter Wuille
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef _BITCOIN_ADDRMAN
#define _BITCOIN_ADDRMAN 1
#include "netbase.h"
#include "protocol.h"
#include "util.h"
#include "sync.h"
#include <map>
#include <vector>
#include <openssl/rand.h>
/** Extended statistics about a CAddress */
class CAddrInfo : public CAddress
{
private:
// where knowledge about this address first came from
CNetAddr source;
// last successful connection by us
int64 nLastSuccess;
// last try whatsoever by us:
// int64 CAddress::nLastTry
// connection attempts since last successful attempt
int nAttempts;
// reference count in new sets (memory only)
int nRefCount;
// in tried set? (memory only)
bool fInTried;
// position in vRandom
int nRandomPos;
friend class CAddrMan;
public:
IMPLEMENT_SERIALIZE(
CAddress* pthis = (CAddress*)(this);
READWRITE(*pthis);
READWRITE(source);
READWRITE(nLastSuccess);
READWRITE(nAttempts);
)
void Init()
{
nLastSuccess = 0;
nLastTry = 0;
nAttempts = 0;
nRefCount = 0;
fInTried = false;
nRandomPos = -1;
}
CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
{
Init();
}
CAddrInfo() : CAddress(), source()
{
Init();
}
// Calculate in which "tried" bucket this entry belongs
int GetTriedBucket(const std::vector<unsigned char> &nKey) const;
// Calculate in which "new" bucket this entry belongs, given a certain source
int GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const;
// Calculate in which "new" bucket this entry belongs, using its default source
int GetNewBucket(const std::vector<unsigned char> &nKey) const
{
return GetNewBucket(nKey, source);
}
// Determine whether the statistics about this entry are bad enough so that it can just be deleted
bool IsTerrible(int64 nNow = GetAdjustedTime()) const;
// Calculate the relative chance this entry should be given when selecting nodes to connect to
double GetChance(int64 nNow = GetAdjustedTime()) const;
};
// Stochastic address manager
//
// Design goals:
// * Only keep a limited number of addresses around, so that addr.dat and memory requirements do not grow without bound.
// * Keep the address tables in-memory, and asynchronously dump the entire to able in addr.dat.
// * Make sure no (localized) attacker can fill the entire table with his nodes/addresses.
//
// To that end:
// * Addresses are organized into buckets.
// * Address that have not yet been tried go into 256 "new" buckets.
// * Based on the address range (/16 for IPv4) of source of the information, 32 buckets are selected at random
// * The actual bucket is chosen from one of these, based on the range the address itself is located.
// * One single address can occur in up to 4 different buckets, to increase selection chances for addresses that
// are seen frequently. The chance for increasing this multiplicity decreases exponentially.
// * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen
// ones) is removed from it first.
// * Addresses of nodes that are known to be accessible go into 64 "tried" buckets.
// * Each address range selects at random 4 of these buckets.
// * The actual bucket is chosen from one of these, based on the full address.
// * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently
// tried ones) is evicted from it, back to the "new" buckets.
// * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not
// be observable by adversaries.
// * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive)
// consistency checks for the entire data structure.
// total number of buckets for tried addresses
#define ADDRMAN_TRIED_BUCKET_COUNT 64
// maximum allowed number of entries in buckets for tried addresses
#define ADDRMAN_TRIED_BUCKET_SIZE 64
// total number of buckets for new addresses
#define ADDRMAN_NEW_BUCKET_COUNT 256
// maximum allowed number of entries in buckets for new addresses
#define ADDRMAN_NEW_BUCKET_SIZE 64
// over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4
// over how many buckets entries with new addresses originating from a single group are spread
#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32
// in how many buckets for entries with new addresses a single address may occur
#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4
// how many entries in a bucket with tried addresses are inspected, when selecting one to replace
#define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4
// how old addresses can maximally be
#define ADDRMAN_HORIZON_DAYS 30
// after how many failed attempts we give up on a new node
#define ADDRMAN_RETRIES 3
// how many successive failures are allowed ...
#define ADDRMAN_MAX_FAILURES 10
// ... in at least this many days
#define ADDRMAN_MIN_FAIL_DAYS 7
// the maximum percentage of nodes to return in a getaddr call
#define ADDRMAN_GETADDR_MAX_PCT 23
// the maximum number of nodes to return in a getaddr call
#define ADDRMAN_GETADDR_MAX 2500
/** Stochastical (IP) address manager */
class CAddrMan
{
private:
// critical section to protect the inner data structures
mutable CCriticalSection cs;
// secret key to randomize bucket select with
std::vector<unsigned char> nKey;
// last used nId
int nIdCount;
// table with information about all nIds
std::map<int, CAddrInfo> mapInfo;
// find an nId based on its network address
std::map<CNetAddr, int> mapAddr;
// randomly-ordered vector of all nIds
std::vector<int> vRandom;
// number of "tried" entries
int nTried;
// list of "tried" buckets
std::vector<std::vector<int> > vvTried;
// number of (unique) "new" entries
int nNew;
// list of "new" buckets
std::vector<std::set<int> > vvNew;
protected:
// Find an entry.
CAddrInfo* Find(const CNetAddr& addr, int *pnId = NULL);
// find an entry, creating it if necessary.
// nTime and nServices of found node is updated, if necessary.
CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = NULL);
// Swap two elements in vRandom.
void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
// Return position in given bucket to replace.
int SelectTried(int nKBucket);
// Remove an element from a "new" bucket.
// This is the only place where actual deletes occur.
// They are never deleted while in the "tried" table, only possibly evicted back to the "new" table.
int ShrinkNew(int nUBucket);
// Move an entry from the "new" table(s) to the "tried" table
// @pre vvUnkown[nOrigin].count(nId) != 0
void MakeTried(CAddrInfo& info, int nId, int nOrigin);
// Mark an entry "good", possibly moving it from "new" to "tried".
void Good_(const CService &addr, int64 nTime);
// Add an entry to the "new" table.
bool Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty);
// Mark an entry as attempted to connect.
void Attempt_(const CService &addr, int64 nTime);
// Select an address to connect to.
// nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100)
CAddress Select_(int nUnkBias);
#ifdef DEBUG_ADDRMAN
// Perform consistency check. Returns an error code or zero.
int Check_();
#endif
// Select several addresses at once.
void GetAddr_(std::vector<CAddress> &vAddr);
// Mark an entry as currently-connected-to.
void Connected_(const CService &addr, int64 nTime);
public:
IMPLEMENT_SERIALIZE
(({
// serialized format:
// * version byte (currently 0)
// * nKey
// * nNew
// * nTried
// * number of "new" buckets
// * all nNew addrinfos in vvNew
// * all nTried addrinfos in vvTried
// * for each bucket:
// * number of elements
// * for each element: index
//
// Notice that vvTried, mapAddr and vVector are never encoded explicitly;
// they are instead reconstructed from the other information.
//
// vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change,
// otherwise it is reconstructed as well.
//
// This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
// changes to the ADDRMAN_ parameters without breaking the on-disk structure.
{
LOCK(cs);
unsigned char nVersion = 0;
READWRITE(nVersion);
READWRITE(nKey);
READWRITE(nNew);
READWRITE(nTried);
CAddrMan *am = const_cast<CAddrMan*>(this);
if (fWrite)
{
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT;
READWRITE(nUBuckets);
std::map<int, int> mapUnkIds;
int nIds = 0;
for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
{
if (nIds == nNew) break; // this means nNew was wrong, oh ow
mapUnkIds[(*it).first] = nIds;
CAddrInfo &info = (*it).second;
if (info.nRefCount)
{
READWRITE(info);
nIds++;
}
}
nIds = 0;
for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
{
if (nIds == nTried) break; // this means nTried was wrong, oh ow
CAddrInfo &info = (*it).second;
if (info.fInTried)
{
READWRITE(info);
nIds++;
}
}
for (std::vector<std::set<int> >::iterator it = am->vvNew.begin(); it != am->vvNew.end(); it++)
{
const std::set<int> &vNew = (*it);
int nSize = vNew.size();
READWRITE(nSize);
for (std::set<int>::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++)
{
int nIndex = mapUnkIds[*it2];
READWRITE(nIndex);
}
}
} else {
int nUBuckets = 0;
READWRITE(nUBuckets);
am->nIdCount = 0;
am->mapInfo.clear();
am->mapAddr.clear();
am->vRandom.clear();
am->vvTried = std::vector<std::vector<int> >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0));
am->vvNew = std::vector<std::set<int> >(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>());
for (int n = 0; n < am->nNew; n++)
{
CAddrInfo &info = am->mapInfo[n];
READWRITE(info);
am->mapAddr[info] = n;
info.nRandomPos = vRandom.size();
am->vRandom.push_back(n);
if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT)
{
am->vvNew[info.GetNewBucket(am->nKey)].insert(n);
info.nRefCount++;
}
}
am->nIdCount = am->nNew;
int nLost = 0;
for (int n = 0; n < am->nTried; n++)
{
CAddrInfo info;
READWRITE(info);
std::vector<int> &vTried = am->vvTried[info.GetTriedBucket(am->nKey)];
if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
{
info.nRandomPos = vRandom.size();
info.fInTried = true;
am->vRandom.push_back(am->nIdCount);
am->mapInfo[am->nIdCount] = info;
am->mapAddr[info] = am->nIdCount;
vTried.push_back(am->nIdCount);
am->nIdCount++;
} else {
nLost++;
}
}
am->nTried -= nLost;
for (int b = 0; b < nUBuckets; b++)
{
std::set<int> &vNew = am->vvNew[b];
int nSize = 0;
READWRITE(nSize);
for (int n = 0; n < nSize; n++)
{
int nIndex = 0;
READWRITE(nIndex);
CAddrInfo &info = am->mapInfo[nIndex];
if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
{
info.nRefCount++;
vNew.insert(nIndex);
}
}
}
}
}
});)
CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>())
{
nKey.resize(32);
RAND_bytes(&nKey[0], 32);
nIdCount = 0;
nTried = 0;
nNew = 0;
}
// Return the number of (unique) addresses in all tables.
int size()
{
return vRandom.size();
}
// Consistency check
void Check()
{
#ifdef DEBUG_ADDRMAN
{
LOCK(cs);
int err;
if ((err=Check_()))
LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
}
#endif
}
// Add a single address.
bool Add(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty = 0)
{
bool fRet = false;
{
LOCK(cs);
Check();
fRet |= Add_(addr, source, nTimePenalty);
Check();
}
if (fRet)
- LogPrint("addr", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew);
+ LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew);
return fRet;
}
// Add multiple addresses.
bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64 nTimePenalty = 0)
{
int nAdd = 0;
{
LOCK(cs);
Check();
for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++)
nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0;
Check();
}
if (nAdd)
- LogPrint("addr", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew);
+ LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew);
return nAdd > 0;
}
// Mark an entry as accessible.
void Good(const CService &addr, int64 nTime = GetAdjustedTime())
{
{
LOCK(cs);
Check();
Good_(addr, nTime);
Check();
}
}
// Mark an entry as connection attempted to.
void Attempt(const CService &addr, int64 nTime = GetAdjustedTime())
{
{
LOCK(cs);
Check();
Attempt_(addr, nTime);
Check();
}
}
// Choose an address to connect to.
// nUnkBias determines how much "new" entries are favored over "tried" ones (0-100).
CAddress Select(int nUnkBias = 50)
{
CAddress addrRet;
{
LOCK(cs);
Check();
addrRet = Select_(nUnkBias);
Check();
}
return addrRet;
}
// Return a bunch of addresses, selected at random.
std::vector<CAddress> GetAddr()
{
Check();
std::vector<CAddress> vAddr;
{
LOCK(cs);
GetAddr_(vAddr);
}
Check();
return vAddr;
}
// Mark an entry as currently-connected-to.
void Connected(const CService &addr, int64 nTime = GetAdjustedTime())
{
{
LOCK(cs);
Check();
Connected_(addr, nTime);
Check();
}
}
};
#endif
diff --git a/src/init.cpp b/src/init.cpp
index 72b53ebec..647b8d52e 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,1048 +1,1061 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include "bitcoin-config.h"
#endif
#include "init.h"
#include "main.h"
#include "core.h"
#include "chainparams.h"
#include "txdb.h"
#include "walletdb.h"
#include "bitcoinrpc.h"
#include "net.h"
#include "util.h"
#include "miner.h"
#include "ui_interface.h"
#include "checkpoints.h"
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <openssl/crypto.h>
#ifndef WIN32
#include <signal.h>
#endif
using namespace std;
using namespace boost;
std::string strWalletFile;
CWallet* pwalletMain;
CClientUIInterface uiInterface;
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for
// accessing block files, don't count towards to fd_set size limit
// anyway.
#define MIN_CORE_FILEDESCRIPTORS 0
#else
#define MIN_CORE_FILEDESCRIPTORS 150
#endif
// Used to pass flags to the Bind() function
enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1)
};
//////////////////////////////////////////////////////////////////////////////
//
// Shutdown
//
//
// Thread management and startup/shutdown:
//
// The network-processing threads are all part of a thread group
// created by AppInit() or the Qt main() function.
//
// A clean exit happens when StartShutdown() or the SIGTERM
// signal handler sets fRequestShutdown, which triggers
// the DetectShutdownThread(), which interrupts the main thread group.
// DetectShutdownThread() then exits, which causes AppInit() to
// continue (it .joins the shutdown thread).
// Shutdown() is then
// called to clean up database connections, and stop other
// threads that should only be stopped after the main network-processing
// threads have exited.
//
// Note that if running -daemon the parent process returns from AppInit2
// before adding any threads to the threadGroup, so .join_all() returns
// immediately and the parent exits from main().
//
// Shutdown for Qt is very similar, only it uses a QTimer to detect
// fRequestShutdown getting set, and then does the normal Qt
// shutdown thing.
//
volatile bool fRequestShutdown = false;
void StartShutdown()
{
fRequestShutdown = true;
}
bool ShutdownRequested()
{
return fRequestShutdown;
}
static CCoinsViewDB *pcoinsdbview;
void Shutdown()
{
LogPrintf("Shutdown : In progress...\n");
static CCriticalSection cs_Shutdown;
TRY_LOCK(cs_Shutdown, lockShutdown);
if (!lockShutdown) return;
RenameThread("bitcoin-shutoff");
nTransactionsUpdated++;
StopRPCThreads();
ShutdownRPCMining();
if (pwalletMain)
bitdb.Flush(false);
GenerateBitcoins(false, NULL);
StopNode();
{
LOCK(cs_main);
if (pwalletMain)
pwalletMain->SetBestChain(chainActive.GetLocator());
if (pblocktree)
pblocktree->Flush();
if (pcoinsTip)
pcoinsTip->Flush();
delete pcoinsTip; pcoinsTip = NULL;
delete pcoinsdbview; pcoinsdbview = NULL;
delete pblocktree; pblocktree = NULL;
}
if (pwalletMain)
bitdb.Flush(true);
boost::filesystem::remove(GetPidFile());
UnregisterAllWallets();
if (pwalletMain)
delete pwalletMain;
LogPrintf("Shutdown : done\n");
}
//
// Signal handlers are very limited in what they are allowed to do, so:
//
void HandleSIGTERM(int)
{
fRequestShutdown = true;
}
void HandleSIGHUP(int)
{
fReopenDebugLog = true;
}
bool static InitError(const std::string &str)
{
uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR);
return false;
}
bool static InitWarning(const std::string &str)
{
uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING);
return true;
}
bool static Bind(const CService &addr, unsigned int flags) {
if (!(flags & BF_EXPLICIT) && IsLimited(addr))
return false;
std::string strError;
if (!BindListenPort(addr, strError)) {
if (flags & BF_REPORT_ERROR)
return InitError(strError);
return false;
}
return true;
}
// Core-specific options shared between UI, daemon and RPC client
std::string HelpMessage(HelpMessageMode hmm)
{
string strUsage = _("Options:") + "\n";
strUsage += " -? " + _("This help message") + "\n";
strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n";
strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n";
strUsage += " -testnet " + _("Use the test network") + "\n";
if(hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT)
{
strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n";
strUsage += " -gen " + _("Generate coins (default: 0)") + "\n";
strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n";
strUsage += " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n";
strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n";
strUsage += " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n";
strUsage += " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n";
strUsage += " -onion=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n";
strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n";
strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n";
strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n";
strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n";
strUsage += " -connect=<ip> " + _("Connect only to the specified node(s)") + "\n";
strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n";
strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n";
strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n";
strUsage += " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n";
strUsage += " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n";
strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n";
strUsage += " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n";
strUsage += " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n";
strUsage += " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n";
strUsage += " -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n";
strUsage += " -maxreceivebuffer=<n> " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)") + "\n";
strUsage += " -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n";
#ifdef USE_UPNP
#if USE_UPNP
strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n";
#else
strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n";
#endif
#endif
strUsage += " -paytxfee=<amt> " + _("Fee per KB to add to transactions you send") + "\n";
- strUsage += " -debug " + _("Output extra debugging information. Implies all other -debug* options") + "\n";
- strUsage += " -debugnet " + _("Output extra network debugging information") + "\n";
+ strUsage += " -debug=<category> " + _("Output debugging information (default: 0, supplying <category> is optional)") + "\n";
+ strUsage += _("If <category> is not supplied, output all debugging information.") + "\n";
+ strUsage += _("<category> can be:");
+ strUsage += " addrman, alert, coindb, db, lock, rand, rpc, selectcoins, mempool, net"; // Don't translate these and qt below
+ if (hmm == HMM_BITCOIN_QT)
+ {
+ strUsage += ", qt.\n";
+ }
+ else
+ {
+ strUsage += ".\n";
+ }
strUsage += " -logtimestamps " + _("Prepend debug output with timestamp") + "\n";
strUsage += " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n";
strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n";
strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be "
"solved instantly. This is intended for regression testing tools and app development.") + "\n";
#ifdef WIN32
strUsage += " -printtodebugger " + _("Send trace/debug info to debugger") + "\n";
#endif
}
if (hmm == HMM_BITCOIN_QT)
{
strUsage += " -server " + _("Accept command line and JSON-RPC commands") + "\n";
}
if (hmm == HMM_BITCOIND)
{
#if !defined(WIN32)
strUsage += " -daemon " + _("Run in the background as a daemon and accept commands") + "\n";
#endif
}
if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_CLI)
{
strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n";
}
strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n";
strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n";
if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT)
{
strUsage += " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n";
} else {
strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n";
}
if(hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT)
{
strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n";
strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n";
strUsage += " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n";
strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n";
strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n";
strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n";
strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n";
strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n";
strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n";
strUsage += " -checkblocks=<n> " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n";
strUsage += " -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n";
strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n";
strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n";
strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n";
strUsage += " -par=<n> " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n";
strUsage += "\n" + _("Block creation options:") + "\n";
strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n";
strUsage += " -blockmaxsize=<n> " + _("Set maximum block size in bytes (default: 250000)") + "\n";
strUsage += " -blockprioritysize=<n> " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n";
}
strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n";
strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n";
if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT)
{
strUsage += " -rpcsslcertificatechainfile=<file.cert> " + _("Server certificate file (default: server.cert)") + "\n";
strUsage += " -rpcsslprivatekeyfile=<file.pem> " + _("Server private key (default: server.pem)") + "\n";
strUsage += " -rpcsslciphers=<ciphers> " + _("Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)") + "\n";
}
return strUsage;
}
struct CImportingNow
{
CImportingNow() {
assert(fImporting == false);
fImporting = true;
}
~CImportingNow() {
assert(fImporting == true);
fImporting = false;
}
};
void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
{
RenameThread("bitcoin-loadblk");
// -reindex
if (fReindex) {
CImportingNow imp;
int nFile = 0;
while (true) {
CDiskBlockPos pos(nFile, 0);
FILE *file = OpenBlockFile(pos, true);
if (!file)
break;
LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
LoadExternalBlockFile(file, &pos);
nFile++;
}
pblocktree->WriteReindexing(false);
fReindex = false;
LogPrintf("Reindexing finished\n");
// To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
InitBlockIndex();
}
// hardcoded $DATADIR/bootstrap.dat
filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat";
if (filesystem::exists(pathBootstrap)) {
FILE *file = fopen(pathBootstrap.string().c_str(), "rb");
if (file) {
CImportingNow imp;
filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
LogPrintf("Importing bootstrap.dat...\n");
LoadExternalBlockFile(file);
RenameOver(pathBootstrap, pathBootstrapOld);
}
}
// -loadblock=
BOOST_FOREACH(boost::filesystem::path &path, vImportFiles) {
FILE *file = fopen(path.string().c_str(), "rb");
if (file) {
CImportingNow imp;
LogPrintf("Importing %s...\n", path.string().c_str());
LoadExternalBlockFile(file);
}
}
}
/** Initialize bitcoin.
* @pre Parameters should be parsed and config file should be read.
*/
bool AppInit2(boost::thread_group& threadGroup)
{
// ********************************************************* Step 1: setup
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
#endif
#if _MSC_VER >= 1400
// Disable confusing "helpful" text message on abort, Ctrl-C
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
#ifdef WIN32
// Enable Data Execution Prevention (DEP)
// Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008
// A failure is non-critical and needs no further attention!
#ifndef PROCESS_DEP_ENABLE
// We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7),
// which is not correct. Can be removed, when GCCs winbase.h is fixed!
#define PROCESS_DEP_ENABLE 0x00000001
#endif
typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD);
PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy");
if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE);
// Initialize Windows Sockets
WSADATA wsadata;
int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2)
{
return InitError(strprintf("Error: Winsock library failed to start (WSAStartup returned error %d)", ret));
}
#endif
#ifndef WIN32
umask(077);
// Clean shutdown on SIGTERM
struct sigaction sa;
sa.sa_handler = HandleSIGTERM;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
// Reopen debug.log on SIGHUP
struct sigaction sa_hup;
sa_hup.sa_handler = HandleSIGHUP;
sigemptyset(&sa_hup.sa_mask);
sa_hup.sa_flags = 0;
sigaction(SIGHUP, &sa_hup, NULL);
#if defined (__SVR4) && defined (__sun)
// ignore SIGPIPE on Solaris
signal(SIGPIPE, SIG_IGN);
#endif
#endif
// ********************************************************* Step 2: parameter interactions
if (mapArgs.count("-bind")) {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
SoftSetBoolArg("-listen", true);
}
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
// when only connecting to trusted nodes, do not seed via DNS, or listen by default
SoftSetBoolArg("-dnsseed", false);
SoftSetBoolArg("-listen", false);
}
if (mapArgs.count("-proxy")) {
// to protect privacy, do not listen by default if a proxy server is specified
SoftSetBoolArg("-listen", false);
}
if (!GetBoolArg("-listen", true)) {
// do not map ports or try to retrieve public IP when not listening (pointless)
SoftSetBoolArg("-upnp", false);
SoftSetBoolArg("-discover", false);
}
if (mapArgs.count("-externalip")) {
// if an explicit public IP is specified, do not try to find others
SoftSetBoolArg("-discover", false);
}
if (GetBoolArg("-salvagewallet", false)) {
// Rewrite just private keys: rescan to find transactions
SoftSetBoolArg("-rescan", true);
}
// Make sure enough file descriptors are available
int nBind = std::max((int)mapArgs.count("-bind"), 1);
nMaxConnections = GetArg("-maxconnections", 125);
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections)
nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS;
// ********************************************************* Step 3: parameter-to-internal-flags
- if (mapMultiArgs.count("-debug")) fDebug = true;
+ fDebug = !mapMultiArgs["-debug"].empty();
+ // Special-case: if -debug=0/-nodebug is set, turn off debugging messages
+ const vector<string>& categories = mapMultiArgs["-debug"];
+ if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), string("0")) != categories.end())
+ fDebug = false;
+
+ // Check for -debugnet (deprecated)
+ if (GetBoolArg("-debugnet", false))
+ InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net"));
+
fBenchmark = GetBoolArg("-benchmark", false);
mempool.fChecks = GetBoolArg("-checkmempool", RegTest());
Checkpoints::fEnabled = GetBoolArg("-checkpoints", true);
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = GetArg("-par", 0);
if (nScriptCheckThreads <= 0)
nScriptCheckThreads += boost::thread::hardware_concurrency();
if (nScriptCheckThreads <= 1)
nScriptCheckThreads = 0;
else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
- // -debug implies fDebug*
- if (fDebug)
- fDebugNet = true;
- else
- fDebugNet = GetBoolArg("-debugnet", false);
-
if (fDaemon)
fServer = true;
else
fServer = GetBoolArg("-server", false);
/* force fServer when running without GUI */
if (!fHaveGUI)
fServer = true;
fPrintToConsole = GetBoolArg("-printtoconsole", false);
fPrintToDebugger = GetBoolArg("-printtodebugger", false);
fLogTimestamps = GetBoolArg("-logtimestamps", false);
if (mapArgs.count("-timeout"))
{
int nNewTimeout = GetArg("-timeout", 5000);
if (nNewTimeout > 0 && nNewTimeout < 600000)
nConnectTimeout = nNewTimeout;
}
// Continue to put "/P2SH/" in the coinbase to monitor
// BIP16 support.
// This can be removed eventually...
const char* pszP2SH = "/P2SH/";
COINBASE_FLAGS << std::vector<unsigned char>(pszP2SH, pszP2SH+strlen(pszP2SH));
// Fee-per-kilobyte amount considered the same as "free"
// If you are mining, be careful setting this:
// if you set it to zero then
// a transaction spammer can cheaply fill blocks using
// 1-satoshi-fee transactions. It should be set above the real
// cost to you of processing a transaction.
if (mapArgs.count("-mintxfee"))
{
int64 n = 0;
if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0)
CTransaction::nMinTxFee = n;
else
return InitError(strprintf(_("Invalid amount for -mintxfee=<amount>: '%s'"), mapArgs["-mintxfee"].c_str()));
}
if (mapArgs.count("-minrelaytxfee"))
{
int64 n = 0;
if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0)
CTransaction::nMinRelayTxFee = n;
else
return InitError(strprintf(_("Invalid amount for -minrelaytxfee=<amount>: '%s'"), mapArgs["-minrelaytxfee"].c_str()));
}
if (mapArgs.count("-paytxfee"))
{
if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee))
return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s'"), mapArgs["-paytxfee"].c_str()));
if (nTransactionFee > 0.25 * COIN)
InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction."));
}
strWalletFile = GetArg("-wallet", "wallet.dat");
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
std::string strDataDir = GetDataDir().string();
// Wallet file must be a plain filename without a directory
if (strWalletFile != boost::filesystem::basename(strWalletFile) + boost::filesystem::extension(strWalletFile))
return InitError(strprintf(_("Wallet %s resides outside data directory %s"), strWalletFile.c_str(), strDataDir.c_str()));
// Make sure only a single Bitcoin process is using the data directory.
boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist.
if (file) fclose(file);
static boost::interprocess::file_lock lock(pathLockFile.string().c_str());
if (!lock.try_lock())
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str()));
if (GetBoolArg("-shrinkdebugfile", !fDebug))
ShrinkDebugFile();
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
LogPrintf("Bitcoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str());
LogPrintf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION));
if (!fLogTimestamps)
LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str());
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string().c_str());
LogPrintf("Using data directory %s\n", strDataDir.c_str());
LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
std::ostringstream strErrors;
if (fDaemon)
fprintf(stdout, "Bitcoin server starting\n");
if (nScriptCheckThreads) {
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
for (int i=0; i<nScriptCheckThreads-1; i++)
threadGroup.create_thread(&ThreadScriptCheck);
}
int64 nStart;
// ********************************************************* Step 5: verify wallet database integrity
uiInterface.InitMessage(_("Verifying wallet..."));
if (!bitdb.Open(GetDataDir()))
{
// try moving the database env out of the way
boost::filesystem::path pathDatabase = GetDataDir() / "database";
boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%"PRI64d".bak", GetTime());
try {
boost::filesystem::rename(pathDatabase, pathDatabaseBak);
LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string().c_str(), pathDatabaseBak.string().c_str());
} catch(boost::filesystem::filesystem_error &error) {
// failure is ok (well, not really, but it's not worse than what we started with)
}
// try again
if (!bitdb.Open(GetDataDir())) {
// if it still fails, it probably means we can't even create the database env
string msg = strprintf(_("Error initializing wallet database environment %s!"), strDataDir.c_str());
return InitError(msg);
}
}
if (GetBoolArg("-salvagewallet", false))
{
// Recover readable keypairs:
if (!CWalletDB::Recover(bitdb, strWalletFile, true))
return false;
}
if (filesystem::exists(GetDataDir() / strWalletFile))
{
CDBEnv::VerifyResult r = bitdb.Verify(strWalletFile, CWalletDB::Recover);
if (r == CDBEnv::RECOVER_OK)
{
string msg = strprintf(_("Warning: wallet.dat corrupt, data salvaged!"
" Original wallet.dat saved as wallet.{timestamp}.bak in %s; if"
" your balance or transactions are incorrect you should"
" restore from a backup."), strDataDir.c_str());
InitWarning(msg);
}
if (r == CDBEnv::RECOVER_FAIL)
return InitError(_("wallet.dat corrupt, salvage failed"));
}
// ********************************************************* Step 6: network initialization
RegisterNodeSignals(GetNodeSignals());
int nSocksVersion = GetArg("-socks", 5);
if (nSocksVersion != 4 && nSocksVersion != 5)
return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion));
if (mapArgs.count("-onlynet")) {
std::set<enum Network> nets;
BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet.c_str()));
nets.insert(net);
}
for (int n = 0; n < NET_MAX; n++) {
enum Network net = (enum Network)n;
if (!nets.count(net))
SetLimited(net);
}
}
#if defined(USE_IPV6)
#if ! USE_IPV6
else
SetLimited(NET_IPV6);
#endif
#endif
CService addrProxy;
bool fProxy = false;
if (mapArgs.count("-proxy")) {
addrProxy = CService(mapArgs["-proxy"], 9050);
if (!addrProxy.IsValid())
return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"].c_str()));
if (!IsLimited(NET_IPV4))
SetProxy(NET_IPV4, addrProxy, nSocksVersion);
if (nSocksVersion > 4) {
#ifdef USE_IPV6
if (!IsLimited(NET_IPV6))
SetProxy(NET_IPV6, addrProxy, nSocksVersion);
#endif
SetNameProxy(addrProxy, nSocksVersion);
}
fProxy = true;
}
// -onion can override normal proxy, -noonion disables tor entirely
// -tor here is a temporary backwards compatibility measure
if (mapArgs.count("-tor"))
printf("Notice: option -tor has been replaced with -onion and will be removed in a later version.\n");
if (!(mapArgs.count("-onion") && mapArgs["-onion"] == "0") &&
!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") &&
(fProxy || mapArgs.count("-onion") || mapArgs.count("-tor"))) {
CService addrOnion;
if (!mapArgs.count("-onion") && !mapArgs.count("-tor"))
addrOnion = addrProxy;
else
addrOnion = mapArgs.count("-onion")?CService(mapArgs["-onion"], 9050):CService(mapArgs["-tor"], 9050);
if (!addrOnion.IsValid())
return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs.count("-onion")?mapArgs["-onion"].c_str():mapArgs["-tor"].c_str()));
SetProxy(NET_TOR, addrOnion, 5);
SetReachable(NET_TOR);
}
// see Step 2: parameter interactions for more information about these
fNoListen = !GetBoolArg("-listen", true);
fDiscover = GetBoolArg("-discover", true);
fNameLookup = GetBoolArg("-dns", true);
bool fBound = false;
if (!fNoListen) {
if (mapArgs.count("-bind")) {
BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind.c_str()));
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
}
}
else {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
#ifdef USE_IPV6
fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE);
#endif
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
}
if (!fBound)
return InitError(_("Failed to listen on any port. Use -listen=0 if you want this."));
}
if (mapArgs.count("-externalip")) {
BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) {
CService addrLocal(strAddr, GetListenPort(), fNameLookup);
if (!addrLocal.IsValid())
return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr.c_str()));
AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL);
}
}
BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"])
AddOneShot(strDest);
// ********************************************************* Step 7: load block chain
fReindex = GetBoolArg("-reindex", false);
// Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/
filesystem::path blocksDir = GetDataDir() / "blocks";
if (!filesystem::exists(blocksDir))
{
filesystem::create_directories(blocksDir);
bool linked = false;
for (unsigned int i = 1; i < 10000; i++) {
filesystem::path source = GetDataDir() / strprintf("blk%04u.dat", i);
if (!filesystem::exists(source)) break;
filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1);
try {
filesystem::create_hard_link(source, dest);
LogPrintf("Hardlinked %s -> %s\n", source.string().c_str(), dest.string().c_str());
linked = true;
} catch (filesystem::filesystem_error & e) {
// Note: hardlink creation failing is not a disaster, it just means
// blocks will get re-downloaded from peers.
LogPrintf("Error hardlinking blk%04u.dat : %s\n", i, e.what());
break;
}
}
if (linked)
{
fReindex = true;
}
}
// cache size calculations
size_t nTotalCache = GetArg("-dbcache", 25) << 20;
if (nTotalCache < (1 << 22))
nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB
size_t nBlockTreeDBCache = nTotalCache / 8;
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false))
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
nTotalCache -= nBlockTreeDBCache;
size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache
nTotalCache -= nCoinDBCache;
nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes
bool fLoaded = false;
while (!fLoaded) {
bool fReset = fReindex;
std::string strLoadError;
uiInterface.InitMessage(_("Loading block index..."));
nStart = GetTimeMillis();
do {
try {
UnloadBlockIndex();
delete pcoinsTip;
delete pcoinsdbview;
delete pblocktree;
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex);
pcoinsTip = new CCoinsViewCache(*pcoinsdbview);
if (fReindex)
pblocktree->WriteReindexing(true);
if (!LoadBlockIndex()) {
strLoadError = _("Error loading block database");
break;
}
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
if (!mapBlockIndex.empty() && chainActive.Genesis() == NULL)
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
// Initialize the block index (no-op if non-empty database was already loaded)
if (!InitBlockIndex()) {
strLoadError = _("Error initializing block database");
break;
}
// Check for changed -txindex state
if (fTxIndex != GetBoolArg("-txindex", false)) {
strLoadError = _("You need to rebuild the database using -reindex to change -txindex");
break;
}
uiInterface.InitMessage(_("Verifying blocks..."));
if (!VerifyDB(GetArg("-checklevel", 3),
GetArg("-checkblocks", 288))) {
strLoadError = _("Corrupted block database detected");
break;
}
} catch(std::exception &e) {
if (fDebug) LogPrintf("%s\n", e.what());
strLoadError = _("Error opening block database");
break;
}
fLoaded = true;
} while(false);
if (!fLoaded) {
// first suggest a reindex
if (!fReset) {
bool fRet = uiInterface.ThreadSafeMessageBox(
strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"),
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
if (fRet) {
fReindex = true;
fRequestShutdown = false;
} else {
LogPrintf("Aborted block database rebuild. Exiting.\n");
return false;
}
} else {
return InitError(strLoadError);
}
}
}
// as LoadBlockIndex can take several minutes, it's possible the user
// requested to kill bitcoin-qt during the last operation. If so, exit.
// As the program has not fully started yet, Shutdown() is possibly overkill.
if (fRequestShutdown)
{
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
LogPrintf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);
if (GetBoolArg("-printblockindex", false) || GetBoolArg("-printblocktree", false))
{
PrintBlockTree();
return false;
}
if (mapArgs.count("-printblock"))
{
string strMatch = mapArgs["-printblock"];
int nFound = 0;
for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
{
uint256 hash = (*mi).first;
if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)
{
CBlockIndex* pindex = (*mi).second;
CBlock block;
ReadBlockFromDisk(block, pindex);
block.BuildMerkleTree();
block.print();
LogPrintf("\n");
nFound++;
}
}
if (nFound == 0)
LogPrintf("No blocks matching %s were found\n", strMatch.c_str());
return false;
}
// ********************************************************* Step 8: load wallet
uiInterface.InitMessage(_("Loading wallet..."));
nStart = GetTimeMillis();
bool fFirstRun = true;
pwalletMain = new CWallet(strWalletFile);
DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
if (nLoadWalletRet == DB_CORRUPT)
strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n";
else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
{
string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect."));
InitWarning(msg);
}
else if (nLoadWalletRet == DB_TOO_NEW)
strErrors << _("Error loading wallet.dat: Wallet requires newer version of Bitcoin") << "\n";
else if (nLoadWalletRet == DB_NEED_REWRITE)
{
strErrors << _("Wallet needed to be rewritten: restart Bitcoin to complete") << "\n";
LogPrintf("%s", strErrors.str().c_str());
return InitError(strErrors.str());
}
else
strErrors << _("Error loading wallet.dat") << "\n";
}
if (GetBoolArg("-upgradewallet", fFirstRun))
{
int nMaxVersion = GetArg("-upgradewallet", 0);
if (nMaxVersion == 0) // the -upgradewallet without argument case
{
LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
nMaxVersion = CLIENT_VERSION;
pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
}
else
LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
if (nMaxVersion < pwalletMain->GetVersion())
strErrors << _("Cannot downgrade wallet") << "\n";
pwalletMain->SetMaxVersion(nMaxVersion);
}
if (fFirstRun)
{
// Create new keyUser and set as default key
RandAddSeedPerfmon();
CPubKey newDefaultKey;
if (pwalletMain->GetKeyFromPool(newDefaultKey)) {
pwalletMain->SetDefaultKey(newDefaultKey);
if (!pwalletMain->SetAddressBook(pwalletMain->vchDefaultKey.GetID(), "", "receive"))
strErrors << _("Cannot write default address") << "\n";
}
pwalletMain->SetBestChain(chainActive.GetLocator());
}
LogPrintf("%s", strErrors.str().c_str());
LogPrintf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart);
RegisterWallet(pwalletMain);
CBlockIndex *pindexRescan = chainActive.Tip();
if (GetBoolArg("-rescan", false))
pindexRescan = chainActive.Genesis();
else
{
CWalletDB walletdb(strWalletFile);
CBlockLocator locator;
if (walletdb.ReadBestBlock(locator))
pindexRescan = chainActive.FindFork(locator);
else
pindexRescan = chainActive.Genesis();
}
if (chainActive.Tip() && chainActive.Tip() != pindexRescan)
{
uiInterface.InitMessage(_("Rescanning..."));
LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight);
nStart = GetTimeMillis();
pwalletMain->ScanForWalletTransactions(pindexRescan, true);
LogPrintf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart);
pwalletMain->SetBestChain(chainActive.GetLocator());
nWalletDBUpdated++;
}
// ********************************************************* Step 9: import blocks
// scan for better chains in the block chain database, that are not yet connected in the active best chain
CValidationState state;
if (!ConnectBestBlock(state))
strErrors << "Failed to connect best block";
std::vector<boost::filesystem::path> vImportFiles;
if (mapArgs.count("-loadblock"))
{
BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"])
vImportFiles.push_back(strFile);
}
threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles));
// ********************************************************* Step 10: load peers
uiInterface.InitMessage(_("Loading addresses..."));
nStart = GetTimeMillis();
{
CAddrDB adb;
if (!adb.Read(addrman))
LogPrintf("Invalid or missing peers.dat; recreating\n");
}
LogPrintf("Loaded %i addresses from peers.dat %"PRI64d"ms\n",
addrman.size(), GetTimeMillis() - nStart);
// ********************************************************* Step 11: start node
if (!CheckDiskSpace())
return false;
if (!strErrors.str().empty())
return InitError(strErrors.str());
RandAddSeedPerfmon();
//// debug print
LogPrintf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size());
LogPrintf("nBestHeight = %d\n", chainActive.Height());
LogPrintf("setKeyPool.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0);
LogPrintf("mapWallet.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapWallet.size() : 0);
LogPrintf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
StartNode(threadGroup);
// InitRPCMining is needed here so getwork/getblocktemplate in the GUI debug console works properly.
InitRPCMining();
if (fServer)
StartRPCThreads();
// Generate coins in the background
if (pwalletMain)
GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain);
// ********************************************************* Step 12: finished
uiInterface.InitMessage(_("Done loading"));
if (pwalletMain) {
// Add wallet transactions that aren't already in a block to mapTransactions
pwalletMain->ReacceptWalletTransactions();
// Run a thread to flush wallet periodically
threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile)));
}
return !fRequestShutdown;
}
diff --git a/src/main.cpp b/src/main.cpp
index 3d339b4fd..640fbac8b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,4259 +1,4259 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "alert.h"
#include "checkpoints.h"
#include "db.h"
#include "txdb.h"
#include "net.h"
#include "init.h"
#include "ui_interface.h"
#include "checkqueue.h"
#include "chainparams.h"
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
using namespace std;
using namespace boost;
//
// Global state
//
CCriticalSection cs_setpwalletRegistered;
set<CWallet*> setpwalletRegistered;
CCriticalSection cs_main;
CTxMemPool mempool;
unsigned int nTransactionsUpdated = 0;
map<uint256, CBlockIndex*> mapBlockIndex;
CChain chainActive;
uint256 nBestInvalidWork = 0;
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed
int64 nTimeBestReceived = 0;
int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
bool fBenchmark = false;
bool fTxIndex = false;
unsigned int nCoinCacheSize = 5000;
bool fHaveGUI = false;
/** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */
int64 CTransaction::nMinTxFee = 10000; // Override with -mintxfee
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */
int64 CTransaction::nMinRelayTxFee = 10000;
CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
map<uint256, CTransaction> mapOrphanTransactions;
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
const string strMessageMagic = "Bitcoin Signed Message:\n";
// Settings
int64 nTransactionFee = 0;
//////////////////////////////////////////////////////////////////////////////
//
// dispatching functions
//
// These functions dispatch to one or all registered wallets
namespace {
struct CMainSignals {
// Notifies listeners of updated transaction data (passing hash, transaction, and optionally the block it is found in.
boost::signals2::signal<void (const uint256 &, const CTransaction &, const CBlock *)> SyncTransaction;
// Notifies listeners of an erased transaction (currently disabled, requires transaction replacement).
boost::signals2::signal<void (const uint256 &)> EraseTransaction;
// Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible).
boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
// Notifies listeners of a new active block chain.
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
// Notifies listeners about an inventory item being seen on the network.
boost::signals2::signal<void (const uint256 &)> Inventory;
// Tells listeners to broadcast their data.
boost::signals2::signal<void ()> Broadcast;
} g_signals;
}
void RegisterWallet(CWalletInterface* pwalletIn) {
g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3));
g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1));
g_signals.UpdatedTransaction.connect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.SetBestChain.connect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1));
g_signals.Inventory.connect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1));
g_signals.Broadcast.connect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn));
}
void UnregisterWallet(CWalletInterface* pwalletIn) {
g_signals.Broadcast.disconnect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn));
g_signals.Inventory.disconnect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1));
g_signals.SetBestChain.disconnect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1));
g_signals.UpdatedTransaction.disconnect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.EraseTransaction.disconnect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1));
g_signals.SyncTransaction.disconnect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3));
}
void UnregisterAllWallets() {
g_signals.Broadcast.disconnect_all_slots();
g_signals.Inventory.disconnect_all_slots();
g_signals.SetBestChain.disconnect_all_slots();
g_signals.UpdatedTransaction.disconnect_all_slots();
g_signals.EraseTransaction.disconnect_all_slots();
g_signals.SyncTransaction.disconnect_all_slots();
}
void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) {
g_signals.SyncTransaction(hash, tx, pblock);
}
//////////////////////////////////////////////////////////////////////////////
//
// Registration of network node signals.
//
int static GetHeight()
{
LOCK(cs_main);
return chainActive.Height();
}
void RegisterNodeSignals(CNodeSignals& nodeSignals)
{
nodeSignals.GetHeight.connect(&GetHeight);
nodeSignals.ProcessMessages.connect(&ProcessMessages);
nodeSignals.SendMessages.connect(&SendMessages);
}
void UnregisterNodeSignals(CNodeSignals& nodeSignals)
{
nodeSignals.GetHeight.disconnect(&GetHeight);
nodeSignals.ProcessMessages.disconnect(&ProcessMessages);
nodeSignals.SendMessages.disconnect(&SendMessages);
}
//////////////////////////////////////////////////////////////////////////////
//
// CChain implementation
//
CBlockIndex *CChain::SetTip(CBlockIndex *pindex) {
if (pindex == NULL) {
vChain.clear();
return NULL;
}
vChain.resize(pindex->nHeight + 1);
while (pindex && vChain[pindex->nHeight] != pindex) {
vChain[pindex->nHeight] = pindex;
pindex = pindex->pprev;
}
return pindex;
}
CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const {
int nStep = 1;
std::vector<uint256> vHave;
vHave.reserve(32);
if (!pindex)
pindex = Tip();
while (pindex) {
vHave.push_back(pindex->GetBlockHash());
// Stop when we have added the genesis block.
if (pindex->nHeight == 0)
break;
// Exponentially larger steps back, plus the genesis block.
int nHeight = std::max(pindex->nHeight - nStep, 0);
// In case pindex is not in this chain, iterate pindex->pprev to find blocks.
while (pindex->nHeight > nHeight && !Contains(pindex))
pindex = pindex->pprev;
// If pindex is in this chain, use direct height-based access.
if (pindex->nHeight > nHeight)
pindex = (*this)[nHeight];
if (vHave.size() > 10)
nStep *= 2;
}
return CBlockLocator(vHave);
}
CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const {
// Find the first block the caller has in the main chain
BOOST_FOREACH(const uint256& hash, locator.vHave) {
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end())
{
CBlockIndex* pindex = (*mi).second;
if (Contains(pindex))
return pindex;
}
}
return Genesis();
}
//////////////////////////////////////////////////////////////////////////////
//
// CCoinsView implementations
//
bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; }
bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; }
bool CCoinsView::HaveCoins(const uint256 &txid) { return false; }
CBlockIndex *CCoinsView::GetBestBlock() { return NULL; }
bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; }
bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; }
bool CCoinsView::GetStats(CCoinsStats &stats) { return false; }
CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { }
bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); }
bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); }
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); }
CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); }
bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); }
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); }
CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { }
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) {
if (cacheCoins.count(txid)) {
coins = cacheCoins[txid];
return true;
}
if (base->GetCoins(txid, coins)) {
cacheCoins[txid] = coins;
return true;
}
return false;
}
std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) {
std::map<uint256,CCoins>::iterator it = cacheCoins.lower_bound(txid);
if (it != cacheCoins.end() && it->first == txid)
return it;
CCoins tmp;
if (!base->GetCoins(txid,tmp))
return cacheCoins.end();
std::map<uint256,CCoins>::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins()));
tmp.swap(ret->second);
return ret;
}
CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) {
std::map<uint256,CCoins>::iterator it = FetchCoins(txid);
assert(it != cacheCoins.end());
return it->second;
}
bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) {
cacheCoins[txid] = coins;
return true;
}
bool CCoinsViewCache::HaveCoins(const uint256 &txid) {
return FetchCoins(txid) != cacheCoins.end();
}
CBlockIndex *CCoinsViewCache::GetBestBlock() {
if (pindexTip == NULL)
pindexTip = base->GetBestBlock();
return pindexTip;
}
bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) {
pindexTip = pindex;
return true;
}
bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) {
for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++)
cacheCoins[it->first] = it->second;
pindexTip = pindex;
return true;
}
bool CCoinsViewCache::Flush() {
bool fOk = base->BatchWrite(cacheCoins, pindexTip);
if (fOk)
cacheCoins.clear();
return fOk;
}
unsigned int CCoinsViewCache::GetCacheSize() {
return cacheCoins.size();
}
/** CCoinsView that brings transactions from a memorypool into view.
It does not check for spendings by memory pool transactions. */
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) {
if (base->GetCoins(txid, coins))
return true;
if (mempool.exists(txid)) {
const CTransaction &tx = mempool.lookup(txid);
coins = CCoins(tx, MEMPOOL_HEIGHT);
return true;
}
return false;
}
bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) {
return mempool.exists(txid) || base->HaveCoins(txid);
}
CCoinsViewCache *pcoinsTip = NULL;
CBlockTreeDB *pblocktree = NULL;
//////////////////////////////////////////////////////////////////////////////
//
// mapOrphanTransactions
//
bool AddOrphanTx(const CTransaction& tx)
{
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
return false;
// Ignore big transactions, to avoid a
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
// large transaction with a missing parent then we assume
// it will rebroadcast it later, after the parent transaction(s)
// have been mined or received.
// 10,000 orphans, each of which is at most 5,000 bytes big is
// at most 500 megabytes of orphans:
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
if (sz > 5000)
{
LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString().c_str());
return false;
}
mapOrphanTransactions[hash] = tx;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
LogPrint("mempool", "stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(),
mapOrphanTransactions.size());
return true;
}
void static EraseOrphanTx(uint256 hash)
{
if (!mapOrphanTransactions.count(hash))
return;
const CTransaction& tx = mapOrphanTransactions[hash];
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
mapOrphanTransactionsByPrev.erase(txin.prevout.hash);
}
mapOrphanTransactions.erase(hash);
}
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
{
unsigned int nEvicted = 0;
while (mapOrphanTransactions.size() > nMaxOrphans)
{
// Evict a random orphan:
uint256 randomhash = GetRandHash();
map<uint256, CTransaction>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
EraseOrphanTx(it->first);
++nEvicted;
}
return nEvicted;
}
bool IsStandardTx(const CTransaction& tx, string& reason)
{
if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) {
reason = "version";
return false;
}
if (!IsFinalTx(tx)) {
reason = "non-final";
return false;
}
// Extremely large transactions with lots of inputs can cost the network
// almost as much to process as they cost the sender in fees, because
// computing signature hashes is O(ninputs*txsize). Limiting transactions
// to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks.
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
if (sz >= MAX_STANDARD_TX_SIZE) {
reason = "tx-size";
return false;
}
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
// Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
// pay-to-script-hash, which is 3 ~80-byte signatures, 3
// ~65-byte public keys, plus a few script ops.
if (txin.scriptSig.size() > 500) {
reason = "scriptsig-size";
return false;
}
if (!txin.scriptSig.IsPushOnly()) {
reason = "scriptsig-not-pushonly";
return false;
}
}
unsigned int nDataOut = 0;
txnouttype whichType;
BOOST_FOREACH(const CTxOut& txout, tx.vout) {
if (!::IsStandard(txout.scriptPubKey, whichType)) {
reason = "scriptpubkey";
return false;
}
if (whichType == TX_NULL_DATA)
nDataOut++;
else if (txout.IsDust(CTransaction::nMinRelayTxFee)) {
reason = "dust";
return false;
}
}
// only one OP_RETURN txout is permitted
if (nDataOut > 1) {
reason = "mucho-data";
return false;
}
return true;
}
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64 nBlockTime)
{
// Time based nLockTime implemented in 0.1.6
if (tx.nLockTime == 0)
return true;
if (nBlockHeight == 0)
nBlockHeight = chainActive.Height();
if (nBlockTime == 0)
nBlockTime = GetAdjustedTime();
if ((int64)tx.nLockTime < ((int64)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime))
return true;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
if (!txin.IsFinal())
return false;
return true;
}
/** Amount of bitcoins spent by the transaction.
@return sum of all outputs (note: does not include fees)
*/
int64 GetValueOut(const CTransaction& tx)
{
int64 nValueOut = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
nValueOut += txout.nValue;
if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut))
throw std::runtime_error("GetValueOut() : value out of range");
}
return nValueOut;
}
//
// Check transaction inputs, and make sure any
// pay-to-script-hash transactions are evaluating IsStandard scripts
//
// Why bother? To avoid denial-of-service attacks; an attacker
// can submit a standard HASH... OP_EQUAL transaction,
// which will get accepted into blocks. The redemption
// script can be anything; an attacker could use a very
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs)
{
if (tx.IsCoinBase())
return true; // Coinbases don't use vin normally
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);
vector<vector<unsigned char> > vSolutions;
txnouttype whichType;
// get the scriptPubKey corresponding to this input:
const CScript& prevScript = prev.scriptPubKey;
if (!Solver(prevScript, whichType, vSolutions))
return false;
int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions);
if (nArgsExpected < 0)
return false;
// Transactions with extra stuff in their scriptSigs are
// non-standard. Note that this EvalScript() call will
// be quick, because if there are any operations
// beside "push data" in the scriptSig the
// IsStandard() call returns false
vector<vector<unsigned char> > stack;
if (!EvalScript(stack, tx.vin[i].scriptSig, tx, i, false, 0))
return false;
if (whichType == TX_SCRIPTHASH)
{
if (stack.empty())
return false;
CScript subscript(stack.back().begin(), stack.back().end());
vector<vector<unsigned char> > vSolutions2;
txnouttype whichType2;
if (!Solver(subscript, whichType2, vSolutions2))
return false;
if (whichType2 == TX_SCRIPTHASH)
return false;
int tmpExpected;
tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2);
if (tmpExpected < 0)
return false;
nArgsExpected += tmpExpected;
}
if (stack.size() != (unsigned int)nArgsExpected)
return false;
}
return true;
}
unsigned int GetLegacySigOpCount(const CTransaction& tx)
{
unsigned int nSigOps = 0;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
nSigOps += txin.scriptSig.GetSigOpCount(false);
}
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
nSigOps += txout.scriptPubKey.GetSigOpCount(false);
}
return nSigOps;
}
unsigned int GetP2SHSigOpCount(const CTransaction& tx, CCoinsViewCache& inputs)
{
if (tx.IsCoinBase())
return 0;
unsigned int nSigOps = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]);
if (prevout.scriptPubKey.IsPayToScriptHash())
nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig);
}
return nSigOps;
}
int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
{
CBlock blockTmp;
if (pblock == NULL) {
CCoins coins;
if (pcoinsTip->GetCoins(GetHash(), coins)) {
CBlockIndex *pindex = chainActive[coins.nHeight];
if (pindex) {
if (!ReadBlockFromDisk(blockTmp, pindex))
return 0;
pblock = &blockTmp;
}
}
}
if (pblock) {
// Update the tx's hashBlock
hashBlock = pblock->GetHash();
// Locate the transaction
for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++)
if (pblock->vtx[nIndex] == *(CTransaction*)this)
break;
if (nIndex == (int)pblock->vtx.size())
{
vMerkleBranch.clear();
nIndex = -1;
LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n");
return 0;
}
// Fill in merkle branch
vMerkleBranch = pblock->GetMerkleBranch(nIndex);
}
// Is the tx in a block that's in the main chain
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
CBlockIndex* pindex = (*mi).second;
if (!pindex || !chainActive.Contains(pindex))
return 0;
return chainActive.Height() - pindex->nHeight + 1;
}
bool CheckTransaction(const CTransaction& tx, CValidationState &state)
{
// Basic checks that don't depend on any context
if (tx.vin.empty())
return state.DoS(10, error("CheckTransaction() : vin empty"));
if (tx.vout.empty())
return state.DoS(10, error("CheckTransaction() : vout empty"));
// Size limits
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));
// Check for negative or overflow output values
int64 nValueOut = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
if (txout.nValue < 0)
return state.DoS(100, error("CheckTransaction() : txout.nValue negative"));
if (txout.nValue > MAX_MONEY)
return state.DoS(100, error("CheckTransaction() : txout.nValue too high"));
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"));
}
// Check for duplicate inputs
set<COutPoint> vInOutPoints;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
if (vInOutPoints.count(txin.prevout))
return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs"));
vInOutPoints.insert(txin.prevout);
}
if (tx.IsCoinBase())
{
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
return state.DoS(100, error("CheckTransaction() : coinbase script size"));
}
else
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
if (txin.prevout.IsNull())
return state.DoS(10, error("CheckTransaction() : prevout is null"));
}
return true;
}
int64 GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mode)
{
// Base fee is either nMinTxFee or nMinRelayTxFee
int64 nBaseFee = (mode == GMF_RELAY) ? tx.nMinRelayTxFee : tx.nMinTxFee;
unsigned int nBytes = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee;
if (fAllowFree)
{
// There is a free transaction area in blocks created by most miners,
// * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000
// to be considered to fall into this category. We don't want to encourage sending
// multiple transactions instead of one big transaction to avoid fees.
// * If we are creating a transaction we allow transactions up to 1,000 bytes
// to be considered safe and assume they can likely make it into this section.
if (nBytes < (mode == GMF_SEND ? 1000 : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)))
nMinFee = 0;
}
// This code can be removed after enough miners have upgraded to version 0.9.
// Until then, be safe when sending and require a fee if any output
// is less than CENT:
if (nMinFee < nBaseFee && mode == GMF_SEND)
{
BOOST_FOREACH(const CTxOut& txout, tx.vout)
if (txout.nValue < CENT)
nMinFee = nBaseFee;
}
if (!MoneyRange(nMinFee))
nMinFee = MAX_MONEY;
return nMinFee;
}
void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
{
LOCK(cs);
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0));
// iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx
while (it != mapNextTx.end() && it->first.hash == hashTx) {
coins.Spend(it->first.n); // and remove those outputs from coins
it++;
}
}
bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectInsaneFee)
{
if (pfMissingInputs)
*pfMissingInputs = false;
if (!CheckTransaction(tx, state))
return error("CTxMemPool::accept() : CheckTransaction failed");
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx"));
// To help v0.1.5 clients who would see it as a negative number
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet");
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason;
if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason))
return error("CTxMemPool::accept() : nonstandard transaction: %s",
reason.c_str());
// is it already in the memory pool?
uint256 hash = tx.GetHash();
{
LOCK(cs);
if (mapTx.count(hash))
return false;
}
// Check for conflicts with in-memory transactions
CTransaction* ptxOld = NULL;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
COutPoint outpoint = tx.vin[i].prevout;
if (mapNextTx.count(outpoint))
{
// Disable replacement feature for now
return false;
// Allow replacing with a newer version of the same transaction
if (i != 0)
return false;
ptxOld = mapNextTx[outpoint].ptx;
if (IsFinalTx(*ptxOld))
return false;
if (!tx.IsNewerThan(*ptxOld))
return false;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
COutPoint outpoint = tx.vin[i].prevout;
if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)
return false;
}
break;
}
}
{
CCoinsView dummy;
CCoinsViewCache view(dummy);
{
LOCK(cs);
CCoinsViewMemPool viewMemPool(*pcoinsTip, *this);
view.SetBackend(viewMemPool);
// do we already have it?
if (view.HaveCoins(hash))
return false;
// do all inputs exist?
// Note that this does not check for the presence of actual outputs (see the next check for that),
// only helps filling in pfMissingInputs (to determine missing vs spent).
BOOST_FOREACH(const CTxIn txin, tx.vin) {
if (!view.HaveCoins(txin.prevout.hash)) {
if (pfMissingInputs)
*pfMissingInputs = true;
return false;
}
}
// are the actual inputs available?
if (!view.HaveInputs(tx))
return state.Invalid(error("CTxMemPool::accept() : inputs already spent"));
// Bring the best block into scope
view.GetBestBlock();
// we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
view.SetBackend(dummy);
}
// Check for non-standard pay-to-script-hash in inputs
if (Params().NetworkID() == CChainParams::MAIN && !AreInputsStandard(tx, view))
return error("CTxMemPool::accept() : nonstandard transaction input");
// Note: if you modify this code to accept non-standard transactions, then
// you should add code here to check that the transaction does a
// reasonable number of ECDSA signature verifications.
int64 nFees = view.GetValueIn(tx)-GetValueOut(tx);
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// Don't accept it if it can't get into a block
int64 txMinFee = GetMinFee(tx, true, GMF_RELAY);
if (fLimitFree && nFees < txMinFee)
return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d,
hash.ToString().c_str(),
nFees, txMinFee);
// Continuously rate-limit free transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make others' transactions take longer to confirm.
if (fLimitFree && nFees < CTransaction::nMinRelayTxFee)
{
static double dFreeCount;
static int64 nLastTime;
int64 nNow = GetTime();
LOCK(cs);
// Use an exponentially decaying ~10-minute window:
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
nLastTime = nNow;
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
return error("CTxMemPool::accept() : free transaction rejected by rate limiter");
if (fDebug)
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize;
}
if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000)
return error("CTxMemPool::accept() : insane fees %s, %"PRI64d" > %"PRI64d,
hash.ToString().c_str(),
nFees, CTransaction::nMinRelayTxFee * 10000);
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str());
}
}
// Store transaction in memory
{
LOCK(cs);
if (ptxOld)
{
LogPrint("mempool", "CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
remove(*ptxOld);
}
addUnchecked(hash, tx);
}
///// are we sure this is ok when loading transactions or restoring block txes
// If updated, erase old tx from wallet
if (ptxOld)
g_signals.EraseTransaction(ptxOld->GetHash());
g_signals.SyncTransaction(hash, tx, NULL);
LogPrint("mempool", "CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n",
hash.ToString().c_str(),
mapTx.size());
return true;
}
bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx)
{
// Add to memory pool without checking anything. Don't call this directly,
// call CTxMemPool::accept to properly check the transaction first.
{
mapTx[hash] = tx;
for (unsigned int i = 0; i < tx.vin.size(); i++)
mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i);
nTransactionsUpdated++;
}
return true;
}
bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive)
{
// Remove transaction from memory pool
{
LOCK(cs);
uint256 hash = tx.GetHash();
if (fRecursive) {
for (unsigned int i = 0; i < tx.vout.size(); i++) {
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i));
if (it != mapNextTx.end())
remove(*it->second.ptx, true);
}
}
if (mapTx.count(hash))
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapNextTx.erase(txin.prevout);
mapTx.erase(hash);
nTransactionsUpdated++;
}
}
return true;
}
bool CTxMemPool::removeConflicts(const CTransaction &tx)
{
// Remove transactions which depend on inputs of tx, recursively
LOCK(cs);
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout);
if (it != mapNextTx.end()) {
const CTransaction &txConflict = *it->second.ptx;
if (txConflict != tx)
remove(txConflict, true);
}
}
return true;
}
void CTxMemPool::clear()
{
LOCK(cs);
mapTx.clear();
mapNextTx.clear();
++nTransactionsUpdated;
}
bool CTxMemPool::fChecks = false;
void CTxMemPool::check(CCoinsViewCache *pcoins) const
{
if (!fChecks)
return;
LogPrintf("Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());
LOCK(cs);
for (std::map<uint256, CTransaction>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
unsigned int i = 0;
BOOST_FOREACH(const CTxIn &txin, it->second.vin) {
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(txin.prevout.hash);
if (it2 != mapTx.end()) {
assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull());
} else {
CCoins &coins = pcoins->GetCoins(txin.prevout.hash);
assert(coins.IsAvailable(txin.prevout.n));
}
// Check whether its inputs are marked in mapNextTx.
std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout);
assert(it3 != mapNextTx.end());
assert(it3->second.ptx == &it->second);
assert(it3->second.n == i);
i++;
}
}
for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) {
uint256 hash = it->second.ptx->GetHash();
std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(hash);
assert(it2 != mapTx.end());
assert(&it2->second == it->second.ptx);
assert(it2->second.vin.size() > it->second.n);
assert(it->first == it->second.ptx->vin[it->second.n].prevout);
}
}
void CTxMemPool::queryHashes(std::vector<uint256>& vtxid)
{
vtxid.clear();
LOCK(cs);
vtxid.reserve(mapTx.size());
for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi)
vtxid.push_back((*mi).first);
}
int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
{
if (hashBlock == 0 || nIndex == -1)
return 0;
// Find the block it claims to be in
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
CBlockIndex* pindex = (*mi).second;
if (!pindex || !chainActive.Contains(pindex))
return 0;
// Make sure the merkle branch connects to this block
if (!fMerkleVerified)
{
if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot)
return 0;
fMerkleVerified = true;
}
pindexRet = pindex;
return chainActive.Height() - pindex->nHeight + 1;
}
int CMerkleTx::GetBlocksToMaturity() const
{
if (!IsCoinBase())
return 0;
return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
}
bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree)
{
CValidationState state;
return mempool.accept(state, *this, fLimitFree, NULL);
}
// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock
bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
{
CBlockIndex *pindexSlow = NULL;
{
LOCK(cs_main);
{
LOCK(mempool.cs);
if (mempool.exists(hash))
{
txOut = mempool.lookup(hash);
return true;
}
}
if (fTxIndex) {
CDiskTxPos postx;
if (pblocktree->ReadTxIndex(hash, postx)) {
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
CBlockHeader header;
try {
file >> header;
fseek(file, postx.nTxOffset, SEEK_CUR);
file >> txOut;
} catch (std::exception &e) {
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
}
hashBlock = header.GetHash();
if (txOut.GetHash() != hash)
return error("%s() : txid mismatch", __PRETTY_FUNCTION__);
return true;
}
}
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
int nHeight = -1;
{
CCoinsViewCache &view = *pcoinsTip;
CCoins coins;
if (view.GetCoins(hash, coins))
nHeight = coins.nHeight;
}
if (nHeight > 0)
pindexSlow = chainActive[nHeight];
}
}
if (pindexSlow) {
CBlock block;
if (ReadBlockFromDisk(block, pindexSlow)) {
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
if (tx.GetHash() == hash) {
txOut = tx;
hashBlock = pindexSlow->GetBlockHash();
return true;
}
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////////
//
// CBlock and CBlockIndex
//
bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos)
{
// Open history file to append
CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
if (!fileout)
return error("WriteBlockToDisk() : OpenBlockFile failed");
// Write index header
unsigned int nSize = fileout.GetSerializeSize(block);
fileout << FLATDATA(Params().MessageStart()) << nSize;
// Write block
long fileOutPos = ftell(fileout);
if (fileOutPos < 0)
return error("WriteBlockToDisk() : ftell failed");
pos.nPos = (unsigned int)fileOutPos;
fileout << block;
// Flush stdio buffers and commit to disk before returning
fflush(fileout);
if (!IsInitialBlockDownload())
FileCommit(fileout);
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos)
{
block.SetNull();
// Open history file to read
CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
if (!filein)
return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : OpenBlockFile failed");
// Read block
try {
filein >> block;
}
catch (std::exception &e) {
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
}
// Check the header
if (!CheckProofOfWork(block.GetHash(), block.nBits))
return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : errors in block header");
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex)
{
if (!ReadBlockFromDisk(block, pindex->GetBlockPos()))
return false;
if (block.GetHash() != pindex->GetBlockHash())
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index");
return true;
}
uint256 static GetOrphanRoot(const CBlockHeader* pblock)
{
// Work back to the first block in the orphan chain
while (mapOrphanBlocks.count(pblock->hashPrevBlock))
pblock = mapOrphanBlocks[pblock->hashPrevBlock];
return pblock->GetHash();
}
int64 GetBlockValue(int nHeight, int64 nFees)
{
int64 nSubsidy = 50 * COIN;
// Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
nSubsidy >>= (nHeight / Params().SubsidyHalvingInterval());
return nSubsidy + nFees;
}
static const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
static const int64 nTargetSpacing = 10 * 60;
static const int64 nInterval = nTargetTimespan / nTargetSpacing;
//
// minimum amount of work that could possibly be required nTime after
// minimum work required was nBase
//
unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
{
const CBigNum &bnLimit = Params().ProofOfWorkLimit();
// Testnet has min-difficulty blocks
// after nTargetSpacing*2 time between blocks:
if (TestNet() && nTime > nTargetSpacing*2)
return bnLimit.GetCompact();
CBigNum bnResult;
bnResult.SetCompact(nBase);
while (nTime > 0 && bnResult < bnLimit)
{
// Maximum 400% adjustment...
bnResult *= 4;
// ... in best-case exactly 4-times-normal target time
nTime -= nTargetTimespan*4;
}
if (bnResult > bnLimit)
bnResult = bnLimit;
return bnResult.GetCompact();
}
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock)
{
unsigned int nProofOfWorkLimit = Params().ProofOfWorkLimit().GetCompact();
// Genesis block
if (pindexLast == NULL)
return nProofOfWorkLimit;
// Only change once per interval
if ((pindexLast->nHeight+1) % nInterval != 0)
{
if (TestNet())
{
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 2* 10 minutes
// then allow mining of a min-difficulty block.
if (pblock->nTime > pindexLast->nTime + nTargetSpacing*2)
return nProofOfWorkLimit;
else
{
// Return the last non-special-min-difficulty-rules-block
const CBlockIndex* pindex = pindexLast;
while (pindex->pprev && pindex->nHeight % nInterval != 0 && pindex->nBits == nProofOfWorkLimit)
pindex = pindex->pprev;
return pindex->nBits;
}
}
return pindexLast->nBits;
}
// Go back by what we want to be 14 days worth of blocks
const CBlockIndex* pindexFirst = pindexLast;
for (int i = 0; pindexFirst && i < nInterval-1; i++)
pindexFirst = pindexFirst->pprev;
assert(pindexFirst);
// Limit adjustment step
int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime();
LogPrintf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan);
if (nActualTimespan < nTargetTimespan/4)
nActualTimespan = nTargetTimespan/4;
if (nActualTimespan > nTargetTimespan*4)
nActualTimespan = nTargetTimespan*4;
// Retarget
CBigNum bnNew;
bnNew.SetCompact(pindexLast->nBits);
bnNew *= nActualTimespan;
bnNew /= nTargetTimespan;
if (bnNew > Params().ProofOfWorkLimit())
bnNew = Params().ProofOfWorkLimit();
/// debug print
LogPrintf("GetNextWorkRequired RETARGET\n");
LogPrintf("nTargetTimespan = %"PRI64d" nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan);
LogPrintf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str());
LogPrintf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());
return bnNew.GetCompact();
}
bool CheckProofOfWork(uint256 hash, unsigned int nBits)
{
CBigNum bnTarget;
bnTarget.SetCompact(nBits);
// Check range
if (bnTarget <= 0 || bnTarget > Params().ProofOfWorkLimit())
return error("CheckProofOfWork() : nBits below minimum work");
// Check proof of work matches claimed amount
if (hash > bnTarget.getuint256())
return error("CheckProofOfWork() : hash doesn't match nBits");
return true;
}
// Return maximum amount of blocks that other nodes claim to have
int GetNumBlocksOfPeers()
{
return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate());
}
bool IsInitialBlockDownload()
{
if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate())
return true;
static int64 nLastUpdate;
static CBlockIndex* pindexLastBest;
if (chainActive.Tip() != pindexLastBest)
{
pindexLastBest = chainActive.Tip();
nLastUpdate = GetTime();
}
return (GetTime() - nLastUpdate < 10 &&
chainActive.Tip()->GetBlockTime() < GetTime() - 24 * 60 * 60);
}
bool fLargeWorkForkFound = false;
bool fLargeWorkInvalidChainFound = false;
CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL;
void CheckForkWarningConditions()
{
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before the last checkpoint)
if (IsInitialBlockDownload())
return;
// If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it)
// of our head, drop it
if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72)
pindexBestForkTip = NULL;
if (pindexBestForkTip || nBestInvalidWork > chainActive.Tip()->nChainWork + (chainActive.Tip()->GetBlockWork() * 6).getuint256())
{
if (!fLargeWorkForkFound)
{
std::string strCmd = GetArg("-alertnotify", "");
if (!strCmd.empty())
{
std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") +
pindexBestForkBase->phashBlock->ToString() + std::string("'");
boost::replace_all(strCmd, "%s", warning);
boost::thread t(runCommand, strCmd); // thread runs free
}
}
if (pindexBestForkTip)
{
LogPrintf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n",
pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString().c_str(),
pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString().c_str());
fLargeWorkForkFound = true;
}
else
{
LogPrintf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n");
fLargeWorkInvalidChainFound = true;
}
}
else
{
fLargeWorkForkFound = false;
fLargeWorkInvalidChainFound = false;
}
}
void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
{
// If we are on a fork that is sufficiently large, set a warning flag
CBlockIndex* pfork = pindexNewForkTip;
CBlockIndex* plonger = chainActive.Tip();
while (pfork && pfork != plonger)
{
while (plonger && plonger->nHeight > pfork->nHeight)
plonger = plonger->pprev;
if (pfork == plonger)
break;
pfork = pfork->pprev;
}
// We define a condition which we should warn the user about as a fork of at least 7 blocks
// who's tip is within 72 blocks (+/- 12 hours if no one mines it) of ours
// We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network
// hash rate operating on the fork.
// or a chain that is entirely longer than ours and invalid (note that this should be detected by both)
// We define it this way because it allows us to only store the highest fork tip (+ base) which meets
// the 7-block condition and from this always have the most-likely-to-cause-warning fork
if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) &&
pindexNewForkTip->nChainWork - pfork->nChainWork > (pfork->GetBlockWork() * 7).getuint256() &&
chainActive.Height() - pindexNewForkTip->nHeight < 72)
{
pindexBestForkTip = pindexNewForkTip;
pindexBestForkBase = pfork;
}
CheckForkWarningConditions();
}
void static InvalidChainFound(CBlockIndex* pindexNew)
{
if (pindexNew->nChainWork > nBestInvalidWork)
{
nBestInvalidWork = pindexNew->nChainWork;
pblocktree->WriteBestInvalidWork(CBigNum(nBestInvalidWork));
uiInterface.NotifyBlocksChanged();
}
LogPrintf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n",
pindexNew->GetBlockHash().ToString().c_str(), pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S",
pindexNew->GetBlockTime()).c_str());
LogPrintf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n",
chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str());
CheckForkWarningConditions();
}
void static InvalidBlockFound(CBlockIndex *pindex) {
pindex->nStatus |= BLOCK_FAILED_VALID;
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
setBlockIndexValid.erase(pindex);
InvalidChainFound(pindex);
if (chainActive.Next(pindex)) {
CValidationState stateDummy;
ConnectBestBlock(stateDummy); // reorganise away from the failed block
}
}
bool ConnectBestBlock(CValidationState &state) {
do {
CBlockIndex *pindexNewBest;
{
std::set<CBlockIndex*,CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexValid.rbegin();
if (it == setBlockIndexValid.rend())
return true;
pindexNewBest = *it;
}
if (pindexNewBest == chainActive.Tip() || (chainActive.Tip() && pindexNewBest->nChainWork == chainActive.Tip()->nChainWork))
return true; // nothing to do
// check ancestry
CBlockIndex *pindexTest = pindexNewBest;
std::vector<CBlockIndex*> vAttach;
do {
if (pindexTest->nStatus & BLOCK_FAILED_MASK) {
// mark descendants failed
CBlockIndex *pindexFailed = pindexNewBest;
while (pindexTest != pindexFailed) {
pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
setBlockIndexValid.erase(pindexFailed);
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed));
pindexFailed = pindexFailed->pprev;
}
InvalidChainFound(pindexNewBest);
break;
}
if (chainActive.Tip() == NULL || pindexTest->nChainWork > chainActive.Tip()->nChainWork)
vAttach.push_back(pindexTest);
if (pindexTest->pprev == NULL || chainActive.Next(pindexTest)) {
reverse(vAttach.begin(), vAttach.end());
BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) {
boost::this_thread::interruption_point();
try {
if (!SetBestChain(state, pindexSwitch))
return false;
} catch(std::runtime_error &e) {
return state.Abort(_("System error: ") + e.what());
}
}
return true;
}
pindexTest = pindexTest->pprev;
} while(true);
} while(true);
}
void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev)
{
block.nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
// Updating time can change work required on testnet:
if (TestNet())
block.nBits = GetNextWorkRequired(pindexPrev, &block);
}
const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input)
{
const CCoins &coins = GetCoins(input.prevout.hash);
assert(coins.IsAvailable(input.prevout.n));
return coins.vout[input.prevout.n];
}
int64 CCoinsViewCache::GetValueIn(const CTransaction& tx)
{
if (tx.IsCoinBase())
return 0;
int64 nResult = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
nResult += GetOutputFor(tx.vin[i]).nValue;
return nResult;
}
void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash)
{
// mark inputs spent
if (!tx.IsCoinBase()) {
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
CCoins &coins = inputs.GetCoins(txin.prevout.hash);
CTxInUndo undo;
assert(coins.Spend(txin.prevout, undo));
txundo.vprevout.push_back(undo);
}
}
// add outputs
assert(inputs.SetCoins(txhash, CCoins(tx, nHeight)));
}
bool CCoinsViewCache::HaveInputs(const CTransaction& tx)
{
if (!tx.IsCoinBase()) {
// first check whether information about the prevout hash is available
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
if (!HaveCoins(prevout.hash))
return false;
}
// then check whether the actual outputs are available
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins &coins = GetCoins(prevout.hash);
if (!coins.IsAvailable(prevout.n))
return false;
}
}
return true;
}
bool CScriptCheck::operator()() const {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType))
return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().c_str());
return true;
}
bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType)
{
return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)();
}
bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks)
{
if (!tx.IsCoinBase())
{
if (pvChecks)
pvChecks->reserve(tx.vin.size());
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
// for an attacker to attempt to split the network.
if (!inputs.HaveInputs(tx))
return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString().c_str()));
// While checking, GetBestBlock() refers to the parent block.
// This is also true for mempool checks.
int nSpendHeight = inputs.GetBestBlock()->nHeight + 1;
int64 nValueIn = 0;
int64 nFees = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins &coins = inputs.GetCoins(prevout.hash);
// If prev is coinbase, check that it's matured
if (coins.IsCoinBase()) {
if (nSpendHeight - coins.nHeight < COINBASE_MATURITY)
return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight));
}
// Check for negative or overflow input values
nValueIn += coins.vout[prevout.n].nValue;
if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
return state.DoS(100, error("CheckInputs() : txin values out of range"));
}
if (nValueIn < GetValueOut(tx))
return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()));
// Tally transaction fees
int64 nTxFee = nValueIn - GetValueOut(tx);
if (nTxFee < 0)
return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()));
nFees += nTxFee;
if (!MoneyRange(nFees))
return state.DoS(100, error("CheckInputs() : nFees out of range"));
// The first loop above does all the inexpensive checks.
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
// Helps prevent CPU exhaustion attacks.
// Skip ECDSA signature verification when connecting blocks
// before the last block chain checkpoint. This is safe because block merkle hashes are
// still computed and checked, and any change will be caught at the next checkpoint.
if (fScriptChecks) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins &coins = inputs.GetCoins(prevout.hash);
// Verify signature
CScriptCheck check(coins, tx, i, flags, 0);
if (pvChecks) {
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
} else if (!check()) {
if (flags & SCRIPT_VERIFY_STRICTENC) {
// For now, check whether the failure was caused by non-canonical
// encodings or not; if so, don't trigger DoS protection.
CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0);
if (check())
return state.Invalid();
}
return state.DoS(100,false);
}
}
}
}
return true;
}
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
{
assert(pindex == view.GetBestBlock());
if (pfClean)
*pfClean = false;
bool fClean = true;
CBlockUndo blockUndo;
CDiskBlockPos pos = pindex->GetUndoPos();
if (pos.IsNull())
return error("DisconnectBlock() : no undo data available");
if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash()))
return error("DisconnectBlock() : failure reading undo data");
if (blockUndo.vtxundo.size() + 1 != block.vtx.size())
return error("DisconnectBlock() : block and undo data inconsistent");
// undo transactions in reverse order
for (int i = block.vtx.size() - 1; i >= 0; i--) {
const CTransaction &tx = block.vtx[i];
uint256 hash = tx.GetHash();
// Check that all outputs are available and match the outputs in the block itself
// exactly. Note that transactions with only provably unspendable outputs won't
// have outputs available even in the block itself, so we handle that case
// specially with outsEmpty.
CCoins outsEmpty;
CCoins &outs = view.HaveCoins(hash) ? view.GetCoins(hash) : outsEmpty;
outs.ClearUnspendable();
CCoins outsBlock = CCoins(tx, pindex->nHeight);
// The CCoins serialization does not serialize negative numbers.
// No network rules currently depend on the version here, so an inconsistency is harmless
// but it must be corrected before txout nversion ever influences a network rule.
if (outsBlock.nVersion < 0)
outs.nVersion = outsBlock.nVersion;
if (outs != outsBlock)
fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted");
// remove outputs
outs = CCoins();
// restore inputs
if (i > 0) { // not coinbases
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
if (txundo.vprevout.size() != tx.vin.size())
return error("DisconnectBlock() : transaction and undo data inconsistent");
for (unsigned int j = tx.vin.size(); j-- > 0;) {
const COutPoint &out = tx.vin[j].prevout;
const CTxInUndo &undo = txundo.vprevout[j];
CCoins coins;
view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent
if (undo.nHeight != 0) {
// undo data contains height: this is the last output of the prevout tx being spent
if (!coins.IsPruned())
fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction");
coins = CCoins();
coins.fCoinBase = undo.fCoinBase;
coins.nHeight = undo.nHeight;
coins.nVersion = undo.nVersion;
} else {
if (coins.IsPruned())
fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction");
}
if (coins.IsAvailable(out.n))
fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output");
if (coins.vout.size() < out.n+1)
coins.vout.resize(out.n+1);
coins.vout[out.n] = undo.txout;
if (!view.SetCoins(out.hash, coins))
return error("DisconnectBlock() : cannot restore coin inputs");
}
}
}
// move best block pointer to prevout block
view.SetBestBlock(pindex->pprev);
if (pfClean) {
*pfClean = fClean;
return true;
} else {
return fClean;
}
}
void static FlushBlockFile(bool fFinalize = false)
{
LOCK(cs_LastBlockFile);
CDiskBlockPos posOld(nLastBlockFile, 0);
FILE *fileOld = OpenBlockFile(posOld);
if (fileOld) {
if (fFinalize)
TruncateFile(fileOld, infoLastBlockFile.nSize);
FileCommit(fileOld);
fclose(fileOld);
}
fileOld = OpenUndoFile(posOld);
if (fileOld) {
if (fFinalize)
TruncateFile(fileOld, infoLastBlockFile.nUndoSize);
FileCommit(fileOld);
fclose(fileOld);
}
}
bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
void ThreadScriptCheck() {
RenameThread("bitcoin-scriptch");
scriptcheckqueue.Thread();
}
bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck)
{
// Check it again in case a previous version let a bad block in
if (!CheckBlock(block, state, !fJustCheck, !fJustCheck))
return false;
// verify that the view's current state corresponds to the previous block
assert(pindex->pprev == view.GetBestBlock());
// Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable)
if (block.GetHash() == Params().HashGenesisBlock()) {
view.SetBestBlock(pindex);
return true;
}
bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate();
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
// If such overwrites are allowed, coinbases and transactions depending upon those
// can be duplicated to remove the ability to spend the first instance -- even after
// being sent to another address.
// See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information.
// This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
// already refuses previously-known transaction ids entirely.
// This rule was originally applied all blocks whose timestamp was after March 15, 2012, 0:00 UTC.
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
// two in the chain that violate it. This prevents exploiting the issue against nodes in their
// initial block download.
bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash.
!((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
(pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
if (fEnforceBIP30) {
for (unsigned int i = 0; i < block.vtx.size(); i++) {
uint256 hash = block.GetTxHash(i);
if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"));
}
}
// BIP16 didn't become active until Apr 1 2012
int64 nBIP16SwitchTime = 1333238400;
bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
unsigned int flags = SCRIPT_VERIFY_NOCACHE |
(fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE);
CBlockUndo blockundo;
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
int64 nStart = GetTimeMicros();
int64 nFees = 0;
int nInputs = 0;
unsigned int nSigOps = 0;
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
vPos.reserve(block.vtx.size());
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
const CTransaction &tx = block.vtx[i];
nInputs += tx.vin.size();
nSigOps += GetLegacySigOpCount(tx);
if (nSigOps > MAX_BLOCK_SIGOPS)
return state.DoS(100, error("ConnectBlock() : too many sigops"));
if (!tx.IsCoinBase())
{
if (!view.HaveInputs(tx))
return state.DoS(100, error("ConnectBlock() : inputs missing/spent"));
if (fStrictPayToScriptHash)
{
// Add in sigops done by pay-to-script-hash inputs;
// this is to prevent a "rogue miner" from creating
// an incredibly-expensive-to-validate block.
nSigOps += GetP2SHSigOpCount(tx, view);
if (nSigOps > MAX_BLOCK_SIGOPS)
return state.DoS(100, error("ConnectBlock() : too many sigops"));
}
nFees += view.GetValueIn(tx)-GetValueOut(tx);
std::vector<CScriptCheck> vChecks;
if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL))
return false;
control.Add(vChecks);
}
CTxUndo txundo;
UpdateCoins(tx, state, view, txundo, pindex->nHeight, block.GetTxHash(i));
if (!tx.IsCoinBase())
blockundo.vtxundo.push_back(txundo);
vPos.push_back(std::make_pair(block.GetTxHash(i), pos));
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
}
int64 nTime = GetTimeMicros() - nStart;
if (fBenchmark)
LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1));
if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees))
return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)));
if (!control.Wait())
return state.DoS(100, false);
int64 nTime2 = GetTimeMicros() - nStart;
if (fBenchmark)
LogPrintf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1));
if (fJustCheck)
return true;
// Write undo information to disk
if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS)
{
if (pindex->GetUndoPos().IsNull()) {
CDiskBlockPos pos;
if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
return error("ConnectBlock() : FindUndoPos failed");
if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash()))
return state.Abort(_("Failed to write undo data"));
// update nUndoPos in block index
pindex->nUndoPos = pos.nPos;
pindex->nStatus |= BLOCK_HAVE_UNDO;
}
pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS;
CDiskBlockIndex blockindex(pindex);
if (!pblocktree->WriteBlockIndex(blockindex))
return state.Abort(_("Failed to write block index"));
}
if (fTxIndex)
if (!pblocktree->WriteTxIndex(vPos))
return state.Abort(_("Failed to write transaction index"));
// add this block to the view's block chain
assert(view.SetBestBlock(pindex));
// Watch for transactions paying to me
for (unsigned int i = 0; i < block.vtx.size(); i++)
g_signals.SyncTransaction(block.GetTxHash(i), block.vtx[i], &block);
return true;
}
bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
{
mempool.check(pcoinsTip);
// All modifications to the coin state will be done in this cache.
// Only when all have succeeded, we push it to pcoinsTip.
CCoinsViewCache view(*pcoinsTip, true);
// Find the fork (typically, there is none)
CBlockIndex* pfork = view.GetBestBlock();
CBlockIndex* plonger = pindexNew;
while (pfork && pfork != plonger)
{
while (plonger->nHeight > pfork->nHeight) {
plonger = plonger->pprev;
assert(plonger != NULL);
}
if (pfork == plonger)
break;
pfork = pfork->pprev;
assert(pfork != NULL);
}
// List of what to disconnect (typically nothing)
vector<CBlockIndex*> vDisconnect;
for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev)
vDisconnect.push_back(pindex);
// List of what to connect (typically only pindexNew)
vector<CBlockIndex*> vConnect;
for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev)
vConnect.push_back(pindex);
reverse(vConnect.begin(), vConnect.end());
if (vDisconnect.size() > 0) {
LogPrintf("REORGANIZE: Disconnect %"PRIszu" blocks; %s...\n", vDisconnect.size(), pfork->GetBlockHash().ToString().c_str());
LogPrintf("REORGANIZE: Connect %"PRIszu" blocks; ...%s\n", vConnect.size(), pindexNew->GetBlockHash().ToString().c_str());
}
// Disconnect shorter branch
list<CTransaction> vResurrect;
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
CBlock block;
if (!ReadBlockFromDisk(block, pindex))
return state.Abort(_("Failed to read block"));
int64 nStart = GetTimeMicros();
if (!DisconnectBlock(block, state, pindex, view))
return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().c_str());
if (fBenchmark)
LogPrintf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Queue memory transactions to resurrect.
// We only do this for blocks after the last checkpoint (reorganisation before that
// point should only happen with -reindex/-loadblock, or a misbehaving peer.
BOOST_REVERSE_FOREACH(const CTransaction& tx, block.vtx)
if (!tx.IsCoinBase() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
vResurrect.push_front(tx);
}
// Connect longer branch
vector<CTransaction> vDelete;
BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
CBlock block;
if (!ReadBlockFromDisk(block, pindex))
return state.Abort(_("Failed to read block"));
int64 nStart = GetTimeMicros();
if (!ConnectBlock(block, state, pindex, view)) {
if (state.IsInvalid()) {
InvalidChainFound(pindexNew);
InvalidBlockFound(pindex);
}
return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().c_str());
}
if (fBenchmark)
LogPrintf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Queue memory transactions to delete
BOOST_FOREACH(const CTransaction& tx, block.vtx)
vDelete.push_back(tx);
}
// Flush changes to global coin state
int64 nStart = GetTimeMicros();
int nModified = view.GetCacheSize();
assert(view.Flush());
int64 nTime = GetTimeMicros() - nStart;
if (fBenchmark)
LogPrintf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified);
// Make sure it's successfully written to disk before changing memory structure
bool fIsInitialDownload = IsInitialBlockDownload();
if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) {
// Typical CCoins structures on disk are around 100 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize()))
return state.Error();
FlushBlockFile();
pblocktree->Sync();
if (!pcoinsTip->Flush())
return state.Abort(_("Failed to write to coin database"));
}
// At this point, all changes have been done to the database.
// Proceed by updating the memory structures.
// Register new best chain
chainActive.SetTip(pindexNew);
// Resurrect memory transactions that were in the disconnected branch
BOOST_FOREACH(CTransaction& tx, vResurrect) {
// ignore validation errors in resurrected transactions
CValidationState stateDummy;
if (!mempool.accept(stateDummy, tx, false, NULL))
mempool.remove(tx, true);
}
// Delete redundant memory transactions that are in the connected branch
BOOST_FOREACH(CTransaction& tx, vDelete) {
mempool.remove(tx);
mempool.removeConflicts(tx);
}
mempool.check(pcoinsTip);
// Update best block in wallet (so we can detect restored wallets)
if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0))
g_signals.SetBestChain(chainActive.GetLocator(pindexNew));
// New best block
nTimeBestReceived = GetTime();
nTransactionsUpdated++;
LogPrintf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str(),
Checkpoints::GuessVerificationProgress(chainActive.Tip()));
// Check the version of the last 100 blocks to see if we need to upgrade:
if (!fIsInitialDownload)
{
int nUpgraded = 0;
const CBlockIndex* pindex = chainActive.Tip();
for (int i = 0; i < 100 && pindex != NULL; i++)
{
if (pindex->nVersion > CBlock::CURRENT_VERSION)
++nUpgraded;
pindex = pindex->pprev;
}
if (nUpgraded > 0)
LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, CBlock::CURRENT_VERSION);
if (nUpgraded > 100/2)
// strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
strMiscWarning = _("Warning: This version is obsolete, upgrade required!");
}
std::string strCmd = GetArg("-blocknotify", "");
if (!fIsInitialDownload && !strCmd.empty())
{
boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
}
return true;
}
bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos)
{
// Check for duplicate
uint256 hash = block.GetHash();
if (mapBlockIndex.count(hash))
return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString().c_str()));
// Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(block);
assert(pindexNew);
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
}
pindexNew->nTx = block.vtx.size();
pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork().getuint256();
pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx;
pindexNew->nFile = pos.nFile;
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA;
setBlockIndexValid.insert(pindexNew);
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)))
return state.Abort(_("Failed to write block index"));
// New best?
if (!ConnectBestBlock(state))
return false;
if (pindexNew == chainActive.Tip())
{
// Clear fork warning if its no longer applicable
CheckForkWarningConditions();
// Notify UI to display prev block's coinbase if it was ours
static uint256 hashPrevBestCoinBase;
g_signals.UpdatedTransaction(hashPrevBestCoinBase);
hashPrevBestCoinBase = block.GetTxHash(0);
} else
CheckForkWarningConditionsOnNewFork(pindexNew);
if (!pblocktree->Flush())
return state.Abort(_("Failed to sync block index"));
uiInterface.NotifyBlocksChanged();
return true;
}
bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false)
{
bool fUpdatedLast = false;
LOCK(cs_LastBlockFile);
if (fKnown) {
if (nLastBlockFile != pos.nFile) {
nLastBlockFile = pos.nFile;
infoLastBlockFile.SetNull();
pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile);
fUpdatedLast = true;
}
} else {
while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str());
FlushBlockFile(true);
nLastBlockFile++;
infoLastBlockFile.SetNull();
pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine
fUpdatedLast = true;
}
pos.nFile = nLastBlockFile;
pos.nPos = infoLastBlockFile.nSize;
}
infoLastBlockFile.nSize += nAddSize;
infoLastBlockFile.AddBlock(nHeight, nTime);
if (!fKnown) {
unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
if (nNewChunks > nOldChunks) {
if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) {
FILE *file = OpenBlockFile(pos);
if (file) {
LogPrintf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile);
AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos);
fclose(file);
}
}
else
return state.Error();
}
}
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return state.Abort(_("Failed to write file info"));
if (fUpdatedLast)
pblocktree->WriteLastBlockFile(nLastBlockFile);
return true;
}
bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;
LOCK(cs_LastBlockFile);
unsigned int nNewSize;
if (nFile == nLastBlockFile) {
pos.nPos = infoLastBlockFile.nUndoSize;
nNewSize = (infoLastBlockFile.nUndoSize += nAddSize);
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return state.Abort(_("Failed to write block info"));
} else {
CBlockFileInfo info;
if (!pblocktree->ReadBlockFileInfo(nFile, info))
return state.Abort(_("Failed to read block info"));
pos.nPos = info.nUndoSize;
nNewSize = (info.nUndoSize += nAddSize);
if (!pblocktree->WriteBlockFileInfo(nFile, info))
return state.Abort(_("Failed to write block info"));
}
unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
if (nNewChunks > nOldChunks) {
if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) {
FILE *file = OpenUndoFile(pos);
if (file) {
LogPrintf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile);
AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos);
fclose(file);
}
}
else
return state.Error();
}
return true;
}
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context
// that can be verified before saving an orphan block.
// Size limits
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return state.DoS(100, error("CheckBlock() : size limits failed"));
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits))
return state.DoS(50, error("CheckBlock() : proof of work failed"));
// Check timestamp
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
return state.Invalid(error("CheckBlock() : block timestamp too far in the future"));
// First transaction must be coinbase, the rest must not be
if (block.vtx.empty() || !block.vtx[0].IsCoinBase())
return state.DoS(100, error("CheckBlock() : first tx is not coinbase"));
for (unsigned int i = 1; i < block.vtx.size(); i++)
if (block.vtx[i].IsCoinBase())
return state.DoS(100, error("CheckBlock() : more than one coinbase"));
// Check transactions
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!CheckTransaction(tx, state))
return error("CheckBlock() : CheckTransaction failed");
// Build the merkle tree already. We need it anyway later, and it makes the
// block cache the transaction hashes, which means they don't need to be
// recalculated many times during this block's validation.
block.BuildMerkleTree();
// Check for duplicate txids. This is caught by ConnectInputs(),
// but catching it earlier avoids a potential DoS attack:
set<uint256> uniqueTx;
for (unsigned int i = 0; i < block.vtx.size(); i++) {
uniqueTx.insert(block.GetTxHash(i));
}
if (uniqueTx.size() != block.vtx.size())
return state.DoS(100, error("CheckBlock() : duplicate transaction"), true);
unsigned int nSigOps = 0;
BOOST_FOREACH(const CTransaction& tx, block.vtx)
{
nSigOps += GetLegacySigOpCount(tx);
}
if (nSigOps > MAX_BLOCK_SIGOPS)
return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
// Check merkle root
if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back())
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
return true;
}
bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
{
// Check for duplicate
uint256 hash = block.GetHash();
if (mapBlockIndex.count(hash))
return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex"));
// Get prev block index
CBlockIndex* pindexPrev = NULL;
int nHeight = 0;
if (hash != Params().HashGenesisBlock()) {
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("AcceptBlock() : prev block not found"));
pindexPrev = (*mi).second;
nHeight = pindexPrev->nHeight+1;
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block))
return state.DoS(100, error("AcceptBlock() : incorrect proof of work"));
// Check timestamp against prev
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
return state.Invalid(error("AcceptBlock() : block's timestamp is too early"));
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!IsFinalTx(tx, nHeight, block.GetBlockTime()))
return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"));
// Check that the block chain matches the known block chain up to a checkpoint
if (!Checkpoints::CheckBlock(nHeight, hash))
return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight));
// Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
if (block.nVersion < 2)
{
if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) ||
(TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100)))
{
return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"));
}
}
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
if (block.nVersion >= 2)
{
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) ||
(TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100)))
{
CScript expect = CScript() << nHeight;
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin()))
return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"));
}
}
}
// Write block to history file
try {
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
if (dbp != NULL)
blockPos = *dbp;
if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.nTime, dbp != NULL))
return error("AcceptBlock() : FindBlockPos failed");
if (dbp == NULL)
if (!WriteBlockToDisk(block, blockPos))
return state.Abort(_("Failed to write block"));
if (!AddToBlockIndex(block, state, blockPos))
return error("AcceptBlock() : AddToBlockIndex failed");
} catch(std::runtime_error &e) {
return state.Abort(_("System error: ") + e.what());
}
// Relay inventory, but don't relay old inventory during initial block download
int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate();
if (chainActive.Tip()->GetBlockHash() == hash)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
pnode->PushInventory(CInv(MSG_BLOCK, hash));
}
return true;
}
bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck)
{
unsigned int nFound = 0;
for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++)
{
if (pstart->nVersion >= minVersion)
++nFound;
pstart = pstart->pprev;
}
return (nFound >= nRequired);
}
int64 CBlockIndex::GetMedianTime() const
{
const CBlockIndex* pindex = this;
for (int i = 0; i < nMedianTimeSpan/2; i++)
{
if (!chainActive.Next(pindex))
return GetBlockTime();
pindex = chainActive.Next(pindex);
}
return pindex->GetMedianTimePast();
}
void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd)
{
// Filter out duplicate requests
if (pindexBegin == pnode->pindexLastGetBlocksBegin && hashEnd == pnode->hashLastGetBlocksEnd)
return;
pnode->pindexLastGetBlocksBegin = pindexBegin;
pnode->hashLastGetBlocksEnd = hashEnd;
pnode->PushMessage("getblocks", chainActive.GetLocator(pindexBegin), hashEnd);
}
bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
{
// Check for duplicate
uint256 hash = pblock->GetHash();
if (mapBlockIndex.count(hash))
return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().c_str()));
if (mapOrphanBlocks.count(hash))
return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString().c_str()));
// Preliminary checks
if (!CheckBlock(*pblock, state))
return error("ProcessBlock() : CheckBlock FAILED");
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0)))
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
if (deltaTime < 0)
{
return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"));
}
CBigNum bnNewBlock;
bnNewBlock.SetCompact(pblock->nBits);
CBigNum bnRequired;
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
if (bnNewBlock > bnRequired)
{
return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"));
}
}
// If we don't already have its previous block, shunt it off to holding area until we get it
if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock))
{
LogPrintf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().c_str());
// Accept orphans as long as there is a node to request its parents from
if (pfrom) {
CBlock* pblock2 = new CBlock(*pblock);
mapOrphanBlocks.insert(make_pair(hash, pblock2));
mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
// Ask this guy to fill in what we're missing
PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(pblock2));
}
return true;
}
// Store to disk
if (!AcceptBlock(*pblock, state, dbp))
return error("ProcessBlock() : AcceptBlock FAILED");
// Recursively process any orphan blocks that depended on this one
vector<uint256> vWorkQueue;
vWorkQueue.push_back(hash);
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
for (multimap<uint256, CBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev);
mi != mapOrphanBlocksByPrev.upper_bound(hashPrev);
++mi)
{
CBlock* pblockOrphan = (*mi).second;
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned)
CValidationState stateDummy;
if (AcceptBlock(*pblockOrphan, stateDummy))
vWorkQueue.push_back(pblockOrphan->GetHash());
mapOrphanBlocks.erase(pblockOrphan->GetHash());
delete pblockOrphan;
}
mapOrphanBlocksByPrev.erase(hashPrev);
}
LogPrintf("ProcessBlock: ACCEPTED\n");
return true;
}
CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter)
{
header = block.GetBlockHeader();
vector<bool> vMatch;
vector<uint256> vHashes;
vMatch.reserve(block.vtx.size());
vHashes.reserve(block.vtx.size());
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
uint256 hash = block.vtx[i].GetHash();
if (filter.IsRelevantAndUpdate(block.vtx[i], hash))
{
vMatch.push_back(true);
vMatchedTxn.push_back(make_pair(i, hash));
}
else
vMatch.push_back(false);
vHashes.push_back(hash);
}
txn = CPartialMerkleTree(vHashes, vMatch);
}
uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector<uint256> &vTxid) {
if (height == 0) {
// hash at height 0 is the txids themself
return vTxid[pos];
} else {
// calculate left hash
uint256 left = CalcHash(height-1, pos*2, vTxid), right;
// calculate right hash if not beyong the end of the array - copy left hash otherwise1
if (pos*2+1 < CalcTreeWidth(height-1))
right = CalcHash(height-1, pos*2+1, vTxid);
else
right = left;
// combine subhashes
return Hash(BEGIN(left), END(left), BEGIN(right), END(right));
}
}
void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) {
// determine whether this node is the parent of at least one matched txid
bool fParentOfMatch = false;
for (unsigned int p = pos << height; p < (pos+1) << height && p < nTransactions; p++)
fParentOfMatch |= vMatch[p];
// store as flag bit
vBits.push_back(fParentOfMatch);
if (height==0 || !fParentOfMatch) {
// if at height 0, or nothing interesting below, store hash and stop
vHash.push_back(CalcHash(height, pos, vTxid));
} else {
// otherwise, don't store any hash, but descend into the subtrees
TraverseAndBuild(height-1, pos*2, vTxid, vMatch);
if (pos*2+1 < CalcTreeWidth(height-1))
TraverseAndBuild(height-1, pos*2+1, vTxid, vMatch);
}
}
uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch) {
if (nBitsUsed >= vBits.size()) {
// overflowed the bits array - failure
fBad = true;
return 0;
}
bool fParentOfMatch = vBits[nBitsUsed++];
if (height==0 || !fParentOfMatch) {
// if at height 0, or nothing interesting below, use stored hash and do not descend
if (nHashUsed >= vHash.size()) {
// overflowed the hash array - failure
fBad = true;
return 0;
}
const uint256 &hash = vHash[nHashUsed++];
if (height==0 && fParentOfMatch) // in case of height 0, we have a matched txid
vMatch.push_back(hash);
return hash;
} else {
// otherwise, descend into the subtrees to extract matched txids and hashes
uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch), right;
if (pos*2+1 < CalcTreeWidth(height-1))
right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch);
else
right = left;
// and combine them before returning
return Hash(BEGIN(left), END(left), BEGIN(right), END(right));
}
}
CPartialMerkleTree::CPartialMerkleTree(const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) : nTransactions(vTxid.size()), fBad(false) {
// reset state
vBits.clear();
vHash.clear();
// calculate height of tree
int nHeight = 0;
while (CalcTreeWidth(nHeight) > 1)
nHeight++;
// traverse the partial tree
TraverseAndBuild(nHeight, 0, vTxid, vMatch);
}
CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {}
uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) {
vMatch.clear();
// An empty set will not work
if (nTransactions == 0)
return 0;
// check for excessively high numbers of transactions
if (nTransactions > MAX_BLOCK_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction
return 0;
// there can never be more hashes provided than one for every txid
if (vHash.size() > nTransactions)
return 0;
// there must be at least one bit per node in the partial tree, and at least one node per hash
if (vBits.size() < vHash.size())
return 0;
// calculate height of tree
int nHeight = 0;
while (CalcTreeWidth(nHeight) > 1)
nHeight++;
// traverse the partial tree
unsigned int nBitsUsed = 0, nHashUsed = 0;
uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch);
// verify that no problems occured during the tree traversal
if (fBad)
return 0;
// verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence)
if ((nBitsUsed+7)/8 != (vBits.size()+7)/8)
return 0;
// verify that all hashes were consumed
if (nHashUsed != vHash.size())
return 0;
return hashMerkleRoot;
}
bool AbortNode(const std::string &strMessage) {
strMiscWarning = strMessage;
LogPrintf("*** %s\n", strMessage.c_str());
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR);
StartShutdown();
return false;
}
bool CheckDiskSpace(uint64 nAdditionalBytes)
{
uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available;
// Check for nMinDiskSpace bytes (currently 50MB)
if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
return AbortNode(_("Error: Disk space is low!"));
return true;
}
CCriticalSection cs_LastBlockFile;
CBlockFileInfo infoLastBlockFile;
int nLastBlockFile = 0;
FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{
if (pos.IsNull())
return NULL;
boost::filesystem::path path = GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
boost::filesystem::create_directories(path.parent_path());
FILE* file = fopen(path.string().c_str(), "rb+");
if (!file && !fReadOnly)
file = fopen(path.string().c_str(), "wb+");
if (!file) {
LogPrintf("Unable to open file %s\n", path.string().c_str());
return NULL;
}
if (pos.nPos) {
if (fseek(file, pos.nPos, SEEK_SET)) {
LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str());
fclose(file);
return NULL;
}
}
return file;
}
FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "blk", fReadOnly);
}
FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "rev", fReadOnly);
}
CBlockIndex * InsertBlockIndex(uint256 hash)
{
if (hash == 0)
return NULL;
// Return existing
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end())
return (*mi).second;
// Create new
CBlockIndex* pindexNew = new CBlockIndex();
if (!pindexNew)
throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
return pindexNew;
}
bool static LoadBlockIndexDB()
{
if (!pblocktree->LoadBlockIndexGuts())
return false;
boost::this_thread::interruption_point();
// Calculate nChainWork
vector<pair<int, CBlockIndex*> > vSortedByHeight;
vSortedByHeight.reserve(mapBlockIndex.size());
BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
{
CBlockIndex* pindex = item.second;
vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
}
sort(vSortedByHeight.begin(), vSortedByHeight.end());
BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
{
CBlockIndex* pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + pindex->GetBlockWork().getuint256();
pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK))
setBlockIndexValid.insert(pindex);
}
// Load block file info
pblocktree->ReadLastBlockFile(nLastBlockFile);
LogPrintf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile);
if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
LogPrintf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString().c_str());
// Load nBestInvalidWork, OK if it doesn't exist
CBigNum bnBestInvalidWork;
pblocktree->ReadBestInvalidWork(bnBestInvalidWork);
nBestInvalidWork = bnBestInvalidWork.getuint256();
// Check whether we need to continue reindexing
bool fReindexing = false;
pblocktree->ReadReindexing(fReindexing);
fReindex |= fReindexing;
// Check whether we have a transaction index
pblocktree->ReadFlag("txindex", fTxIndex);
LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled");
// Load hashBestChain pointer to end of best chain
chainActive.SetTip(pcoinsTip->GetBestBlock());
if (chainActive.Tip() == NULL)
return true;
// register best chain
LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n",
chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str());
return true;
}
bool VerifyDB(int nCheckLevel, int nCheckDepth)
{
if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL)
return true;
// Verify blocks in the best chain
if (nCheckDepth <= 0)
nCheckDepth = 1000000000; // suffices until the year 19000
if (nCheckDepth > chainActive.Height())
nCheckDepth = chainActive.Height();
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CCoinsViewCache coins(*pcoinsTip, true);
CBlockIndex* pindexState = chainActive.Tip();
CBlockIndex* pindexFailure = NULL;
int nGoodTransactions = 0;
CValidationState state;
for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev)
{
boost::this_thread::interruption_point();
if (pindex->nHeight < chainActive.Height()-nCheckDepth)
break;
CBlock block;
// check level 0: read from disk
if (!ReadBlockFromDisk(block, pindex))
return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
// check level 1: verify block validity
if (nCheckLevel >= 1 && !CheckBlock(block, state))
return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
CBlockUndo undo;
CDiskBlockPos pos = pindex->GetUndoPos();
if (!pos.IsNull()) {
if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash()))
return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) {
bool fClean = true;
if (!DisconnectBlock(block, state, pindex, coins, &fClean))
return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
pindexState = pindex->pprev;
if (!fClean) {
nGoodTransactions = 0;
pindexFailure = pindex;
} else
nGoodTransactions += block.vtx.size();
}
}
if (pindexFailure)
return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
CBlockIndex *pindex = pindexState;
while (pindex != chainActive.Tip()) {
boost::this_thread::interruption_point();
pindex = chainActive.Next(pindex);
CBlock block;
if (!ReadBlockFromDisk(block, pindex))
return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
if (!ConnectBlock(block, state, pindex, coins))
return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
}
}
LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions);
return true;
}
void UnloadBlockIndex()
{
mapBlockIndex.clear();
setBlockIndexValid.clear();
chainActive.SetTip(NULL);
nBestInvalidWork = 0;
}
bool LoadBlockIndex()
{
// Load block index from databases
if (!fReindex && !LoadBlockIndexDB())
return false;
return true;
}
bool InitBlockIndex() {
// Check whether we're already initialized
if (chainActive.Genesis() != NULL)
return true;
// Use the provided setting for -txindex in the new database
fTxIndex = GetBoolArg("-txindex", false);
pblocktree->WriteFlag("txindex", fTxIndex);
LogPrintf("Initializing databases...\n");
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
if (!fReindex) {
try {
CBlock &block = const_cast<CBlock&>(Params().GenesisBlock());
// Start new block file
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
CValidationState state;
if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime))
return error("LoadBlockIndex() : FindBlockPos failed");
if (!WriteBlockToDisk(block, blockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!AddToBlockIndex(block, state, blockPos))
return error("LoadBlockIndex() : genesis block not accepted");
} catch(std::runtime_error &e) {
return error("LoadBlockIndex() : failed to initialize block database: %s", e.what());
}
}
return true;
}
void PrintBlockTree()
{
// pre-compute tree structure
map<CBlockIndex*, vector<CBlockIndex*> > mapNext;
for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
{
CBlockIndex* pindex = (*mi).second;
mapNext[pindex->pprev].push_back(pindex);
// test
//while (rand() % 3 == 0)
// mapNext[pindex->pprev].push_back(pindex);
}
vector<pair<int, CBlockIndex*> > vStack;
vStack.push_back(make_pair(0, chainActive.Genesis()));
int nPrevCol = 0;
while (!vStack.empty())
{
int nCol = vStack.back().first;
CBlockIndex* pindex = vStack.back().second;
vStack.pop_back();
// print split or gap
if (nCol > nPrevCol)
{
for (int i = 0; i < nCol-1; i++)
LogPrintf("| ");
LogPrintf("|\\\n");
}
else if (nCol < nPrevCol)
{
for (int i = 0; i < nCol; i++)
LogPrintf("| ");
LogPrintf("|\n");
}
nPrevCol = nCol;
// print columns
for (int i = 0; i < nCol; i++)
LogPrintf("| ");
// print item
CBlock block;
ReadBlockFromDisk(block, pindex);
LogPrintf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"",
pindex->nHeight,
pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()).c_str(),
block.vtx.size());
// put the main time-chain first
vector<CBlockIndex*>& vNext = mapNext[pindex];
for (unsigned int i = 0; i < vNext.size(); i++)
{
if (chainActive.Next(vNext[i]))
{
swap(vNext[0], vNext[i]);
break;
}
}
// iterate children
for (unsigned int i = 0; i < vNext.size(); i++)
vStack.push_back(make_pair(nCol+i, vNext[i]));
}
}
bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
{
int64 nStart = GetTimeMillis();
int nLoaded = 0;
try {
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION);
uint64 nStartByte = 0;
if (dbp) {
// (try to) skip already indexed part
CBlockFileInfo info;
if (pblocktree->ReadBlockFileInfo(dbp->nFile, info)) {
nStartByte = info.nSize;
blkdat.Seek(info.nSize);
}
}
uint64 nRewind = blkdat.GetPos();
while (blkdat.good() && !blkdat.eof()) {
boost::this_thread::interruption_point();
blkdat.SetPos(nRewind);
nRewind++; // start one byte further next time, in case of failure
blkdat.SetLimit(); // remove former limit
unsigned int nSize = 0;
try {
// locate a header
unsigned char buf[4];
blkdat.FindByte(Params().MessageStart()[0]);
nRewind = blkdat.GetPos()+1;
blkdat >> FLATDATA(buf);
if (memcmp(buf, Params().MessageStart(), 4))
continue;
// read size
blkdat >> nSize;
if (nSize < 80 || nSize > MAX_BLOCK_SIZE)
continue;
} catch (std::exception &e) {
// no valid block header found; don't complain
break;
}
try {
// read block
uint64 nBlockPos = blkdat.GetPos();
blkdat.SetLimit(nBlockPos + nSize);
CBlock block;
blkdat >> block;
nRewind = blkdat.GetPos();
// process block
if (nBlockPos >= nStartByte) {
LOCK(cs_main);
if (dbp)
dbp->nPos = nBlockPos;
CValidationState state;
if (ProcessBlock(state, NULL, &block, dbp))
nLoaded++;
if (state.IsError())
break;
}
} catch (std::exception &e) {
LogPrintf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__);
}
}
fclose(fileIn);
} catch(std::runtime_error &e) {
AbortNode(_("Error: system error: ") + e.what());
}
if (nLoaded > 0)
LogPrintf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart);
return nLoaded > 0;
}
//////////////////////////////////////////////////////////////////////////////
//
// CAlert
//
extern map<uint256, CAlert> mapAlerts;
extern CCriticalSection cs_mapAlerts;
string GetWarnings(string strFor)
{
int nPriority = 0;
string strStatusBar;
string strRPC;
if (GetBoolArg("-testsafemode", false))
strRPC = "test";
if (!CLIENT_VERSION_IS_RELEASE)
strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
// Misc warnings like out of disk space and clock is wrong
if (strMiscWarning != "")
{
nPriority = 1000;
strStatusBar = strMiscWarning;
}
if (fLargeWorkForkFound)
{
nPriority = 2000;
strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
}
else if (fLargeWorkInvalidChainFound)
{
nPriority = 2000;
strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
}
// Alerts
{
LOCK(cs_mapAlerts);
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
{
const CAlert& alert = item.second;
if (alert.AppliesToMe() && alert.nPriority > nPriority)
{
nPriority = alert.nPriority;
strStatusBar = alert.strStatusBar;
}
}
}
if (strFor == "statusbar")
return strStatusBar;
else if (strFor == "rpc")
return strRPC;
assert(!"GetWarnings() : invalid parameter");
return "error";
}
//////////////////////////////////////////////////////////////////////////////
//
// Messages
//
bool static AlreadyHave(const CInv& inv)
{
switch (inv.type)
{
case MSG_TX:
{
bool txInMap = false;
{
LOCK(mempool.cs);
txInMap = mempool.exists(inv.hash);
}
return txInMap || mapOrphanTransactions.count(inv.hash) ||
pcoinsTip->HaveCoins(inv.hash);
}
case MSG_BLOCK:
return mapBlockIndex.count(inv.hash) ||
mapOrphanBlocks.count(inv.hash);
}
// Don't know what it is, just say we already got one
return true;
}
void static ProcessGetData(CNode* pfrom)
{
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
vector<CInv> vNotFound;
LOCK(cs_main);
while (it != pfrom->vRecvGetData.end()) {
// Don't bother if send buffer is too full to respond anyway
if (pfrom->nSendSize >= SendBufferSize())
break;
const CInv &inv = *it;
{
boost::this_thread::interruption_point();
it++;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
{
// Send block from disk
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash);
if (mi != mapBlockIndex.end())
{
CBlock block;
ReadBlockFromDisk(block, (*mi).second);
if (inv.type == MSG_BLOCK)
pfrom->PushMessage("block", block);
else // MSG_FILTERED_BLOCK)
{
LOCK(pfrom->cs_filter);
if (pfrom->pfilter)
{
CMerkleBlock merkleBlock(block, *pfrom->pfilter);
pfrom->PushMessage("merkleblock", merkleBlock);
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip
// Note that there is currently no way for a node to request any single transactions we didnt send here -
// they must either disconnect and retry or request the full block.
// Thus, the protocol spec specified allows for us to provide duplicate txn here,
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second)))
pfrom->PushMessage("tx", block.vtx[pair.first]);
}
// else
// no response
}
// Trigger them to send a getblocks request for the next batch of inventory
if (inv.hash == pfrom->hashContinue)
{
// Bypass PushInventory, this must send even if redundant,
// and we want it right after the last block so they don't
// wait for other stuff first.
vector<CInv> vInv;
vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash()));
pfrom->PushMessage("inv", vInv);
pfrom->hashContinue = 0;
}
}
}
else if (inv.IsKnownType())
{
// Send stream from relay memory
bool pushed = false;
{
LOCK(cs_mapRelay);
map<CInv, CDataStream>::iterator mi = mapRelay.find(inv);
if (mi != mapRelay.end()) {
pfrom->PushMessage(inv.GetCommand(), (*mi).second);
pushed = true;
}
}
if (!pushed && inv.type == MSG_TX) {
LOCK(mempool.cs);
if (mempool.exists(inv.hash)) {
CTransaction tx = mempool.lookup(inv.hash);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss.reserve(1000);
ss << tx;
pfrom->PushMessage("tx", ss);
pushed = true;
}
}
if (!pushed) {
vNotFound.push_back(inv);
}
}
// Track requests for our stuff.
g_signals.Inventory(inv.hash);
}
}
pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it);
if (!vNotFound.empty()) {
// Let the peer know that we didn't find what it asked for, so it doesn't
// have to wait around forever. Currently only SPV clients actually care
// about this message: it's needed when they are recursively walking the
// dependencies of relevant unconfirmed transactions. SPV clients want to
// do that because they want to know about (and store and rebroadcast and
// risk analyze) the dependencies of transactions relevant to them, without
// having to download the entire memory pool.
pfrom->PushMessage("notfound", vNotFound);
}
}
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{
RandAddSeedPerfmon();
LogPrint("net", "received: %s (%"PRIszu" bytes)\n", strCommand.c_str(), vRecv.size());
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
return true;
}
if (strCommand == "version")
{
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
pfrom->Misbehaving(1);
return false;
}
int64 nTime;
CAddress addrMe;
CAddress addrFrom;
uint64 nNonce = 1;
vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
if (pfrom->nVersion < MIN_PROTO_VERSION)
{
// Since February 20, 2012, the protocol is initiated at version 209,
// and earlier versions are no longer supported
LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion);
pfrom->fDisconnect = true;
return false;
}
if (pfrom->nVersion == 10300)
pfrom->nVersion = 300;
if (!vRecv.empty())
vRecv >> addrFrom >> nNonce;
if (!vRecv.empty())
vRecv >> pfrom->strSubVer;
if (!vRecv.empty())
vRecv >> pfrom->nStartingHeight;
if (!vRecv.empty())
vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
else
pfrom->fRelayTxes = true;
if (pfrom->fInbound && addrMe.IsRoutable())
{
pfrom->addrLocal = addrMe;
SeenLocal(addrMe);
}
// Disconnect if we connected to ourself
if (nNonce == nLocalHostNonce && nNonce > 1)
{
LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str());
pfrom->fDisconnect = true;
return true;
}
// Be shy and don't send version until we hear
if (pfrom->fInbound)
pfrom->PushVersion();
pfrom->fClient = !(pfrom->nServices & NODE_NETWORK);
// Change version
pfrom->PushMessage("verack");
pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
if (!pfrom->fInbound)
{
// Advertise our address
if (!fNoListen && !IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom->addr);
if (addr.IsRoutable())
pfrom->PushAddress(addr);
}
// Get recent addresses
if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000)
{
pfrom->PushMessage("getaddr");
pfrom->fGetAddr = true;
}
addrman.Good(pfrom->addr);
} else {
if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom)
{
addrman.Add(addrFrom, addrFrom);
addrman.Good(addrFrom);
}
}
// Relay alerts
{
LOCK(cs_mapAlerts);
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
item.second.RelayTo(pfrom);
}
pfrom->fSuccessfullyConnected = true;
LogPrintf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str());
LOCK(cs_main);
AddTimeData(pfrom->addr, nTime);
cPeerBlockCounts.input(pfrom->nStartingHeight);
}
else if (pfrom->nVersion == 0)
{
// Must have a version message before anything else
pfrom->Misbehaving(1);
return false;
}
else if (strCommand == "verack")
{
pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
}
else if (strCommand == "addr")
{
vector<CAddress> vAddr;
vRecv >> vAddr;
// Don't want addr from older versions unless seeding
if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000)
return true;
if (vAddr.size() > 1000)
{
pfrom->Misbehaving(20);
return error("message addr size() = %"PRIszu"", vAddr.size());
}
// Store the new addresses
vector<CAddress> vAddrOk;
int64 nNow = GetAdjustedTime();
int64 nSince = nNow - 10 * 60;
BOOST_FOREACH(CAddress& addr, vAddr)
{
boost::this_thread::interruption_point();
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom->AddAddressKnown(addr);
bool fReachable = IsReachable(addr);
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
// Relay to a limited number of other nodes
{
LOCK(cs_vNodes);
// Use deterministic randomness to send to the same nodes for 24 hours
// at a time so the setAddrKnowns of the chosen nodes prevent repeats
static uint256 hashSalt;
if (hashSalt == 0)
hashSalt = GetRandHash();
uint64 hashAddr = addr.GetHash();
uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60));
hashRand = Hash(BEGIN(hashRand), END(hashRand));
multimap<uint256, CNode*> mapMix;
BOOST_FOREACH(CNode* pnode, vNodes)
{
if (pnode->nVersion < CADDR_TIME_VERSION)
continue;
unsigned int nPointer;
memcpy(&nPointer, &pnode, sizeof(nPointer));
uint256 hashKey = hashRand ^ nPointer;
hashKey = Hash(BEGIN(hashKey), END(hashKey));
mapMix.insert(make_pair(hashKey, pnode));
}
int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
for (multimap<uint256, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi)
((*mi).second)->PushAddress(addr);
}
}
// Do not store addresses outside our network
if (fReachable)
vAddrOk.push_back(addr);
}
addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60);
if (vAddr.size() < 1000)
pfrom->fGetAddr = false;
if (pfrom->fOneShot)
pfrom->fDisconnect = true;
}
else if (strCommand == "inv")
{
vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
pfrom->Misbehaving(20);
return error("message inv size() = %"PRIszu"", vInv.size());
}
// find last block in inv vector
unsigned int nLastBlock = (unsigned int)(-1);
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) {
if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) {
nLastBlock = vInv.size() - 1 - nInv;
break;
}
}
LOCK(cs_main);
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
{
const CInv &inv = vInv[nInv];
boost::this_thread::interruption_point();
pfrom->AddInventoryKnown(inv);
bool fAlreadyHave = AlreadyHave(inv);
LogPrint("net", " got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
if (!fAlreadyHave) {
if (!fImporting && !fReindex)
pfrom->AskFor(inv);
} else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) {
PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(mapOrphanBlocks[inv.hash]));
} else if (nInv == nLastBlock) {
// In case we are on a very long side-chain, it is possible that we already have
// the last block in an inv bundle sent in response to getblocks. Try to detect
// this situation and push another getblocks to continue.
PushGetBlocks(pfrom, mapBlockIndex[inv.hash], uint256(0));
if (fDebug)
LogPrintf("force request: %s\n", inv.ToString().c_str());
}
// Track requests for our stuff
g_signals.Inventory(inv.hash);
}
}
else if (strCommand == "getdata")
{
vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
pfrom->Misbehaving(20);
return error("message getdata size() = %"PRIszu"", vInv.size());
}
- if (fDebugNet || (vInv.size() != 1))
+ if (fDebug || (vInv.size() != 1))
LogPrint("net", "received getdata (%"PRIszu" invsz)\n", vInv.size());
- if ((fDebugNet && vInv.size() > 0) || (vInv.size() == 1))
+ if ((fDebug && vInv.size() > 0) || (vInv.size() == 1))
LogPrint("net", "received getdata for: %s\n", vInv[0].ToString().c_str());
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
ProcessGetData(pfrom);
}
else if (strCommand == "getblocks")
{
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
LOCK(cs_main);
// Find the last block the caller has in the main chain
CBlockIndex* pindex = chainActive.FindFork(locator);
// Send the rest of the chain
if (pindex)
pindex = chainActive.Next(pindex);
int nLimit = 500;
LogPrint("net", "getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit);
for (; pindex; pindex = chainActive.Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
{
LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
break;
}
pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
if (--nLimit <= 0)
{
// When this block is requested, we'll send an inv that'll make them
// getblocks the next batch of inventory.
LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
pfrom->hashContinue = pindex->GetBlockHash();
break;
}
}
}
else if (strCommand == "getheaders")
{
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
LOCK(cs_main);
CBlockIndex* pindex = NULL;
if (locator.IsNull())
{
// If locator is null, return the hashStop block
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashStop);
if (mi == mapBlockIndex.end())
return true;
pindex = (*mi).second;
}
else
{
// Find the last block the caller has in the main chain
pindex = chainActive.FindFork(locator);
if (pindex)
pindex = chainActive.Next(pindex);
}
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
vector<CBlock> vHeaders;
int nLimit = 2000;
LogPrint("net", "getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str());
for (; pindex; pindex = chainActive.Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
pfrom->PushMessage("headers", vHeaders);
}
else if (strCommand == "tx")
{
vector<uint256> vWorkQueue;
vector<uint256> vEraseQueue;
CTransaction tx;
vRecv >> tx;
CInv inv(MSG_TX, tx.GetHash());
pfrom->AddInventoryKnown(inv);
LOCK(cs_main);
bool fMissingInputs = false;
CValidationState state;
if (mempool.accept(state, tx, true, &fMissingInputs))
{
mempool.check(pcoinsTip);
RelayTransaction(tx, inv.hash);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
vEraseQueue.push_back(inv.hash);
// Recursively process any orphan transactions that depended on this one
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
for (set<uint256>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
mi != mapOrphanTransactionsByPrev[hashPrev].end();
++mi)
{
const uint256& orphanHash = *mi;
const CTransaction& orphanTx = mapOrphanTransactions[orphanHash];
bool fMissingInputs2 = false;
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
// anyone relaying LegitTxX banned)
CValidationState stateDummy;
if (mempool.accept(stateDummy, orphanTx, true, &fMissingInputs2))
{
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString().c_str());
RelayTransaction(orphanTx, orphanHash);
mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash));
vWorkQueue.push_back(orphanHash);
vEraseQueue.push_back(orphanHash);
}
else if (!fMissingInputs2)
{
// invalid or too-little-fee orphan
vEraseQueue.push_back(orphanHash);
LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString().c_str());
}
mempool.check(pcoinsTip);
}
}
BOOST_FOREACH(uint256 hash, vEraseQueue)
EraseOrphanTx(hash);
}
else if (fMissingInputs)
{
AddOrphanTx(tx);
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
if (nEvicted > 0)
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
}
int nDoS = 0;
if (state.IsInvalid(nDoS))
if (nDoS > 0)
pfrom->Misbehaving(nDoS);
}
else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing
{
CBlock block;
vRecv >> block;
LogPrint("net", "received block %s\n", block.GetHash().ToString().c_str());
// block.print();
CInv inv(MSG_BLOCK, block.GetHash());
pfrom->AddInventoryKnown(inv);
LOCK(cs_main);
CValidationState state;
if (ProcessBlock(state, pfrom, &block) || state.CorruptionPossible())
mapAlreadyAskedFor.erase(inv);
int nDoS = 0;
if (state.IsInvalid(nDoS))
if (nDoS > 0)
pfrom->Misbehaving(nDoS);
}
else if (strCommand == "getaddr")
{
pfrom->vAddrToSend.clear();
vector<CAddress> vAddr = addrman.GetAddr();
BOOST_FOREACH(const CAddress &addr, vAddr)
pfrom->PushAddress(addr);
}
else if (strCommand == "mempool")
{
LOCK(cs_main);
std::vector<uint256> vtxid;
LOCK2(mempool.cs, pfrom->cs_filter);
mempool.queryHashes(vtxid);
vector<CInv> vInv;
BOOST_FOREACH(uint256& hash, vtxid) {
CInv inv(MSG_TX, hash);
if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(mempool.lookup(hash), hash)) ||
(!pfrom->pfilter))
vInv.push_back(inv);
if (vInv.size() == MAX_INV_SZ) {
pfrom->PushMessage("inv", vInv);
vInv.clear();
}
}
if (vInv.size() > 0)
pfrom->PushMessage("inv", vInv);
}
else if (strCommand == "ping")
{
if (pfrom->nVersion > BIP0031_VERSION)
{
uint64 nonce = 0;
vRecv >> nonce;
// Echo the message back with the nonce. This allows for two useful features:
//
// 1) A remote node can quickly check if the connection is operational
// 2) Remote nodes can measure the latency of the network thread. If this node
// is overloaded it won't respond to pings quickly and the remote node can
// avoid sending us more work, like chain download requests.
//
// The nonce stops the remote getting confused between different pings: without
// it, if the remote node sends a ping once per second and this node takes 5
// seconds to respond to each, the 5th ping the remote sends would appear to
// return very quickly.
pfrom->PushMessage("pong", nonce);
}
}
else if (strCommand == "pong")
{
int64 pingUsecEnd = GetTimeMicros();
uint64 nonce = 0;
size_t nAvail = vRecv.in_avail();
bool bPingFinished = false;
std::string sProblem;
if (nAvail >= sizeof(nonce)) {
vRecv >> nonce;
// Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
if (pfrom->nPingNonceSent != 0) {
if (nonce == pfrom->nPingNonceSent) {
// Matching pong received, this ping is no longer outstanding
bPingFinished = true;
int64 pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart;
if (pingUsecTime > 0) {
// Successful ping time measurement, replace previous
pfrom->nPingUsecTime = pingUsecTime;
} else {
// This should never happen
sProblem = "Timing mishap";
}
} else {
// Nonce mismatches are normal when pings are overlapping
sProblem = "Nonce mismatch";
if (nonce == 0) {
// This is most likely a bug in another implementation somewhere, cancel this ping
bPingFinished = true;
sProblem = "Nonce zero";
}
}
} else {
sProblem = "Unsolicited pong without ping";
}
} else {
// This is most likely a bug in another implementation somewhere, cancel this ping
bPingFinished = true;
sProblem = "Short payload";
}
if (!(sProblem.empty())) {
LogPrint("net", "pong %s %s: %s, %"PRI64x" expected, %"PRI64x" received, %"PRIszu" bytes\n",
pfrom->addr.ToString().c_str(),
pfrom->strSubVer.c_str(),
sProblem.c_str(),
pfrom->nPingNonceSent,
nonce,
nAvail);
}
if (bPingFinished) {
pfrom->nPingNonceSent = 0;
}
}
else if (strCommand == "alert")
{
CAlert alert;
vRecv >> alert;
uint256 alertHash = alert.GetHash();
if (pfrom->setKnown.count(alertHash) == 0)
{
if (alert.ProcessAlert())
{
// Relay
pfrom->setKnown.insert(alertHash);
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
alert.RelayTo(pnode);
}
}
else {
// Small DoS penalty so peers that send us lots of
// duplicate/expired/invalid-signature/whatever alerts
// eventually get banned.
// This isn't a Misbehaving(100) (immediate ban) because the
// peer might be an older or different implementation with
// a different signature key, etc.
pfrom->Misbehaving(10);
}
}
}
else if (strCommand == "filterload")
{
CBloomFilter filter;
vRecv >> filter;
if (!filter.IsWithinSizeConstraints())
// There is no excuse for sending a too-large filter
pfrom->Misbehaving(100);
else
{
LOCK(pfrom->cs_filter);
delete pfrom->pfilter;
pfrom->pfilter = new CBloomFilter(filter);
pfrom->pfilter->UpdateEmptyFull();
}
pfrom->fRelayTxes = true;
}
else if (strCommand == "filteradd")
{
vector<unsigned char> vData;
vRecv >> vData;
// Nodes must NEVER send a data item > 520 bytes (the max size for a script data object,
// and thus, the maximum size any matched object can have) in a filteradd message
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE)
{
pfrom->Misbehaving(100);
} else {
LOCK(pfrom->cs_filter);
if (pfrom->pfilter)
pfrom->pfilter->insert(vData);
else
pfrom->Misbehaving(100);
}
}
else if (strCommand == "filterclear")
{
LOCK(pfrom->cs_filter);
delete pfrom->pfilter;
pfrom->pfilter = new CBloomFilter();
pfrom->fRelayTxes = true;
}
else
{
// Ignore unknown commands for extensibility
}
// Update the last seen time for this node's address
if (pfrom->fNetworkNode)
if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping")
AddressCurrentlyConnected(pfrom->addr);
return true;
}
// requires LOCK(cs_vRecvMsg)
bool ProcessMessages(CNode* pfrom)
{
//if (fDebug)
// LogPrintf("ProcessMessages(%"PRIszu" messages)\n", pfrom->vRecvMsg.size());
//
// Message format
// (4) message start
// (12) command
// (4) size
// (4) checksum
// (x) data
//
bool fOk = true;
if (!pfrom->vRecvGetData.empty())
ProcessGetData(pfrom);
std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin();
while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) {
// Don't bother if send buffer is too full to respond anyway
if (pfrom->nSendSize >= SendBufferSize())
break;
// get next message
CNetMessage& msg = *it;
//if (fDebug)
// LogPrintf("ProcessMessages(message %u msgsz, %"PRIszu" bytes, complete:%s)\n",
// msg.hdr.nMessageSize, msg.vRecv.size(),
// msg.complete() ? "Y" : "N");
// end, if an incomplete message is found
if (!msg.complete())
break;
// at this point, any failure means we can delete the current message
it++;
// Scan for message start
if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) {
LogPrintf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n");
fOk = false;
break;
}
// Read header
CMessageHeader& hdr = msg.hdr;
if (!hdr.IsValid())
{
LogPrintf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str());
continue;
}
string strCommand = hdr.GetCommand();
// Message size
unsigned int nMessageSize = hdr.nMessageSize;
// Checksum
CDataStream& vRecv = msg.vRecv;
uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
unsigned int nChecksum = 0;
memcpy(&nChecksum, &hash, sizeof(nChecksum));
if (nChecksum != hdr.nChecksum)
{
LogPrintf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n",
strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum);
continue;
}
// Process message
bool fRet = false;
try
{
fRet = ProcessMessage(pfrom, strCommand, vRecv);
boost::this_thread::interruption_point();
}
catch (std::ios_base::failure& e)
{
if (strstr(e.what(), "end of data"))
{
// Allow exceptions from under-length message on vRecv
LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what());
}
else if (strstr(e.what(), "size too large"))
{
// Allow exceptions from over-long size
LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what());
}
else
{
PrintExceptionContinue(&e, "ProcessMessages()");
}
}
catch (boost::thread_interrupted) {
throw;
}
catch (std::exception& e) {
PrintExceptionContinue(&e, "ProcessMessages()");
} catch (...) {
PrintExceptionContinue(NULL, "ProcessMessages()");
}
if (!fRet)
LogPrintf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize);
}
// In case the connection got shut down, its receive buffer was wiped
if (!pfrom->fDisconnect)
pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it);
return fOk;
}
bool SendMessages(CNode* pto, bool fSendTrickle)
{
{
// Don't send anything until we get their version message
if (pto->nVersion == 0)
return true;
//
// Message: ping
//
bool pingSend = false;
if (pto->fPingQueued) {
// RPC ping request by user
pingSend = true;
}
if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSendMsg.empty()) {
// Ping automatically sent as a keepalive
pingSend = true;
}
if (pingSend) {
uint64 nonce = 0;
while (nonce == 0) {
RAND_bytes((unsigned char*)&nonce, sizeof(nonce));
}
pto->nPingNonceSent = nonce;
pto->fPingQueued = false;
if (pto->nVersion > BIP0031_VERSION) {
// Take timestamp as close as possible before transmitting ping
pto->nPingUsecStart = GetTimeMicros();
pto->PushMessage("ping", nonce);
} else {
// Peer is too old to support ping command with nonce, pong will never arrive, disable timing
pto->nPingUsecStart = 0;
pto->PushMessage("ping");
}
}
// Address refresh broadcast
static int64 nLastRebroadcast;
if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60))
{
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
{
// Periodically clear setAddrKnown to allow refresh broadcasts
if (nLastRebroadcast)
pnode->setAddrKnown.clear();
// Rebroadcast our address
if (!fNoListen)
{
CAddress addr = GetLocalAddress(&pnode->addr);
if (addr.IsRoutable())
pnode->PushAddress(addr);
}
}
}
nLastRebroadcast = GetTime();
}
//
// Message: addr
//
if (fSendTrickle)
{
vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
{
// returns true if wasn't already contained in the set
if (pto->setAddrKnown.insert(addr).second)
{
vAddr.push_back(addr);
// receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000)
{
pto->PushMessage("addr", vAddr);
vAddr.clear();
}
}
}
pto->vAddrToSend.clear();
if (!vAddr.empty())
pto->PushMessage("addr", vAddr);
}
TRY_LOCK(cs_main, lockMain);
if (!lockMain)
return true;
// Start block sync
if (pto->fStartSync && !fImporting && !fReindex) {
pto->fStartSync = false;
PushGetBlocks(pto, chainActive.Tip(), uint256(0));
}
// Resend wallet transactions that haven't gotten in a block yet
// Except during reindex, importing and IBD, when old wallet
// transactions become unconfirmed and spams other nodes.
if (!fReindex && !fImporting && !IsInitialBlockDownload())
{
g_signals.Broadcast();
}
//
// Message: inventory
//
vector<CInv> vInv;
vector<CInv> vInvWait;
{
LOCK(pto->cs_inventory);
vInv.reserve(pto->vInventoryToSend.size());
vInvWait.reserve(pto->vInventoryToSend.size());
BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend)
{
if (pto->setInventoryKnown.count(inv))
continue;
// trickle out tx inv to protect privacy
if (inv.type == MSG_TX && !fSendTrickle)
{
// 1/4 of tx invs blast to all immediately
static uint256 hashSalt;
if (hashSalt == 0)
hashSalt = GetRandHash();
uint256 hashRand = inv.hash ^ hashSalt;
hashRand = Hash(BEGIN(hashRand), END(hashRand));
bool fTrickleWait = ((hashRand & 3) != 0);
if (fTrickleWait)
{
vInvWait.push_back(inv);
continue;
}
}
// returns true if wasn't already contained in the set
if (pto->setInventoryKnown.insert(inv).second)
{
vInv.push_back(inv);
if (vInv.size() >= 1000)
{
pto->PushMessage("inv", vInv);
vInv.clear();
}
}
}
pto->vInventoryToSend = vInvWait;
}
if (!vInv.empty())
pto->PushMessage("inv", vInv);
//
// Message: getdata
//
vector<CInv> vGetData;
int64 nNow = GetTime() * 1000000;
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{
const CInv& inv = (*pto->mapAskFor.begin()).second;
if (!AlreadyHave(inv))
{
- if (fDebugNet)
+ if (fDebug)
LogPrint("net", "sending getdata: %s\n", inv.ToString().c_str());
vGetData.push_back(inv);
if (vGetData.size() >= 1000)
{
pto->PushMessage("getdata", vGetData);
vGetData.clear();
}
}
pto->mapAskFor.erase(pto->mapAskFor.begin());
}
if (!vGetData.empty())
pto->PushMessage("getdata", vGetData);
}
return true;
}
class CMainCleanup
{
public:
CMainCleanup() {}
~CMainCleanup() {
// block headers
std::map<uint256, CBlockIndex*>::iterator it1 = mapBlockIndex.begin();
for (; it1 != mapBlockIndex.end(); it1++)
delete (*it1).second;
mapBlockIndex.clear();
// orphan blocks
std::map<uint256, CBlock*>::iterator it2 = mapOrphanBlocks.begin();
for (; it2 != mapOrphanBlocks.end(); it2++)
delete (*it2).second;
mapOrphanBlocks.clear();
// orphan transactions
mapOrphanTransactions.clear();
}
} instance_of_cmaincleanup;
diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp
index f6a898ff7..c8afd837d 100644
--- a/src/qt/paymentrequestplus.cpp
+++ b/src/qt/paymentrequestplus.cpp
@@ -1,205 +1,204 @@
//
// Wraps dumb protocol buffer paymentRequest
// with some extra methods
//
#include <QDateTime>
#include <QDebug>
#include <QSslCertificate>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <stdexcept>
#include "paymentrequestplus.h"
class SSLVerifyError : public std::runtime_error
{
public:
SSLVerifyError(std::string err) : std::runtime_error(err) { }
};
bool PaymentRequestPlus::parse(const QByteArray& data)
{
bool parseOK = paymentRequest.ParseFromArray(data.data(), data.size());
if (!parseOK) {
qDebug() << "PaymentRequestPlus::parse : Error parsing payment request";
return false;
}
if (paymentRequest.payment_details_version() > 1) {
qDebug() << "PaymentRequestPlus::parse : Received up-version payment details, version=" << paymentRequest.payment_details_version();
return false;
}
parseOK = details.ParseFromString(paymentRequest.serialized_payment_details());
if (!parseOK)
{
qDebug() << "PaymentRequestPlus::parse : Error parsing payment details";
paymentRequest.Clear();
return false;
}
return true;
}
bool PaymentRequestPlus::SerializeToString(string* output) const
{
return paymentRequest.SerializeToString(output);
}
bool PaymentRequestPlus::IsInitialized() const
{
return paymentRequest.IsInitialized();
}
QString PaymentRequestPlus::getPKIType() const
{
if (!IsInitialized()) return QString("none");
return QString::fromStdString(paymentRequest.pki_type());
}
bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) const
{
merchant.clear();
if (!IsInitialized())
return false;
// One day we'll support more PKI types, but just
// x509 for now:
const EVP_MD* digestAlgorithm = NULL;
if (paymentRequest.pki_type() == "x509+sha256") {
digestAlgorithm = EVP_sha256();
}
else if (paymentRequest.pki_type() == "x509+sha1") {
digestAlgorithm = EVP_sha1();
}
else if (paymentRequest.pki_type() == "none") {
- if (fDebug)
- qDebug() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none";
+ qDebug() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none";
return false;
}
else {
qDebug() << "PaymentRequestPlus::getMerchant : Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type());
return false;
}
payments::X509Certificates certChain;
if (!certChain.ParseFromString(paymentRequest.pki_data())) {
qDebug() << "PaymentRequestPlus::getMerchant : Payment request: error parsing pki_data";
return false;
}
std::vector<X509*> certs;
const QDateTime currentTime = QDateTime::currentDateTime();
for (int i = 0; i < certChain.certificate_size(); i++) {
QByteArray certData(certChain.certificate(i).data(), certChain.certificate(i).size());
QSslCertificate qCert(certData, QSsl::Der);
if (currentTime < qCert.effectiveDate() || currentTime > qCert.expiryDate()) {
qDebug() << "PaymentRequestPlus::getMerchant : Payment request: certificate expired or not yet active: " << qCert;
return false;
}
#if QT_VERSION >= 0x050000
if (qCert.isBlacklisted()) {
qDebug() << "PaymentRequestPlus::getMerchant : Payment request: certificate blacklisted: " << qCert;
return false;
}
#endif
const unsigned char *data = (const unsigned char *)certChain.certificate(i).data();
X509 *cert = d2i_X509(NULL, &data, certChain.certificate(i).size());
if (cert)
certs.push_back(cert);
}
if (certs.empty()) {
qDebug() << "PaymentRequestPlus::getMerchant : Payment request: empty certificate chain";
return false;
}
// The first cert is the signing cert, the rest are untrusted certs that chain
// to a valid root authority. OpenSSL needs them separately.
STACK_OF(X509) *chain = sk_X509_new_null();
for (int i = certs.size()-1; i > 0; i--) {
sk_X509_push(chain, certs[i]);
}
X509 *signing_cert = certs[0];
// Now create a "store context", which is a single use object for checking,
// load the signing cert into it and verify.
X509_STORE_CTX *store_ctx = X509_STORE_CTX_new();
if (!store_ctx) {
qDebug() << "PaymentRequestPlus::getMerchant : Payment request: error creating X509_STORE_CTX";
return false;
}
char *website = NULL;
bool fResult = true;
try
{
if (!X509_STORE_CTX_init(store_ctx, certStore, signing_cert, chain))
{
int error = X509_STORE_CTX_get_error(store_ctx);
throw SSLVerifyError(X509_verify_cert_error_string(error));
}
// Now do the verification!
int result = X509_verify_cert(store_ctx);
if (result != 1) {
int error = X509_STORE_CTX_get_error(store_ctx);
throw SSLVerifyError(X509_verify_cert_error_string(error));
}
X509_NAME *certname = X509_get_subject_name(signing_cert);
// Valid cert; check signature:
payments::PaymentRequest rcopy(paymentRequest); // Copy
rcopy.set_signature(std::string(""));
std::string data_to_verify; // Everything but the signature
rcopy.SerializeToString(&data_to_verify);
EVP_MD_CTX ctx;
EVP_PKEY *pubkey = X509_get_pubkey(signing_cert);
EVP_MD_CTX_init(&ctx);
if (!EVP_VerifyInit_ex(&ctx, digestAlgorithm, NULL) ||
!EVP_VerifyUpdate(&ctx, data_to_verify.data(), data_to_verify.size()) ||
!EVP_VerifyFinal(&ctx, (const unsigned char*)paymentRequest.signature().data(), paymentRequest.signature().size(), pubkey)) {
throw SSLVerifyError("Bad signature, invalid PaymentRequest.");
}
// OpenSSL API for getting human printable strings from certs is baroque.
int textlen = X509_NAME_get_text_by_NID(certname, NID_commonName, NULL, 0);
website = new char[textlen + 1];
if (X509_NAME_get_text_by_NID(certname, NID_commonName, website, textlen + 1) == textlen && textlen > 0) {
merchant = website;
}
else {
throw SSLVerifyError("Bad certificate, missing common name.");
}
// TODO: detect EV certificates and set merchant = business name instead of unfriendly NID_commonName ?
}
catch (SSLVerifyError& err)
{
fResult = false;
qDebug() << "PaymentRequestPlus::getMerchant : SSL error: " << err.what();
}
if (website)
delete[] website;
X509_STORE_CTX_free(store_ctx);
for (unsigned int i = 0; i < certs.size(); i++)
X509_free(certs[i]);
return fResult;
}
QList<std::pair<CScript,qint64> > PaymentRequestPlus::getPayTo() const
{
QList<std::pair<CScript,qint64> > result;
for (int i = 0; i < details.outputs_size(); i++)
{
const unsigned char* scriptStr = (const unsigned char*)details.outputs(i).script().data();
CScript s(scriptStr, scriptStr+details.outputs(i).script().size());
result.append(make_pair(s, details.outputs(i).amount()));
}
return result;
}
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 43c4bdd19..0f386680a 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -1,648 +1,642 @@
// Copyright (c) 2009-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <QApplication>
#include <QByteArray>
#include <QDataStream>
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QFileOpenEvent>
#include <QHash>
#include <QList>
#include <QLocalServer>
#include <QLocalSocket>
#include <QStringList>
#include <QTextDocument>
#include <QNetworkAccessManager>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QSslCertificate>
#include <QSslError>
#include <QSslSocket>
#if QT_VERSION < 0x050000
#include <QUrl>
#else
#include <QUrlQuery>
#endif
#include <cstdlib>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include "base58.h"
#include "bitcoinunits.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "paymentserver.h"
#include "ui_interface.h"
#include "util.h"
#include "wallet.h"
#include "walletmodel.h"
const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
const QString BITCOIN_IPC_PREFIX("bitcoin:");
const char* BITCOIN_REQUEST_MIMETYPE = "application/bitcoin-paymentrequest";
const char* BITCOIN_PAYMENTACK_MIMETYPE = "application/bitcoin-paymentack";
const char* BITCOIN_PAYMENTACK_CONTENTTYPE = "application/bitcoin-payment";
X509_STORE* PaymentServer::certStore = NULL;
void PaymentServer::freeCertStore()
{
if (PaymentServer::certStore != NULL)
{
X509_STORE_free(PaymentServer::certStore);
PaymentServer::certStore = NULL;
}
}
//
// Create a name that is unique for:
// testnet / non-testnet
// data directory
//
static QString ipcServerName()
{
QString name("BitcoinQt");
// Append a simple hash of the datadir
// Note that GetDataDir(true) returns a different path
// for -testnet versus main net
QString ddir(QString::fromStdString(GetDataDir(true).string()));
name.append(QString::number(qHash(ddir)));
return name;
}
//
// We store payment URIs and requests received before
// the main GUI window is up and ready to ask the user
// to send payment.
static QList<QString> savedPaymentRequests;
static void ReportInvalidCertificate(const QSslCertificate& cert)
{
- if (fDebug) {
- qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName);
- }
+ qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName);
}
//
// Load OpenSSL's list of root certificate authorities
//
void PaymentServer::LoadRootCAs(X509_STORE* _store)
{
if (PaymentServer::certStore == NULL)
atexit(PaymentServer::freeCertStore);
else
freeCertStore();
// Unit tests mostly use this, to pass in fake root CAs:
if (_store)
{
PaymentServer::certStore = _store;
return;
}
// Normal execution, use either -rootcertificates or system certs:
PaymentServer::certStore = X509_STORE_new();
// Note: use "-system-" default here so that users can pass -rootcertificates=""
// and get 'I don't like X.509 certificates, don't trust anybody' behavior:
QString certFile = QString::fromStdString(GetArg("-rootcertificates", "-system-"));
if (certFile.isEmpty())
return; // Empty store
QList<QSslCertificate> certList;
if (certFile != "-system-")
{
certList = QSslCertificate::fromPath(certFile);
// Use those certificates when fetching payment requests, too:
QSslSocket::setDefaultCaCertificates(certList);
}
else
certList = QSslSocket::systemCaCertificates ();
int nRootCerts = 0;
const QDateTime currentTime = QDateTime::currentDateTime();
foreach (const QSslCertificate& cert, certList)
{
if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
ReportInvalidCertificate(cert);
continue;
}
#if QT_VERSION >= 0x050000
if (cert.isBlacklisted()) {
ReportInvalidCertificate(cert);
continue;
}
#endif
QByteArray certData = cert.toDer();
const unsigned char *data = (const unsigned char *)certData.data();
X509* x509 = d2i_X509(0, &data, certData.size());
if (x509 && X509_STORE_add_cert(PaymentServer::certStore, x509))
{
// Note: X509_STORE_free will free the X509* objects when
// the PaymentServer is destroyed
++nRootCerts;
}
else
{
ReportInvalidCertificate(cert);
continue;
}
}
- if (fDebug)
- qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates";
+ qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates";
// Project for another day:
// Fetch certificate revocation lists, and add them to certStore.
// Issues to consider:
// performance (start a thread to fetch in background?)
// privacy (fetch through tor/proxy so IP address isn't revealed)
// would it be easier to just use a compiled-in blacklist?
// or use Qt's blacklist?
// "certificate stapling" with server-side caching is more efficient
}
//
// Sending to the server is done synchronously, at startup.
// If the server isn't already running, startup continues,
// and the items in savedPaymentRequest will be handled
// when uiReady() is called.
//
bool PaymentServer::ipcSendCommandLine(int argc, char* argv[])
{
bool fResult = false;
for (int i = 1; i < argc; i++)
{
QString arg(argv[i]);
if (arg.startsWith("-"))
continue;
if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
{
savedPaymentRequests.append(arg);
SendCoinsRecipient r;
if (GUIUtil::parseBitcoinURI(arg, &r))
{
CBitcoinAddress address(r.address.toStdString());
SelectParams(CChainParams::MAIN);
if (!address.IsValid())
{
SelectParams(CChainParams::TESTNET);
}
}
}
else if (QFile::exists(arg)) // Filename
{
savedPaymentRequests.append(arg);
PaymentRequestPlus request;
if (readPaymentRequest(arg, request))
{
if (request.getDetails().network() == "main")
SelectParams(CChainParams::MAIN);
else
SelectParams(CChainParams::TESTNET);
}
}
else
{
// Printing to debug.log is about the best we can do here, the
// GUI hasn't started yet so we can't pop up a message box.
qDebug() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << arg;
}
}
foreach (const QString& r, savedPaymentRequests)
{
QLocalSocket* socket = new QLocalSocket();
socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT))
return false;
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << r;
out.device()->seek(0);
socket->write(block);
socket->flush();
socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT);
socket->disconnectFromServer();
delete socket;
fResult = true;
}
return fResult;
}
PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
QObject(parent),
saveURIs(true),
uriServer(0),
netManager(0),
optionsModel(0)
{
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
// Install global event filter to catch QFileOpenEvents
// on Mac: sent when you click bitcoin: links
// other OSes: helpful when dealing with payment request files (in the future)
if (parent)
parent->installEventFilter(this);
QString name = ipcServerName();
// Clean up old socket leftover from a crash:
QLocalServer::removeServer(name);
if (startLocalServer)
{
uriServer = new QLocalServer(this);
if (!uriServer->listen(name))
qDebug() << "PaymentServer::PaymentServer : Cannot start bitcoin: click-to-pay handler";
else {
connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection()));
connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString)));
}
}
// netManager is null until uiReady() is called
netManager = NULL;
}
PaymentServer::~PaymentServer()
{
google::protobuf::ShutdownProtobufLibrary();
}
//
// OSX-specific way of handling bitcoin: URIs and
// PaymentRequest mime types
//
bool PaymentServer::eventFilter(QObject *, QEvent *event)
{
// clicking on bitcoin: URIs creates FileOpen events on the Mac:
if (event->type() == QEvent::FileOpen)
{
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
if (!fileEvent->file().isEmpty())
handleURIOrFile(fileEvent->file());
else if (!fileEvent->url().isEmpty())
handleURIOrFile(fileEvent->url().toString());
return true;
}
return false;
}
void PaymentServer::initNetManager()
{
if (!optionsModel)
return;
if (netManager != NULL)
delete netManager;
// netManager is used to fetch paymentrequests given in bitcoin: URIs
netManager = new QNetworkAccessManager(this);
// Use proxy settings from optionsModel
QString proxyIP;
quint16 proxyPort;
if (optionsModel->getProxySettings(proxyIP, proxyPort))
{
QNetworkProxy proxy;
proxy.setType(QNetworkProxy::Socks5Proxy);
proxy.setHostName(proxyIP);
proxy.setPort(proxyPort);
netManager->setProxy(proxy);
}
connect(netManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(netRequestFinished(QNetworkReply*)));
connect(netManager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)),
this, SLOT(reportSslErrors(QNetworkReply*, const QList<QSslError> &)));
}
void PaymentServer::uiReady()
{
assert(netManager != NULL); // Must call initNetManager before uiReady()
saveURIs = false;
foreach (const QString& s, savedPaymentRequests)
{
handleURIOrFile(s);
}
savedPaymentRequests.clear();
}
void PaymentServer::handleURIOrFile(const QString& s)
{
if (saveURIs)
{
savedPaymentRequests.append(s);
return;
}
if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin:
{
#if QT_VERSION >= 0x050000
QUrlQuery uri((QUrl(s)));
#else
QUrl uri(s);
#endif
if (uri.hasQueryItem("request"))
{
QByteArray temp;
temp.append(uri.queryItemValue("request"));
QString decoded = QUrl::fromPercentEncoding(temp);
QUrl fetchUrl(decoded, QUrl::StrictMode);
- if (fDebug)
- qDebug() << "PaymentServer::handleURIOrFile : fetchRequest(" << fetchUrl << ")";
+ qDebug() << "PaymentServer::handleURIOrFile : fetchRequest(" << fetchUrl << ")";
if (fetchUrl.isValid())
fetchRequest(fetchUrl);
else
qDebug() << "PaymentServer::handleURIOrFile : Invalid URL: " << fetchUrl;
return;
}
SendCoinsRecipient recipient;
if (GUIUtil::parseBitcoinURI(s, &recipient))
emit receivedPaymentRequest(recipient);
else
emit message(tr("URI handling"),
tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
CClientUIInterface::ICON_WARNING);
return;
}
if (QFile::exists(s))
{
PaymentRequestPlus request;
QList<SendCoinsRecipient> recipients;
if (readPaymentRequest(s, request) && processPaymentRequest(request, recipients)) {
foreach (const SendCoinsRecipient& recipient, recipients){
emit receivedPaymentRequest(recipient);
}
}
return;
}
}
void PaymentServer::handleURIConnection()
{
QLocalSocket *clientConnection = uriServer->nextPendingConnection();
while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
clientConnection->waitForReadyRead();
connect(clientConnection, SIGNAL(disconnected()),
clientConnection, SLOT(deleteLater()));
QDataStream in(clientConnection);
in.setVersion(QDataStream::Qt_4_0);
if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
return;
}
QString msg;
in >> msg;
handleURIOrFile(msg);
}
bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPlus& request)
{
QFile f(filename);
if (!f.open(QIODevice::ReadOnly))
{
qDebug() << "PaymentServer::readPaymentRequest : Failed to open " << filename;
return false;
}
if (f.size() > MAX_PAYMENT_REQUEST_SIZE)
{
qDebug() << "PaymentServer::readPaymentRequest : " << filename << " too large";
return false;
}
QByteArray data = f.readAll();
return request.parse(data);
}
bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, QList<SendCoinsRecipient>& recipients)
{
if (!optionsModel)
return false;
QList<std::pair<CScript,qint64> > sendingTos = request.getPayTo();
qint64 totalAmount = 0;
foreach(const PAIRTYPE(CScript, qint64)& sendingTo, sendingTos) {
CTxOut txOut(sendingTo.second, sendingTo.first);
if (txOut.IsDust(CTransaction::nMinRelayTxFee)) {
QString msg = tr("Requested payment amount of %1 is too small (considered dust).")
.arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second));
qDebug() << "PaymentServer::processPaymentRequest : " << msg;
emit message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR);
return false;
}
totalAmount += sendingTo.second;
}
recipients.append(SendCoinsRecipient());
if (request.getMerchant(PaymentServer::certStore, recipients[0].authenticatedMerchant)) {
recipients[0].paymentRequest = request;
recipients[0].amount = totalAmount;
- if (fDebug)
- qDebug() << "PaymentServer::processPaymentRequest : Payment request from " << recipients[0].authenticatedMerchant;
+ qDebug() << "PaymentServer::processPaymentRequest : Payment request from " << recipients[0].authenticatedMerchant;
}
else {
recipients.clear();
// Insecure payment requests may turn into more than one recipient if
// the merchant is requesting payment to more than one address.
for (int i = 0; i < sendingTos.size(); i++) {
std::pair<CScript, qint64>& sendingTo = sendingTos[i];
recipients.append(SendCoinsRecipient());
recipients[i].amount = sendingTo.second;
QString memo = QString::fromStdString(request.getDetails().memo());
recipients[i].label = GUIUtil::HtmlEscape(memo);
CTxDestination dest;
if (ExtractDestination(sendingTo.first, dest)) {
if (i == 0) // Tie request to first pay-to, we don't want multiple ACKs
recipients[i].paymentRequest = request;
recipients[i].address = QString::fromStdString(CBitcoinAddress(dest).ToString());
- if (fDebug)
- qDebug() << "PaymentServer::processPaymentRequest : Payment request, insecure " << recipients[i].address;
+ qDebug() << "PaymentServer::processPaymentRequest : Payment request, insecure " << recipients[i].address;
}
else {
// Insecure payments to custom bitcoin addresses are not supported
// (there is no good way to tell the user where they are paying in a way
// they'd have a chance of understanding).
emit message(tr("Payment request error"),
tr("Insecure requests to custom payment scripts unsupported"),
CClientUIInterface::MSG_ERROR);
return false;
}
}
}
return true;
}
void PaymentServer::fetchRequest(const QUrl& url)
{
QNetworkRequest netRequest;
netRequest.setAttribute(QNetworkRequest::User, "PaymentRequest");
netRequest.setUrl(url);
netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str());
netRequest.setRawHeader("Accept", BITCOIN_REQUEST_MIMETYPE);
netManager->get(netRequest);
}
void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction)
{
const payments::PaymentDetails& details = recipient.paymentRequest.getDetails();
if (!details.has_payment_url())
return;
QNetworkRequest netRequest;
netRequest.setAttribute(QNetworkRequest::User, "PaymentACK");
netRequest.setUrl(QString::fromStdString(details.payment_url()));
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, BITCOIN_PAYMENTACK_CONTENTTYPE);
netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str());
netRequest.setRawHeader("Accept", BITCOIN_PAYMENTACK_MIMETYPE);
payments::Payment payment;
payment.set_merchant_data(details.merchant_data());
payment.add_transactions(transaction.data(), transaction.size());
// Create a new refund address, or re-use:
QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant);
std::string strAccount = account.toStdString();
set<CTxDestination> refundAddresses = wallet->GetAccountAddresses(strAccount);
if (!refundAddresses.empty()) {
CScript s; s.SetDestination(*refundAddresses.begin());
payments::Output* refund_to = payment.add_refund_to();
refund_to->set_script(&s[0], s.size());
}
else {
CPubKey newKey;
if (wallet->GetKeyFromPool(newKey)) {
CKeyID keyID = newKey.GetID();
wallet->SetAddressBook(keyID, strAccount, "refund");
CScript s; s.SetDestination(keyID);
payments::Output* refund_to = payment.add_refund_to();
refund_to->set_script(&s[0], s.size());
}
else {
// This should never happen, because sending coins should have just unlocked the wallet
// and refilled the keypool
qDebug() << "PaymentServer::fetchPaymentACK : Error getting refund key, refund_to not set";
}
}
int length = payment.ByteSize();
netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length);
QByteArray serData(length, '\0');
if (payment.SerializeToArray(serData.data(), length)) {
netManager->post(netRequest, serData);
}
else {
// This should never happen, either:
qDebug() << "PaymentServer::fetchPaymentACK : Error serializing payment message";
}
}
void PaymentServer::netRequestFinished(QNetworkReply* reply)
{
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError)
{
QString msg = tr("Error communicating with %1: %2")
.arg(reply->request().url().toString())
.arg(reply->errorString());
qDebug() << "PaymentServer::netRequestFinished : " << msg;
emit message(tr("Network request error"), msg, CClientUIInterface::MSG_ERROR);
return;
}
QByteArray data = reply->readAll();
QString requestType = reply->request().attribute(QNetworkRequest::User).toString();
if (requestType == "PaymentRequest")
{
PaymentRequestPlus request;
QList<SendCoinsRecipient> recipients;
if (request.parse(data) && processPaymentRequest(request, recipients)) {
foreach (const SendCoinsRecipient& recipient, recipients) {
emit receivedPaymentRequest(recipient);
}
}
else
qDebug() << "PaymentServer::netRequestFinished : Error processing payment request";
return;
}
else if (requestType == "PaymentACK")
{
payments::PaymentACK paymentACK;
if (!paymentACK.ParseFromArray(data.data(), data.size()))
{
QString msg = tr("Bad response from server %1")
.arg(reply->request().url().toString());
qDebug() << "PaymentServer::netRequestFinished : " << msg;
emit message(tr("Network request error"), msg, CClientUIInterface::MSG_ERROR);
}
else {
emit receivedPaymentACK(GUIUtil::HtmlEscape(paymentACK.memo()));
}
}
}
void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> &errs)
{
Q_UNUSED(reply);
QString errString;
foreach (const QSslError& err, errs) {
qDebug() << "PaymentServer::reportSslErrors : " << err;
errString += err.errorString() + "\n";
}
emit message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR);
}
void PaymentServer::setOptionsModel(OptionsModel *optionsModel)
{
this->optionsModel = optionsModel;
}
void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
{
// currently we don't futher process or store the paymentACK message
emit message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL);
}
diff --git a/src/util.cpp b/src/util.cpp
index 71994587c..9ee1ad5f5 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -1,1478 +1,1487 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 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 <algorithm>
#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;
string strMiscWarning;
bool fNoListen = false;
bool fLogTimestamps = false;
CMedianFilter<int64> vTimeOffsets(200,0);
volatile bool fReopenDebugLog = 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]);
}
}
// 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);
LogPrint("rand", "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;
}
// LogPrintf() 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 LogPrint(const char* category, const char* pszFormat, ...)
{
if (category != NULL)
{
- if (!fDebug) return 0;
- const vector<string>& categories = mapMultiArgs["-debug"];
- if (find(categories.begin(), categories.end(), string(category)) == categories.end())
+ if (!fDebug)
return 0;
+
+ const vector<string>& categories = mapMultiArgs["-debug"];
+ bool allCategories = count(categories.begin(), categories.end(), string(""));
+
+ // Only look for categories, if not -debug/-debug=1 was passed,
+ // as that implies every category should be logged.
+ if (!allCategories)
+ {
+ // Category was not found (not supplied via -debug=<category>)
+ if (find(categories.begin(), categories.end(), string(category)) == categories.end())
+ return 0;
+ }
}
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;
while (true)
{
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);
LogPrintf("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;
while (true)
{
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;
while (true)
{
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)
{
while (true)
{
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);
LogPrintf("\n%s", message.c_str());
}
void PrintException(std::exception* pex, const char* pszThread)
{
std::string message = FormatException(pex, pszThread);
LogPrintf("\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);
LogPrintf("\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
}
static boost::filesystem::path pathCached[CChainParams::MAX_NETWORK_TYPES+1];
static CCriticalSection csPathCached;
const boost::filesystem::path &GetDataDir(bool fNetSpecific)
{
namespace fs = boost::filesystem;
LOCK(csPathCached);
int nNet = CChainParams::MAX_NETWORK_TYPES;
if (fNetSpecific) nNet = Params().NetworkID();
fs::path &path = pathCached[nNet];
// This can be called during exceptions by LogPrintf(), so we cache the
// value so we don't have to do memory allocations after that.
if (!path.empty())
return path;
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);
return path;
}
void ClearDatadirCache()
{
std::fill(&pathCached[0], &pathCached[CChainParams::MAX_NETWORK_TYPES+1],
boost::filesystem::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
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]);
}
// If datadir is changed in .conf file:
ClearDatadirCache();
}
boost::filesystem::path GetPidFile()
{
boost::filesystem::path pathPidFile(GetArg("-pid", "bitcoind.pid"));
if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile;
return pathPidFile;
}
#ifndef WIN32
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);
}
}
#endif
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));
#elif defined(__APPLE__) && defined(F_FULLFSYNC)
fcntl(fileno(fileout), F_FULLFSYNC, 0);
#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);
LogPrintf("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;
LogPrintf("*** %s\n", strMessage.c_str());
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
}
}
}
if (fDebug) {
BOOST_FOREACH(int64 n, vSorted)
LogPrintf("%+"PRI64d" ", n);
LogPrintf("| ");
}
LogPrintf("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);
}
LogPrintf("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)) {
LogPrintf("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)
LogPrintf("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
}
diff --git a/src/util.h b/src/util.h
index 258910e2f..257369404 100644
--- a/src/util.h
+++ b/src/util.h
@@ -1,602 +1,601 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 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 BITCOIN_UTIL_H
#define BITCOIN_UTIL_H
#include "uint256.h"
#include <stdarg.h>
#ifndef WIN32
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#endif
#include <map>
#include <list>
#include <utility>
#include <vector>
#include <string>
#include <boost/version.hpp>
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/date_time/gregorian/gregorian_types.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include "netbase.h" // for AddTimeData
typedef long long int64;
typedef unsigned long long uint64;
static const int64 COIN = 100000000;
static const int64 CENT = 1000000;
#define BEGIN(a) ((char*)&(a))
#define END(a) ((char*)&((&(a))[1]))
#define UBEGIN(a) ((unsigned char*)&(a))
#define UEND(a) ((unsigned char*)&((&(a))[1]))
#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0]))
#ifndef PRI64d
#if defined(_MSC_VER) || defined(__MSVCRT__)
#define PRI64d "I64d"
#define PRI64u "I64u"
#define PRI64x "I64x"
#else
#define PRI64d "lld"
#define PRI64u "llu"
#define PRI64x "llx"
#endif
#endif
/* Format characters for (s)size_t and ptrdiff_t */
#if defined(_MSC_VER) || defined(__MSVCRT__)
/* (s)size_t and ptrdiff_t have the same size specifier in MSVC:
http://msdn.microsoft.com/en-us/library/tcxf1dw6%28v=vs.100%29.aspx
*/
#define PRIszx "Ix"
#define PRIszu "Iu"
#define PRIszd "Id"
#define PRIpdx "Ix"
#define PRIpdu "Iu"
#define PRIpdd "Id"
#else /* C99 standard */
#define PRIszx "zx"
#define PRIszu "zu"
#define PRIszd "zd"
#define PRIpdx "tx"
#define PRIpdu "tu"
#define PRIpdd "td"
#endif
// This is needed because the foreach macro can't get over the comma in pair<t1, t2>
#define PAIRTYPE(t1, t2) std::pair<t1, t2>
// Align by increasing pointer, must have extra space at end of buffer
template <size_t nBytes, typename T>
T* alignup(T* p)
{
union
{
T* ptr;
size_t n;
} u;
u.ptr = p;
u.n = (u.n + (nBytes-1)) & ~(nBytes-1);
return u.ptr;
}
#ifdef WIN32
#define MSG_DONTWAIT 0
#ifndef S_IRUSR
#define S_IRUSR 0400
#define S_IWUSR 0200
#endif
#else
#define MAX_PATH 1024
#endif
// As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall, it is defined as 0
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
inline void MilliSleep(int64 n)
{
// Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50
// until fixed in 1.52. Use the deprecated sleep method for the broken case.
// See: https://svn.boost.org/trac/boost/ticket/7238
#if defined(HAVE_WORKING_BOOST_SLEEP_FOR)
boost::this_thread::sleep_for(boost::chrono::milliseconds(n));
#elif defined(HAVE_WORKING_BOOST_SLEEP)
boost::this_thread::sleep(boost::posix_time::milliseconds(n));
#else
//should never get here
#error missing boost sleep implementation
#endif
}
/* This GNU C extension enables the compiler to check the format string against the parameters provided.
* X is the number of the "format string" parameter, and Y is the number of the first variadic parameter.
* Parameters count from 1.
*/
#ifdef __GNUC__
#define ATTR_WARN_PRINTF(X,Y) __attribute__((format(printf,X,Y)))
#else
#define ATTR_WARN_PRINTF(X,Y)
#endif
extern std::map<std::string, std::string> mapArgs;
extern std::map<std::string, std::vector<std::string> > mapMultiArgs;
extern bool fDebug;
-extern bool fDebugNet;
extern bool fPrintToConsole;
extern bool fPrintToDebugger;
extern bool fDaemon;
extern bool fServer;
extern std::string strMiscWarning;
extern bool fNoListen;
extern bool fLogTimestamps;
extern volatile bool fReopenDebugLog;
void RandAddSeed();
void RandAddSeedPerfmon();
// Print to debug.log if -debug=category switch is given OR category is NULL.
int ATTR_WARN_PRINTF(2,3) LogPrint(const char* category, const char* pszFormat, ...);
#define LogPrintf(...) LogPrint(NULL, __VA_ARGS__)
/*
Rationale for the real_strprintf / strprintf construction:
It is not allowed to use va_start with a pass-by-reference argument.
(C++ standard, 18.7, paragraph 3). Use a dummy argument to work around this, and use a
macro to keep similar semantics.
*/
/** Overload strprintf for char*, so that GCC format type warnings can be given */
std::string ATTR_WARN_PRINTF(1,3) real_strprintf(const char *format, int dummy, ...);
/** Overload strprintf for std::string, to be able to use it with _ (translation).
* This will not support GCC format type warnings (-Wformat) so be careful.
*/
std::string real_strprintf(const std::string &format, int dummy, ...);
#define strprintf(format, ...) real_strprintf(format, 0, __VA_ARGS__)
std::string vstrprintf(const char *format, va_list ap);
bool ATTR_WARN_PRINTF(1,2) error(const char *format, ...);
void LogException(std::exception* pex, const char* pszThread);
void PrintException(std::exception* pex, const char* pszThread);
void PrintExceptionContinue(std::exception* pex, const char* pszThread);
void ParseString(const std::string& str, char c, std::vector<std::string>& v);
std::string FormatMoney(int64 n, bool fPlus=false);
bool ParseMoney(const std::string& str, int64& nRet);
bool ParseMoney(const char* pszIn, int64& nRet);
std::vector<unsigned char> ParseHex(const char* psz);
std::vector<unsigned char> ParseHex(const std::string& str);
bool IsHex(const std::string& str);
std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = NULL);
std::string DecodeBase64(const std::string& str);
std::string EncodeBase64(const unsigned char* pch, size_t len);
std::string EncodeBase64(const std::string& str);
std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid = NULL);
std::string DecodeBase32(const std::string& str);
std::string EncodeBase32(const unsigned char* pch, size_t len);
std::string EncodeBase32(const std::string& str);
void ParseParameters(int argc, const char*const argv[]);
bool WildcardMatch(const char* psz, const char* mask);
bool WildcardMatch(const std::string& str, const std::string& mask);
void FileCommit(FILE *fileout);
int GetFilesize(FILE* file);
bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest);
boost::filesystem::path GetDefaultDataDir();
const boost::filesystem::path &GetDataDir(bool fNetSpecific = true);
boost::filesystem::path GetConfigFile();
boost::filesystem::path GetPidFile();
#ifndef WIN32
void CreatePidFile(const boost::filesystem::path &path, pid_t pid);
#endif
void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet, std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet);
#ifdef WIN32
boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
boost::filesystem::path GetTempPath();
void ShrinkDebugFile();
int GetRandInt(int nMax);
uint64 GetRand(uint64 nMax);
uint256 GetRandHash();
int64 GetTime();
void SetMockTime(int64 nMockTimeIn);
int64 GetAdjustedTime();
int64 GetTimeOffset();
std::string FormatFullVersion();
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments);
void AddTimeData(const CNetAddr& ip, int64 nTime);
void runCommand(std::string strCommand);
inline std::string i64tostr(int64 n)
{
return strprintf("%"PRI64d, n);
}
inline std::string itostr(int n)
{
return strprintf("%d", n);
}
inline int64 atoi64(const char* psz)
{
#ifdef _MSC_VER
return _atoi64(psz);
#else
return strtoll(psz, NULL, 10);
#endif
}
inline int64 atoi64(const std::string& str)
{
#ifdef _MSC_VER
return _atoi64(str.c_str());
#else
return strtoll(str.c_str(), NULL, 10);
#endif
}
inline int atoi(const std::string& str)
{
return atoi(str.c_str());
}
inline int roundint(double d)
{
return (int)(d > 0 ? d + 0.5 : d - 0.5);
}
inline int64 roundint64(double d)
{
return (int64)(d > 0 ? d + 0.5 : d - 0.5);
}
inline int64 abs64(int64 n)
{
return (n >= 0 ? n : -n);
}
template<typename T>
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
{
std::string rv;
static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
rv.reserve((itend-itbegin)*3);
for(T it = itbegin; it < itend; ++it)
{
unsigned char val = (unsigned char)(*it);
if(fSpaces && it != itbegin)
rv.push_back(' ');
rv.push_back(hexmap[val>>4]);
rv.push_back(hexmap[val&15]);
}
return rv;
}
template<typename T>
inline std::string HexStr(const T& vch, bool fSpaces=false)
{
return HexStr(vch.begin(), vch.end(), fSpaces);
}
template<typename T>
void PrintHex(const T pbegin, const T pend, const char* pszFormat="%s", bool fSpaces=true)
{
LogPrintf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str());
}
inline void PrintHex(const std::vector<unsigned char>& vch, const char* pszFormat="%s", bool fSpaces=true)
{
LogPrintf(pszFormat, HexStr(vch, fSpaces).c_str());
}
inline int64 GetPerformanceCounter()
{
int64 nCounter = 0;
#ifdef WIN32
QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
#else
timeval t;
gettimeofday(&t, NULL);
nCounter = (int64) t.tv_sec * 1000000 + t.tv_usec;
#endif
return nCounter;
}
inline int64 GetTimeMillis()
{
return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) -
boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds();
}
inline int64 GetTimeMicros()
{
return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) -
boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds();
}
inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime)
{
time_t n = nTime;
struct tm* ptmTime = gmtime(&n);
char pszTime[200];
strftime(pszTime, sizeof(pszTime), pszFormat, ptmTime);
return pszTime;
}
template<typename T>
void skipspaces(T& it)
{
while (isspace(*it))
++it;
}
inline bool IsSwitchChar(char c)
{
#ifdef WIN32
return c == '-' || c == '/';
#else
return c == '-';
#endif
}
/**
* Return string argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param default (e.g. "1")
* @return command-line argument or default value
*/
std::string GetArg(const std::string& strArg, const std::string& strDefault);
/**
* Return integer argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param default (e.g. 1)
* @return command-line argument (0 if invalid number) or default value
*/
int64 GetArg(const std::string& strArg, int64 nDefault);
/**
* Return boolean argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param default (true or false)
* @return command-line argument or default value
*/
bool GetBoolArg(const std::string& strArg, bool fDefault);
/**
* Set an argument if it doesn't already have a value
*
* @param strArg Argument to set (e.g. "-foo")
* @param strValue Value (e.g. "1")
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetArg(const std::string& strArg, const std::string& strValue);
/**
* Set a boolean argument if it doesn't already have a value
*
* @param strArg Argument to set (e.g. "-foo")
* @param fValue Value (e.g. false)
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetBoolArg(const std::string& strArg, bool fValue);
/**
* MWC RNG of George Marsaglia
* This is intended to be fast. It has a period of 2^59.3, though the
* least significant 16 bits only have a period of about 2^30.1.
*
* @return random value
*/
extern uint32_t insecure_rand_Rz;
extern uint32_t insecure_rand_Rw;
static inline uint32_t insecure_rand(void)
{
insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16);
insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16);
return (insecure_rand_Rw << 16) + insecure_rand_Rz;
}
/**
* Seed insecure_rand using the random pool.
* @param Deterministic Use a determinstic seed
*/
void seed_insecure_rand(bool fDeterministic=false);
/**
* Timing-attack-resistant comparison.
* Takes time proportional to length
* of first argument.
*/
template <typename T>
bool TimingResistantEqual(const T& a, const T& b)
{
if (b.size() == 0) return a.size() == 0;
size_t accumulator = a.size() ^ b.size();
for (size_t i = 0; i < a.size(); i++)
accumulator |= a[i] ^ b[i%b.size()];
return accumulator == 0;
}
/** Median filter over a stream of values.
* Returns the median of the last N numbers
*/
template <typename T> class CMedianFilter
{
private:
std::vector<T> vValues;
std::vector<T> vSorted;
unsigned int nSize;
public:
CMedianFilter(unsigned int size, T initial_value):
nSize(size)
{
vValues.reserve(size);
vValues.push_back(initial_value);
vSorted = vValues;
}
void input(T value)
{
if(vValues.size() == nSize)
{
vValues.erase(vValues.begin());
}
vValues.push_back(value);
vSorted.resize(vValues.size());
std::copy(vValues.begin(), vValues.end(), vSorted.begin());
std::sort(vSorted.begin(), vSorted.end());
}
T median() const
{
int size = vSorted.size();
assert(size>0);
if(size & 1) // Odd number of elements
{
return vSorted[size/2];
}
else // Even number of elements
{
return (vSorted[size/2-1] + vSorted[size/2]) / 2;
}
}
int size() const
{
return vValues.size();
}
std::vector<T> sorted () const
{
return vSorted;
}
};
#ifdef WIN32
inline void SetThreadPriority(int nPriority)
{
SetThreadPriority(GetCurrentThread(), nPriority);
}
#else
// PRIO_MAX is not defined on Solaris
#ifndef PRIO_MAX
#define PRIO_MAX 20
#endif
#define THREAD_PRIORITY_LOWEST PRIO_MAX
#define THREAD_PRIORITY_BELOW_NORMAL 2
#define THREAD_PRIORITY_NORMAL 0
#define THREAD_PRIORITY_ABOVE_NORMAL (-2)
inline void SetThreadPriority(int nPriority)
{
// It's unclear if it's even possible to change thread priorities on Linux,
// but we really and truly need it for the generation threads.
#ifdef PRIO_THREAD
setpriority(PRIO_THREAD, 0, nPriority);
#else
setpriority(PRIO_PROCESS, 0, nPriority);
#endif
}
#endif
void RenameThread(const char* name);
inline uint32_t ByteReverse(uint32_t value)
{
value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8);
return (value<<16) | (value>>16);
}
// Standard wrapper for do-something-forever thread functions.
// "Forever" really means until the thread is interrupted.
// Use it like:
// new boost::thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 900000));
// or maybe:
// boost::function<void()> f = boost::bind(&FunctionWithArg, argument);
// threadGroup.create_thread(boost::bind(&LoopForever<boost::function<void()> >, "nothing", f, milliseconds));
template <typename Callable> void LoopForever(const char* name, Callable func, int64 msecs)
{
std::string s = strprintf("bitcoin-%s", name);
RenameThread(s.c_str());
LogPrintf("%s thread start\n", name);
try
{
while (1)
{
MilliSleep(msecs);
func();
}
}
catch (boost::thread_interrupted)
{
LogPrintf("%s thread stop\n", name);
throw;
}
catch (std::exception& e) {
PrintException(&e, name);
}
catch (...) {
PrintException(NULL, name);
}
}
// .. and a wrapper that just calls func once
template <typename Callable> void TraceThread(const char* name, Callable func)
{
std::string s = strprintf("bitcoin-%s", name);
RenameThread(s.c_str());
try
{
LogPrintf("%s thread start\n", name);
func();
LogPrintf("%s thread exit\n", name);
}
catch (boost::thread_interrupted)
{
LogPrintf("%s thread interrupt\n", name);
throw;
}
catch (std::exception& e) {
PrintException(&e, name);
}
catch (...) {
PrintException(NULL, name);
}
}
#endif

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 23:56 (1 d, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5866180
Default Alt Text
(308 KB)

Event Timeline