diff options
Diffstat (limited to 'src')
109 files changed, 3036 insertions, 1413 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000000..c45f62adf5 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,3 @@ + +test_bitcoin + diff --git a/src/Makefile.am b/src/Makefile.am index 3697b9ff22..49249fedc7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,7 @@ +include Makefile.include AM_CPPFLAGS = $(INCLUDES) -I$(top_builddir)/src/obj \ -I$(top_srcdir)/src/leveldb/include -I$(top_srcdir)/src/leveldb/helpers \ - $(BOOST_INCLUDES) + -I$(builddir) $(BOOST_INCLUDES) $(BDB_CPPFLAGS) AM_LDFLAGS = $(PTHREAD_CFLAGS) noinst_LIBRARIES = libbitcoin.a @@ -30,7 +31,7 @@ obj/build.h: FORCE $(abs_top_srcdir) version.o: obj/build.h -libbitcoin_a_SOURCES = addrman.cpp alert.cpp bitcoinrpc.cpp bloom.cpp \ +libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp bitcoinrpc.cpp bloom.cpp \ chainparams.cpp checkpoints.cpp core.cpp crypter.cpp db.cpp hash.cpp \ init.cpp key.cpp keystore.cpp leveldb.cpp main.cpp miner.cpp \ netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \ @@ -47,13 +48,23 @@ bitcoind_LDADD = libbitcoin.a leveldb/libleveldb.a leveldb/libmemenv.a \ bitcoind_SOURCES = bitcoind.cpp # -leveldb/libleveldb.a: - @echo "Building LevelDB ..." && cd leveldb && CXX="$(CXX)" CC="$(CC)" \ - PLATFORM=$(TARGET_OS) AR="$(AR)" $(MAKE) $(LEVELDB_TARGET_FLAGS) OPT="$(CXXFLAGS) $(CPPFLAGS)" libleveldb.a +if TARGET_WINDOWS +bitcoind_SOURCES += bitcoind-res.rc +endif -leveldb/libmemenv.a: - @echo "Building LevelDB ..." && cd leveldb && CXX="$(CXX)" CC="$(CC)" \ - PLATFORM=$(TARGET_OS) AR="$(AR)" $(MAKE) $(LEVELDB_TARGET_FLAGS) OPT="$(CXXFLAGS) $(CPPFLAGS)" libmemenv.a +AM_CPPFLAGS += $(BDB_CPPFLAGS) +bitcoind_LDADD += $(BDB_LIBS) + +leveldb/libleveldb.a: leveldb/libmemenv.a + +leveldb/%.a: + @echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ + CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ + OPT="$(CXXFLAGS) $(CPPFLAGS)" + +qt/bitcoinstrings.cpp: $(libbitcoin_a_SOURCES) + @test -n $(XGETTEXT) || echo "xgettext is required for updating translations" + @cd $(top_srcdir); XGETTEXT=$(XGETTEXT) share/qt/extract_strings_qt.py CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno diff --git a/src/Makefile.include b/src/Makefile.include index 231888e49f..352471061d 100644 --- a/src/Makefile.include +++ b/src/Makefile.include @@ -1,4 +1,5 @@ -.PHONY: FORCE +# Helper for rules and subdir Makefiles to find parent targets. +# Flags and other non-target variables should not be set here. LIBBITCOIN=$(top_builddir)/src/libbitcoin.a LIBLEVELDB=$(top_builddir)/src/leveldb/libleveldb.a @@ -46,3 +47,19 @@ moc_%.cpp: %.h %.pb.cc %.pb.h: %.proto test -f $(PROTOC) && $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $(<D) $<) || \ echo error: could not build $@ + +%.json.h: %.json + @$(MKDIR_P) $(@D) + @echo "namespace json_tests{" > $@ + @echo "static unsigned const char $(*F)[] = {" >> $@ + @$(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' >> $@ + @echo "};};" >> $@ + @echo "Generated $@" + +%.raw.h: %.raw + @$(MKDIR_P) $(@D) + @echo "namespace alert_tests{" > $@ + @echo "static unsigned const char $(*F)[] = {" >> $@ + @$(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' >> $@ + @echo "};};" >> $@ + @echo "Generated $@" diff --git a/src/addrman.cpp b/src/addrman.cpp index 780edde90f..731ff2abed 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -261,8 +261,6 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin) void CAddrMan::Good_(const CService &addr, int64 nTime) { -// printf("Good: addr=%s\n", addr.ToString().c_str()); - int nId; CAddrInfo *pinfo = Find(addr, &nId); @@ -304,7 +302,7 @@ void CAddrMan::Good_(const CService &addr, int64 nTime) // TODO: maybe re-add the node, but for now, just bail out if (nUBucket == -1) return; - printf("Moving %s to tried\n", addr.ToString().c_str()); + LogPrint("addrman", "Moving %s to tried\n", addr.ToString().c_str()); // move nId to the tried tables MakeTried(info, nId, nUBucket); @@ -351,7 +349,6 @@ bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePen } else { pinfo = Create(addr, source, &nId); pinfo->nTime = max((int64)0, (int64)pinfo->nTime - nTimePenalty); -// printf("Added %s [nTime=%fhr]\n", pinfo->ToString().c_str(), (GetAdjustedTime() - pinfo->nTime) / 3600.0); nNew++; fNew = true; } diff --git a/src/addrman.h b/src/addrman.h index 7af6afd78f..081543ace4 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -403,7 +403,7 @@ public: LOCK(cs); int err; if ((err=Check_())) - printf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err); + LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err); } #endif } @@ -419,7 +419,7 @@ public: Check(); } if (fRet) - printf("Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew); + LogPrint("addr", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew); return fRet; } @@ -435,7 +435,7 @@ public: Check(); } if (nAdd) - printf("Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew); + LogPrint("addr", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew); return nAdd > 0; } diff --git a/src/alert.cpp b/src/alert.cpp index e00847aadb..c9cde17955 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -76,7 +76,7 @@ std::string CUnsignedAlert::ToString() const void CUnsignedAlert::print() const { - printf("%s", ToString().c_str()); + LogPrintf("%s", ToString().c_str()); } void CAlert::SetNull() @@ -200,13 +200,13 @@ bool CAlert::ProcessAlert(bool fThread) const CAlert& alert = (*mi).second; if (Cancels(alert)) { - printf("cancelling alert %d\n", alert.nID); + LogPrint("alert", "cancelling alert %d\n", alert.nID); uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); mapAlerts.erase(mi++); } else if (!alert.IsInEffect()) { - printf("expiring alert %d\n", alert.nID); + LogPrint("alert", "expiring alert %d\n", alert.nID); uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); mapAlerts.erase(mi++); } @@ -220,7 +220,7 @@ bool CAlert::ProcessAlert(bool fThread) const CAlert& alert = item.second; if (alert.Cancels(*this)) { - printf("alert already cancelled by %d\n", alert.nID); + LogPrint("alert", "alert already cancelled by %d\n", alert.nID); return false; } } @@ -258,6 +258,6 @@ bool CAlert::ProcessAlert(bool fThread) } } - printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); + LogPrint("alert", "accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); return true; } diff --git a/src/allocators.cpp b/src/allocators.cpp new file mode 100644 index 0000000000..b239b623d8 --- /dev/null +++ b/src/allocators.cpp @@ -0,0 +1,64 @@ +// 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 "allocators.h" + +#ifdef WIN32 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include <windows.h> +// This is used to attempt to keep keying material out of swap +// Note that VirtualLock does not provide this as a guarantee on Windows, +// but, in practice, memory that has been VirtualLock'd almost never gets written to +// the pagefile except in rare circumstances where memory is extremely low. +#else +#include <sys/mman.h> +#include <limits.h> // for PAGESIZE +#include <unistd.h> // for sysconf +#endif + +/** Determine system page size in bytes */ +static inline size_t GetSystemPageSize() +{ + size_t page_size; +#if defined(WIN32) + SYSTEM_INFO sSysInfo; + GetSystemInfo(&sSysInfo); + page_size = sSysInfo.dwPageSize; +#elif defined(PAGESIZE) // defined in limits.h + page_size = PAGESIZE; +#else // assume some POSIX OS + page_size = sysconf(_SC_PAGESIZE); +#endif + return page_size; +} + +bool MemoryPageLocker::Lock(const void *addr, size_t len) +{ +#ifdef WIN32 + return VirtualLock(const_cast<void*>(addr), len); +#else + return mlock(addr, len) == 0; +#endif +} + +bool MemoryPageLocker::Unlock(const void *addr, size_t len) +{ +#ifdef WIN32 + return VirtualUnlock(const_cast<void*>(addr), len); +#else + return munlock(addr, len) == 0; +#endif +} + +LockedPageManager::LockedPageManager() : LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize()) +{ +} + diff --git a/src/allocators.h b/src/allocators.h index 85af8fe376..fd6f51b27e 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -11,25 +11,6 @@ #include <map> #include <openssl/crypto.h> // for OPENSSL_cleanse() -#ifdef WIN32 -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0501 -#define WIN32_LEAN_AND_MEAN 1 -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include <windows.h> -// This is used to attempt to keep keying material out of swap -// Note that VirtualLock does not provide this as a guarantee on Windows, -// but, in practice, memory that has been VirtualLock'd almost never gets written to -// the pagefile except in rare circumstances where memory is extremely low. -#else -#include <sys/mman.h> -#include <limits.h> // for PAGESIZE -#include <unistd.h> // for sysconf -#endif /** * Thread-safe class to keep track of locked (ie, non-swappable) memory pages. @@ -115,21 +96,6 @@ private: Histogram histogram; }; -/** Determine system page size in bytes */ -static inline size_t GetSystemPageSize() -{ - size_t page_size; -#if defined(WIN32) - SYSTEM_INFO sSysInfo; - GetSystemInfo(&sSysInfo); - page_size = sSysInfo.dwPageSize; -#elif defined(PAGESIZE) // defined in limits.h - page_size = PAGESIZE; -#else // assume some POSIX OS - page_size = sysconf(_SC_PAGESIZE); -#endif - return page_size; -} /** * OS-dependent memory page locking/unlocking. @@ -141,25 +107,11 @@ public: /** Lock memory pages. * addr and len must be a multiple of the system page size */ - bool Lock(const void *addr, size_t len) - { -#ifdef WIN32 - return VirtualLock(const_cast<void*>(addr), len); -#else - return mlock(addr, len) == 0; -#endif - } + bool Lock(const void *addr, size_t len); /** Unlock memory pages. * addr and len must be a multiple of the system page size */ - bool Unlock(const void *addr, size_t len) - { -#ifdef WIN32 - return VirtualUnlock(const_cast<void*>(addr), len); -#else - return munlock(addr, len) == 0; -#endif - } + bool Unlock(const void *addr, size_t len); }; /** @@ -171,9 +123,7 @@ class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker> public: static LockedPageManager instance; // instantiated in util.cpp private: - LockedPageManager(): - LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize()) - {} + LockedPageManager(); }; // diff --git a/src/bitcoind-res.rc b/src/bitcoind-res.rc new file mode 100644 index 0000000000..202b7ab352 --- /dev/null +++ b/src/bitcoind-res.rc @@ -0,0 +1,36 @@ +#include <windows.h> // needed for VERSIONINFO +#include "clientversion.h" // holds the needed client version information + +#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION,CLIENT_VERSION_BUILD +#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#define VER_FILEVERSION VER_PRODUCTVERSION +#define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR +#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " The Bitcoin developers" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "Bitcoin" + VALUE "FileDescription", "Bitcoind (OSS daemon/client for Bitcoin)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "bitcoind" + VALUE "LegalCopyright", COPYRIGHT_STR + VALUE "LegalTrademarks1", "Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." + VALUE "OriginalFilename", "bitcoind.exe" + VALUE "ProductName", "Bitcoind" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 02e4e7d6e7..add3b4e1f0 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -9,15 +9,18 @@ void DetectShutdownThread(boost::thread_group* threadGroup) { - bool shutdown = ShutdownRequested(); + bool fShutdown = ShutdownRequested(); // Tell the main threads to shutdown. - while (!shutdown) + while (!fShutdown) { MilliSleep(200); - shutdown = ShutdownRequested(); + fShutdown = ShutdownRequested(); } if (threadGroup) + { threadGroup->interrupt_all(); + threadGroup->join_all(); + } } ////////////////////////////////////////////////////////////////////////////// @@ -66,6 +69,7 @@ bool AppInit(int argc, char* argv[]) } // Command-line RPC + bool fCommandLine = false; for (int i = 1; i < argc; i++) if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "bitcoin:")) fCommandLine = true; @@ -107,10 +111,16 @@ bool AppInit(int argc, char* argv[]) } catch (...) { PrintExceptionContinue(NULL, "AppInit()"); } - if (!fRet) { + + if (!fRet) + { if (detectShutdownThread) detectShutdownThread->interrupt(); + threadGroup.interrupt_all(); + // threadGroup.join_all(); was left out intentionally here, because we didn't re-test all of + // the startup-failure cases to make sure they don't result in a hang due to some + // thread-blocking-waiting-for-another-thread-during-startup case } if (detectShutdownThread) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 47c7383564..798660dff3 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -113,6 +113,34 @@ std::string HexBits(unsigned int nBits) return HexStr(BEGIN(uBits.cBits), END(uBits.cBits)); } +uint256 ParseHashV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) // Note: IsHex("") is false + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + uint256 result; + result.SetHex(strHex); + return result; +} +uint256 ParseHashO(const Object& o, string strKey) +{ + return ParseHashV(find_value(o, strKey), strKey); +} +vector<unsigned char> ParseHexV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + return ParseHex(strHex); +} +vector<unsigned char> ParseHexO(const Object& o, string strKey) +{ + return ParseHexV(find_value(o, strKey), strKey); +} /// @@ -132,6 +160,9 @@ string CRPCTable::help(string strCommand) const continue; if (strCommand != "" && strMethod != strCommand) continue; + if (pcmd->reqWallet && !pwalletMain) + continue; + try { Array params; @@ -190,75 +221,79 @@ Value stop(const Array& params, bool fHelp) static const CRPCCommand vRPCCommands[] = -{ // name actor (function) okSafeMode threadSafe - // ------------------------ ----------------------- ---------- ---------- - { "help", &help, true, true }, - { "stop", &stop, true, true }, - { "getblockcount", &getblockcount, true, false }, - { "getbestblockhash", &getbestblockhash, true, false }, - { "getconnectioncount", &getconnectioncount, true, false }, - { "getpeerinfo", &getpeerinfo, true, false }, - { "addnode", &addnode, true, true }, - { "getaddednodeinfo", &getaddednodeinfo, true, true }, - { "getdifficulty", &getdifficulty, true, false }, - { "getgenerate", &getgenerate, true, false }, - { "setgenerate", &setgenerate, true, false }, - { "gethashespersec", &gethashespersec, true, false }, - { "getinfo", &getinfo, true, false }, - { "getmininginfo", &getmininginfo, true, false }, - { "getnewaddress", &getnewaddress, true, false }, - { "getaccountaddress", &getaccountaddress, true, false }, - { "getrawchangeaddress", &getrawchangeaddress, true, false }, - { "setaccount", &setaccount, true, false }, - { "getaccount", &getaccount, false, false }, - { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, - { "sendtoaddress", &sendtoaddress, false, false }, - { "getreceivedbyaddress", &getreceivedbyaddress, false, false }, - { "getreceivedbyaccount", &getreceivedbyaccount, false, false }, - { "listreceivedbyaddress", &listreceivedbyaddress, false, false }, - { "listreceivedbyaccount", &listreceivedbyaccount, false, false }, - { "backupwallet", &backupwallet, true, false }, - { "keypoolrefill", &keypoolrefill, true, false }, - { "walletpassphrase", &walletpassphrase, true, false }, - { "walletpassphrasechange", &walletpassphrasechange, false, false }, - { "walletlock", &walletlock, true, false }, - { "encryptwallet", &encryptwallet, false, false }, - { "validateaddress", &validateaddress, true, false }, - { "getbalance", &getbalance, false, false }, - { "move", &movecmd, false, false }, - { "sendfrom", &sendfrom, false, false }, - { "sendmany", &sendmany, false, false }, - { "addmultisigaddress", &addmultisigaddress, false, false }, - { "createmultisig", &createmultisig, true, true }, - { "getrawmempool", &getrawmempool, true, false }, - { "getblock", &getblock, false, false }, - { "getblockhash", &getblockhash, false, false }, - { "gettransaction", &gettransaction, false, false }, - { "listtransactions", &listtransactions, false, false }, - { "listaddressgroupings", &listaddressgroupings, false, false }, - { "signmessage", &signmessage, false, false }, - { "verifymessage", &verifymessage, false, false }, - { "getwork", &getwork, true, false }, - { "listaccounts", &listaccounts, false, false }, - { "settxfee", &settxfee, false, false }, - { "getblocktemplate", &getblocktemplate, true, false }, - { "submitblock", &submitblock, false, false }, - { "listsinceblock", &listsinceblock, false, false }, - { "dumpprivkey", &dumpprivkey, true, false }, - { "dumpwallet", &dumpwallet, true, false }, - { "importprivkey", &importprivkey, false, false }, - { "importwallet", &importwallet, false, false }, - { "listunspent", &listunspent, false, false }, - { "getrawtransaction", &getrawtransaction, false, false }, - { "createrawtransaction", &createrawtransaction, false, false }, - { "decoderawtransaction", &decoderawtransaction, false, false }, - { "signrawtransaction", &signrawtransaction, false, false }, - { "sendrawtransaction", &sendrawtransaction, false, false }, - { "gettxoutsetinfo", &gettxoutsetinfo, true, false }, - { "gettxout", &gettxout, true, false }, - { "lockunspent", &lockunspent, false, false }, - { "listlockunspent", &listlockunspent, false, false }, - { "verifychain", &verifychain, true, false }, +{ // name actor (function) okSafeMode threadSafe reqWallet + // ------------------------ ----------------------- ---------- ---------- --------- + { "help", &help, true, true, false }, + { "stop", &stop, true, true, false }, + { "getblockcount", &getblockcount, true, false, false }, + { "getbestblockhash", &getbestblockhash, true, false, false }, + { "getconnectioncount", &getconnectioncount, true, false, false }, + { "getpeerinfo", &getpeerinfo, true, false, false }, + { "ping", &ping, true, false, false }, + { "addnode", &addnode, true, true, false }, + { "getaddednodeinfo", &getaddednodeinfo, true, true, false }, + { "getnettotals", &getnettotals, true, true, false }, + { "getdifficulty", &getdifficulty, true, false, false }, + { "getnetworkhashps", &getnetworkhashps, true, false, false }, + { "getgenerate", &getgenerate, true, false, false }, + { "setgenerate", &setgenerate, true, false, true }, + { "gethashespersec", &gethashespersec, true, false, false }, + { "getinfo", &getinfo, true, false, false }, + { "getmininginfo", &getmininginfo, true, false, false }, + { "getnewaddress", &getnewaddress, true, false, true }, + { "getaccountaddress", &getaccountaddress, true, false, true }, + { "getrawchangeaddress", &getrawchangeaddress, true, false, true }, + { "setaccount", &setaccount, true, false, true }, + { "getaccount", &getaccount, false, false, true }, + { "getaddressesbyaccount", &getaddressesbyaccount, true, false, true }, + { "sendtoaddress", &sendtoaddress, false, false, true }, + { "getreceivedbyaddress", &getreceivedbyaddress, false, false, true }, + { "getreceivedbyaccount", &getreceivedbyaccount, false, false, true }, + { "listreceivedbyaddress", &listreceivedbyaddress, false, false, true }, + { "listreceivedbyaccount", &listreceivedbyaccount, false, false, true }, + { "backupwallet", &backupwallet, true, false, true }, + { "keypoolrefill", &keypoolrefill, true, false, true }, + { "walletpassphrase", &walletpassphrase, true, false, true }, + { "walletpassphrasechange", &walletpassphrasechange, false, false, true }, + { "walletlock", &walletlock, true, false, true }, + { "encryptwallet", &encryptwallet, false, false, true }, + { "validateaddress", &validateaddress, true, false, false }, + { "getbalance", &getbalance, false, false, true }, + { "move", &movecmd, false, false, true }, + { "sendfrom", &sendfrom, false, false, true }, + { "sendmany", &sendmany, false, false, true }, + { "addmultisigaddress", &addmultisigaddress, false, false, true }, + { "createmultisig", &createmultisig, true, true , false }, + { "getrawmempool", &getrawmempool, true, false, false }, + { "getblock", &getblock, false, false, false }, + { "getblockhash", &getblockhash, false, false, false }, + { "gettransaction", &gettransaction, false, false, true }, + { "listtransactions", &listtransactions, false, false, true }, + { "listaddressgroupings", &listaddressgroupings, false, false, true }, + { "signmessage", &signmessage, false, false, true }, + { "verifymessage", &verifymessage, false, false, false }, + { "getwork", &getwork, true, false, true }, + { "listaccounts", &listaccounts, false, false, true }, + { "settxfee", &settxfee, false, false, true }, + { "getblocktemplate", &getblocktemplate, true, false, false }, + { "submitblock", &submitblock, false, false, false }, + { "listsinceblock", &listsinceblock, false, false, true }, + { "dumpprivkey", &dumpprivkey, true, false, true }, + { "dumpwallet", &dumpwallet, true, false, true }, + { "importprivkey", &importprivkey, false, false, true }, + { "importwallet", &importwallet, false, false, true }, + { "listunspent", &listunspent, false, false, true }, + { "getrawtransaction", &getrawtransaction, false, false, false }, + { "createrawtransaction", &createrawtransaction, false, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false, false }, + { "decodescript", &decodescript, false, false, false }, + { "signrawtransaction", &signrawtransaction, false, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false, false }, + { "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, + { "gettxout", &gettxout, true, false, false }, + { "lockunspent", &lockunspent, false, false, true }, + { "listlockunspent", &listlockunspent, false, false, true }, + { "verifychain", &verifychain, true, false, false }, }; CRPCTable::CRPCTable() @@ -767,12 +802,12 @@ void StartRPCThreads() filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); - else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); + else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); - else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); + else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); @@ -847,7 +882,8 @@ void StopRPCThreads() deadlineTimers.clear(); rpc_io_service->stop(); - rpc_worker_group->join_all(); + if (rpc_worker_group != NULL) + rpc_worker_group->join_all(); delete rpc_worker_group; rpc_worker_group = NULL; delete rpc_ssl_context; rpc_ssl_context = NULL; delete rpc_io_service; rpc_io_service = NULL; @@ -902,7 +938,7 @@ void JSONRequest::parse(const Value& valRequest) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); if (strMethod != "getwork" && strMethod != "getblocktemplate") - printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + LogPrint("rpc", "ThreadRPCServer method=%s\n", strMethod.c_str()); // Parse params Value valParams = find_value(request, "params"); @@ -976,10 +1012,10 @@ void ServiceConnection(AcceptedConnection *conn) } if (!HTTPAuthorized(mapHeaders)) { - printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str()); + LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str()); /* Deter brute-forcing short passwords. - If this results in a DOS the user really - shouldn't have their RPC port exposed.*/ + If this results in a DoS the user really + shouldn't have their RPC port exposed. */ if (mapArgs["-rpcpassword"].size() < 20) MilliSleep(250); @@ -1035,6 +1071,8 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s const CRPCCommand *pcmd = tableRPC[strMethod]; if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); + if (pcmd->reqWallet && !pwalletMain) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); // Observe safe mode string strWarning = GetWarnings("rpc"); @@ -1049,7 +1087,10 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s { if (pcmd->threadSafe) result = pcmd->actor(params, false); - else { + else if (!pwalletMain) { + LOCK(cs_main); + result = pcmd->actor(params, false); + } else { LOCK2(cs_main, pwalletMain->cs_wallet); result = pcmd->actor(params, false); } @@ -1159,6 +1200,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]); if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]); if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]); if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]); if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]); @@ -1194,6 +1237,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]); if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true); if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true); + if (strMethod == "sendrawtransaction" && n > 1) ConvertTo<bool>(params[1], true); if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]); if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]); @@ -1290,7 +1334,7 @@ int main(int argc, char *argv[]) { if (argc >= 2 && string(argv[1]) == "-server") { - printf("server ready\n"); + LogPrintf("server ready\n"); ThreadRPCServer(NULL); } else diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 1aa2e70d26..1cad12f14a 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -104,6 +104,7 @@ public: rpcfn_type actor; bool okSafeMode; bool threadSafe; + bool reqWallet; }; /** @@ -130,6 +131,15 @@ public: extern const CRPCTable tableRPC; +// +// Utilities: convert hex-encoded Values +// (throws error if not hex). +// +extern uint256 ParseHashV(const json_spirit::Value& v, std::string strName); +extern uint256 ParseHashO(const json_spirit::Object& o, std::string strKey); +extern std::vector<unsigned char> ParseHexV(const json_spirit::Value& v, std::string strName); +extern std::vector<unsigned char> ParseHexO(const json_spirit::Object& o, std::string strKey); + extern void InitRPCMining(); extern void ShutdownRPCMining(); @@ -143,8 +153,10 @@ extern void EnsureWalletIsUnlocked(); extern json_spirit::Value getconnectioncount(const json_spirit::Array& params, bool fHelp); // in rpcnet.cpp extern json_spirit::Value getpeerinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value ping(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value addnode(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getaddednodeinfo(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp); @@ -153,6 +165,7 @@ extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fH extern json_spirit::Value getgenerate(const json_spirit::Array& params, bool fHelp); // in rpcmining.cpp extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getnetworkhashps(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gethashespersec(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getmininginfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getwork(const json_spirit::Array& params, bool fHelp); @@ -198,6 +211,7 @@ extern json_spirit::Value lockunspent(const json_spirit::Array& params, bool fHe extern json_spirit::Value listlockunspent(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value createrawtransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value decoderawtransaction(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value decodescript(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value signrawtransaction(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendrawtransaction(const json_spirit::Array& params, bool fHelp); diff --git a/src/chainparams.h b/src/chainparams.h index ce3c14306d..3f99b7eb06 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -68,7 +68,7 @@ public: virtual const vector<CAddress>& FixedSeeds() const = 0; int RPCPort() const { return nRPCPort; } protected: - CChainParams() {}; + CChainParams() {} uint256 hashGenesisBlock; MessageStartChars pchMessageStart; diff --git a/src/compat.h b/src/compat.h index 9caf5e4810..b126df901a 100644 --- a/src/compat.h +++ b/src/compat.h @@ -6,11 +6,17 @@ #define _BITCOIN_COMPAT_H #ifdef WIN32 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif #define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif +#ifdef FD_SETSIZE +#undef FD_SETSIZE // prevent redefinition compiler warning +#endif #define FD_SETSIZE 1024 // max number of fds in fd_set #include <winsock2.h> #include <ws2tcpip.h> diff --git a/src/core.cpp b/src/core.cpp index afba0959cf..99b5c6641a 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -13,7 +13,7 @@ std::string COutPoint::ToString() const void COutPoint::print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, unsigned int nSequenceIn) @@ -47,7 +47,7 @@ std::string CTxIn::ToString() const void CTxIn::print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } CTxOut::CTxOut(int64 nValueIn, CScript scriptPubKeyIn) @@ -70,7 +70,7 @@ std::string CTxOut::ToString() const void CTxOut::print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } uint256 CTransaction::GetHash() const @@ -125,7 +125,7 @@ std::string CTransaction::ToString() const void CTransaction::print() const { - printf("%s", ToString().c_str()); + LogPrintf("%s", ToString().c_str()); } // Amount compression: @@ -282,7 +282,7 @@ uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMer void CBlock::print() const { - printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n", + LogPrintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n", GetHash().ToString().c_str(), nVersion, hashPrevBlock.ToString().c_str(), @@ -291,11 +291,11 @@ void CBlock::print() const vtx.size()); for (unsigned int i = 0; i < vtx.size(); i++) { - printf(" "); + LogPrintf(" "); vtx[i].print(); } - printf(" vMerkleTree: "); + LogPrintf(" vMerkleTree: "); for (unsigned int i = 0; i < vMerkleTree.size(); i++) - printf("%s ", vMerkleTree[i].ToString().c_str()); - printf("\n"); + LogPrintf("%s ", vMerkleTree[i].ToString().c_str()); + LogPrintf("\n"); } diff --git a/src/core.h b/src/core.h index 1b9d4dd765..9ee8b2edce 100644 --- a/src/core.h +++ b/src/core.h @@ -389,7 +389,9 @@ public: int nVersion; // construct a CCoins from a CTransaction, at a given height - CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { } + CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { + ClearUnspendable(); + } // empty constructor CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } @@ -402,6 +404,14 @@ public: std::vector<CTxOut>().swap(vout); } + void ClearUnspendable() { + BOOST_FOREACH(CTxOut &txout, vout) { + if (txout.scriptPubKey.IsUnspendable()) + txout.SetNull(); + } + Cleanup(); + } + void swap(CCoins &to) { std::swap(to.fCoinBase, fCoinBase); to.vout.swap(vout); @@ -651,4 +661,38 @@ public: void print() const; }; + +/** Describes a place in the block chain to another node such that if the + * other node doesn't have the same branch, it can find a recent common trunk. + * The further back it is, the further before the fork it may be. + */ +struct CBlockLocator +{ + std::vector<uint256> vHave; + + CBlockLocator() {} + + CBlockLocator(const std::vector<uint256>& vHaveIn) + { + vHave = vHaveIn; + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vHave); + ) + + void SetNull() + { + vHave.clear(); + } + + bool IsNull() + { + return vHave.empty(); + } +}; + #endif diff --git a/src/db.cpp b/src/db.cpp index 03f46f3c26..f722c52e93 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -38,7 +38,7 @@ void CDBEnv::EnvShutdown() fDbEnvInit = false; int ret = dbenv.close(0); if (ret != 0) - printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret); + LogPrintf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret); if (!fMockDb) DbEnv(0).remove(path.string().c_str(), 0); } @@ -70,7 +70,7 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn) filesystem::path pathLogDir = path / "database"; filesystem::create_directory(pathLogDir); filesystem::path pathErrorFile = path / "db.log"; - printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); + LogPrintf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); unsigned int nEnvFlags = 0; if (GetBoolArg("-privdb", true)) @@ -111,7 +111,7 @@ void CDBEnv::MakeMock() boost::this_thread::interruption_point(); - printf("CDBEnv::MakeMock()\n"); + LogPrint("db", "CDBEnv::MakeMock()\n"); dbenv.set_cachesize(1, 0, 1); dbenv.set_lg_bsize(10485760*4); @@ -168,16 +168,16 @@ bool CDBEnv::Salvage(std::string strFile, bool fAggressive, int result = db.verify(strFile.c_str(), NULL, &strDump, flags); if (result == DB_VERIFY_BAD) { - printf("Error: Salvage found errors, all data may not be recoverable.\n"); + LogPrintf("Error: Salvage found errors, all data may not be recoverable.\n"); if (!fAggressive) { - printf("Error: Rerun with aggressive mode to ignore errors and continue.\n"); + LogPrintf("Error: Rerun with aggressive mode to ignore errors and continue.\n"); return false; } } if (result != 0 && result != DB_VERIFY_BAD) { - printf("ERROR: db salvage failed: %d\n",result); + LogPrintf("ERROR: db salvage failed: %d\n",result); return false; } @@ -348,7 +348,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) bitdb.mapFileUseCount.erase(strFile); bool fSuccess = true; - printf("Rewriting %s...\n", strFile.c_str()); + LogPrintf("Rewriting %s...\n", strFile.c_str()); string strFileRes = strFile + ".rewrite"; { // surround usage of db with extra {} CDB db(strFile.c_str(), "r"); @@ -362,7 +362,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) 0); if (ret > 0) { - printf("Cannot create database file %s\n", strFileRes.c_str()); + LogPrintf("Cannot create database file %s\n", strFileRes.c_str()); fSuccess = false; } @@ -418,7 +418,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) fSuccess = false; } if (!fSuccess) - printf("Rewriting of %s FAILED!\n", strFileRes.c_str()); + LogPrintf("Rewriting of %s FAILED!\n", strFileRes.c_str()); return fSuccess; } } @@ -433,7 +433,7 @@ void CDBEnv::Flush(bool fShutdown) int64 nStart = GetTimeMillis(); // Flush log data to the actual data file // on all files that are not in use - printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); + LogPrint("db", "Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); if (!fDbEnvInit) return; { @@ -443,23 +443,23 @@ void CDBEnv::Flush(bool fShutdown) { string strFile = (*mi).first; int nRefCount = (*mi).second; - printf("%s refcount=%d\n", strFile.c_str(), nRefCount); + LogPrint("db", "%s refcount=%d\n", strFile.c_str(), nRefCount); if (nRefCount == 0) { // Move log data to the dat file CloseDb(strFile); - printf("%s checkpoint\n", strFile.c_str()); + LogPrint("db", "%s checkpoint\n", strFile.c_str()); dbenv.txn_checkpoint(0, 0, 0); - printf("%s detach\n", strFile.c_str()); + LogPrint("db", "%s detach\n", strFile.c_str()); if (!fMockDb) dbenv.lsn_reset(strFile.c_str(), 0); - printf("%s closed\n", strFile.c_str()); + LogPrint("db", "%s closed\n", strFile.c_str()); mapFileUseCount.erase(mi++); } else mi++; } - printf("DBFlush(%s)%s ended %15"PRI64d"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart); + LogPrint("db", "DBFlush(%s)%s ended %15"PRI64d"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart); if (fShutdown) { char** listp; @@ -16,7 +16,7 @@ #include <db_cxx.h> class CAddrMan; -class CBlockLocator; +struct CBlockLocator; class CDiskBlockIndex; class CMasterKey; class COutPoint; diff --git a/src/init.cpp b/src/init.cpp index 0d90f0295d..28ec409bae 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -100,6 +100,7 @@ static CCoinsViewDB *pcoinsdbview; void Shutdown() { + LogPrintf("Shutdown : In progress...\n"); static CCriticalSection cs_Shutdown; TRY_LOCK(cs_Shutdown, lockShutdown); if (!lockShutdown) return; @@ -108,13 +109,14 @@ void Shutdown() nTransactionsUpdated++; StopRPCThreads(); ShutdownRPCMining(); - bitdb.Flush(false); + if (pwalletMain) + bitdb.Flush(false); GenerateBitcoins(false, NULL); StopNode(); { LOCK(cs_main); if (pwalletMain) - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + pwalletMain->SetBestChain(chainActive.GetLocator()); if (pblocktree) pblocktree->Flush(); if (pcoinsTip) @@ -123,10 +125,13 @@ void Shutdown() delete pcoinsdbview; pcoinsdbview = NULL; delete pblocktree; pblocktree = NULL; } - bitdb.Flush(true); + if (pwalletMain) + bitdb.Flush(true); boost::filesystem::remove(GetPidFile()); UnregisterAllWallets(); - delete pwalletMain; + if (pwalletMain) + delete pwalletMain; + LogPrintf("Shutdown : done\n"); } // @@ -284,13 +289,13 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) FILE *file = OpenBlockFile(pos, true); if (!file) break; - printf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); + LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); LoadExternalBlockFile(file, &pos); nFile++; } pblocktree->WriteReindexing(false); fReindex = false; - printf("Reindexing finished\n"); + LogPrintf("Reindexing finished\n"); // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): InitBlockIndex(); } @@ -302,7 +307,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) if (file) { CImportingNow imp; filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; - printf("Importing bootstrap.dat...\n"); + LogPrintf("Importing bootstrap.dat...\n"); LoadExternalBlockFile(file); RenameOver(pathBootstrap, pathBootstrapOld); } @@ -313,7 +318,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) FILE *file = fopen(path.string().c_str(), "rb"); if (file) { CImportingNow imp; - printf("Importing %s...\n", path.string().c_str()); + LogPrintf("Importing %s...\n", path.string().c_str()); LoadExternalBlockFile(file); } } @@ -339,8 +344,8 @@ bool AppInit2(boost::thread_group& threadGroup) // 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! + // 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); @@ -426,7 +431,7 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 3: parameter-to-internal-flags - fDebug = GetBoolArg("-debug", false); + if (mapMultiArgs.count("-debug")) fDebug = true; fBenchmark = GetBoolArg("-benchmark", false); mempool.fChecks = GetBoolArg("-checkmempool", RegTest()); Checkpoints::fEnabled = GetBoolArg("-checkpoints", true); @@ -522,21 +527,21 @@ bool AppInit2(boost::thread_group& threadGroup) if (GetBoolArg("-shrinkdebugfile", !fDebug)) ShrinkDebugFile(); - printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - printf("Bitcoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str()); - printf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION)); + 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) - printf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str()); - printf("Default data directory %s\n", GetDefaultDataDir().string().c_str()); - printf("Using data directory %s\n", strDataDir.c_str()); - printf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD); + 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) { - printf("Using %u threads for script verification\n", nScriptCheckThreads); + LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); for (int i=0; i<nScriptCheckThreads-1; i++) threadGroup.create_thread(&ThreadScriptCheck); } @@ -554,7 +559,7 @@ bool AppInit2(boost::thread_group& threadGroup) boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%"PRI64d".bak", GetTime()); try { boost::filesystem::rename(pathDatabase, pathDatabaseBak); - printf("Moved old %s to %s. Retrying.\n", pathDatabase.string().c_str(), pathDatabaseBak.string().c_str()); + 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) } @@ -710,12 +715,12 @@ bool AppInit2(boost::thread_group& threadGroup) filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1); try { filesystem::create_hard_link(source, dest); - printf("Hardlinked %s -> %s\n", source.string().c_str(), dest.string().c_str()); + 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. - printf("Error hardlinking blk%04u.dat : %s\n", i, e.what()); + LogPrintf("Error hardlinking blk%04u.dat : %s\n", i, e.what()); break; } } @@ -766,7 +771,7 @@ bool AppInit2(boost::thread_group& threadGroup) // 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() && pindexGenesisBlock == NULL) + 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) @@ -783,12 +788,12 @@ bool AppInit2(boost::thread_group& threadGroup) uiInterface.InitMessage(_("Verifying blocks...")); if (!VerifyDB(GetArg("-checklevel", 3), - GetArg( "-checkblocks", 288))) { + GetArg("-checkblocks", 288))) { strLoadError = _("Corrupted block database detected"); break; } } catch(std::exception &e) { - if (fDebug) printf("%s\n", e.what()); + if (fDebug) LogPrintf("%s\n", e.what()); strLoadError = _("Error opening block database"); break; } @@ -806,7 +811,7 @@ bool AppInit2(boost::thread_group& threadGroup) fReindex = true; fRequestShutdown = false; } else { - printf("Aborted block database rebuild. Exiting.\n"); + LogPrintf("Aborted block database rebuild. Exiting.\n"); return false; } } else { @@ -820,10 +825,10 @@ bool AppInit2(boost::thread_group& threadGroup) // As the program has not fully started yet, Shutdown() is possibly overkill. if (fRequestShutdown) { - printf("Shutdown requested. Exiting.\n"); + LogPrintf("Shutdown requested. Exiting.\n"); return false; } - printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); + LogPrintf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); if (GetBoolArg("-printblockindex", false) || GetBoolArg("-printblocktree", false)) { @@ -845,12 +850,12 @@ bool AppInit2(boost::thread_group& threadGroup) ReadBlockFromDisk(block, pindex); block.BuildMerkleTree(); block.print(); - printf("\n"); + LogPrintf("\n"); nFound++; } } if (nFound == 0) - printf("No blocks matching %s were found\n", strMatch.c_str()); + LogPrintf("No blocks matching %s were found\n", strMatch.c_str()); return false; } @@ -877,7 +882,7 @@ bool AppInit2(boost::thread_group& threadGroup) else if (nLoadWalletRet == DB_NEED_REWRITE) { strErrors << _("Wallet needed to be rewritten: restart Bitcoin to complete") << "\n"; - printf("%s", strErrors.str().c_str()); + LogPrintf("%s", strErrors.str().c_str()); return InitError(strErrors.str()); } else @@ -889,12 +894,12 @@ bool AppInit2(boost::thread_group& threadGroup) int nMaxVersion = GetArg("-upgradewallet", 0); if (nMaxVersion == 0) // the -upgradewallet without argument case { - printf("Performing wallet upgrade to %i\n", FEATURE_LATEST); + LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); nMaxVersion = CLIENT_VERSION; pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately } else - printf("Allowing wallet upgrade up to %i\n", nMaxVersion); + LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); if (nMaxVersion < pwalletMain->GetVersion()) strErrors << _("Cannot downgrade wallet") << "\n"; pwalletMain->SetMaxVersion(nMaxVersion); @@ -912,34 +917,34 @@ bool AppInit2(boost::thread_group& threadGroup) strErrors << _("Cannot write default address") << "\n"; } - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + pwalletMain->SetBestChain(chainActive.GetLocator()); } - printf("%s", strErrors.str().c_str()); - printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); + LogPrintf("%s", strErrors.str().c_str()); + LogPrintf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart); RegisterWallet(pwalletMain); - CBlockIndex *pindexRescan = pindexBest; + CBlockIndex *pindexRescan = chainActive.Tip(); if (GetBoolArg("-rescan", false)) - pindexRescan = pindexGenesisBlock; + pindexRescan = chainActive.Genesis(); else { CWalletDB walletdb(strWalletFile); CBlockLocator locator; if (walletdb.ReadBestBlock(locator)) - pindexRescan = locator.GetBlockIndex(); + pindexRescan = chainActive.FindFork(locator); else - pindexRescan = pindexGenesisBlock; + pindexRescan = chainActive.Genesis(); } - if (pindexBest && pindexBest != pindexRescan) + if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { uiInterface.InitMessage(_("Rescanning...")); - printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); + LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); pwalletMain->ScanForWalletTransactions(pindexRescan, true); - printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); - pwalletMain->SetBestChain(CBlockLocator(pindexBest)); + LogPrintf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); + pwalletMain->SetBestChain(chainActive.GetLocator()); nWalletDBUpdated++; } @@ -967,10 +972,10 @@ bool AppInit2(boost::thread_group& threadGroup) { CAddrDB adb; if (!adb.Read(addrman)) - printf("Invalid or missing peers.dat; recreating\n"); + LogPrintf("Invalid or missing peers.dat; recreating\n"); } - printf("Loaded %i addresses from peers.dat %"PRI64d"ms\n", + LogPrintf("Loaded %i addresses from peers.dat %"PRI64d"ms\n", addrman.size(), GetTimeMillis() - nStart); // ********************************************************* Step 11: start node @@ -984,11 +989,11 @@ bool AppInit2(boost::thread_group& threadGroup) RandAddSeedPerfmon(); //// debug print - printf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); - printf("nBestHeight = %d\n", nBestHeight); - printf("setKeyPool.size() = %"PRIszu"\n", pwalletMain->setKeyPool.size()); - printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size()); - printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size()); + 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); @@ -998,17 +1003,20 @@ bool AppInit2(boost::thread_group& threadGroup) StartRPCThreads(); // Generate coins in the background - GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); + if (pwalletMain) + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); // ********************************************************* Step 12: finished uiInterface.InitMessage(_("Done loading")); - // Add wallet transactions that aren't already in a block to mapTransactions - pwalletMain->ReacceptWalletTransactions(); + 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))); + // Run a thread to flush wallet periodically + threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); + } return !fRequestShutdown; } diff --git a/src/key.cpp b/src/key.cpp index 85dc9cda2b..414845a9da 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -166,9 +166,12 @@ public: assert(nSize == nSize2); } - bool SetPrivKey(const CPrivKey &privkey) { + bool SetPrivKey(const CPrivKey &privkey, bool fSkipCheck=false) { const unsigned char* pbegin = &privkey[0]; if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) { + if(fSkipCheck) + return true; + // d2i_ECPrivateKey returns true if parsing succeeds. // This doesn't necessarily mean the key is valid. if (EC_KEY_check_key(pkey)) @@ -199,17 +202,19 @@ public: ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); if (sig == NULL) return false; - if (BN_is_odd(sig->s)) { - // enforce even S values, by negating the value (modulo the order) if odd - BN_CTX *ctx = BN_CTX_new(); - BN_CTX_start(ctx); - const EC_GROUP *group = EC_KEY_get0_group(pkey); - BIGNUM *order = BN_CTX_get(ctx); - EC_GROUP_get_order(group, order, ctx); + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + const EC_GROUP *group = EC_KEY_get0_group(pkey); + BIGNUM *order = BN_CTX_get(ctx); + BIGNUM *halforder = BN_CTX_get(ctx); + EC_GROUP_get_order(group, order, ctx); + BN_rshift1(halforder, order); + if (BN_cmp(sig->s, halforder) > 0) { + // enforce low S values, by negating the value (modulo the order) if above order/2. BN_sub(sig->s, order, sig->s); - BN_CTX_end(ctx); - BN_CTX_free(ctx); } + BN_CTX_end(ctx); + BN_CTX_free(ctx); unsigned int nSize = ECDSA_size(pkey); vchSig.resize(nSize); // Make sure it is big enough unsigned char *pos = &vchSig[0]; @@ -409,6 +414,24 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) return true; } +bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { + CECKey key; + if (!key.SetPrivKey(privkey, fSkipCheck)) + return false; + + key.GetSecretBytes(vch); + fCompressed = vchPubKey.IsCompressed(); + fValid = true; + + if (fSkipCheck) + return true; + + if (GetPubKey() != vchPubKey) + return false; + + return true; +} + bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const { if (!IsValid()) return false; @@ -261,6 +261,9 @@ public: // Derive BIP32 child key. bool Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const; + + // Load private key and check that public key matches. + bool Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck); }; struct CExtPubKey { diff --git a/src/leveldb.cpp b/src/leveldb.cpp index 5e3fa08f5e..fb202367c4 100644 --- a/src/leveldb.cpp +++ b/src/leveldb.cpp @@ -15,7 +15,7 @@ void HandleError(const leveldb::Status &status) throw(leveldb_error) { if (status.ok()) return; - printf("%s\n", status.ToString().c_str()); + LogPrintf("%s\n", status.ToString().c_str()); if (status.IsCorruption()) throw leveldb_error("Database corrupted"); if (status.IsIOError()) @@ -48,15 +48,15 @@ CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool options.env = penv; } else { if (fWipe) { - printf("Wiping LevelDB in %s\n", path.string().c_str()); + LogPrintf("Wiping LevelDB in %s\n", path.string().c_str()); leveldb::DestroyDB(path.string(), options); } boost::filesystem::create_directory(path); - printf("Opening LevelDB in %s\n", path.string().c_str()); + LogPrintf("Opening LevelDB in %s\n", path.string().c_str()); } leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); HandleError(status); - printf("Opened LevelDB successfully\n"); + LogPrintf("Opened LevelDB successfully\n"); } CLevelDB::~CLevelDB() { diff --git a/src/leveldb.h b/src/leveldb.h index 79262edbb5..7daddeb493 100644 --- a/src/leveldb.h +++ b/src/leveldb.h @@ -5,6 +5,7 @@ #define BITCOIN_LEVELDB_H #include "serialize.h" +#include "util.h" #include <leveldb/db.h> #include <leveldb/write_batch.h> @@ -91,7 +92,7 @@ public: if (!status.ok()) { if (status.IsNotFound()) return false; - printf("LevelDB read failure: %s\n", status.ToString().c_str()); + LogPrintf("LevelDB read failure: %s\n", status.ToString().c_str()); HandleError(status); } try { @@ -120,7 +121,7 @@ public: if (!status.ok()) { if (status.IsNotFound()) return false; - printf("LevelDB read failure: %s\n", status.ToString().c_str()); + LogPrintf("LevelDB read failure: %s\n", status.ToString().c_str()); HandleError(status); } return true; diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile index 96af7765be..20c9c4f287 100644 --- a/src/leveldb/Makefile +++ b/src/leveldb/Makefile @@ -31,6 +31,7 @@ TESTHARNESS = ./util/testharness.o $(TESTUTIL) TESTS = \ arena_test \ + autocompact_test \ bloom_test \ c_test \ cache_test \ @@ -70,7 +71,7 @@ SHARED = $(SHARED1) else # Update db.h if you change these. SHARED_MAJOR = 1 -SHARED_MINOR = 12 +SHARED_MINOR = 13 SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) SHARED2 = $(SHARED1).$(SHARED_MAJOR) SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) @@ -114,6 +115,9 @@ leveldbutil: db/leveldb_main.o $(LIBOBJECTS) arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) +autocompact_test: db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) + $(CXX) $(LDFLAGS) db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) + bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) diff --git a/src/leveldb/db/autocompact_test.cc b/src/leveldb/db/autocompact_test.cc new file mode 100644 index 0000000000..d20a2362c3 --- /dev/null +++ b/src/leveldb/db/autocompact_test.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2013 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/db.h" +#include "db/db_impl.h" +#include "leveldb/cache.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class AutoCompactTest { + public: + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + AutoCompactTest() { + dbname_ = test::TmpDir() + "/autocompact_test"; + tiny_cache_ = NewLRUCache(100); + options_.block_cache = tiny_cache_; + DestroyDB(dbname_, options_); + options_.create_if_missing = true; + options_.compression = kNoCompression; + ASSERT_OK(DB::Open(options_, dbname_, &db_)); + } + + ~AutoCompactTest() { + delete db_; + DestroyDB(dbname_, Options()); + delete tiny_cache_; + } + + std::string Key(int i) { + char buf[100]; + snprintf(buf, sizeof(buf), "key%06d", i); + return std::string(buf); + } + + uint64_t Size(const Slice& start, const Slice& limit) { + Range r(start, limit); + uint64_t size; + db_->GetApproximateSizes(&r, 1, &size); + return size; + } + + void DoReads(int n); +}; + +static const int kValueSize = 200 * 1024; +static const int kTotalSize = 100 * 1024 * 1024; +static const int kCount = kTotalSize / kValueSize; + +// Read through the first n keys repeatedly and check that they get +// compacted (verified by checking the size of the key space). +void AutoCompactTest::DoReads(int n) { + std::string value(kValueSize, 'x'); + DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); + + // Fill database + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Put(WriteOptions(), Key(i), value)); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Delete everything + for (int i = 0; i < kCount; i++) { + ASSERT_OK(db_->Delete(WriteOptions(), Key(i))); + } + ASSERT_OK(dbi->TEST_CompactMemTable()); + + // Get initial measurement of the space we will be reading. + const int64_t initial_size = Size(Key(0), Key(n)); + const int64_t initial_other_size = Size(Key(n), Key(kCount)); + + // Read until size drops significantly. + std::string limit_key = Key(n); + for (int read = 0; true; read++) { + ASSERT_LT(read, 100) << "Taking too long to compact"; + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); + iter->Valid() && iter->key().ToString() < limit_key; + iter->Next()) { + // Drop data + } + delete iter; + // Wait a little bit to allow any triggered compactions to complete. + Env::Default()->SleepForMicroseconds(1000000); + uint64_t size = Size(Key(0), Key(n)); + fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", + read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0); + if (size <= initial_size/10) { + break; + } + } + + // Verify that the size of the key space not touched by the reads + // is pretty much unchanged. + const int64_t final_other_size = Size(Key(n), Key(kCount)); + ASSERT_LE(final_other_size, initial_other_size + 1048576); + ASSERT_GE(final_other_size, initial_other_size/5 - 1048576); +} + +TEST(AutoCompactTest, ReadAll) { + DoReads(kCount); +} + +TEST(AutoCompactTest, ReadHalf) { + DoReads(kCount/2); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc index 31b2d5f416..b37ffdfe64 100644 --- a/src/leveldb/db/corruption_test.cc +++ b/src/leveldb/db/corruption_test.cc @@ -35,6 +35,7 @@ class CorruptionTest { CorruptionTest() { tiny_cache_ = NewLRUCache(100); options_.env = &env_; + options_.block_cache = tiny_cache_; dbname_ = test::TmpDir() + "/db_test"; DestroyDB(dbname_, options_); @@ -50,17 +51,14 @@ class CorruptionTest { delete tiny_cache_; } - Status TryReopen(Options* options = NULL) { + Status TryReopen() { delete db_; db_ = NULL; - Options opt = (options ? *options : options_); - opt.env = &env_; - opt.block_cache = tiny_cache_; - return DB::Open(opt, dbname_, &db_); + return DB::Open(options_, dbname_, &db_); } - void Reopen(Options* options = NULL) { - ASSERT_OK(TryReopen(options)); + void Reopen() { + ASSERT_OK(TryReopen()); } void RepairDB() { @@ -92,6 +90,10 @@ class CorruptionTest { for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { uint64_t key; Slice in(iter->key()); + if (in == "" || in == "~") { + // Ignore boundary keys. + continue; + } if (!ConsumeDecimalNumber(&in, &key) || !in.empty() || key < next_expected) { @@ -233,7 +235,7 @@ TEST(CorruptionTest, TableFile) { dbi->TEST_CompactRange(1, NULL, NULL); Corrupt(kTableFile, 100, 1); - Check(99, 99); + Check(90, 99); } TEST(CorruptionTest, TableFileIndexData) { @@ -299,7 +301,7 @@ TEST(CorruptionTest, CompactionInputError) { ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last))); Corrupt(kTableFile, 100, 1); - Check(9, 9); + Check(5, 9); // Force compactions by writing lots of values Build(10000); @@ -307,32 +309,23 @@ TEST(CorruptionTest, CompactionInputError) { } TEST(CorruptionTest, CompactionInputErrorParanoid) { - Options options; - options.paranoid_checks = true; - options.write_buffer_size = 1048576; - Reopen(&options); + options_.paranoid_checks = true; + options_.write_buffer_size = 512 << 10; + Reopen(); DBImpl* dbi = reinterpret_cast<DBImpl*>(db_); - // Fill levels >= 1 so memtable compaction outputs to level 1 - for (int level = 1; level < config::kNumLevels; level++) { - dbi->Put(WriteOptions(), "", "begin"); - dbi->Put(WriteOptions(), "~", "end"); + // Make multiple inputs so we need to compact. + for (int i = 0; i < 2; i++) { + Build(10); dbi->TEST_CompactMemTable(); + Corrupt(kTableFile, 100, 1); + env_.SleepForMicroseconds(100000); } + dbi->CompactRange(NULL, NULL); - Build(10); - dbi->TEST_CompactMemTable(); - ASSERT_EQ(1, Property("leveldb.num-files-at-level0")); - - Corrupt(kTableFile, 100, 1); - Check(9, 9); - - // Write must eventually fail because of corrupted table - Status s; + // Write must fail because of corrupted table std::string tmp1, tmp2; - for (int i = 0; i < 10000 && s.ok(); i++) { - s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2)); - } + Status s = db_->Put(WriteOptions(), Key(5, &tmp1), Value(5, &tmp2)); ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db"; } diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc index 395d3172ad..fa1351038b 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -113,14 +113,14 @@ Options SanitizeOptions(const std::string& dbname, return result; } -DBImpl::DBImpl(const Options& options, const std::string& dbname) - : env_(options.env), - internal_comparator_(options.comparator), - internal_filter_policy_(options.filter_policy), - options_(SanitizeOptions( - dbname, &internal_comparator_, &internal_filter_policy_, options)), - owns_info_log_(options_.info_log != options.info_log), - owns_cache_(options_.block_cache != options.block_cache), +DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) + : env_(raw_options.env), + internal_comparator_(raw_options.comparator), + internal_filter_policy_(raw_options.filter_policy), + options_(SanitizeOptions(dbname, &internal_comparator_, + &internal_filter_policy_, raw_options)), + owns_info_log_(options_.info_log != raw_options.info_log), + owns_cache_(options_.block_cache != raw_options.block_cache), dbname_(dbname), db_lock_(NULL), shutting_down_(NULL), @@ -130,6 +130,7 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname) logfile_(NULL), logfile_number_(0), log_(NULL), + seed_(0), tmp_batch_(new WriteBatch), bg_compaction_scheduled_(false), manual_compaction_(NULL), @@ -138,7 +139,7 @@ DBImpl::DBImpl(const Options& options, const std::string& dbname) has_imm_.Release_Store(NULL); // Reserve ten files or so for other uses and give the rest to TableCache. - const int table_cache_size = options.max_open_files - kNumNonTableCacheFiles; + const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles; table_cache_ = new TableCache(dbname_, &options_, table_cache_size); versions_ = new VersionSet(dbname_, &options_, table_cache_, @@ -1027,7 +1028,8 @@ static void CleanupIteratorState(void* arg1, void* arg2) { } // namespace Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, - SequenceNumber* latest_snapshot) { + SequenceNumber* latest_snapshot, + uint32_t* seed) { IterState* cleanup = new IterState; mutex_.Lock(); *latest_snapshot = versions_->LastSequence(); @@ -1051,13 +1053,15 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, cleanup->version = versions_->current(); internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + *seed = ++seed_; mutex_.Unlock(); return internal_iter; } Iterator* DBImpl::TEST_NewInternalIterator() { SequenceNumber ignored; - return NewInternalIterator(ReadOptions(), &ignored); + uint32_t ignored_seed; + return NewInternalIterator(ReadOptions(), &ignored, &ignored_seed); } int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { @@ -1114,12 +1118,21 @@ Status DBImpl::Get(const ReadOptions& options, Iterator* DBImpl::NewIterator(const ReadOptions& options) { SequenceNumber latest_snapshot; - Iterator* internal_iter = NewInternalIterator(options, &latest_snapshot); + uint32_t seed; + Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); return NewDBIterator( - &dbname_, env_, user_comparator(), internal_iter, + this, user_comparator(), iter, (options.snapshot != NULL ? reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_ - : latest_snapshot)); + : latest_snapshot), + seed); +} + +void DBImpl::RecordReadSample(Slice key) { + MutexLock l(&mutex_); + if (versions_->current()->RecordReadSample(key)) { + MaybeScheduleCompaction(); + } } const Snapshot* DBImpl::GetSnapshot() { diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h index 3c8d711ae0..75fd30abe9 100644 --- a/src/leveldb/db/db_impl.h +++ b/src/leveldb/db/db_impl.h @@ -59,13 +59,19 @@ class DBImpl : public DB { // file at a level >= 1. int64_t TEST_MaxNextLevelOverlappingBytes(); + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. + void RecordReadSample(Slice key); + private: friend class DB; struct CompactionState; struct Writer; Iterator* NewInternalIterator(const ReadOptions&, - SequenceNumber* latest_snapshot); + SequenceNumber* latest_snapshot, + uint32_t* seed); Status NewDB(); @@ -135,6 +141,7 @@ class DBImpl : public DB { WritableFile* logfile_; uint64_t logfile_number_; log::Writer* log_; + uint32_t seed_; // For sampling. // Queue of writers. std::deque<Writer*> writers_; diff --git a/src/leveldb/db/db_iter.cc b/src/leveldb/db/db_iter.cc index 87dca2ded4..071a54e3f4 100644 --- a/src/leveldb/db/db_iter.cc +++ b/src/leveldb/db/db_iter.cc @@ -5,12 +5,14 @@ #include "db/db_iter.h" #include "db/filename.h" +#include "db/db_impl.h" #include "db/dbformat.h" #include "leveldb/env.h" #include "leveldb/iterator.h" #include "port/port.h" #include "util/logging.h" #include "util/mutexlock.h" +#include "util/random.h" namespace leveldb { @@ -46,15 +48,16 @@ class DBIter: public Iterator { kReverse }; - DBIter(const std::string* dbname, Env* env, - const Comparator* cmp, Iterator* iter, SequenceNumber s) - : dbname_(dbname), - env_(env), + DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s, + uint32_t seed) + : db_(db), user_comparator_(cmp), iter_(iter), sequence_(s), direction_(kForward), - valid_(false) { + valid_(false), + rnd_(seed), + bytes_counter_(RandomPeriod()) { } virtual ~DBIter() { delete iter_; @@ -100,8 +103,12 @@ class DBIter: public Iterator { } } - const std::string* const dbname_; - Env* const env_; + // Pick next gap with average value of config::kReadBytesPeriod. + ssize_t RandomPeriod() { + return rnd_.Uniform(2*config::kReadBytesPeriod); + } + + DBImpl* db_; const Comparator* const user_comparator_; Iterator* const iter_; SequenceNumber const sequence_; @@ -112,13 +119,23 @@ class DBIter: public Iterator { Direction direction_; bool valid_; + Random rnd_; + ssize_t bytes_counter_; + // No copying allowed DBIter(const DBIter&); void operator=(const DBIter&); }; inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { - if (!ParseInternalKey(iter_->key(), ikey)) { + Slice k = iter_->key(); + ssize_t n = k.size() + iter_->value().size(); + bytes_counter_ -= n; + while (bytes_counter_ < 0) { + bytes_counter_ += RandomPeriod(); + db_->RecordReadSample(k); + } + if (!ParseInternalKey(k, ikey)) { status_ = Status::Corruption("corrupted internal key in DBIter"); return false; } else { @@ -288,12 +305,12 @@ void DBIter::SeekToLast() { } // anonymous namespace Iterator* NewDBIterator( - const std::string* dbname, - Env* env, + DBImpl* db, const Comparator* user_key_comparator, Iterator* internal_iter, - const SequenceNumber& sequence) { - return new DBIter(dbname, env, user_key_comparator, internal_iter, sequence); + SequenceNumber sequence, + uint32_t seed) { + return new DBIter(db, user_key_comparator, internal_iter, sequence, seed); } } // namespace leveldb diff --git a/src/leveldb/db/db_iter.h b/src/leveldb/db/db_iter.h index d9e1b174ab..04927e937b 100644 --- a/src/leveldb/db/db_iter.h +++ b/src/leveldb/db/db_iter.h @@ -11,15 +11,17 @@ namespace leveldb { +class DBImpl; + // Return a new iterator that converts internal keys (yielded by // "*internal_iter") that were live at the specified "sequence" number // into appropriate user keys. extern Iterator* NewDBIterator( - const std::string* dbname, - Env* env, + DBImpl* db, const Comparator* user_key_comparator, Iterator* internal_iter, - const SequenceNumber& sequence); + SequenceNumber sequence, + uint32_t seed); } // namespace leveldb diff --git a/src/leveldb/db/dbformat.h b/src/leveldb/db/dbformat.h index f7f64dafb6..5d8a032bd3 100644 --- a/src/leveldb/db/dbformat.h +++ b/src/leveldb/db/dbformat.h @@ -38,6 +38,9 @@ static const int kL0_StopWritesTrigger = 12; // space if the same key space is being repeatedly overwritten. static const int kMaxMemCompactLevel = 2; +// Approximate gap in bytes between samples of data read during iteration. +static const int kReadBytesPeriod = 1048576; + } // namespace config class InternalKey; diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc index 4fd1ddef21..66d73be71f 100644 --- a/src/leveldb/db/version_set.cc +++ b/src/leveldb/db/version_set.cc @@ -289,6 +289,51 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) { return a->number > b->number; } +void Version::ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)) { + // TODO(sanjay): Change Version::Get() to use this function. + const Comparator* ucmp = vset_->icmp_.user_comparator(); + + // Search level-0 in order from newest to oldest. + std::vector<FileMetaData*> tmp; + tmp.reserve(files_[0].size()); + for (uint32_t i = 0; i < files_[0].size(); i++) { + FileMetaData* f = files_[0][i]; + if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && + ucmp->Compare(user_key, f->largest.user_key()) <= 0) { + tmp.push_back(f); + } + } + if (!tmp.empty()) { + std::sort(tmp.begin(), tmp.end(), NewestFirst); + for (uint32_t i = 0; i < tmp.size(); i++) { + if (!(*func)(arg, 0, tmp[i])) { + return; + } + } + } + + // Search other levels. + for (int level = 1; level < config::kNumLevels; level++) { + size_t num_files = files_[level].size(); + if (num_files == 0) continue; + + // Binary search to find earliest index whose largest key >= internal_key. + uint32_t index = FindFile(vset_->icmp_, files_[level], internal_key); + if (index < num_files) { + FileMetaData* f = files_[level][index]; + if (ucmp->Compare(user_key, f->smallest.user_key()) < 0) { + // All of "f" is past any data for user_key + } else { + if (!(*func)(arg, level, f)) { + return; + } + } + } + } +} + Status Version::Get(const ReadOptions& options, const LookupKey& k, std::string* value, @@ -401,6 +446,44 @@ bool Version::UpdateStats(const GetStats& stats) { return false; } +bool Version::RecordReadSample(Slice internal_key) { + ParsedInternalKey ikey; + if (!ParseInternalKey(internal_key, &ikey)) { + return false; + } + + struct State { + GetStats stats; // Holds first matching file + int matches; + + static bool Match(void* arg, int level, FileMetaData* f) { + State* state = reinterpret_cast<State*>(arg); + state->matches++; + if (state->matches == 1) { + // Remember first match. + state->stats.seek_file = f; + state->stats.seek_file_level = level; + } + // We can stop iterating once we have a second match. + return state->matches < 2; + } + }; + + State state; + state.matches = 0; + ForEachOverlapping(ikey.user_key, internal_key, &state, &State::Match); + + // Must have at least two matches since we want to merge across + // files. But what if we have a single file that contains many + // overwrites and deletions? Should we have another mechanism for + // finding such files? + if (state.matches >= 2) { + // 1MB cost is about 1 seek (see comment in Builder::Apply). + return UpdateStats(state.stats); + } + return false; +} + void Version::Ref() { ++refs_; } @@ -435,10 +518,13 @@ int Version::PickLevelForMemTableOutput( if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) { break; } - GetOverlappingInputs(level + 2, &start, &limit, &overlaps); - const int64_t sum = TotalFileSize(overlaps); - if (sum > kMaxGrandParentOverlapBytes) { - break; + if (level + 2 < config::kNumLevels) { + // Check that file does not overlap too many grandparent bytes. + GetOverlappingInputs(level + 2, &start, &limit, &overlaps); + const int64_t sum = TotalFileSize(overlaps); + if (sum > kMaxGrandParentOverlapBytes) { + break; + } } level++; } @@ -452,6 +538,8 @@ void Version::GetOverlappingInputs( const InternalKey* begin, const InternalKey* end, std::vector<FileMetaData*>* inputs) { + assert(level >= 0); + assert(level < config::kNumLevels); inputs->clear(); Slice user_begin, user_end; if (begin != NULL) { diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h index 9d084fdb7d..20de0e2629 100644 --- a/src/leveldb/db/version_set.h +++ b/src/leveldb/db/version_set.h @@ -78,6 +78,12 @@ class Version { // REQUIRES: lock is held bool UpdateStats(const GetStats& stats); + // Record a sample of bytes read at the specified internal key. + // Samples are taken approximately once every config::kReadBytesPeriod + // bytes. Returns true if a new compaction may need to be triggered. + // REQUIRES: lock is held + bool RecordReadSample(Slice key); + // Reference count management (so Versions do not disappear out from // under live iterators) void Ref(); @@ -114,6 +120,15 @@ class Version { class LevelFileNumIterator; Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; + // Call func(arg, level, f) for every file that overlaps user_key in + // order from newest to oldest. If an invocation of func returns + // false, makes no more calls. + // + // REQUIRES: user portion of internal_key == user_key. + void ForEachOverlapping(Slice user_key, Slice internal_key, + void* arg, + bool (*func)(void*, int, FileMetaData*)); + VersionSet* vset_; // VersionSet to which this Version belongs Version* next_; // Next version in linked list Version* prev_; // Previous version in linked list diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h index da8b11a8c0..57c00a5da0 100644 --- a/src/leveldb/include/leveldb/db.h +++ b/src/leveldb/include/leveldb/db.h @@ -14,7 +14,7 @@ namespace leveldb { // Update Makefile if you change these static const int kMajorVersion = 1; -static const int kMinorVersion = 12; +static const int kMinorVersion = 13; struct Options; struct ReadOptions; diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc index 6badfdc230..0f5dcfac5a 100644 --- a/src/leveldb/util/env_posix.cc +++ b/src/leveldb/util/env_posix.cc @@ -320,8 +320,39 @@ class PosixMmapFile : public WritableFile { return Status::OK(); } - virtual Status Sync() { + Status SyncDirIfManifest() { + const char* f = filename_.c_str(); + const char* sep = strrchr(f, '/'); + Slice basename; + std::string dir; + if (sep == NULL) { + dir = "."; + basename = f; + } else { + dir = std::string(f, sep - f); + basename = sep + 1; + } Status s; + if (basename.starts_with("MANIFEST")) { + int fd = open(dir.c_str(), O_RDONLY); + if (fd < 0) { + s = IOError(dir, errno); + } else { + if (fsync(fd) < 0) { + s = IOError(dir, errno); + } + close(fd); + } + } + return s; + } + + virtual Status Sync() { + // Ensure new files referred to by the manifest are in the filesystem. + Status s = SyncDirIfManifest(); + if (!s.ok()) { + return s; + } if (pending_sync_) { // Some unmapped data was not synced diff --git a/src/leveldb/util/random.h b/src/leveldb/util/random.h index 07538242ea..ddd51b1c7b 100644 --- a/src/leveldb/util/random.h +++ b/src/leveldb/util/random.h @@ -16,7 +16,12 @@ class Random { private: uint32_t seed_; public: - explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { } + explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { + // Avoid bad seeds. + if (seed_ == 0 || seed_ == 2147483647L) { + seed_ = 1; + } + } uint32_t Next() { static const uint32_t M = 2147483647L; // 2^31-1 static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 diff --git a/src/m4/bitcoin_find_bdb48.m4 b/src/m4/bitcoin_find_bdb48.m4 new file mode 100644 index 0000000000..72ec49b635 --- /dev/null +++ b/src/m4/bitcoin_find_bdb48.m4 @@ -0,0 +1,66 @@ +AC_DEFUN([BITCOIN_FIND_BDB48],[ + AC_MSG_CHECKING([for Berkeley DB C++ headers]) + BDB_CPPFLAGS= + BDB_LIBS= + bdbpath=X + bdb48path=X + bdbdirlist= + for _vn in 4.8 48 4 5 ''; do + for _pfx in b lib ''; do + bdbdirlist="$bdbdirlist ${_pfx}db${_vn}" + done + done + for searchpath in $bdbdirlist ''; do + test -n "${searchpath}" && searchpath="${searchpath}/" + AC_TRY_COMPILE([ + #include <${searchpath}db_cxx.h> + ],[ + #if !((DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 8) || DB_VERSION_MAJOR > 4) + #error "failed to find bdb 4.8+" + #endif + ],[ + if test "x$bdbpath" = "xX"; then + bdbpath="${searchpath}" + fi + ],[ + continue + ]) + AC_TRY_COMPILE([ + #include <${searchpath}db_cxx.h> + ],[ + #if !(DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 8) + #error "failed to find bdb 4.8" + #endif + ],[ + bdb48path="${searchpath}" + break + ]) + done + if test "x$bdbpath" = "xX"; then + AC_MSG_RESULT([no]) + AC_MSG_ERROR(libdb_cxx headers missing) + elif test "x$bdb48path" = "xX"; then + BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdbpath}],db_cxx) + AC_ARG_WITH([incompatible-bdb],[AS_HELP_STRING([--with-incompatible-bdb], [allow using a bdb version other than 4.8])],[ + AC_MSG_WARN([Found Berkeley DB other than 4.8; wallets opened by this build will not be portable!]) + ],[ + AC_MSG_ERROR([Found Berkeley DB other than 4.8, required for portable wallets (--with-incompatible-bdb to ignore)]) + ]) + else + BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdb48path}],db_cxx) + bdbpath="${bdb48path}" + fi + AC_SUBST(BDB_CPPFLAGS) + + # TODO: Ideally this could find the library version and make sure it matches the headers being used + for searchlib in db_cxx-4.8 db_cxx; do + AC_CHECK_LIB([$searchlib],[main],[ + BDB_LIBS="-l${searchlib}" + break + ]) + done + if test "x$BDB_LIBS" = "x"; then + AC_MSG_ERROR(libdb_cxx missing) + fi + AC_SUBST(BDB_LIBS) +]) diff --git a/src/m4/bitcoin_subdir_to_include.m4 b/src/m4/bitcoin_subdir_to_include.m4 new file mode 100644 index 0000000000..66f106c7d4 --- /dev/null +++ b/src/m4/bitcoin_subdir_to_include.m4 @@ -0,0 +1,14 @@ +dnl BITCOIN_SUBDIR_TO_INCLUDE([CPPFLAGS-VARIABLE-NAME],[SUBDIRECTORY-NAME],[HEADER-FILE]) +dnl SUBDIRECTORY-NAME must end with a path separator +AC_DEFUN([BITCOIN_SUBDIR_TO_INCLUDE],[ + if test "x$2" = "x"; then + AC_MSG_RESULT([default]) + else + echo "#include <$2$3.h>" >conftest.cpp + newinclpath=`${CXXCPP} ${CPPFLAGS} -M conftest.cpp 2>/dev/null | [ tr -d '\\n\\r\\\\' | sed -e 's/^.*[[:space:]:]\(\/[^[:space:]]*\)]$3[\.h[[:space:]].*$/\1/' -e t -e d`] + AC_MSG_RESULT([${newinclpath}]) + if test "x${newinclpath}" != "x"; then + eval "$1=\"\$$1\"' -I${newinclpath}'" + fi + fi +]) diff --git a/src/main.cpp b/src/main.cpp index 24fd1fadbc..215a7ba620 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,13 +32,8 @@ CTxMemPool mempool; unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; -std::vector<CBlockIndex*> vBlockIndexByHeight; -CBlockIndex* pindexGenesisBlock = NULL; -int nBestHeight = -1; -uint256 nBestChainWork = 0; +CChain chainActive; uint256 nBestInvalidWork = 0; -uint256 hashBestChain = 0; -CBlockIndex* pindexBest = NULL; 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; @@ -67,9 +62,6 @@ CScript COINBASE_FLAGS; const string strMessageMagic = "Bitcoin Signed Message:\n"; -double dHashesPerSec = 0.0; -int64 nHPSTimerStart = 0; - // Settings int64 nTransactionFee = 0; @@ -176,106 +168,83 @@ void static ResendWalletTransactions() // 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); } ////////////////////////////////////////////////////////////////////////////// // -// CBlockLocator implementation +// CChain implementation // -CBlockLocator::CBlockLocator(uint256 hashBlock) -{ - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end()) - Set((*mi).second); +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; } -void CBlockLocator::Set(const CBlockIndex* pindex) -{ - vHave.clear(); +CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const { int nStep = 1; - while (pindex) - { - vHave.push_back(pindex->GetBlockHash()); + std::vector<uint256> vHave; + vHave.reserve(32); - // Exponentially larger steps back - for (int i = 0; pindex && i < nStep; i++) + 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; } - vHave.push_back(Params().HashGenesisBlock()); -} -int CBlockLocator::GetDistanceBack() -{ - // Retrace how far back it was in the sender's branch - int nDistance = 0; - int nStep = 1; - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return nDistance; - } - nDistance += nStep; - if (nDistance > 10) - nStep *= 2; - } - return nDistance; + return CBlockLocator(vHave); } -CBlockIndex *CBlockLocator::GetBlockIndex() -{ +CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { + 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 (pindex->IsInMainChain()) + if (Contains(pindex)) return pindex; } } - return pindexGenesisBlock; -} - -uint256 CBlockLocator::GetBlockHash() -{ - // Find the first block the caller has in the main chain - BOOST_FOREACH(const uint256& hash, vHave) - { - std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) - return hash; - } - } - return Params().HashGenesisBlock(); -} - -int CBlockLocator::GetHeight() -{ - CBlockIndex* pindex = GetBlockIndex(); - if (!pindex) - return 0; - return pindex->nHeight; + return Genesis(); } ////////////////////////////////////////////////////////////////////////////// @@ -415,7 +384,7 @@ bool AddOrphanTx(const CTransaction& tx) unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); if (sz > 5000) { - printf("ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString().c_str()); + LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString().c_str()); return false; } @@ -423,7 +392,7 @@ bool AddOrphanTx(const CTransaction& tx) BOOST_FOREACH(const CTxIn& txin, tx.vin) mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); - printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(), + LogPrint("mempool", "stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(), mapOrphanTransactions.size()); return true; } @@ -466,7 +435,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) bool IsStandardTx(const CTransaction& tx, string& reason) { - if (tx.nVersion > CTransaction::CURRENT_VERSION) { + if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) { reason = "version"; return false; } @@ -520,7 +489,7 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64 nBlockTime) if (tx.nLockTime == 0) return true; if (nBlockHeight == 0) - nBlockHeight = nBestHeight; + nBlockHeight = chainActive.Height(); if (nBlockTime == 0) nBlockTime = GetAdjustedTime(); if ((int64)tx.nLockTime < ((int64)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) @@ -647,7 +616,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) if (pblock == NULL) { CCoins coins; if (pcoinsTip->GetCoins(GetHash(), coins)) { - CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); + CBlockIndex *pindex = chainActive[coins.nHeight]; if (pindex) { if (!ReadBlockFromDisk(blockTmp, pindex)) return 0; @@ -668,7 +637,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { vMerkleBranch.clear(); nIndex = -1; - printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); + LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); return 0; } @@ -681,10 +650,10 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) + if (!pindex || !chainActive.Contains(pindex)) return 0; - return pindexBest->nHeight - pindex->nHeight + 1; + return chainActive.Height() - pindex->nHeight + 1; } @@ -787,7 +756,7 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) } bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs) + bool* pfMissingInputs, bool fRejectInsaneFee) { if (pfMissingInputs) *pfMissingInputs = false; @@ -917,10 +886,15 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); if (fDebug) - printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + 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)) @@ -934,7 +908,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL LOCK(cs); if (ptxOld) { - printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); + LogPrint("mempool", "CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); remove(*ptxOld); } addUnchecked(hash, tx); @@ -946,7 +920,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL EraseFromWallets(ptxOld->GetHash()); SyncWithWallets(hash, tx, NULL, true); - printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", + LogPrint("mempool", "CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", hash.ToString().c_str(), mapTx.size()); return true; @@ -1021,7 +995,7 @@ void CTxMemPool::check(CCoinsViewCache *pcoins) const if (!fChecks) return; - printf("Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); + 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++) { @@ -1076,7 +1050,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; - if (!pindex || !pindex->IsInMainChain()) + if (!pindex || !chainActive.Contains(pindex)) return 0; // Make sure the merkle branch connects to this block @@ -1088,7 +1062,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const } pindexRet = pindex; - return pindexBest->nHeight - pindex->nHeight + 1; + return chainActive.Height() - pindex->nHeight + 1; } @@ -1096,7 +1070,7 @@ int CMerkleTx::GetBlocksToMaturity() const { if (!IsCoinBase()) return 0; - return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); + return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); } @@ -1171,7 +1145,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock nHeight = coins.nHeight; } if (nHeight > 0) - pindexSlow = FindBlockByHeight(nHeight); + pindexSlow = chainActive[nHeight]; } } @@ -1201,14 +1175,6 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -static CBlockIndex* pblockindexFBBHLast; -CBlockIndex* FindBlockByHeight(int nHeight) -{ - if (nHeight >= (int)vBlockIndexByHeight.size()) - return NULL; - return vBlockIndexByHeight[nHeight]; -} - bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) { // Open history file to append @@ -1354,7 +1320,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead // Limit adjustment step int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); - printf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan); + LogPrintf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan); if (nActualTimespan < nTargetTimespan/4) nActualTimespan = nTargetTimespan/4; if (nActualTimespan > nTargetTimespan*4) @@ -1370,10 +1336,10 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead bnNew = Params().ProofOfWorkLimit(); /// debug print - printf("GetNextWorkRequired RETARGET\n"); - printf("nTargetTimespan = %"PRI64d" nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan); - printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str()); - printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str()); + 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(); } @@ -1402,17 +1368,17 @@ int GetNumBlocksOfPeers() bool IsInitialBlockDownload() { - if (pindexBest == NULL || fImporting || fReindex || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) + if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; - if (pindexBest != pindexLastBest) + if (chainActive.Tip() != pindexLastBest) { - pindexLastBest = pindexBest; + pindexLastBest = chainActive.Tip(); nLastUpdate = GetTime(); } return (GetTime() - nLastUpdate < 10 && - pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); + chainActive.Tip()->GetBlockTime() < GetTime() - 24 * 60 * 60); } bool fLargeWorkForkFound = false; @@ -1421,12 +1387,17 @@ 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 && nBestHeight - pindexBestForkTip->nHeight >= 72) + if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) pindexBestForkTip = NULL; - if (pindexBestForkTip || nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) + if (pindexBestForkTip || nBestInvalidWork > chainActive.Tip()->nChainWork + (chainActive.Tip()->GetBlockWork() * 6).getuint256()) { if (!fLargeWorkForkFound) { @@ -1441,14 +1412,14 @@ void CheckForkWarningConditions() } if (pindexBestForkTip) { - printf("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", + 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 { - printf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n"); + LogPrintf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n"); fLargeWorkInvalidChainFound = true; } } @@ -1463,7 +1434,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) { // If we are on a fork that is sufficiently large, set a warning flag CBlockIndex* pfork = pindexNewForkTip; - CBlockIndex* plonger = pindexBest; + CBlockIndex* plonger = chainActive.Tip(); while (pfork && pfork != plonger) { while (plonger && plonger->nHeight > pfork->nHeight) @@ -1482,7 +1453,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) // 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() && - nBestHeight - pindexNewForkTip->nHeight < 72) + chainActive.Height() - pindexNewForkTip->nHeight < 72) { pindexBestForkTip = pindexNewForkTip; pindexBestForkBase = pfork; @@ -1499,13 +1470,13 @@ void static InvalidChainFound(CBlockIndex* pindexNew) pblocktree->WriteBestInvalidWork(CBigNum(nBestInvalidWork)); uiInterface.NotifyBlocksChanged(); } - printf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", + 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()); - printf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", - hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->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(); } @@ -1514,7 +1485,7 @@ void static InvalidBlockFound(CBlockIndex *pindex) { pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); setBlockIndexValid.erase(pindex); InvalidChainFound(pindex); - if (pindex->GetNextInMainChain()) { + if (chainActive.Next(pindex)) { CValidationState stateDummy; ConnectBestBlock(stateDummy); // reorganise away from the failed block } @@ -1531,7 +1502,7 @@ bool ConnectBestBlock(CValidationState &state) { pindexNewBest = *it; } - if (pindexNewBest == pindexBest || (pindexBest && pindexNewBest->nChainWork == pindexBest->nChainWork)) + if (pindexNewBest == chainActive.Tip() || (chainActive.Tip() && pindexNewBest->nChainWork == chainActive.Tip()->nChainWork)) return true; // nothing to do // check ancestry @@ -1551,10 +1522,10 @@ bool ConnectBestBlock(CValidationState &state) { break; } - if (pindexBest == NULL || pindexTest->nChainWork > pindexBest->nChainWork) + if (chainActive.Tip() == NULL || pindexTest->nChainWork > chainActive.Tip()->nChainWork) vAttach.push_back(pindexTest); - if (pindexTest->pprev == NULL || pindexTest->GetNextInMainChain()) { + if (pindexTest->pprev == NULL || chainActive.Next(pindexTest)) { reverse(vAttach.begin(), vAttach.end()); BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { boost::this_thread::interruption_point(); @@ -1771,8 +1742,14 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex view.SetCoins(hash, CCoins()); } CCoins &outs = view.GetCoins(hash); + 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"); @@ -1868,7 +1845,6 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C // (its coinbase is unspendable) if (block.GetHash() == Params().HashGenesisBlock()) { view.SetBestBlock(pindex); - pindexGenesisBlock = pindex; return true; } @@ -1957,7 +1933,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C } int64 nTime = GetTimeMicros() - nStart; if (fBenchmark) - printf("- 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)); + 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))); @@ -1966,7 +1942,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return state.DoS(100, false); int64 nTime2 = GetTimeMicros() - nStart; if (fBenchmark) - printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1)); + 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; @@ -2042,8 +2018,8 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) reverse(vConnect.begin(), vConnect.end()); if (vDisconnect.size() > 0) { - printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..\n", vDisconnect.size(), pfork->GetBlockHash().ToString().c_str()); - printf("REORGANIZE: Connect %"PRIszu" blocks; ..%s\n", vConnect.size(), pindexNew->GetBlockHash().ToString().c_str()); + 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 @@ -2056,7 +2032,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) if (!DisconnectBlock(block, state, pindex, view)) return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); if (fBenchmark) - printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + 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 @@ -2081,7 +2057,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); } if (fBenchmark) - printf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + LogPrintf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); // Queue memory transactions to delete BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2094,7 +2070,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) assert(view.Flush()); int64 nTime = GetTimeMicros() - nStart; if (fBenchmark) - printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); + 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(); @@ -2116,9 +2092,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // Proceed by updating the memory structures. // Register new best chain - vBlockIndexByHeight.resize(pindexNew->nHeight + 1); - BOOST_FOREACH(CBlockIndex* pindex, vConnect) - vBlockIndexByHeight[pindex->nHeight] = pindex; + chainActive.SetTip(pindexNew); // Resurrect memory transactions that were in the disconnected branch BOOST_FOREACH(CTransaction& tx, vResurrect) { @@ -2138,29 +2112,21 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // Update best block in wallet (so we can detect restored wallets) if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) - { - const CBlockLocator locator(pindexNew); - ::SetBestChain(locator); - } + ::SetBestChain(chainActive.GetLocator(pindexNew)); // New best block - hashBestChain = pindexNew->GetBlockHash(); - pindexBest = pindexNew; - pblockindexFBBHLast = NULL; - nBestHeight = pindexBest->nHeight; - nBestChainWork = pindexNew->nChainWork; nTimeBestReceived = GetTime(); nTransactionsUpdated++; - printf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", - hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str(), - Checkpoints::GuessVerificationProgress(pindexBest)); + 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 = pindexBest; + const CBlockIndex* pindex = chainActive.Tip(); for (int i = 0; i < 100 && pindex != NULL; i++) { if (pindex->nVersion > CBlock::CURRENT_VERSION) @@ -2168,7 +2134,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) pindex = pindex->pprev; } if (nUpgraded > 0) - printf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, CBlock::CURRENT_VERSION); + 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!"); @@ -2178,7 +2144,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) if (!fIsInitialDownload && !strCmd.empty()) { - boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); + boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } @@ -2220,7 +2186,7 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos if (!ConnectBestBlock(state)) return false; - if (pindexNew == pindexBest) + if (pindexNew == chainActive.Tip()) { // Clear fork warning if its no longer applicable CheckForkWarningConditions(); @@ -2254,7 +2220,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd } } else { while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { - printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); + LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); FlushBlockFile(true); nLastBlockFile++; infoLastBlockFile.SetNull(); @@ -2275,7 +2241,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) { FILE *file = OpenBlockFile(pos); if (file) { - printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); + 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); } @@ -2321,7 +2287,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) { FILE *file = OpenUndoFile(pos); if (file) { - printf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); + 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); } @@ -2469,11 +2435,11 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) // Relay inventory, but don't relay old inventory during initial block download int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); - if (hashBestChain == hash) + if (chainActive.Tip()->GetBlockHash() == hash) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) - if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) pnode->PushInventory(CInv(MSG_BLOCK, hash)); } @@ -2492,6 +2458,18 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns 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 @@ -2500,7 +2478,7 @@ void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) pnode->pindexLastGetBlocksBegin = pindexBegin; pnode->hashLastGetBlocksEnd = hashEnd; - pnode->PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); + pnode->PushMessage("getblocks", chainActive.GetLocator(pindexBegin), hashEnd); } bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) @@ -2517,7 +2495,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl return error("ProcessBlock() : CheckBlock FAILED"); CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); - if (pcheckpoint && pblock->hashPrevBlock != hashBestChain) + 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; @@ -2539,7 +2517,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl // 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)) { - printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().c_str()); + 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) { @@ -2548,7 +2526,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); // Ask this guy to fill in what we're missing - PushGetBlocks(pfrom, pindexBest, GetOrphanRoot(pblock2)); + PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(pblock2)); } return true; } @@ -2578,7 +2556,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl mapOrphanBlocksByPrev.erase(hashPrev); } - printf("ProcessBlock: ACCEPTED\n"); + LogPrintf("ProcessBlock: ACCEPTED\n"); return true; } @@ -2744,7 +2722,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) { bool AbortNode(const std::string &strMessage) { strMiscWarning = strMessage; - printf("*** %s\n", strMessage.c_str()); + LogPrintf("*** %s\n", strMessage.c_str()); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; @@ -2775,12 +2753,12 @@ FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) if (!file && !fReadOnly) file = fopen(path.string().c_str(), "wb+"); if (!file) { - printf("Unable to open file %s\n", path.string().c_str()); + LogPrintf("Unable to open file %s\n", path.string().c_str()); return NULL; } if (pos.nPos) { if (fseek(file, pos.nPos, SEEK_SET)) { - printf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str()); + LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str()); fclose(file); return NULL; } @@ -2843,9 +2821,9 @@ bool static LoadBlockIndexDB() // Load block file info pblocktree->ReadLastBlockFile(nLastBlockFile); - printf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile); + LogPrintf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile); if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - printf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString().c_str()); + LogPrintf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString().c_str()); // Load nBestInvalidWork, OK if it doesn't exist CBigNum bnBestInvalidWork; @@ -2859,51 +2837,42 @@ bool static LoadBlockIndexDB() // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); - printf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); + LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); // Load hashBestChain pointer to end of best chain - pindexBest = pcoinsTip->GetBestBlock(); - if (pindexBest == NULL) + chainActive.SetTip(pcoinsTip->GetBestBlock()); + if (chainActive.Tip() == NULL) return true; - hashBestChain = pindexBest->GetBlockHash(); - nBestHeight = pindexBest->nHeight; - nBestChainWork = pindexBest->nChainWork; // register best chain - CBlockIndex *pindex = pindexBest; - vBlockIndexByHeight.resize(pindexBest->nHeight + 1); - while(pindex != NULL) { - vBlockIndexByHeight[pindex->nHeight] = pindex; - pindex = pindex->pprev; - } - printf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", - hashBestChain.ToString().c_str(), nBestHeight, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + 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 (pindexBest == NULL || pindexBest->pprev == NULL) + 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 > nBestHeight) - nCheckDepth = nBestHeight; + if (nCheckDepth > chainActive.Height()) + nCheckDepth = chainActive.Height(); nCheckLevel = std::max(0, std::min(4, nCheckLevel)); - printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); + LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CCoinsViewCache coins(*pcoinsTip, true); - CBlockIndex* pindexState = pindexBest; + CBlockIndex* pindexState = chainActive.Tip(); CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; CValidationState state; - for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) + for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); - if (pindex->nHeight < nBestHeight-nCheckDepth) + if (pindex->nHeight < chainActive.Height()-nCheckDepth) break; CBlock block; // check level 0: read from disk @@ -2935,14 +2904,14 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) } } if (pindexFailure) - return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", pindexBest->nHeight - pindexFailure->nHeight + 1, nGoodTransactions); + 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 != pindexBest) { + while (pindex != chainActive.Tip()) { boost::this_thread::interruption_point(); - pindex = pindex->GetNextInMainChain(); + 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()); @@ -2951,7 +2920,7 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) } } - printf("No coin database inconsistencies in last %i blocks (%i transactions)\n", pindexBest->nHeight - pindexState->nHeight, nGoodTransactions); + LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); return true; } @@ -2960,12 +2929,8 @@ void UnloadBlockIndex() { mapBlockIndex.clear(); setBlockIndexValid.clear(); - pindexGenesisBlock = NULL; - nBestHeight = 0; - nBestChainWork = 0; + chainActive.SetTip(NULL); nBestInvalidWork = 0; - hashBestChain = 0; - pindexBest = NULL; } bool LoadBlockIndex() @@ -2979,13 +2944,13 @@ bool LoadBlockIndex() bool InitBlockIndex() { // Check whether we're already initialized - if (pindexGenesisBlock != NULL) + if (chainActive.Genesis() != NULL) return true; // Use the provided setting for -txindex in the new database fTxIndex = GetBoolArg("-txindex", false); pblocktree->WriteFlag("txindex", fTxIndex); - printf("Initializing databases...\n"); + LogPrintf("Initializing databases...\n"); // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) if (!fReindex) { @@ -3025,7 +2990,7 @@ void PrintBlockTree() } vector<pair<int, CBlockIndex*> > vStack; - vStack.push_back(make_pair(0, pindexGenesisBlock)); + vStack.push_back(make_pair(0, chainActive.Genesis())); int nPrevCol = 0; while (!vStack.empty()) @@ -3038,25 +3003,25 @@ void PrintBlockTree() if (nCol > nPrevCol) { for (int i = 0; i < nCol-1; i++) - printf("| "); - printf("|\\\n"); + LogPrintf("| "); + LogPrintf("|\\\n"); } else if (nCol < nPrevCol) { for (int i = 0; i < nCol; i++) - printf("| "); - printf("|\n"); + LogPrintf("| "); + LogPrintf("|\n"); } nPrevCol = nCol; // print columns for (int i = 0; i < nCol; i++) - printf("| "); + LogPrintf("| "); // print item CBlock block; ReadBlockFromDisk(block, pindex); - printf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"", + 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(), @@ -3068,7 +3033,7 @@ void PrintBlockTree() vector<CBlockIndex*>& vNext = mapNext[pindex]; for (unsigned int i = 0; i < vNext.size(); i++) { - if (vNext[i]->GetNextInMainChain()) + if (chainActive.Next(vNext[i])) { swap(vNext[0], vNext[i]); break; @@ -3141,7 +3106,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) break; } } catch (std::exception &e) { - printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); + LogPrintf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); } } fclose(fileIn); @@ -3149,7 +3114,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) AbortNode(_("Error: system error: ") + e.what()); } if (nLoaded > 0) - printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); + LogPrintf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); return nLoaded > 0; } @@ -3315,7 +3280,7 @@ void static ProcessGetData(CNode* pfrom) // 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, hashBestChain)); + vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); pfrom->PushMessage("inv", vInv); pfrom->hashContinue = 0; } @@ -3371,11 +3336,10 @@ void static ProcessGetData(CNode* pfrom) bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { RandAddSeedPerfmon(); - if (fDebug) - printf("received: %s (%"PRIszu" bytes)\n", strCommand.c_str(), vRecv.size()); + LogPrint("net", "received: %s (%"PRIszu" bytes)\n", strCommand.c_str(), vRecv.size()); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { - printf("dropmessagestest DROPPING RECV MESSAGE\n"); + LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; } @@ -3401,7 +3365,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // Since February 20, 2012, the protocol is initiated at version 209, // and earlier versions are no longer supported - printf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); + LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); pfrom->fDisconnect = true; return false; } @@ -3428,7 +3392,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) { - printf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str()); + LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str()); pfrom->fDisconnect = true; return true; } @@ -3479,7 +3443,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; - printf("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()); + 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()); cPeerBlockCounts.input(pfrom->nStartingHeight); } @@ -3592,21 +3556,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(inv); - if (fDebug) - printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); + 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, pindexBest, GetOrphanRoot(mapOrphanBlocks[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) - printf("force request: %s\n", inv.ToString().c_str()); + LogPrintf("force request: %s\n", inv.ToString().c_str()); } // Track requests for our stuff @@ -3626,10 +3589,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } if (fDebugNet || (vInv.size() != 1)) - printf("received getdata (%"PRIszu" invsz)\n", vInv.size()); + LogPrint("net", "received getdata (%"PRIszu" invsz)\n", vInv.size()); if ((fDebugNet && vInv.size() > 0) || (vInv.size() == 1)) - printf("received getdata for: %s\n", vInv[0].ToString().c_str()); + LogPrint("net", "received getdata for: %s\n", vInv[0].ToString().c_str()); pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(pfrom); @@ -3643,18 +3606,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> locator >> hashStop; // Find the last block the caller has in the main chain - CBlockIndex* pindex = locator.GetBlockIndex(); + CBlockIndex* pindex = chainActive.FindFork(locator); // Send the rest of the chain if (pindex) - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); int nLimit = 500; - printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit); - for (; pindex; pindex = pindex->GetNextInMainChain()) + 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) { - printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); @@ -3662,7 +3625,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // When this block is requested, we'll send an inv that'll make them // getblocks the next batch of inventory. - printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); pfrom->hashContinue = pindex->GetBlockHash(); break; } @@ -3688,16 +3651,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else { // Find the last block the caller has in the main chain - pindex = locator.GetBlockIndex(); + pindex = chainActive.FindFork(locator); if (pindex) - pindex = pindex->GetNextInMainChain(); + 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; - printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str()); - for (; pindex; pindex = pindex->GetNextInMainChain()) + 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) @@ -3746,7 +3709,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (mempool.accept(stateDummy, orphanTx, true, &fMissingInputs2)) { - printf(" accepted orphan tx %s\n", orphanHash.ToString().c_str()); + LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString().c_str()); RelayTransaction(orphanTx, orphanHash); mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash)); vWorkQueue.push_back(orphanHash); @@ -3756,7 +3719,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // invalid or too-little-fee orphan vEraseQueue.push_back(orphanHash); - printf(" removed orphan tx %s\n", orphanHash.ToString().c_str()); + LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString().c_str()); } mempool.check(pcoinsTip); } @@ -3772,11 +3735,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // DoS prevention: do not allow mapOrphanTransactions to grow unbounded unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); if (nEvicted > 0) - printf("mapOrphan overflow, removed %u tx\n", nEvicted); + LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); } - int nDoS; + int nDoS = 0; if (state.IsInvalid(nDoS)) - pfrom->Misbehaving(nDoS); + if (nDoS > 0) + pfrom->Misbehaving(nDoS); } @@ -3785,7 +3749,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CBlock block; vRecv >> block; - printf("received block %s\n", block.GetHash().ToString().c_str()); + LogPrint("net", "received block %s\n", block.GetHash().ToString().c_str()); // block.print(); CInv inv(MSG_BLOCK, block.GetHash()); @@ -3794,9 +3758,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CValidationState state; if (ProcessBlock(state, pfrom, &block)) mapAlreadyAskedFor.erase(inv); - int nDoS; + int nDoS = 0; if (state.IsInvalid(nDoS)) - pfrom->Misbehaving(nDoS); + if (nDoS > 0) + pfrom->Misbehaving(nDoS); } @@ -3850,6 +3815,63 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + 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; @@ -3948,7 +3970,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) bool ProcessMessages(CNode* pfrom) { //if (fDebug) - // printf("ProcessMessages(%zu messages)\n", pfrom->vRecvMsg.size()); + // LogPrintf("ProcessMessages(%"PRIszu" messages)\n", pfrom->vRecvMsg.size()); // // Message format @@ -3973,7 +3995,7 @@ bool ProcessMessages(CNode* pfrom) CNetMessage& msg = *it; //if (fDebug) - // printf("ProcessMessages(message %u msgsz, %zu bytes, complete:%s)\n", + // LogPrintf("ProcessMessages(message %u msgsz, %"PRIszu" bytes, complete:%s)\n", // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); @@ -3986,7 +4008,7 @@ bool ProcessMessages(CNode* pfrom) // Scan for message start if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { - printf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); + LogPrintf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); fOk = false; break; } @@ -3995,7 +4017,7 @@ bool ProcessMessages(CNode* pfrom) CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid()) { - printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); + LogPrintf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); continue; } string strCommand = hdr.GetCommand(); @@ -4010,7 +4032,7 @@ bool ProcessMessages(CNode* pfrom) memcpy(&nChecksum, &hash, sizeof(nChecksum)); if (nChecksum != hdr.nChecksum) { - printf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + LogPrintf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); continue; } @@ -4030,12 +4052,12 @@ bool ProcessMessages(CNode* pfrom) if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv - printf("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()); + 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 - printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); + LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); } else { @@ -4052,7 +4074,7 @@ bool ProcessMessages(CNode* pfrom) } if (!fRet) - printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + LogPrintf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); } // In case the connection got shut down, its receive buffer was wiped @@ -4071,20 +4093,40 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (pto->nVersion == 0) return true; - // Keep-alive ping. We send a nonce of zero because we don't use it anywhere - // right now. + // + // 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; - if (pto->nVersion > BIP0031_VERSION) + 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 + } else { + // Peer is too old to support ping command with nonce, pong will never arrive, disable timing + pto->nPingUsecStart = 0; pto->PushMessage("ping"); + } } // Start block sync if (pto->fStartSync && !fImporting && !fReindex) { pto->fStartSync = false; - PushGetBlocks(pto, pindexBest, uint256(0)); + PushGetBlocks(pto, chainActive.Tip(), uint256(0)); } // Resend wallet transactions that haven't gotten in a block yet @@ -4215,7 +4257,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!AlreadyHave(inv)) { if (fDebugNet) - printf("sending getdata: %s\n", inv.ToString().c_str()); + LogPrint("net", "sending getdata: %s\n", inv.ToString().c_str()); vGetData.push_back(inv); if (vGetData.size() >= 1000) { diff --git a/src/main.h b/src/main.h index 38079cb6ec..fc60ccc0b5 100644 --- a/src/main.h +++ b/src/main.h @@ -74,20 +74,12 @@ extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern std::map<uint256, CBlockIndex*> mapBlockIndex; -extern std::vector<CBlockIndex*> vBlockIndexByHeight; extern std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; -extern CBlockIndex* pindexGenesisBlock; -extern int nBestHeight; -extern uint256 nBestChainWork; extern uint256 nBestInvalidWork; -extern uint256 hashBestChain; -extern CBlockIndex* pindexBest; extern unsigned int nTransactionsUpdated; extern uint64 nLastBlockTx; extern uint64 nLastBlockSize; extern const std::string strMessageMagic; -extern double dHashesPerSec; -extern int64 nHPSTimerStart; extern int64 nTimeBestReceived; extern CCriticalSection cs_setpwalletRegistered; extern std::set<CWallet*> setpwalletRegistered; @@ -155,8 +147,6 @@ void UnloadBlockIndex(); bool VerifyDB(int nCheckLevel, int nCheckDepth); /** Print the loaded block tree */ void PrintBlockTree(); -/** Find a block by height in the currently-connected chain */ -CBlockIndex* FindBlockByHeight(int nHeight); /** Process protocol messages received from a given node */ bool ProcessMessages(CNode* pfrom); /** Send queued protocol messages to be sent to a give node */ @@ -655,7 +645,7 @@ public: if (nBlocks==0 || nTimeFirst > nTimeIn) nTimeFirst = nTimeIn; nBlocks++; - if (nHeightIn > nHeightFirst) + if (nHeightIn > nHeightLast) nHeightLast = nHeightIn; if (nTimeIn > nTimeLast) nTimeLast = nTimeIn; @@ -821,15 +811,6 @@ public: return (CBigNum(1)<<256) / (bnTarget+1); } - bool IsInMainChain() const - { - return nHeight < (int)vBlockIndexByHeight.size() && vBlockIndexByHeight[nHeight] == this; - } - - CBlockIndex *GetNextInMainChain() const { - return nHeight+1 >= (int)vBlockIndexByHeight.size() ? NULL : vBlockIndexByHeight[nHeight+1]; - } - bool CheckIndex() const { return CheckProofOfWork(GetBlockHash(), nBits); @@ -851,17 +832,7 @@ public: return pbegin[(pend - pbegin)/2]; } - int64 GetMedianTime() const - { - const CBlockIndex* pindex = this; - for (int i = 0; i < nMedianTimeSpan/2; i++) - { - if (!pindex->GetNextInMainChain()) - return GetBlockTime(); - pindex = pindex->GetNextInMainChain(); - } - return pindex->GetMedianTimePast(); - } + int64 GetMedianTime() const; /** * Returns true if there are nRequired or more blocks of minVersion or above @@ -872,15 +843,15 @@ public: std::string ToString() const { - return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", - pprev, GetNextInMainChain(), nHeight, + return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, nHeight, hashMerkleRoot.ToString().c_str(), GetBlockHash().ToString().c_str()); } void print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } }; @@ -962,7 +933,7 @@ public: void print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } }; @@ -1013,64 +984,67 @@ public: } }; +/** An in-memory indexed chain of blocks. */ +class CChain { +private: + std::vector<CBlockIndex*> vChain; - - - - - -/** Describes a place in the block chain to another node such that if the - * other node doesn't have the same branch, it can find a recent common trunk. - * The further back it is, the further before the fork it may be. - */ -class CBlockLocator -{ -protected: - std::vector<uint256> vHave; public: - CBlockLocator() {} + /** Returns the index entry for the genesis block of this chain, or NULL if none. */ + CBlockIndex *Genesis() const { + return vChain.size() > 0 ? vChain[0] : NULL; + } - explicit CBlockLocator(const CBlockIndex* pindex) - { - Set(pindex); + /** Returns the index entry for the tip of this chain, or NULL if none. */ + CBlockIndex *Tip() const { + return vChain.size() > 0 ? vChain[vChain.size() - 1] : NULL; } - explicit CBlockLocator(uint256 hashBlock); + /** Returns the index entry at a particular height in this chain, or NULL if no such height exists. */ + CBlockIndex *operator[](int nHeight) const { + if (nHeight < 0 || nHeight >= (int)vChain.size()) + return NULL; + return vChain[nHeight]; + } - CBlockLocator(const std::vector<uint256>& vHaveIn) - { - vHave = vHaveIn; + /** Compare two chains efficiently. */ + friend bool operator==(const CChain &a, const CChain &b) { + return a.vChain.size() == b.vChain.size() && + a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1]; } - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vHave); - ) + /** Efficiently check whether a block is present in this chain. */ + bool Contains(const CBlockIndex *pindex) const { + return (*this)[pindex->nHeight] == pindex; + } - void SetNull() - { - vHave.clear(); + /** Find the successor of a block in this chain, or NULL if the given index is not found or is the tip. */ + CBlockIndex *Next(const CBlockIndex *pindex) const { + if (Contains(pindex)) + return (*this)[pindex->nHeight + 1]; + else + return NULL; } - bool IsNull() - { - return vHave.empty(); + /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */ + int Height() const { + return vChain.size() - 1; } - /** Given a block initialises the locator to that point in the chain. */ - void Set(const CBlockIndex* pindex); - /** Returns the distance in blocks this locator is from our chain head. */ - int GetDistanceBack(); - /** Returns the first best-chain block the locator contains. */ - CBlockIndex* GetBlockIndex(); - /** Returns the hash of the first best chain block the locator contains. */ - uint256 GetBlockHash(); - /** Returns the height of the first best chain block the locator has. */ - int GetHeight(); + /** Set/initialize a chain with a given tip. Returns the forking point. */ + CBlockIndex *SetTip(CBlockIndex *pindex); + + /** Return a CBlockLocator that refers to a block in this chain (by default the tip). */ + CBlockLocator GetLocator(const CBlockIndex *pindex = NULL) const; + + /** Find the last common block between this chain and a locator. */ + CBlockIndex *FindFork(const CBlockLocator &locator) const; }; +/** The currently-connected chain of blocks. */ +extern CChain chainActive; + + @@ -1086,7 +1060,7 @@ public: std::map<uint256, CTransaction> mapTx; std::map<COutPoint, CInPoint> mapNextTx; - bool accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs); + bool accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee = false); bool addUnchecked(const uint256& hash, const CTransaction &tx); bool remove(const CTransaction &tx, bool fRecursive = false); bool removeConflicts(const CTransaction &tx); @@ -1214,7 +1188,7 @@ public: @see CTransaction::FetchInputs */ int64 GetValueIn(const CTransaction& tx); - + // Check whether all prevouts of the transaction are present in the UTXO set represented by this view bool HaveInputs(const CTransaction& tx); diff --git a/src/miner.cpp b/src/miner.cpp index 3ecf1609e1..e9c1d9aff9 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -3,15 +3,11 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "core.h" -#include "wallet.h" #include "miner.h" #include "main.h" - - - - +double dHashesPerSec = 0.0; +int64 nHPSTimerStart = 0; ////////////////////////////////////////////////////////////////////////////// // @@ -106,10 +102,10 @@ public: void print() const { - printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n", + LogPrintf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n", ptx->GetHash().ToString().c_str(), dPriority, dFeePerKb); BOOST_FOREACH(uint256 hash, setDependsOn) - printf(" setDependsOn %s\n", hash.ToString().c_str()); + LogPrintf(" setDependsOn %s\n", hash.ToString().c_str()); } }; @@ -180,7 +176,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) int64 nFees = 0; { LOCK2(cs_main, mempool.cs); - CBlockIndex* pindexPrev = pindexBest; + CBlockIndex* pindexPrev = chainActive.Tip(); CCoinsViewCache view(*pcoinsTip, true); // Priority order to process transactions @@ -211,7 +207,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // or other transactions in the memory pool. if (!mempool.mapTx.count(txin.prevout.hash)) { - printf("ERROR: mempool transaction missing input\n"); + LogPrintf("ERROR: mempool transaction missing input\n"); if (fDebug) assert("mempool transaction missing input" == 0); fMissingInputs = true; if (porphan) @@ -331,7 +327,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) if (fPrintPriority) { - printf("priority %.1f feeperkb %.1f txid %s\n", + LogPrintf("priority %.1f feeperkb %.1f txid %s\n", dPriority, dFeePerKb, tx.GetHash().ToString().c_str()); } @@ -355,7 +351,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; - printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); + LogPrintf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); pblocktemplate->vTxFees[0] = -nFees; @@ -463,15 +459,15 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) return false; //// debug print - printf("BitcoinMiner:\n"); - printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + LogPrintf("BitcoinMiner:\n"); + LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); pblock->print(); - printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); + LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); // Found a solution { LOCK(cs_main); - if (pblock->hashPrevBlock != hashBestChain) + if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash()) return error("BitcoinMiner : generated block is stale"); // Remove key from key pool @@ -494,7 +490,7 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) void static BitcoinMiner(CWallet *pwallet) { - printf("BitcoinMiner started\n"); + LogPrintf("BitcoinMiner started\n"); SetThreadPriority(THREAD_PRIORITY_LOWEST); RenameThread("bitcoin-miner"); @@ -514,7 +510,7 @@ void static BitcoinMiner(CWallet *pwallet) // Create new block // unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrev = pindexBest; + CBlockIndex* pindexPrev = chainActive.Tip(); auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); if (!pblocktemplate.get()) @@ -522,7 +518,7 @@ void static BitcoinMiner(CWallet *pwallet) CBlock *pblock = &pblocktemplate->block; IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), + LogPrintf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); // @@ -603,7 +599,7 @@ void static BitcoinMiner(CWallet *pwallet) if (GetTime() - nLogTime > 30 * 60) { nLogTime = GetTime(); - printf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0); + LogPrintf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0); } } } @@ -617,7 +613,7 @@ void static BitcoinMiner(CWallet *pwallet) break; if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) break; - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) break; // Update nTime every few seconds @@ -633,7 +629,7 @@ void static BitcoinMiner(CWallet *pwallet) } } catch (boost::thread_interrupted) { - printf("BitcoinMiner terminated\n"); + LogPrintf("BitcoinMiner terminated\n"); throw; } } diff --git a/src/miner.h b/src/miner.h index 36d58be00f..7e60b9e53b 100644 --- a/src/miner.h +++ b/src/miner.h @@ -22,4 +22,7 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); /** Base sha256 mining transform */ void SHA256Transform(void* pstate, void* pinput, const void* pinit); +extern double dHashesPerSec; +extern int64 nHPSTimerStart; + #endif // BITCOIN_MINER_H diff --git a/src/net.cpp b/src/net.cpp index 02b99fd049..99457be0f5 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -173,14 +173,14 @@ bool RecvLine(SOCKET hSocket, string& strLine) if (nBytes == 0) { // socket closed - printf("socket closed\n"); + LogPrint("net", "socket closed\n"); return false; } else { // socket error int nErr = WSAGetLastError(); - printf("recv failed: %d\n", nErr); + LogPrint("net", "recv failed: %d\n", nErr); return false; } } @@ -226,7 +226,7 @@ bool AddLocal(const CService& addr, int nScore) if (IsLimited(addr)) return false; - printf("AddLocal(%s,%i)\n", addr.ToString().c_str(), nScore); + LogPrintf("AddLocal(%s,%i)\n", addr.ToString().c_str(), nScore); { LOCK(cs_mapLocalHost); @@ -334,7 +334,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha while (strLine.size() > 0 && isspace(strLine[strLine.size()-1])) strLine.resize(strLine.size()-1); CService addr(strLine,0,true); - printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); + LogPrintf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); if (!addr.IsValid() || !addr.IsRoutable()) return false; ipRet.SetIP(addr); @@ -409,7 +409,7 @@ void ThreadGetMyExternalIP() CNetAddr addrLocalHost; if (GetMyExternalIP(addrLocalHost)) { - printf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str()); + LogPrintf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP().c_str()); AddLocal(addrLocalHost, LOCAL_HTTP); } } @@ -426,8 +426,10 @@ void AddressCurrentlyConnected(const CService& addr) - - +uint64 CNode::nTotalBytesRecv = 0; +uint64 CNode::nTotalBytesSent = 0; +CCriticalSection CNode::cs_totalBytesRecv; +CCriticalSection CNode::cs_totalBytesSent; CNode* FindNode(const CNetAddr& ip) { @@ -473,7 +475,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) /// debug print - printf("trying connection %s lastseen=%.1fhrs\n", + LogPrint("net", "trying connection %s lastseen=%.1fhrs\n", pszDest ? pszDest : addrConnect.ToString().c_str(), pszDest ? 0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); @@ -483,17 +485,16 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) { addrman.Attempt(addrConnect); - /// debug print - printf("connected %s\n", pszDest ? pszDest : addrConnect.ToString().c_str()); + LogPrint("net", "connected %s\n", pszDest ? pszDest : addrConnect.ToString().c_str()); // Set to non-blocking #ifdef WIN32 u_long nOne = 1; if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) - printf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError()); + LogPrintf("ConnectSocket() : ioctlsocket non-blocking setting failed, error %d\n", WSAGetLastError()); #else if (fcntl(hSocket, F_SETFL, O_NONBLOCK) == SOCKET_ERROR) - printf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno); + LogPrintf("ConnectSocket() : fcntl non-blocking setting failed, error %d\n", errno); #endif // Add node @@ -519,7 +520,7 @@ void CNode::CloseSocketDisconnect() fDisconnect = true; if (hSocket != INVALID_SOCKET) { - printf("disconnecting node %s\n", addrName.c_str()); + LogPrint("net", "disconnecting node %s\n", addrName.c_str()); closesocket(hSocket); hSocket = INVALID_SOCKET; } @@ -541,12 +542,14 @@ void CNode::Cleanup() void CNode::PushVersion() { + int nBestHeight = g_signals.GetHeight().get_value_or(0); + /// when NTP implemented, change to just nTime = GetAdjustedTime() int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); CAddress addrMe = GetLocalAddress(&addr); RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); - printf("send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString().c_str(), addrYou.ToString().c_str(), addr.ToString().c_str()); + LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString().c_str(), addrYou.ToString().c_str(), addr.ToString().c_str()); PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight, true); } @@ -583,7 +586,7 @@ bool CNode::Misbehaving(int howmuch) { if (addr.IsLocal()) { - printf("Warning: Local node %s misbehaving (delta: %d)!\n", addrName.c_str(), howmuch); + LogPrintf("Warning: Local node %s misbehaving (delta: %d)!\n", addrName.c_str(), howmuch); return false; } @@ -591,7 +594,7 @@ bool CNode::Misbehaving(int howmuch) if (nMisbehavior >= GetArg("-banscore", 100)) { int64 banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban - printf("Misbehaving: %s (%d -> %d) DISCONNECTING\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); + LogPrintf("Misbehaving: %s (%d -> %d) DISCONNECTING\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); { LOCK(cs_setBanned); if (setBanned[addr] < banTime) @@ -600,7 +603,7 @@ bool CNode::Misbehaving(int howmuch) CloseSocketDisconnect(); return true; } else - printf("Misbehaving: %s (%d -> %d)\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); + LogPrintf("Misbehaving: %s (%d -> %d)\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); return false; } @@ -621,6 +624,21 @@ void CNode::copyStats(CNodeStats &stats) X(nSendBytes); X(nRecvBytes); stats.fSyncNode = (this == pnodeSync); + + // It is common for nodes with good ping times to suddenly become lagged, + // due to a new block arriving or other large transfer. + // Merely reporting pingtime might fool the caller into thinking the node was still responsive, + // since pingtime does not update until the ping is complete, which might take a while. + // So, if a ping is taking an unusually long time in flight, + // the caller can immediately detect that this is happening. + int64 nPingUsecWait = 0; + if ((0 != nPingNonceSent) && (0 != nPingUsecStart)) { + nPingUsecWait = GetTimeMicros() - nPingUsecStart; + } + + // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :) + stats.dPingTime = (((double)nPingUsecTime) / 1e6); + stats.dPingWait = (((double)nPingUsecWait) / 1e6); } #undef X @@ -717,6 +735,7 @@ void SocketSendData(CNode *pnode) pnode->nLastSend = GetTime(); pnode->nSendBytes += nBytes; pnode->nSendOffset += nBytes; + pnode->RecordBytesSent(nBytes); if (pnode->nSendOffset == data.size()) { pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); @@ -731,7 +750,7 @@ void SocketSendData(CNode *pnode) int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { - printf("socket send error %d\n", nErr); + LogPrintf("socket send error %d\n", nErr); pnode->CloseSocketDisconnect(); } } @@ -812,10 +831,9 @@ void ThreadSocketHandler() } } } - if (vNodes.size() != nPrevNodeCount) - { + if(vNodes.size() != nPrevNodeCount) { nPrevNodeCount = vNodes.size(); - uiInterface.NotifyNumConnectionsChanged(vNodes.size()); + uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); } @@ -891,7 +909,7 @@ void ThreadSocketHandler() if (have_fds) { int nErr = WSAGetLastError(); - printf("socket select error %d\n", nErr); + LogPrintf("socket select error %d\n", nErr); for (unsigned int i = 0; i <= hSocketMax; i++) FD_SET(i, &fdsetRecv); } @@ -919,7 +937,7 @@ void ThreadSocketHandler() if (hSocket != INVALID_SOCKET) if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) - printf("Warning: Unknown socket family\n"); + LogPrintf("Warning: Unknown socket family\n"); { LOCK(cs_vNodes); @@ -932,7 +950,7 @@ void ThreadSocketHandler() { int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK) - printf("socket error accept failed: %d\n", nErr); + LogPrintf("socket error accept failed: %d\n", nErr); } else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) { @@ -944,12 +962,12 @@ void ThreadSocketHandler() } else if (CNode::IsBanned(addr)) { - printf("connection from %s dropped (banned)\n", addr.ToString().c_str()); + LogPrintf("connection from %s dropped (banned)\n", addr.ToString().c_str()); closesocket(hSocket); } else { - printf("accepted connection %s\n", addr.ToString().c_str()); + LogPrint("net", "accepted connection %s\n", addr.ToString().c_str()); CNode* pnode = new CNode(hSocket, addr, "", true); pnode->AddRef(); { @@ -994,12 +1012,13 @@ void ThreadSocketHandler() pnode->CloseSocketDisconnect(); pnode->nLastRecv = GetTime(); pnode->nRecvBytes += nBytes; + pnode->RecordBytesRecv(nBytes); } else if (nBytes == 0) { // socket closed gracefully if (!pnode->fDisconnect) - printf("socket closed\n"); + LogPrint("net", "socket closed\n"); pnode->CloseSocketDisconnect(); } else if (nBytes < 0) @@ -1009,7 +1028,7 @@ void ThreadSocketHandler() if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { if (!pnode->fDisconnect) - printf("socket recv error %d\n", nErr); + LogPrintf("socket recv error %d\n", nErr); pnode->CloseSocketDisconnect(); } } @@ -1038,17 +1057,17 @@ void ThreadSocketHandler() { if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) { - printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); + LogPrint("net", "socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); pnode->fDisconnect = true; } else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60) { - printf("socket not sending\n"); + LogPrintf("socket not sending\n"); pnode->fDisconnect = true; } else if (GetTime() - pnode->nLastRecv > 90*60) { - printf("socket inactivity timeout\n"); + LogPrintf("socket inactivity timeout\n"); pnode->fDisconnect = true; } } @@ -1100,16 +1119,16 @@ void ThreadMapPort() char externalIPAddress[40]; r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress); if(r != UPNPCOMMAND_SUCCESS) - printf("UPnP: GetExternalIPAddress() returned %d\n", r); + LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r); else { if(externalIPAddress[0]) { - printf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); + LogPrintf("UPnP: ExternalIPAddress = %s\n", externalIPAddress); AddLocal(CNetAddr(externalIPAddress), LOCAL_UPNP); } else - printf("UPnP: GetExternalIPAddress failed.\n"); + LogPrintf("UPnP: GetExternalIPAddress failed.\n"); } } @@ -1128,10 +1147,10 @@ void ThreadMapPort() #endif if(r!=UPNPCOMMAND_SUCCESS) - printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port.c_str(), port.c_str(), lanaddr, r, strupnperror(r)); else - printf("UPnP Port Mapping successful.\n");; + LogPrintf("UPnP Port Mapping successful.\n");; MilliSleep(20*60*1000); // Refresh every 20 minutes } @@ -1139,13 +1158,13 @@ void ThreadMapPort() catch (boost::thread_interrupted) { r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); - printf("UPNP_DeletePortMapping() returned : %d\n", r); + LogPrintf("UPNP_DeletePortMapping() returned : %d\n", r); freeUPNPDevlist(devlist); devlist = 0; FreeUPNPUrls(&urls); throw; } } else { - printf("No valid UPnP IGDs found\n"); + LogPrintf("No valid UPnP IGDs found\n"); freeUPNPDevlist(devlist); devlist = 0; if (r != 0) FreeUPNPUrls(&urls); @@ -1190,7 +1209,7 @@ void ThreadDNSAddressSeed() const vector<CDNSSeedData> &vSeeds = Params().DNSSeeds(); int found = 0; - printf("Loading addresses from DNS seeds (could take a while)\n"); + LogPrintf("Loading addresses from DNS seeds (could take a while)\n"); BOOST_FOREACH(const CDNSSeedData &seed, vSeeds) { if (HaveNameProxy()) { @@ -1213,7 +1232,7 @@ void ThreadDNSAddressSeed() } } - printf("%d addresses found from DNS seeds\n", found); + LogPrintf("%d addresses found from DNS seeds\n", found); } @@ -1234,7 +1253,7 @@ void DumpAddresses() CAddrDB adb; adb.Write(addrman); - printf("Flushed %d addresses to peers.dat %"PRI64d"ms\n", + LogPrint("net", "Flushed %d addresses to peers.dat %"PRI64d"ms\n", addrman.size(), GetTimeMillis() - nStart); } @@ -1292,7 +1311,7 @@ void ThreadOpenConnections() if (addrman.size() == 0 && (GetTime() - nStart > 60)) { static bool done = false; if (!done) { - printf("Adding fixed seed nodes as DNS doesn't seem to be available.\n"); + LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n"); addrman.Add(Params().FixedSeeds(), CNetAddr("127.0.0.1")); done = true; } @@ -1468,6 +1487,8 @@ void static StartSync(const vector<CNode*> &vNodes) { CNode *pnodeNewSync = NULL; double dBestScore = 0; + int nBestHeight = g_signals.GetHeight().get_value_or(0); + // Iterate over all nodes BOOST_FOREACH(CNode* pnode, vNodes) { // check preconditions for allowing a sync @@ -1568,7 +1589,7 @@ bool BindListenPort(const CService &addrBind, string& strError) if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { strError = strprintf("Error: bind address family for %s not supported", addrBind.ToString().c_str()); - printf("%s\n", strError.c_str()); + LogPrintf("%s\n", strError.c_str()); return false; } @@ -1576,7 +1597,7 @@ bool BindListenPort(const CService &addrBind, string& strError) if (hListenSocket == INVALID_SOCKET) { strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); + LogPrintf("%s\n", strError.c_str()); return false; } @@ -1600,7 +1621,7 @@ bool BindListenPort(const CService &addrBind, string& strError) #endif { strError = strprintf("Error: Couldn't set properties on socket for incoming connections (error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); + LogPrintf("%s\n", strError.c_str()); return false; } @@ -1631,16 +1652,16 @@ bool BindListenPort(const CService &addrBind, string& strError) strError = strprintf(_("Unable to bind to %s on this computer. Bitcoin is probably already running."), addrBind.ToString().c_str()); else strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %d, %s)"), addrBind.ToString().c_str(), nErr, strerror(nErr)); - printf("%s\n", strError.c_str()); + LogPrintf("%s\n", strError.c_str()); return false; } - printf("Bound to %s\n", addrBind.ToString().c_str()); + LogPrintf("Bound to %s\n", addrBind.ToString().c_str()); // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) { strError = strprintf("Error: Listening for incoming connections failed (listen returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); + LogPrintf("%s\n", strError.c_str()); return false; } @@ -1687,7 +1708,7 @@ void static Discover() struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); CNetAddr addr(s4->sin_addr); if (AddLocal(addr, LOCAL_IF)) - printf("IPv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); + LogPrintf("IPv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); } #ifdef USE_IPV6 else if (ifa->ifa_addr->sa_family == AF_INET6) @@ -1695,7 +1716,7 @@ void static Discover() struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); CNetAddr addr(s6->sin6_addr); if (AddLocal(addr, LOCAL_IF)) - printf("IPv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); + LogPrintf("IPv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str()); } #endif } @@ -1726,7 +1747,7 @@ void StartNode(boost::thread_group& threadGroup) // if (!GetBoolArg("-dnsseed", true)) - printf("DNS seeding disabled\n"); + LogPrintf("DNS seeding disabled\n"); else threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "dnsseed", &ThreadDNSAddressSeed)); @@ -1753,7 +1774,7 @@ void StartNode(boost::thread_group& threadGroup) bool StopNode() { - printf("StopNode()\n"); + LogPrintf("StopNode()\n"); MapPort(false); if (semOutbound) for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) @@ -1779,7 +1800,7 @@ public: BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) if (hListenSocket != INVALID_SOCKET) if (closesocket(hListenSocket) == SOCKET_ERROR) - printf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); + LogPrintf("closesocket(hListenSocket) failed with error %d\n", WSAGetLastError()); // clean up some globals (to help leak detection) BOOST_FOREACH(CNode *pnode, vNodes) @@ -1845,3 +1866,27 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt pnode->PushInventory(inv); } } + +void CNode::RecordBytesRecv(uint64 bytes) +{ + LOCK(cs_totalBytesRecv); + nTotalBytesRecv += bytes; +} + +void CNode::RecordBytesSent(uint64 bytes) +{ + LOCK(cs_totalBytesSent); + nTotalBytesSent += bytes; +} + +uint64 CNode::GetTotalBytesRecv() +{ + LOCK(cs_totalBytesRecv); + return nTotalBytesRecv; +} + +uint64 CNode::GetTotalBytesSent() +{ + LOCK(cs_totalBytesSent); + return nTotalBytesSent; +} @@ -28,7 +28,6 @@ static const unsigned int MAX_INV_SZ = 50000; class CNode; class CBlockIndex; -extern int nBestHeight; @@ -52,6 +51,7 @@ void SocketSendData(CNode *pnode); // Signals for message handling struct CNodeSignals { + boost::signals2::signal<int ()> GetHeight; boost::signals2::signal<bool (CNode*)> ProcessMessages; boost::signals2::signal<bool (CNode*, bool)> SendMessages; }; @@ -119,6 +119,8 @@ public: uint64 nSendBytes; uint64 nRecvBytes; bool fSyncNode; + double dPingTime; + double dPingWait; }; @@ -234,6 +236,12 @@ public: CCriticalSection cs_inventory; std::multimap<int64, CInv> mapAskFor; + // Ping time measurement + uint64 nPingNonceSent; + int64 nPingUsecStart; + int64 nPingUsecTime; + bool fPingQueued; + CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, MIN_PROTO_VERSION) { nServices = 0; @@ -268,6 +276,10 @@ public: fRelayTxes = false; setInventoryKnown.max_size(SendBufferSize() / 1000); pfilter = new CBloomFilter(); + nPingNonceSent = 0; + nPingUsecStart = 0; + nPingUsecTime = 0; + fPingQueued = false; // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) @@ -286,8 +298,15 @@ public: } private: + // Network usage totals + static CCriticalSection cs_totalBytesRecv; + static CCriticalSection cs_totalBytesSent; + static uint64 nTotalBytesRecv; + static uint64 nTotalBytesSent; + CNode(const CNode&); void operator=(const CNode&); + public: @@ -301,7 +320,7 @@ public: unsigned int GetTotalRecvSize() { unsigned int total = 0; - BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) + BOOST_FOREACH(const CNetMessage &msg, vRecvMsg) total += msg.vRecv.size() + 24; return total; } @@ -372,8 +391,7 @@ public: nRequestTime = it->second; else nRequestTime = 0; - if (fDebugNet) - printf("askfor %s %"PRI64d" (%s)\n", inv.ToString().c_str(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str()); + LogPrint("net", "askfor %s %"PRI64d" (%s)\n", inv.ToString().c_str(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str()); // Make sure not to reuse time indexes to keep things in the same order int64 nNow = (GetTime() - 1) * 1000000; @@ -399,8 +417,7 @@ public: ENTER_CRITICAL_SECTION(cs_vSend); assert(ssSend.size() == 0); ssSend << CMessageHeader(pszCommand, 0); - if (fDebug) - printf("sending: %s ", pszCommand); + LogPrint("net", "sending: %s ", pszCommand); } // TODO: Document the precondition of this function. Is cs_vSend locked? @@ -410,8 +427,7 @@ public: LEAVE_CRITICAL_SECTION(cs_vSend); - if (fDebug) - printf("(aborted)\n"); + LogPrint("net", "(aborted)\n"); } // TODO: Document the precondition of this function. Is cs_vSend locked? @@ -419,7 +435,7 @@ public: { if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { - printf("dropmessages DROPPING SEND MESSAGE\n"); + LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n"); AbortMessage(); return; } @@ -438,9 +454,7 @@ public: assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); - if (fDebug) { - printf("(%d bytes)\n", nSize); - } + LogPrint("net", "(%d bytes)\n", nSize); std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); ssSend.GetAndClear(*it); @@ -639,6 +653,13 @@ public: static bool IsBanned(CNetAddr ip); bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot void copyStats(CNodeStats &stats); + + // Network stats + static void RecordBytesRecv(uint64 bytes); + static void RecordBytesSent(uint64 bytes); + + static uint64 GetTotalBytesRecv(); + static uint64 GetTotalBytesSent(); }; diff --git a/src/netbase.cpp b/src/netbase.cpp index d02490ad4a..360ecdd959 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -169,7 +169,7 @@ bool LookupNumeric(const char *pszName, CService& addr, int portDefault) bool static Socks4(const CService &addrDest, SOCKET& hSocket) { - printf("SOCKS4 connecting %s\n", addrDest.ToString().c_str()); + LogPrintf("SOCKS4 connecting %s\n", addrDest.ToString().c_str()); if (!addrDest.IsIPv4()) { closesocket(hSocket); @@ -204,16 +204,16 @@ bool static Socks4(const CService &addrDest, SOCKET& hSocket) { closesocket(hSocket); if (pchRet[1] != 0x5b) - printf("ERROR: Proxy returned error %d\n", pchRet[1]); + LogPrintf("ERROR: Proxy returned error %d\n", pchRet[1]); return false; } - printf("SOCKS4 connected %s\n", addrDest.ToString().c_str()); + LogPrintf("SOCKS4 connected %s\n", addrDest.ToString().c_str()); return true; } bool static Socks5(string strDest, int port, SOCKET& hSocket) { - printf("SOCKS5 connecting %s\n", strDest.c_str()); + LogPrintf("SOCKS5 connecting %s\n", strDest.c_str()); if (strDest.size() > 255) { closesocket(hSocket); @@ -309,7 +309,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket) closesocket(hSocket); return error("Error reading from proxy"); } - printf("SOCKS5 connected %s\n", strDest.c_str()); + LogPrintf("SOCKS5 connected %s\n", strDest.c_str()); return true; } @@ -324,7 +324,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe #endif socklen_t len = sizeof(sockaddr); if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { - printf("Cannot connect to %s: unsupported network\n", addrConnect.ToString().c_str()); + LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToString().c_str()); return false; } @@ -363,13 +363,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); if (nRet == 0) { - printf("connection timeout\n"); + LogPrint("net", "connection timeout\n"); closesocket(hSocket); return false; } if (nRet == SOCKET_ERROR) { - printf("select() for connection failed: %i\n",WSAGetLastError()); + LogPrintf("select() for connection failed: %i\n",WSAGetLastError()); closesocket(hSocket); return false; } @@ -380,13 +380,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) #endif { - printf("getsockopt() for connection failed: %i\n",WSAGetLastError()); + LogPrintf("getsockopt() for connection failed: %i\n",WSAGetLastError()); closesocket(hSocket); return false; } if (nRet != 0) { - printf("connect() failed after select(): %s\n",strerror(nRet)); + LogPrintf("connect() failed after select(): %s\n",strerror(nRet)); closesocket(hSocket); return false; } @@ -397,7 +397,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe else #endif { - printf("connect() failed: %i\n",WSAGetLastError()); + LogPrintf("connect() failed: %i\n",WSAGetLastError()); closesocket(hSocket); return false; } @@ -864,7 +864,7 @@ std::vector<unsigned char> CNetAddr::GetGroup() const nBits = 4; } // for he.net, use /36 groups - else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && GetByte(13) == 0x04 && GetByte(12) == 0x70) + else if (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x04 && GetByte(12) == 0x70) nBits = 36; // for the rest of the IPv6 network, use /32 groups else @@ -893,7 +893,7 @@ uint64 CNetAddr::GetHash() const void CNetAddr::print() const { - printf("CNetAddr(%s)\n", ToString().c_str()); + LogPrintf("CNetAddr(%s)\n", ToString().c_str()); } // private extensions to enum Network, only returned by GetExtNetwork, @@ -1134,7 +1134,7 @@ std::string CService::ToString() const void CService::print() const { - printf("CService(%s)\n", ToString().c_str()); + LogPrintf("CService(%s)\n", ToString().c_str()); } void CService::SetPort(unsigned short portIn) diff --git a/src/netbase.h b/src/netbase.h index 8d5135e970..e1f80b4ee8 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -54,7 +54,7 @@ class CNetAddr bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16) bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16) - bool IsRFC4193() const; // IPv6 unique local (FC00::/15) + bool IsRFC4193() const; // IPv6 unique local (FC00::/7) bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32) bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28) bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64) diff --git a/src/noui.cpp b/src/noui.cpp index c0e00c4715..67eac944c6 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -27,7 +27,7 @@ static bool noui_ThreadSafeMessageBox(const std::string& message, const std::str strCaption += caption; // Use supplied caption (can be empty) } - printf("%s: %s\n", strCaption.c_str(), message.c_str()); + LogPrintf("%s: %s\n", strCaption.c_str(), message.c_str()); fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str()); return false; } @@ -39,7 +39,7 @@ static bool noui_ThreadSafeAskFee(int64 /*nFeeRequired*/) static void noui_InitMessage(const std::string &message) { - printf("init message: %s\n", message.c_str()); + LogPrintf("init message: %s\n", message.c_str()); } void noui_connect() diff --git a/src/protocol.cpp b/src/protocol.cpp index 745b4338e4..ba254be3e1 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -67,7 +67,7 @@ bool CMessageHeader::IsValid() const // Message size if (nMessageSize > MAX_SIZE) { - printf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand().c_str(), nMessageSize); + LogPrintf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand().c_str(), nMessageSize); return false; } @@ -146,6 +146,6 @@ std::string CInv::ToString() const void CInv::print() const { - printf("CInv(%s)\n", ToString().c_str()); + LogPrintf("CInv(%s)\n", ToString().c_str()); } diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index c6c4cb37a3..e7ae0a905f 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS = $(INCLUDES) -I$(top_builddir)/src/obj \ -I$(top_srcdir)/src/leveldb/include -I$(top_srcdir)/src \ -I$(top_srcdir)/src/leveldb/helpers -I$(top_builddir)/src/qt \ -I$(top_builddir)/src/qt/forms $(BOOST_INCLUDES) $(PROTOBUF_CFLAGS) \ - $(QR_CFLAGS) + $(QR_CFLAGS) $(BDB_CPPFLAGS) AM_LDFLAGS = $(PTHREAD_CFLAGS) bin_PROGRAMS = bitcoin-qt noinst_LIBRARIES = libbitcoinqt.a @@ -48,11 +48,14 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \ moc_optionsmodel.cpp moc_overviewpage.cpp moc_paymentserver.cpp \ moc_qrcodedialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ moc_rpcconsole.cpp moc_sendcoinsdialog.cpp moc_sendcoinsentry.cpp \ - moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_transactiondesc.cpp \ + moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_trafficgraphwidget.cpp moc_transactiondesc.cpp \ moc_transactiondescdialog.cpp moc_transactionfilterproxy.cpp \ moc_transactiontablemodel.cpp moc_transactionview.cpp moc_walletframe.cpp \ moc_walletmodel.cpp moc_walletstack.cpp moc_walletview.cpp +BITCOIN_MM = macdockiconhandler.mm macnotificationhandler.mm +QR_CPP = qrcodedialog.cpp + QT_MOC = intro.moc overviewpage.moc rpcconsole.moc QT_QRC_CPP = qrc_bitcoin.cpp @@ -70,7 +73,7 @@ BITCOIN_QT_H = aboutdialog.h addressbookpage.h addresstablemodel.h \ optionsmodel.h overviewpage.h paymentrequestplus.h paymentserver.h \ qrcodedialog.h qvalidatedlineedit.h qvaluecombobox.h rpcconsole.h \ sendcoinsdialog.h sendcoinsentry.h signverifymessagedialog.h splashscreen.h \ - transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \ + trafficgraphwidget.h transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \ transactionrecord.h transactiontablemodel.h transactionview.h walletframe.h \ walletmodel.h walletmodeltransaction.h walletstack.h walletview.h @@ -91,6 +94,19 @@ RES_ICONS = res/icons/bitcoin.png res/icons/address-book.png \ res/icons/qrcode.png res/icons/debugwindow.png res/icons/bitcoin.ico \ res/icons/bitcoin_testnet.ico +BITCOIN_QT_CPP = aboutdialog.cpp addressbookpage.cpp \ + addresstablemodel.cpp askpassphrasedialog.cpp bitcoinaddressvalidator.cpp \ + bitcoinamountfield.cpp bitcoin.cpp bitcoingui.cpp \ + bitcoinunits.cpp clientmodel.cpp csvmodelwriter.cpp editaddressdialog.cpp \ + guiutil.cpp intro.cpp monitoreddatamapper.cpp notificator.cpp \ + optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \ + paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \ + rpcconsole.cpp sendcoinsdialog.cpp sendcoinsentry.cpp \ + signverifymessagedialog.cpp splashscreen.cpp trafficgraphwidget.cpp transactiondesc.cpp \ + transactiondescdialog.cpp transactionfilterproxy.cpp transactionrecord.cpp \ + transactiontablemodel.cpp transactionview.cpp walletframe.cpp \ + walletmodel.cpp walletmodeltransaction.cpp walletstack.cpp walletview.cpp + RES_IMAGES = res/images/about.png res/images/splash.png \ res/images/splash_testnet.png @@ -100,20 +116,8 @@ BITCOIN_RC = res/bitcoin-qt-res.rc libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) \ -I$(top_srcdir)/src/qt/forms $(QT_DBUS_INCLUDES) -libbitcoinqt_a_SOURCES = aboutdialog.cpp addressbookpage.cpp \ - addresstablemodel.cpp askpassphrasedialog.cpp bitcoinaddressvalidator.cpp \ - bitcoinamountfield.cpp bitcoin.cpp bitcoingui.cpp bitcoinstrings.cpp \ - bitcoinunits.cpp clientmodel.cpp csvmodelwriter.cpp editaddressdialog.cpp \ - guiutil.cpp intro.cpp monitoreddatamapper.cpp notificator.cpp \ - optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \ - paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \ - rpcconsole.cpp sendcoinsdialog.cpp sendcoinsentry.cpp \ - signverifymessagedialog.cpp splashscreen.cpp transactiondesc.cpp \ - transactiondescdialog.cpp transactionfilterproxy.cpp transactionrecord.cpp \ - transactiontablemodel.cpp transactionview.cpp walletframe.cpp \ - walletmodel.cpp walletmodeltransaction.cpp walletstack.cpp walletview.cpp \ - $(BITCOIN_QT_H) $(QT_FORMS_UI) $(QT_QRC) $(QT_TS) $(PROTOBUF_PROTO) \ - $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) +libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \ + $(QT_QRC) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) nodist_libbitcoinqt_a_SOURCES = $(QT_MOC_CPP) $(QT_MOC) $(PROTOBUF_CC) \ $(PROTOBUF_H) $(QT_QRC_CPP) @@ -126,13 +130,13 @@ $(QT_MOC): $(PROTOBUF_H) $(QT_MOC_CPP): $(PROTOBUF_H) if TARGET_DARWIN - libbitcoinqt_a_SOURCES += macdockiconhandler.mm macnotificationhandler.mm + libbitcoinqt_a_SOURCES += $(BITCOIN_MM) endif if TARGET_WINDOWS libbitcoinqt_a_SOURCES += $(BITCOIN_RC) endif if USE_QRCODE - libbitcoinqt_a_SOURCES += qrcodedialog.cpp + libbitcoinqt_a_SOURCES += $(QR_CPP) endif # @@ -141,7 +145,7 @@ bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) \ -I$(top_srcdir)/src/qt/forms bitcoin_qt_SOURCES = bitcoin.cpp bitcoin_qt_LDADD = libbitcoinqt.a $(LIBBITCOIN) $(LIBLEVELDB) $(LIBMEMENV) \ - $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) + $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) # forms/foo.h -> forms/ui_foo.h QT_FORMS_H=$(join $(dir $(QT_FORMS_UI)),$(addprefix ui_, $(notdir $(QT_FORMS_UI:.ui=.h)))) @@ -149,8 +153,16 @@ QT_FORMS_H=$(join $(dir $(QT_FORMS_UI)),$(addprefix ui_, $(notdir $(QT_FORMS_UI: #locale/foo.ts -> locale/foo.qm QT_QM=$(QT_TS:.ts=.qm) +.PHONY: FORCE .SECONDARY: $(QT_QM) +bitcoinstrings.cpp: FORCE + $(MAKE) -C $(top_srcdir)/src qt/bitcoinstrings.cpp + +translate: bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) $(QR_CPP) + @test -n $(LUPDATE) || echo "lupdate is required for updating translations" + @$(LUPDATE) $^ -locations relative -no-obsolete -ts locale/bitcoin_en.ts + $(QT_QRC_CPP): $(QT_QRC) $(QT_QM) $(QT_FORMS_H) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) $(PROTOBUF_H) @cd $(abs_srcdir); test -f $(RCC) && $(RCC) -name bitcoin -o $(abs_builddir)/$@ $< || \ echo error: could not build $@ diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index f165c11cb1..2b7671f209 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -16,6 +16,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : fCapsLock(false) { ui->setupUi(this); + ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE); ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index a4d589e167..78693971da 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -62,7 +62,7 @@ static bool ThreadSafeMessageBox(const std::string& message, const std::string& } else { - printf("%s: %s\n", caption.c_str(), message.c_str()); + LogPrintf("%s: %s\n", caption.c_str(), message.c_str()); fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); return false; } @@ -91,7 +91,7 @@ static void InitMessage(const std::string &message) splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(55,55,55)); qApp->processEvents(); } - printf("init message: %s\n", message.c_str()); + LogPrintf("init message: %s\n", message.c_str()); } /* @@ -155,12 +155,15 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans #if QT_VERSION < 0x050000 void DebugMessageHandler(QtMsgType type, const char * msg) { - OutputDebugStringF("Bitcoin-Qt: %s\n", msg); + Q_UNUSED(type); + LogPrint("qt", "Bitcoin-Qt: %s\n", msg); } #else void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg) { - OutputDebugStringF("Bitcoin-Qt: %s\n", qPrintable(msg)); + Q_UNUSED(type); + Q_UNUSED(context); + LogPrint("qt", "Bitcoin-Qt: %s\n", qPrintable(msg)); } #endif diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index d9d4e3b23d..37b8743eff 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -130,9 +130,10 @@ void BitcoinAmountField::setValue(qint64 value) setText(BitcoinUnits::format(currentUnit, value)); } -void BitcoinAmountField::setReadOnly(bool fReadeOnly) +void BitcoinAmountField::setReadOnly(bool fReadOnly) { - // TODO ... + amount->setReadOnly(fReadOnly); + unit->setEnabled(!fReadOnly); } void BitcoinAmountField::unitChanged(int idx) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bb9eb60e5b..23a221120f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -157,6 +157,8 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : rpcConsole = new RPCConsole(this); connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show())); + // prevents an oben debug window from becoming stuck/unusable on client shutdown + connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide())); // Install event filter to be able to catch status tip events (QEvent::StatusTip) this->installEventFilter(this); @@ -233,7 +235,11 @@ void BitcoinGUI::createActions(bool fIsTestnet) aboutAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&About Bitcoin"), this); aboutAction->setStatusTip(tr("Show information about Bitcoin")); aboutAction->setMenuRole(QAction::AboutRole); +#if QT_VERSION < 0x050000 aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); +#else + aboutQtAction = new QAction(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this); +#endif aboutQtAction->setStatusTip(tr("Show information about Qt")); aboutQtAction->setMenuRole(QAction::AboutQtRole); optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); @@ -590,21 +596,28 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned int nMBoxIcon = QMessageBox::Information; int nNotifyIcon = Notificator::Information; - // Override title based on style QString msgType; - switch (style) { - case CClientUIInterface::MSG_ERROR: - msgType = tr("Error"); - break; - case CClientUIInterface::MSG_WARNING: - msgType = tr("Warning"); - break; - case CClientUIInterface::MSG_INFORMATION: - msgType = tr("Information"); - break; - default: - msgType = title; // Use supplied title + + // Prefer supplied title over style based title + if (!title.isEmpty()) { + msgType = title; } + else { + switch (style) { + case CClientUIInterface::MSG_ERROR: + msgType = tr("Error"); + break; + case CClientUIInterface::MSG_WARNING: + msgType = tr("Warning"); + break; + case CClientUIInterface::MSG_INFORMATION: + msgType = tr("Information"); + break; + default: + break; + } + } + // Append title to "Bitcoin - " if (!msgType.isEmpty()) strTitle += " - " + msgType; @@ -625,7 +638,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK))) buttons = QMessageBox::Ok; - QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons); + QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this); int r = mBox.exec(); if (ret != NULL) *ret = r == QMessageBox::Ok; @@ -744,13 +757,9 @@ void BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient) walletFrame->handlePaymentRequest(recipient); } -void BitcoinGUI::showPaymentACK(QString msg) +void BitcoinGUI::showPaymentACK(const QString& msg) { -#if QT_VERSION < 0x050000 - message(tr("Payment acknowledged"), Qt::escape(msg), CClientUIInterface::MODAL); -#else - message(tr("Payment acknowledged"), msg.toHtmlEscaped(), CClientUIInterface::MODAL); -#endif + message(tr("Payment acknowledged"), GUIUtil::HtmlEscape(msg), CClientUIInterface::MODAL); } void BitcoinGUI::setEncryptionStatus(int status) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index fc25e867fc..e2dd5dc6bc 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -154,7 +154,7 @@ public slots: void askFee(qint64 nFeeRequired, bool *payFee); void handlePaymentRequest(const SendCoinsRecipient& recipient); - void showPaymentACK(QString msg); + void showPaymentACK(const QString& msg); /** Show incoming transaction notification for new transactions. */ void incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index f7dd8adb6b..2bab488135 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -24,9 +24,8 @@ ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : numBlocksAtStartup(-1), pollTimer(0) { pollTimer = new QTimer(this); - pollTimer->setInterval(MODEL_UPDATE_DELAY); - pollTimer->start(); connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); + pollTimer->start(MODEL_UPDATE_DELAY); subscribeToCoreSignals(); } @@ -43,7 +42,7 @@ int ClientModel::getNumConnections() const int ClientModel::getNumBlocks() const { - return nBestHeight; + return chainActive.Height(); } int ClientModel::getNumBlocksAtStartup() @@ -52,10 +51,20 @@ int ClientModel::getNumBlocksAtStartup() return numBlocksAtStartup; } +quint64 ClientModel::getTotalBytesRecv() const +{ + return CNode::GetTotalBytesRecv(); +} + +quint64 ClientModel::getTotalBytesSent() const +{ + return CNode::GetTotalBytesSent(); +} + QDateTime ClientModel::getLastBlockDate() const { - if (pindexBest) - return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + if (chainActive.Tip()) + return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime()); else if(!isTestNet()) return QDateTime::fromTime_t(1231006505); // Genesis block's time else @@ -64,7 +73,7 @@ QDateTime ClientModel::getLastBlockDate() const double ClientModel::getVerificationProgress() const { - return Checkpoints::GuessVerificationProgress(pindexBest); + return Checkpoints::GuessVerificationProgress(chainActive.Tip()); } void ClientModel::updateTimer() @@ -86,6 +95,8 @@ void ClientModel::updateTimer() // ensure we return the maximum of newNumBlocksOfPeers and newNumBlocks to not create weird displays in the GUI emit numBlocksChanged(newNumBlocks, std::max(newNumBlocksOfPeers, newNumBlocks)); } + + emit bytesChanged(getTotalBytesRecv(), getTotalBytesSent()); } void ClientModel::updateNumConnections(int numConnections) diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 15074300b4..925f20acd9 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -35,6 +35,9 @@ public: int getNumBlocks() const; int getNumBlocksAtStartup(); + quint64 getTotalBytesRecv() const; + quint64 getTotalBytesSent() const; + double getVerificationProgress() const; QDateTime getLastBlockDate() const; @@ -74,6 +77,7 @@ signals: void numConnectionsChanged(int count); void numBlocksChanged(int count, int countOfPeers); void alertsChanged(const QString &warnings); + void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); //! Asynchronous message notification void message(const QString &title, const QString &message, unsigned int style); diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp index 8a50bbab3f..ad8e0d618a 100644 --- a/src/qt/csvmodelwriter.cpp +++ b/src/qt/csvmodelwriter.cpp @@ -85,4 +85,3 @@ bool CSVModelWriter::write() return file.error() == QFile::NoError; } - diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index bb53021cfd..1e4335c645 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -461,7 +461,7 @@ <customwidgets> <customwidget> <class>BitcoinAmountField</class> - <extends>QSpinBox</extends> + <extends>QLineEdit</extends> <header>bitcoinamountfield.h</header> </customwidget> <customwidget> diff --git a/src/qt/forms/qrcodedialog.ui b/src/qt/forms/qrcodedialog.ui index 52e9db3762..1cec9066f8 100644 --- a/src/qt/forms/qrcodedialog.ui +++ b/src/qt/forms/qrcodedialog.ui @@ -186,7 +186,7 @@ <customwidgets> <customwidget> <class>BitcoinAmountField</class> - <extends>QSpinBox</extends> + <extends>QLineEdit</extends> <header>bitcoinamountfield.h</header> </customwidget> </customwidgets> diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index d1d8ab42a0..54c41ffb67 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -445,10 +445,271 @@ </item> </layout> </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>&Network Traffic</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="TrafficGraphWidget" name="trafficGraph" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QSlider" name="sldGraphRange"> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>288</number> + </property> + <property name="pageStep"> + <number>12</number> + </property> + <property name="value"> + <number>6</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblGraphRange"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnClearTrafficGraph"> + <property name="text"> + <string>&Clear</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Totals</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="Line" name="line"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>10</width> + <height>0</height> + </size> + </property> + <property name="palette"> + <palette> + <active> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>255</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>255</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>255</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_16"> + <property name="text"> + <string>In:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblBytesIn"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="Line" name="line_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>10</width> + <height>0</height> + </size> + </property> + <property name="palette"> + <palette> + <active> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_17"> + <property name="text"> + <string>Out:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lblBytesOut"> + <property name="minimumSize"> + <size> + <width>50</width> + <height>0</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>407</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </widget> </widget> </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>TrafficGraphWidget</class> + <extends>QWidget</extends> + <header>trafficgraphwidget.h</header> + <container>1</container> + <slots> + <slot>clear()</slot> + </slots> + </customwidget> + </customwidgets> <resources> <include location="../bitcoin.qrc"/> </resources> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 6e17565ab0..2a00fc5455 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -28,7 +28,7 @@ <height>165</height> </rect> </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1"> <property name="margin"> <number>0</number> </property> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index a2ef9a0a38..5c6afd6c71 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -10,19 +10,13 @@ <height>150</height> </rect> </property> - <property name="windowTitle"> - <string>StackedWidget</string> - </property> <property name="autoFillBackground"> <bool>false</bool> </property> <property name="currentIndex"> - <number>1</number> + <number>0</number> </property> <widget class="QFrame" name="SendCoinsInsecure"> - <property name="windowTitle"> - <string>Form</string> - </property> <property name="frameShape"> <enum>QFrame::StyledPanel</enum> </property> @@ -34,7 +28,7 @@ <number>12</number> </property> <item row="5" column="0"> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="amountLabel"> <property name="text"> <string>A&mount:</string> </property> @@ -47,7 +41,7 @@ </widget> </item> <item row="3" column="0"> - <widget class="QLabel" name="label_2"> + <widget class="QLabel" name="payToLabel"> <property name="text"> <string>Pay &To:</string> </property> @@ -63,7 +57,7 @@ <widget class="BitcoinAmountField" name="payAmount"/> </item> <item row="4" column="0"> - <widget class="QLabel" name="label_4"> + <widget class="QLabel" name="labellLabel"> <property name="text"> <string>&Label:</string> </property> @@ -592,9 +586,6 @@ </disabled> </palette> </property> - <property name="windowTitle"> - <string>SecureSend</string> - </property> <property name="autoFillBackground"> <bool>true</bool> </property> @@ -609,7 +600,7 @@ <number>12</number> </property> <item row="4" column="0"> - <widget class="QLabel" name="label_s4"> + <widget class="QLabel" name="memoLabel_s"> <property name="text"> <string>Memo:</string> </property> @@ -622,7 +613,7 @@ </widget> </item> <item row="5" column="0"> - <widget class="QLabel" name="label_s1"> + <widget class="QLabel" name="amountLabel_s"> <property name="text"> <string>A&mount:</string> </property> @@ -630,12 +621,12 @@ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="buddy"> - <cstring>payAmount</cstring> + <cstring>payAmount_s</cstring> </property> </widget> </item> <item row="3" column="0"> - <widget class="QLabel" name="label_s2"> + <widget class="QLabel" name="payToLabel_s"> <property name="text"> <string>Pay &To:</string> </property> @@ -649,15 +640,9 @@ </item> <item row="5" column="2"> <widget class="BitcoinAmountField" name="payAmount_s"> - <property name="enabled"> - <bool>false</bool> - </property> <property name="acceptDrops"> <bool>false</bool> </property> - <property name="readOnly"> - <bool>true</bool> - </property> </widget> </item> <item row="3" column="2"> @@ -667,14 +652,17 @@ </property> <item> <widget class="QLabel" name="payTo_s"> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> </widget> </item> </layout> </item> <item row="4" column="2"> - <widget class="QLabel" name="memo_s"> - <property name="text"> - <string>message from merchant</string> + <widget class="QLabel" name="memoTextLabel_s"> + <property name="textFormat"> + <enum>Qt::PlainText</enum> </property> </widget> </item> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 7d419b6bd9..4d1a8574d0 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -67,7 +67,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&New Address</translation> </message> <message> - <location filename="../addressbookpage.cpp" line="+63"/> + <location filename="../addressbookpage.cpp" line="+67"/> <source>These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you.</source> <translation>These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you.</translation> </message> @@ -165,7 +165,7 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>AddressTableModel</name> <message> - <location filename="../addresstablemodel.cpp" line="+144"/> + <location filename="../addresstablemodel.cpp" line="+164"/> <source>Label</source> <translation>Label</translation> </message> @@ -203,7 +203,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Repeat new passphrase</translation> </message> <message> - <location filename="../askpassphrasedialog.cpp" line="+33"/> + <location filename="../askpassphrasedialog.cpp" line="+34"/> <source>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>.</source> <translation>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>.</translation> </message> @@ -324,7 +324,7 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+254"/> + <location filename="../bitcoingui.cpp" line="+255"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> @@ -623,12 +623,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Information</translation> </message> <message> - <location line="+70"/> + <location line="+73"/> <source>This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee?</source> <translation>This transaction is over the size limit. You can still send it for a fee of %1, which goes to the nodes that process your transaction and helps to support the network. Do you want to pay the fee?</translation> </message> <message> - <location line="-140"/> + <location line="-143"/> <source>Up to date</source> <translation>Up to date</translation> </message> @@ -638,7 +638,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Catching up...</translation> </message> <message> - <location line="+113"/> + <location line="+116"/> <source>Confirm transaction fee</source> <translation>Confirm transaction fee</translation> </message> @@ -666,18 +666,22 @@ Address: %4 </translation> </message> <message> - <location line="+33"/> - <location line="+23"/> + <location line="+34"/> <source>URI handling</source> <translation>URI handling</translation> </message> <message> - <location line="-23"/> - <location line="+23"/> + <location line="+0"/> <source>URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source> <translation>URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</translation> </message> <message> + <location line="+27"/> + <location line="+2"/> + <source>Payment acknowledged</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+17"/> <source>Wallet is <b>encrypted</b> and currently <b>unlocked</b></source> <translation>Wallet is <b>encrypted</b> and currently <b>unlocked</b></translation> @@ -696,7 +700,7 @@ Address: %4 <context> <name>ClientModel</name> <message> - <location filename="../clientmodel.cpp" line="+105"/> + <location filename="../clientmodel.cpp" line="+106"/> <source>Network Alert</source> <translation>Network Alert</translation> </message> @@ -800,7 +804,7 @@ Address: %4 <context> <name>GUIUtil::HelpMessageBox</name> <message> - <location filename="../guiutil.cpp" line="+517"/> + <location filename="../guiutil.cpp" line="+525"/> <location line="+13"/> <source>Bitcoin-Qt</source> <translation>Bitcoin-Qt</translation> @@ -879,7 +883,7 @@ Address: %4 <translation>Use a custom data directory:</translation> </message> <message> - <location filename="../intro.cpp" line="+100"/> + <location filename="../intro.cpp" line="+105"/> <source>Error</source> <translation>Error</translation> </message> @@ -1067,7 +1071,7 @@ Address: %4 <translation>&Apply</translation> </message> <message> - <location filename="../optionsdialog.cpp" line="+54"/> + <location filename="../optionsdialog.cpp" line="+58"/> <source>default</source> <translation>default</translation> </message> @@ -1177,29 +1181,68 @@ Address: %4 <context> <name>PaymentServer</name> <message> - <location filename="../paymentserver.cpp" line="+108"/> - <source>Cannot start bitcoin: click-to-pay handler</source> - <translation>Cannot start bitcoin: click-to-pay handler</translation> + <location filename="../paymentserver.cpp" line="+450"/> + <location line="+41"/> + <source>Payment request error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Insecure requests to custom payment scripts unsupported</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+38"/> + <source>Refund from</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+46"/> + <location line="+28"/> + <location line="+17"/> + <source>Network request error</source> + <translation type="unfinished"></translation> </message> </context> <context> <name>QObject</name> <message> - <location filename="../bitcoin.cpp" line="+92"/> + <location filename="../bitcoin.cpp" line="+111"/> + <location line="+5"/> <location filename="../intro.cpp" line="-32"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> <message> - <location line="+1"/> + <location line="-4"/> <source>Error: Specified data directory "%1" does not exist.</source> <translation>Error: Specified data directory "%1" does not exist.</translation> </message> <message> + <location line="+4"/> + <source>Error: Invalid combination of -regtest and -testnet.</source> + <translation type="unfinished"></translation> + </message> + <message> <location filename="../intro.cpp" line="+1"/> <source>Error: Specified data directory "%1" can not be created.</source> <translation>Error: Specified data directory "%1" can not be created.</translation> </message> + <message> + <location filename="../paymentserver.cpp" line="-175"/> + <source>Requested payment amount (%1) too small</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+126"/> + <source>Error communicating with %1: %2</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+29"/> + <source>Bad response from server %1</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>QRCodeDialog</name> @@ -1410,13 +1453,15 @@ Address: %4 <name>SendCoinsDialog</name> <message> <location filename="../forms/sendcoinsdialog.ui" line="+14"/> - <location filename="../sendcoinsdialog.cpp" line="+128"/> + <location filename="../sendcoinsdialog.cpp" line="+138"/> <location line="+5"/> <location line="+5"/> <location line="+5"/> <location line="+6"/> <location line="+5"/> - <location line="+5"/> + <location line="+50"/> + <location line="+145"/> + <location line="+9"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -1461,25 +1506,19 @@ Address: %4 <translation>S&end</translation> </message> <message> - <location filename="../sendcoinsdialog.cpp" line="-62"/> - <location line="+2"/> - <source><b>%1</b> to %2 (%3)</source> - <translation><b>%1</b> to %2 (%3)</translation> - </message> - <message> - <location line="+6"/> + <location filename="../sendcoinsdialog.cpp" line="-170"/> <source>Confirm send coins</source> <translation>Confirm send coins</translation> </message> <message> - <location line="+1"/> - <source>Are you sure you want to send %1?</source> - <translation>Are you sure you want to send %1?</translation> + <location line="-97"/> + <source>to</source> + <translation type="unfinished">to</translation> </message> <message> - <location line="+0"/> - <source> and </source> - <translation> and </translation> + <location line="+15"/> + <source><b>%1</b> to %2</source> + <translation type="unfinished"></translation> </message> <message> <location line="+23"/> @@ -1512,36 +1551,63 @@ Address: %4 <translation>Error: Transaction creation failed!</translation> </message> <message> - <location line="+5"/> + <location line="+15"/> + <source>Are you sure you want to send?</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> + <source>added as transaction fee</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+6"/> + <source>Total Amount %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+20"/> <source>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</source> <translation>Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.</translation> </message> + <message> + <location line="+145"/> + <source>Payment request expired</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> + <source>Invalid payment address %1</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>SendCoinsEntry</name> <message> - <location filename="../forms/sendcoinsentry.ui" line="+14"/> + <location filename="../forms/sendcoinsentry.ui" line="+24"/> <source>Form</source> <translation>Form</translation> </message> <message> <location line="+15"/> + <location line="+588"/> <source>A&mount:</source> <translation>A&mount:</translation> </message> <message> - <location line="+13"/> + <location line="-575"/> + <location line="+588"/> <source>Pay &To:</source> <translation>Pay &To:</translation> </message> <message> - <location line="+34"/> + <location line="-554"/> <source>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</source> <translation>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> </message> <message> <location line="+60"/> - <location filename="../sendcoinsentry.cpp" line="+26"/> + <location filename="../sendcoinsentry.cpp" line="+28"/> <source>Enter a label for this address to add it to your address book</source> <translation>Enter a label for this address to add it to your address book</translation> </message> @@ -1551,7 +1617,12 @@ Address: %4 <translation>&Label:</translation> </message> <message> - <location line="+28"/> + <location line="-54"/> + <source>StackedWidget</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+82"/> <source>Choose address from address book</source> <translation>Choose address from address book</translation> </message> @@ -1576,6 +1647,21 @@ Address: %4 <translation>Remove this recipient</translation> </message> <message> + <location line="+466"/> + <source>SecureSend</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+18"/> + <source>Memo:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+63"/> + <source>message from merchant</source> + <translation type="unfinished"></translation> + </message> + <message> <location filename="../sendcoinsentry.cpp" line="+1"/> <source>Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</source> <translation>Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</translation> @@ -1708,7 +1794,7 @@ Address: %4 <translation>Enter Bitcoin signature</translation> </message> <message> - <location line="+82"/> + <location line="+85"/> <location line="+81"/> <source>The entered address is invalid.</source> <translation>The entered address is invalid.</translation> @@ -1777,7 +1863,7 @@ Address: %4 <context> <name>SplashScreen</name> <message> - <location filename="../splashscreen.cpp" line="+22"/> + <location filename="../splashscreen.cpp" line="+23"/> <source>The Bitcoin developers</source> <translation>The Bitcoin developers</translation> </message> @@ -1790,7 +1876,7 @@ Address: %4 <context> <name>TransactionDesc</name> <message> - <location filename="../transactiondesc.cpp" line="+20"/> + <location filename="../transactiondesc.cpp" line="+22"/> <source>Open until %1</source> <translation>Open until %1</translation> </message> @@ -1866,12 +1952,12 @@ Address: %4 <location line="+12"/> <location line="+45"/> <location line="+17"/> - <location line="+30"/> + <location line="+45"/> <source>Credit</source> <translation>Credit</translation> </message> <message numerus="yes"> - <location line="-102"/> + <location line="-117"/> <source>matures in %n more block(s)</source> <translation> <numerusform>matures in %n more block</numerusform> @@ -1887,12 +1973,12 @@ Address: %4 <location line="+44"/> <location line="+8"/> <location line="+15"/> - <location line="+30"/> + <location line="+45"/> <source>Debit</source> <translation>Debit</translation> </message> <message> - <location line="-39"/> + <location line="-54"/> <source>Transaction fee</source> <translation>Transaction fee</translation> </message> @@ -1917,7 +2003,12 @@ Address: %4 <translation>Transaction ID</translation> </message> <message> - <location line="+3"/> + <location line="+13"/> + <source>Merchant</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> <source>Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</source> <translation>Generated coins must mature 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.</translation> </message> @@ -1952,7 +2043,7 @@ Address: %4 <translation>false</translation> </message> <message> - <location line="-209"/> + <location line="-224"/> <source>, has not been successfully broadcast yet</source> <translation>, has not been successfully broadcast yet</translation> </message> @@ -1986,7 +2077,7 @@ Address: %4 <context> <name>TransactionTableModel</name> <message> - <location filename="../transactiontablemodel.cpp" line="+225"/> + <location filename="../transactiontablemodel.cpp" line="+227"/> <source>Date</source> <translation>Date</translation> </message> @@ -2279,7 +2370,7 @@ Address: %4 <context> <name>WalletModel</name> <message> - <location filename="../walletmodel.cpp" line="+193"/> + <location filename="../walletmodel.cpp" line="+218"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -2297,7 +2388,7 @@ Address: %4 <translation>Export the data in the current tab to a file</translation> </message> <message> - <location line="+197"/> + <location line="+198"/> <source>Backup Wallet</source> <translation>Backup Wallet</translation> </message> diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index 50fb1bd777..f6a898ff7c 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -80,7 +80,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c return false; } else { - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: unknown pki_type " << paymentRequest.pki_type().c_str(); + qDebug() << "PaymentRequestPlus::getMerchant : Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type()); return false; } @@ -152,7 +152,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c // Valid cert; check signature: payments::PaymentRequest rcopy(paymentRequest); // Copy rcopy.set_signature(std::string("")); - std::string data_to_verify; // Everything but the signature + std::string data_to_verify; // Everything but the signature rcopy.SerializeToString(&data_to_verify); EVP_MD_CTX ctx; diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 6c10d8a04d..96ba997943 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -44,12 +44,11 @@ #include "wallet.h" #include "walletmodel.h" -using namespace boost; - 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() @@ -73,14 +72,14 @@ static QString ipcServerName() // Append a simple hash of the datadir // Note that GetDataDir(true) returns a different path // for -testnet versus main net - QString ddir(GetDataDir(true).string().c_str()); + QString ddir(QString::fromStdString(GetDataDir(true).string())); name.append(QString::number(qHash(ddir))); return name; } // -// We store payment URLs and requests received before +// We store payment URIs and requests received before // the main GUI window is up and ready to ask the user // to send payment. @@ -250,8 +249,7 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) return fResult; } -PaymentServer::PaymentServer(QObject* parent, - bool startLocalServer) : QObject(parent), saveURIs(true) +PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : QObject(parent), saveURIs(true) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. @@ -286,12 +284,12 @@ PaymentServer::~PaymentServer() } // -// OSX-specific way of handling bitcoin uris and +// OSX-specific way of handling bitcoin: URIs and // PaymentRequest mime types // bool PaymentServer::eventFilter(QObject *, QEvent *event) { - // clicking on bitcoin: URLs creates FileOpen events on the Mac: + // clicking on bitcoin: URIs creates FileOpen events on the Mac: if (event->type() == QEvent::FileOpen) { QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event); @@ -356,13 +354,13 @@ void PaymentServer::handleURIOrFile(const QString& s) if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: { #if QT_VERSION >= 0x050000 - QUrlQuery url((QUrl(s))); + QUrlQuery uri((QUrl(s))); #else - QUrl url(s); + QUrl uri(s); #endif - if (url.hasQueryItem("request")) + if (uri.hasQueryItem("request")) { - QByteArray temp; temp.append(url.queryItemValue("request")); + QByteArray temp; temp.append(uri.queryItemValue("request")); QString decoded = QUrl::fromPercentEncoding(temp); QUrl fetchUrl(decoded, QUrl::StrictMode); @@ -436,9 +434,7 @@ bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPl return request.parse(data); } -bool -PaymentServer::processPaymentRequest(PaymentRequestPlus& request, - QList<SendCoinsRecipient>& recipients) +bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, QList<SendCoinsRecipient>& recipients) { if (!optionsModel) return false; @@ -476,11 +472,7 @@ PaymentServer::processPaymentRequest(PaymentRequestPlus& request, recipients.append(SendCoinsRecipient()); recipients[i].amount = sendingTo.second; QString memo = QString::fromStdString(request.getDetails().memo()); -#if QT_VERSION < 0x050000 - recipients[i].label = Qt::escape(memo); -#else - recipients[i].label = memo.toHtmlEscaped(); -#endif + 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 @@ -493,7 +485,7 @@ PaymentServer::processPaymentRequest(PaymentRequestPlus& request, // 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 reportError(tr("Payment request error"), + emit reportError(tr("Payment request error"), tr("Insecure requests to custom payment scripts unsupported"), CClientUIInterface::MODAL); return false; @@ -504,8 +496,7 @@ PaymentServer::processPaymentRequest(PaymentRequestPlus& request, return true; } -void -PaymentServer::fetchRequest(const QUrl& url) +void PaymentServer::fetchRequest(const QUrl& url) { QNetworkRequest netRequest; netRequest.setAttribute(QNetworkRequest::User, "PaymentRequest"); @@ -515,8 +506,7 @@ PaymentServer::fetchRequest(const QUrl& url) netManager->get(netRequest); } -void -PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction) +void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction) { const payments::PaymentDetails& details = recipient.paymentRequest.getDetails(); if (!details.has_payment_url()) @@ -525,7 +515,7 @@ PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QB QNetworkRequest netRequest; netRequest.setAttribute(QNetworkRequest::User, "PaymentACK"); netRequest.setUrl(QString::fromStdString(details.payment_url())); - netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/bitcoin-payment"); + netRequest.setHeader(QNetworkRequest::ContentTypeHeader, BITCOIN_PAYMENTACK_CONTENTTYPE); netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); netRequest.setRawHeader("Accept", BITCOIN_PAYMENTACK_MIMETYPE); @@ -571,8 +561,7 @@ PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QB } } -void -PaymentServer::netRequestFinished(QNetworkReply* reply) +void PaymentServer::netRequestFinished(QNetworkReply* reply) { reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) @@ -617,9 +606,10 @@ PaymentServer::netRequestFinished(QNetworkReply* reply) } } -void -PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> &errs) +void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> &errs) { + Q_UNUSED(reply); + QString errString; foreach (const QSslError& err, errs) { qDebug() << "PaymentServer::reportSslErrors : " << err; diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 131ede518e..f9d827204b 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -36,6 +36,8 @@ class CWallet; class OptionsModel; + +QT_BEGIN_NAMESPACE class QApplication; class QByteArray; class QLocalServer; @@ -43,6 +45,7 @@ class QNetworkAccessManager; class QNetworkReply; class QSslError; class QUrl; +QT_END_NAMESPACE class PaymentServer : public QObject { @@ -56,8 +59,8 @@ public: // will be called so we startup in the right mode. static bool ipcSendCommandLine(int argc, char *argv[]); - PaymentServer(QObject* parent, // parent should be QApplication object - bool startLocalServer=true); + // parent should be QApplication object + PaymentServer(QObject* parent, bool startLocalServer = true); ~PaymentServer(); // Load root certificate authorities. Pass NULL (default) @@ -65,7 +68,7 @@ public: // or, if that's not set, to use the system default root certificates. // If you pass in a store, you should not X509_STORE_free it: it will be // freed either at exit or when another set of CAs are loaded. - static void LoadRootCAs(X509_STORE* store=NULL); + static void LoadRootCAs(X509_STORE* store = NULL); // Return certificate store static X509_STORE* getCertStore() { return certStore; } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 8953c36579..e7dcdf62a1 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -22,6 +22,8 @@ const int CONSOLE_HISTORY = 50; const QSize ICON_SIZE(24, 24); +const int INITIAL_TRAFFIC_GRAPH_MINS = 30; + const struct { const char *url; const char *source; @@ -204,6 +206,7 @@ RPCConsole::RPCConsole(QWidget *parent) : ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION)); startExecutor(); + setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); clear(); } @@ -253,7 +256,8 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event) void RPCConsole::setClientModel(ClientModel *model) { - this->clientModel = model; + clientModel = model; + ui->trafficGraph->setClientModel(model); if(model) { // Keep up to date with client @@ -263,6 +267,9 @@ void RPCConsole::setClientModel(ClientModel *model) setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers()); connect(model, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int))); + updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent()); + connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64))); + // Provide initial values ui->clientVersion->setText(model->formatFullVersion()); ui->clientName->setText(model->clientName()); @@ -431,3 +438,49 @@ void RPCConsole::on_showCLOptionsButton_clicked() GUIUtil::HelpMessageBox help; help.exec(); } + +void RPCConsole::on_sldGraphRange_valueChanged(int value) +{ + const int multiplier = 5; // each position on the slider represents 5 min + int mins = value * multiplier; + setTrafficGraphRange(mins); +} + +QString RPCConsole::FormatBytes(quint64 bytes) +{ + if(bytes < 1024) + return QString(tr("%1 B")).arg(bytes); + if(bytes < 1024 * 1024) + return QString(tr("%1 KB")).arg(bytes / 1024); + if(bytes < 1024 * 1024 * 1024) + return QString(tr("%1 MB")).arg(bytes / 1024 / 1024); + + return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); +} + +void RPCConsole::setTrafficGraphRange(int mins) +{ + ui->trafficGraph->setGraphRangeMins(mins); + if(mins < 60) { + ui->lblGraphRange->setText(QString(tr("%1 m")).arg(mins)); + } else { + int hours = mins / 60; + int minsLeft = mins % 60; + if(minsLeft == 0) { + ui->lblGraphRange->setText(QString(tr("%1 h")).arg(hours)); + } else { + ui->lblGraphRange->setText(QString(tr("%1 h %2 m")).arg(hours).arg(minsLeft)); + } + } +} + +void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) +{ + ui->lblBytesIn->setText(FormatBytes(totalBytesIn)); + ui->lblBytesOut->setText(FormatBytes(totalBytesOut)); +} + +void RPCConsole::on_btnClearTrafficGraph_clicked() +{ + ui->trafficGraph->clear(); +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 3c38b4b8de..af92b55770 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -37,6 +37,12 @@ private slots: void on_openDebugLogfileButton_clicked(); /** display messagebox with program parameters (same as bitcoin-qt --help) */ void on_showCLOptionsButton_clicked(); + /** change the time range of the network traffic graph */ + void on_sldGraphRange_valueChanged(int value); + /** update traffic statistics */ + void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut); + /** clear traffic graph */ + void on_btnClearTrafficGraph_clicked(); public slots: void clear(); @@ -55,6 +61,9 @@ signals: void cmdRequest(const QString &command); private: + static QString FormatBytes(quint64 bytes); + void setTrafficGraphRange(int mins); + Ui::RPCConsole *ui; ClientModel *clientModel; QStringList history; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 809eff9c27..3fd4a26e76 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -94,27 +94,33 @@ void SendCoinsDialog::on_sendButton_clicked() QStringList formatted; foreach(const SendCoinsRecipient &rcp, recipients) { - QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); + // generate bold amount string + QString amount = "<b>" + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); + amount.append("</b>"); + // generate monospace address string + QString address = "<span style='font-family: monospace;'>" + rcp.address; + address.append("</span>"); + + QString recipientElement; + if (rcp.authenticatedMerchant.isEmpty()) { - QString recipientElement = QString("<b>%1</b> ").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount)); - recipientElement.append(tr("to")); - - if(rcp.label.length() > 0) + if(rcp.label.length() > 0) // label with address { - recipientElement.append(QString(" %1 <span style='font-size:8px;'>%2</span><br />").arg(GUIUtil::HtmlEscape(rcp.label), rcp.address)); // add address with label + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)); + recipientElement.append(QString(" (%1)").arg(address)); } - else + else // just address { - recipientElement.append(QString(" %1<br />").arg(rcp.address)); // add address WITHOUT label + recipientElement = tr("%1 to %2").arg(amount, address); } - formatted.append(recipientElement); } - else + else // just merchant { - QString merchant = GUIUtil::HtmlEscape(rcp.authenticatedMerchant); - formatted.append(tr("<b>%1</b> to %2").arg(amount, merchant)); + recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); } + + formatted.append(recipientElement); } fNewRecipientAllowed = false; @@ -132,42 +138,38 @@ void SendCoinsDialog::on_sendButton_clicked() WalletModelTransaction currentTransaction(recipients); WalletModel::SendCoinsReturn prepareStatus = model->prepareTransaction(currentTransaction); + QString strSendCoins = tr("Send Coins"); switch(prepareStatus.status) { case WalletModel::InvalidAddress: - QMessageBox::warning(this, tr("Send Coins"), - tr("The recipient address is not valid, please recheck."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("The recipient address is not valid, please recheck.")); break; case WalletModel::InvalidAmount: - QMessageBox::warning(this, tr("Send Coins"), - tr("The amount to pay must be larger than 0."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("The amount to pay must be larger than 0.")); break; case WalletModel::AmountExceedsBalance: - QMessageBox::warning(this, tr("Send Coins"), - tr("The amount exceeds your balance."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("The amount exceeds your balance.")); break; case WalletModel::AmountWithFeeExceedsBalance: - QMessageBox::warning(this, tr("Send Coins"), + QMessageBox::warning(this, strSendCoins, tr("The total exceeds your balance when the %1 transaction fee is included."). - arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())), - QMessageBox::Ok, QMessageBox::Ok); + arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee()))); break; case WalletModel::DuplicateAddress: - QMessageBox::warning(this, tr("Send Coins"), - tr("Duplicate address found, can only send to each address once per send operation."), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("Duplicate address found, can only send to each address once per send operation.")); break; case WalletModel::TransactionCreationFailed: - QMessageBox::warning(this, tr("Send Coins"), - tr("Error: Transaction creation failed!"), - QMessageBox::Ok, QMessageBox::Ok); + QMessageBox::warning(this, strSendCoins, + tr("Error: Transaction creation failed!")); break; - case WalletModel::Aborted: // User aborted, nothing to do - case WalletModel::OK: case WalletModel::TransactionCommitFailed: + case WalletModel::OK: + case WalletModel::Aborted: // User aborted, nothing to do + default: break; } @@ -197,7 +199,7 @@ void SendCoinsDialog::on_sendButton_clicked() QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), questionString.arg(formatted.join("<br />")), - QMessageBox::Yes|QMessageBox::Cancel, + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if(retval != QMessageBox::Yes) @@ -211,15 +213,13 @@ void SendCoinsDialog::on_sendButton_clicked() switch(sendstatus.status) { case WalletModel::TransactionCommitFailed: - QMessageBox::warning(this, tr("Send Coins"), - tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."), - QMessageBox::Ok, QMessageBox::Ok); - break; - case WalletModel::Aborted: // User aborted, nothing to do + QMessageBox::warning(this, strSendCoins, + tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.")); break; case WalletModel::OK: accept(); break; + case WalletModel::Aborted: // User aborted, nothing to do default: break; } @@ -351,22 +351,22 @@ void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) { + QString strSendCoins = tr("Send Coins"); if (!rv.authenticatedMerchant.isEmpty()) { // Expired payment request? const payments::PaymentDetails& details = rv.paymentRequest.getDetails(); if (details.has_expires() && (int64)details.expires() < GetTime()) { - QMessageBox::warning(this, tr("Send Coins"), - tr("Payment request expired")); + QMessageBox::warning(this, strSendCoins, + tr("Payment request expired")); return false; } } else { CBitcoinAddress address(rv.address.toStdString()); if (!address.IsValid()) { - QString strAddress(address.ToString().c_str()); - QMessageBox::warning(this, tr("Send Coins"), - tr("Invalid payment address %1").arg(strAddress)); + QMessageBox::warning(this, strSendCoins, + tr("Invalid payment address %1").arg(rv.address)); return false; } } diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 75610f199e..aa671e0540 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -85,10 +85,17 @@ void SendCoinsEntry::setRemoveEnabled(bool enabled) void SendCoinsEntry::clear() { + // clear UI elements for insecure payments ui->payTo->clear(); ui->addAsLabel->clear(); ui->payAmount->clear(); + // and the ones for secure payments just to be sure + ui->payTo_s->clear(); + ui->memoTextLabel_s->clear(); + ui->payAmount_s->clear(); + ui->payTo->setFocus(); + // update the display unit, to not use the default ("BTC") updateDisplayUnit(); } @@ -106,8 +113,8 @@ bool SendCoinsEntry::validate() if (!recipient.authenticatedMerchant.isEmpty()) return retval; - if(!ui->payTo->hasAcceptableInput() || - (model && !model->validateAddress(ui->payTo->text()))) + if (!ui->payTo->hasAcceptableInput() || + (model && !model->validateAddress(ui->payTo->text()))) { ui->payTo->setValid(false); retval = false; @@ -154,18 +161,20 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value) { recipient = value; - ui->payTo->setText(value.address); - ui->addAsLabel->setText(value.label); - ui->payAmount->setValue(value.amount); - - if (!recipient.authenticatedMerchant.isEmpty()) + if (recipient.authenticatedMerchant.isEmpty()) + { + ui->payTo->setText(recipient.address); + ui->addAsLabel->setText(recipient.label); + ui->payAmount->setValue(recipient.amount); + } + else { - const payments::PaymentDetails& details = value.paymentRequest.getDetails(); + const payments::PaymentDetails& details = recipient.paymentRequest.getDetails(); - ui->payTo_s->setText(value.authenticatedMerchant); - ui->memo_s->setTextFormat(Qt::PlainText); - ui->memo_s->setText(QString::fromStdString(details.memo())); - ui->payAmount_s->setValue(value.amount); + ui->payTo_s->setText(recipient.authenticatedMerchant); + ui->memoTextLabel_s->setText(QString::fromStdString(details.memo())); + ui->payAmount_s->setValue(recipient.amount); + ui->payAmount_s->setReadOnly(true); setCurrentWidget(ui->SendCoinsSecure); } } diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 9c7bfe9521..49e622daf1 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -33,7 +33,8 @@ public: void setValue(const SendCoinsRecipient &value); void setAddress(const QString &address); - /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907). + /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases + * (issue https://bugreports.qt-project.org/browse/QTBUG-10907). */ QWidget *setupTabChain(QWidget *prev); diff --git a/src/qt/test/Makefile.am b/src/qt/test/Makefile.am index f51ac9bd6d..f8fe97462b 100644 --- a/src/qt/test/Makefile.am +++ b/src/qt/test/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS = $(INCLUDES) -I$(top_builddir)/src/obj \ -I$(top_srcdir)/src/leveldb/include -I$(top_srcdir)/src \ -I$(top_srcdir)/src/leveldb/helpers -I$(top_srcdir)/src/qt \ -I$(top_builddir)/src/qt $(BOOST_INCLUDES) $(PROTOBUF_CFLAGS) \ - $(QR_CFLAGS) + $(QR_CFLAGS) $(BDB_CPPFLAGS) AM_LDFLAGS = $(PTHREAD_CFLAGS) bin_PROGRAMS = test_bitcoin-qt TESTS = test_bitcoin-qt @@ -20,7 +20,7 @@ test_bitcoin_qt_SOURCES = test_main.cpp uritests.cpp paymentservertests.cpp $(TE nodist_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP) test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN) $(LIBLEVELDB) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) \ - $(QR_LIBS) $(PROTOBUF_LIBS) + $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) CLEANFILES = $(BUILT_SOURCES) *.gcda *.gcno diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp new file mode 100644 index 0000000000..d49bc31f3e --- /dev/null +++ b/src/qt/trafficgraphwidget.cpp @@ -0,0 +1,169 @@ +#include "trafficgraphwidget.h" +#include "clientmodel.h" + +#include <QPainter> +#include <QColor> +#include <QTimer> + +#include <cmath> + +#define DESIRED_SAMPLES 800 + +#define XMARGIN 10 +#define YMARGIN 10 + +TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) : + QWidget(parent), + timer(0), + fMax(0.0f), + nMins(0), + vSamplesIn(), + vSamplesOut(), + nLastBytesIn(0), + nLastBytesOut(0), + clientModel(0) +{ + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(updateRates())); +} + +void TrafficGraphWidget::setClientModel(ClientModel *model) +{ + clientModel = model; + if(model) { + nLastBytesIn = model->getTotalBytesRecv(); + nLastBytesOut = model->getTotalBytesSent(); + } +} + +int TrafficGraphWidget::getGraphRangeMins() const +{ + return nMins; +} + +void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue<float> &samples) +{ + int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2; + int sampleCount = samples.size(), x = XMARGIN + w, y; + if(sampleCount > 0) { + path.moveTo(x, YMARGIN + h); + for(int i = 0; i < sampleCount; ++i) { + x = XMARGIN + w - w * i / DESIRED_SAMPLES; + y = YMARGIN + h - (int)(h * samples.at(i) / fMax); + path.lineTo(x, y); + } + path.lineTo(x, YMARGIN + h); + } +} + +void TrafficGraphWidget::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.fillRect(rect(), Qt::black); + + if(fMax <= 0.0f) return; + + QColor axisCol(Qt::gray); + int h = height() - YMARGIN * 2; + painter.setPen(axisCol); + painter.drawLine(XMARGIN, YMARGIN + h, width() - XMARGIN, YMARGIN + h); + + // decide what order of magnitude we are + int base = floor(log10(fMax)); + float val = pow(10.0f, base); + + const QString units = tr("KB/s"); + // draw lines + painter.setPen(axisCol); + painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units)); + for(float y = val; y < fMax; y += val) { + int yy = YMARGIN + h - h * y / fMax; + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); + } + // if we drew 3 or fewer lines, break them up at the next lower order of magnitude + if(fMax / val <= 3.0f) { + axisCol = axisCol.darker(); + val = pow(10.0f, base - 1); + painter.setPen(axisCol); + painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units)); + int count = 1; + for(float y = val; y < fMax; y += val, count++) { + // don't overwrite lines drawn above + if(count % 10 == 0) + continue; + int yy = YMARGIN + h - h * y / fMax; + painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy); + } + } + + if(!vSamplesIn.empty()) { + QPainterPath p; + paintPath(p, vSamplesIn); + painter.fillPath(p, QColor(0, 255, 0, 128)); + painter.setPen(Qt::green); + painter.drawPath(p); + } + if(!vSamplesOut.empty()) { + QPainterPath p; + paintPath(p, vSamplesOut); + painter.fillPath(p, QColor(255, 0, 0, 128)); + painter.setPen(Qt::red); + painter.drawPath(p); + } +} + +void TrafficGraphWidget::updateRates() +{ + if(!clientModel) return; + + quint64 bytesIn = clientModel->getTotalBytesRecv(), + bytesOut = clientModel->getTotalBytesSent(); + float inRate = (bytesIn - nLastBytesIn) / 1024.0f * 1000 / timer->interval(); + float outRate = (bytesOut - nLastBytesOut) / 1024.0f * 1000 / timer->interval(); + vSamplesIn.push_front(inRate); + vSamplesOut.push_front(outRate); + nLastBytesIn = bytesIn; + nLastBytesOut = bytesOut; + + while(vSamplesIn.size() > DESIRED_SAMPLES) { + vSamplesIn.pop_back(); + } + while(vSamplesOut.size() > DESIRED_SAMPLES) { + vSamplesOut.pop_back(); + } + + float tmax = 0.0f; + foreach(float f, vSamplesIn) { + if(f > tmax) tmax = f; + } + foreach(float f, vSamplesOut) { + if(f > tmax) tmax = f; + } + fMax = tmax; + update(); +} + +void TrafficGraphWidget::setGraphRangeMins(int mins) +{ + nMins = mins; + int msecsPerSample = nMins * 60 * 1000 / DESIRED_SAMPLES; + timer->stop(); + timer->setInterval(msecsPerSample); + + clear(); +} + +void TrafficGraphWidget::clear() +{ + timer->stop(); + + vSamplesOut.clear(); + vSamplesIn.clear(); + fMax = 0.0f; + + if(clientModel) { + nLastBytesIn = clientModel->getTotalBytesRecv(); + nLastBytesOut = clientModel->getTotalBytesSent(); + } + timer->start(); +} diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h new file mode 100644 index 0000000000..b31d1d5b0a --- /dev/null +++ b/src/qt/trafficgraphwidget.h @@ -0,0 +1,44 @@ +#ifndef TRAFFICGRAPHWIDGET_H +#define TRAFFICGRAPHWIDGET_H + +#include <QWidget> +#include <QQueue> + +class ClientModel; + +QT_BEGIN_NAMESPACE +class QPaintEvent; +class QTimer; +QT_END_NAMESPACE + +class TrafficGraphWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TrafficGraphWidget(QWidget *parent = 0); + void setClientModel(ClientModel *model); + int getGraphRangeMins() const; + +protected: + void paintEvent(QPaintEvent *); + +public slots: + void updateRates(); + void setGraphRangeMins(int mins); + void clear(); + +private: + void paintPath(QPainterPath &path, QQueue<float> &samples); + + QTimer *timer; + float fMax; + int nMins; + QQueue<float> vSamplesIn; + QQueue<float> vSamplesOut; + quint64 nLastBytesIn; + quint64 nLastBytesOut; + ClientModel *clientModel; +}; + +#endif // TRAFFICGRAPHWIDGET_H diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index e27aa93a4a..93fc8cab22 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -17,7 +17,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) - return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight + 1); + return tr("Open for %n more block(s)", "", wtx.nLockTime - chainActive.Height() + 1); else return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime)); } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index ea2c1f0a5c..162908a9a4 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -160,14 +160,14 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) idx); status.confirmed = wtx.IsConfirmed(); status.depth = wtx.GetDepthInMainChain(); - status.cur_num_blocks = nBestHeight; + status.cur_num_blocks = chainActive.Height(); if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) { status.status = TransactionStatus::OpenUntilBlock; - status.open_for = wtx.nLockTime - nBestHeight + 1; + status.open_for = wtx.nLockTime - chainActive.Height() + 1; } else { @@ -221,7 +221,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) bool TransactionRecord::statusUpdateNeeded() { - return status.cur_num_blocks != nBestHeight; + return status.cur_num_blocks != chainActive.Height(); } QString TransactionRecord::getTxID() const diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 07f6a62150..6f7a5933ab 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -250,9 +250,9 @@ void TransactionTableModel::updateTransaction(const QString &hash, int status) void TransactionTableModel::updateConfirmations() { - if(nBestHeight != cachedNumBlocks) + if(chainActive.Height() != cachedNumBlocks) { - cachedNumBlocks = nBestHeight; + cachedNumBlocks = chainActive.Height(); // Blocks came in since last poll. // Invalidate status (number of confirmations) and (possibly) description // for all rows. Qt is smart enough to only actually request the data for the diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 99a6647a65..8d6a1b387e 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -56,12 +56,9 @@ bool WalletFrame::handlePaymentRequest(const SendCoinsRecipient &recipient) void WalletFrame::showOutOfSyncWarning(bool fShow) { - if (!walletStack) { - QMessageBox box; - box.setText("walletStack is null"); - box.exec(); + if (!walletStack) return; - } + walletStack->showOutOfSyncWarning(fShow); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 1dcecbe60b..417bac9928 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -73,10 +73,10 @@ void WalletModel::updateStatus() void WalletModel::pollBalanceChanged() { - if(nBestHeight != cachedNumBlocks) + if(chainActive.Height() != cachedNumBlocks) { // Balance and number of transactions might have changed - cachedNumBlocks = nBestHeight; + cachedNumBlocks = chainActive.Height(); checkBalanceChanged(); } } @@ -143,7 +143,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact foreach(const SendCoinsRecipient &rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) - { // PaymentRequest... + { // PaymentRequest... int64 subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) @@ -258,22 +258,26 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran // and emit coinsSent signal for each recipient foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) { - std::string strAddress = rcp.address.toStdString(); - CTxDestination dest = CBitcoinAddress(strAddress).Get(); - std::string strLabel = rcp.label.toStdString(); + // Don't touch the address book when we have a secure payment-request + if (rcp.authenticatedMerchant.isEmpty()) { - LOCK(wallet->cs_wallet); - - std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); - - // Check if we have a new address or an updated label - if (mi == wallet->mapAddressBook.end()) - { - wallet->SetAddressBook(dest, strLabel, "send"); - } - else if (mi->second.name != strLabel) + std::string strAddress = rcp.address.toStdString(); + CTxDestination dest = CBitcoinAddress(strAddress).Get(); + std::string strLabel = rcp.label.toStdString(); { - wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose + LOCK(wallet->cs_wallet); + + std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); + + // Check if we have a new address or an updated label + if (mi == wallet->mapAddressBook.end()) + { + wallet->SetAddressBook(dest, strLabel, "send"); + } + else if (mi->second.name != strLabel) + { + wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose + } } } emit coinsSent(wallet, rcp, transaction_array); diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 96fc3edbb2..706ed60b77 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -32,7 +32,7 @@ qint64 WalletModelTransaction::getTransactionFee() void WalletModelTransaction::setTransactionFee(qint64 newFee) { - fee=newFee; + fee = newFee; } qint64 WalletModelTransaction::getTotalTransactionAmount() @@ -40,7 +40,7 @@ qint64 WalletModelTransaction::getTotalTransactionAmount() qint64 totalTransactionAmount = 0; foreach(const SendCoinsRecipient &rcp, recipients) { - totalTransactionAmount+=rcp.amount; + totalTransactionAmount += rcp.amount; } return totalTransactionAmount; } diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index adff708a06..957241d6a0 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -9,7 +9,7 @@ using namespace json_spirit; using namespace std; -void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out); +void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex); double GetDifficulty(const CBlockIndex* blockindex) { @@ -17,10 +17,10 @@ double GetDifficulty(const CBlockIndex* blockindex) // minimum difficulty = 1.0. if (blockindex == NULL) { - if (pindexBest == NULL) + if (chainActive.Tip() == NULL) return 1.0; else - blockindex = pindexBest; + blockindex = chainActive.Tip(); } int nShift = (blockindex->nBits >> 24) & 0xff; @@ -66,7 +66,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); - CBlockIndex *pnext = blockindex->GetNextInMainChain(); + CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); return result; @@ -80,7 +80,7 @@ Value getblockcount(const Array& params, bool fHelp) "getblockcount\n" "Returns the number of blocks in the longest block chain."); - return nBestHeight; + return chainActive.Height(); } Value getbestblockhash(const Array& params, bool fHelp) @@ -90,7 +90,7 @@ Value getbestblockhash(const Array& params, bool fHelp) "getbestblockhash\n" "Returns the hash of the best (tip) block in the longest block chain."); - return hashBestChain.GetHex(); + return chainActive.Tip()->GetBlockHash().GetHex(); } Value getdifficulty(const Array& params, bool fHelp) @@ -145,11 +145,11 @@ Value getblockhash(const Array& params, bool fHelp) "Returns hash of block in best-block-chain at <index>."); int nHeight = params[0].get_int(); - if (nHeight < 0 || nHeight > nBestHeight) + if (nHeight < 0 || nHeight > chainActive.Height()) throw runtime_error("Block number out of range."); - CBlockIndex* pblockindex = FindBlockByHeight(nHeight); - return pblockindex->phashBlock->GetHex(); + CBlockIndex* pblockindex = chainActive[nHeight]; + return pblockindex->GetBlockHash().GetHex(); } Value getblock(const Array& params, bool fHelp) @@ -245,7 +245,7 @@ Value gettxout(const Array& params, bool fHelp) ret.push_back(Pair("confirmations", pcoinsTip->GetBestBlock()->nHeight - coins.nHeight + 1)); ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); Object o; - ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o); + ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); ret.push_back(Pair("scriptPubKey", o)); ret.push_back(Pair("version", coins.nVersion)); ret.push_back(Pair("coinbase", coins.fCoinBase)); diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index e166f76bf1..61cd07d507 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -15,8 +15,6 @@ #include <boost/variant/get.hpp> #include <boost/algorithm/string.hpp> -#define printf OutputDebugStringF - using namespace json_spirit; using namespace std; @@ -104,7 +102,7 @@ Value importprivkey(const Array& params, bool fHelp) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); if (fRescan) { - pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); pwalletMain->ReacceptWalletTransactions(); } } @@ -126,7 +124,7 @@ Value importwallet(const Array& params, bool fHelp) if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); - int64 nTimeBegin = pindexBest->nTime; + int64 nTimeBegin = chainActive.Tip()->nTime; bool fGood = true; @@ -147,7 +145,7 @@ Value importwallet(const Array& params, bool fHelp) CPubKey pubkey = key.GetPubKey(); CKeyID keyid = pubkey.GetID(); if (pwalletMain->HaveKey(keyid)) { - printf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString().c_str()); + LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString().c_str()); continue; } int64 nTime = DecodeDumpTime(vstr[1]); @@ -165,7 +163,7 @@ Value importwallet(const Array& params, bool fHelp) fLabel = true; } } - printf("Importing %s...\n", CBitcoinAddress(keyid).ToString().c_str()); + LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString().c_str()); if (!pwalletMain->AddKeyPubKey(key, pubkey)) { fGood = false; continue; @@ -177,11 +175,11 @@ Value importwallet(const Array& params, bool fHelp) } file.close(); - CBlockIndex *pindex = pindexBest; + CBlockIndex *pindex = chainActive.Tip(); while (pindex && pindex->pprev && pindex->nTime > nTimeBegin - 7200) pindex = pindex->pprev; - printf("Rescanning last %i blocks\n", pindexBest->nHeight - pindex->nHeight + 1); + LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); pwalletMain->ScanForWalletTransactions(pindex); pwalletMain->ReacceptWalletTransactions(); pwalletMain->MarkDirty(); @@ -245,8 +243,8 @@ Value dumpwallet(const Array& params, bool fHelp) // produce output file << strprintf("# Wallet dump created by Bitcoin %s (%s)\n", CLIENT_BUILD.c_str(), CLIENT_DATE.c_str()); file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()).c_str()); - file << strprintf("# * Best block at time of backup was %i (%s),\n", nBestHeight, hashBestChain.ToString().c_str()); - file << strprintf("# mined on %s\n", EncodeDumpTime(pindexBest->nTime).c_str()); + file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString().c_str()); + file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->nTime).c_str()); file << "\n"; for (std::vector<std::pair<int64, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index c7f516caa7..f123c3a9e0 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -18,15 +18,71 @@ static CReserveKey* pMiningKey = NULL; void InitRPCMining() { + if (!pwalletMain) + return; + // getwork/getblocktemplate mining rewards paid here: pMiningKey = new CReserveKey(pwalletMain); } void ShutdownRPCMining() { + if (!pMiningKey) + return; + delete pMiningKey; pMiningKey = NULL; } +// Return average network hashes per second based on the last 'lookup' blocks, +// or from the last difficulty change if 'lookup' is nonpositive. +// If 'height' is nonnegative, compute the estimate at the time when a given block was found. +Value GetNetworkHashPS(int lookup, int height) { + CBlockIndex *pb = chainActive[height]; + + if (pb == NULL || !pb->nHeight) + return 0; + + // If lookup is -1, then use blocks since last difficulty change. + if (lookup <= 0) + lookup = pb->nHeight % 2016 + 1; + + // If lookup is larger than chain, then set it to chain length. + if (lookup > pb->nHeight) + lookup = pb->nHeight; + + CBlockIndex *pb0 = pb; + int64 minTime = pb0->GetBlockTime(); + int64 maxTime = minTime; + for (int i = 0; i < lookup; i++) { + pb0 = pb0->pprev; + int64 time = pb0->GetBlockTime(); + minTime = std::min(time, minTime); + maxTime = std::max(time, maxTime); + } + + // In case there's a situation where minTime == maxTime, we don't want a divide by zero exception. + if (minTime == maxTime) + return 0; + + uint256 workDiff = pb->nChainWork - pb0->nChainWork; + int64 timeDiff = maxTime - minTime; + + return (boost::int64_t)(workDiff.getdouble() / timeDiff); +} + +Value getnetworkhashps(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getnetworkhashps [blocks] [height]\n" + "Returns the estimated network hashes per second based on the last 120 blocks.\n" + "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" + "Pass in [height] to estimate the network speed at the time when a certain block was found."); + + return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); +} + + Value getgenerate(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -34,6 +90,9 @@ Value getgenerate(const Array& params, bool fHelp) "getgenerate\n" "Returns true or false."); + if (!pMiningKey) + return false; + return GetBoolArg("-gen", false); } @@ -59,6 +118,7 @@ Value setgenerate(const Array& params, bool fHelp) } mapArgs["-gen"] = (fGenerate ? "1" : "0"); + assert(pwalletMain != NULL); GenerateBitcoins(fGenerate, pwalletMain); return Value::null; } @@ -85,14 +145,15 @@ Value getmininginfo(const Array& params, bool fHelp) "Returns an object containing mining-related information."); Object obj; - obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("errors", GetWarnings("statusbar"))); - obj.push_back(Pair("generate", GetBoolArg("-gen", false))); + obj.push_back(Pair("generate", getgenerate(params, false))); obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); obj.push_back(Pair("hashespersec", gethashespersec(params, false))); + obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("testnet", TestNet())); return obj; @@ -128,10 +189,10 @@ Value getwork(const Array& params, bool fHelp) static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || + if (pindexPrev != chainActive.Tip() || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); @@ -145,7 +206,7 @@ Value getwork(const Array& params, bool fHelp) // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block @@ -207,6 +268,7 @@ Value getwork(const Array& params, bool fHelp) pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + assert(pwalletMain != NULL); return CheckWork(pblock, *pwalletMain, *pMiningKey); } } @@ -263,7 +325,7 @@ Value getblocktemplate(const Array& params, bool fHelp) static CBlockIndex* pindexPrev; static int64 nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || + if (pindexPrev != chainActive.Tip() || (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on @@ -271,7 +333,7 @@ Value getblocktemplate(const Array& params, bool fHelp) // Store the pindexBest used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index bd7bc0ba10..f78f034e32 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -19,6 +19,24 @@ Value getconnectioncount(const Array& params, bool fHelp) return (int)vNodes.size(); } +Value ping(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "ping\n" + "Requests that a ping be sent to all other nodes, to measure ping time.\n" + "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" + "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping."); + + // Request that each node send a ping during next message processing pass + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pNode, vNodes) { + pNode->fPingQueued = true; + } + + return Value::null; +} + static void CopyNodeStats(std::vector<CNodeStats>& vstats) { vstats.clear(); @@ -54,6 +72,9 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("bytessent", (boost::int64_t)stats.nSendBytes)); obj.push_back(Pair("bytesrecv", (boost::int64_t)stats.nRecvBytes)); obj.push_back(Pair("conntime", (boost::int64_t)stats.nTimeConnected)); + obj.push_back(Pair("pingtime", stats.dPingTime)); + if (stats.dPingWait > 0.0) + obj.push_back(Pair("pingwait", stats.dPingWait)); obj.push_back(Pair("version", stats.nVersion)); obj.push_back(Pair("subver", stats.strSubVer)); obj.push_back(Pair("inbound", stats.fInbound)); @@ -202,3 +223,17 @@ Value getaddednodeinfo(const Array& params, bool fHelp) return ret; } +Value getnettotals(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "getnettotals\n" + "Returns information about network traffic, including bytes in, bytes out,\n" + "and current time."); + + Object obj; + obj.push_back(Pair("totalbytesrecv", static_cast< boost::uint64_t>(CNode::GetTotalBytesRecv()))); + obj.push_back(Pair("totalbytessent", static_cast<boost::uint64_t>(CNode::GetTotalBytesSent()))); + obj.push_back(Pair("timemillis", static_cast<boost::int64_t>(GetTimeMillis()))); + return obj; +} diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 580120f2a2..5384b65906 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -17,47 +17,15 @@ using namespace boost; using namespace boost::assign; using namespace json_spirit; -// -// Utilities: convert hex-encoded Values -// (throws error if not hex). -// -uint256 ParseHashV(const Value& v, string strName) -{ - string strHex; - if (v.type() == str_type) - strHex = v.get_str(); - if (!IsHex(strHex)) // Note: IsHex("") is false - throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); - uint256 result; - result.SetHex(strHex); - return result; -} -uint256 ParseHashO(const Object& o, string strKey) -{ - return ParseHashV(find_value(o, strKey), strKey); -} -vector<unsigned char> ParseHexV(const Value& v, string strName) -{ - string strHex; - if (v.type() == str_type) - strHex = v.get_str(); - if (!IsHex(strHex)) - throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); - return ParseHex(strHex); -} -vector<unsigned char> ParseHexO(const Object& o, string strKey) -{ - return ParseHexV(find_value(o, strKey), strKey); -} - -void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out) +void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex) { txnouttype type; vector<CTxDestination> addresses; int nRequired; out.push_back(Pair("asm", scriptPubKey.ToString())); - out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + if (fIncludeHex) + out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { @@ -106,7 +74,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) out.push_back(Pair("value", ValueFromAmount(txout.nValue))); out.push_back(Pair("n", (boost::int64_t)i)); Object o; - ScriptPubKeyToJSON(txout.scriptPubKey, o); + ScriptPubKeyToJSON(txout.scriptPubKey, o, false); out.push_back(Pair("scriptPubKey", o)); vout.push_back(out); } @@ -119,9 +87,9 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; - if (pindex->IsInMainChain()) + if (chainActive.Contains(pindex)) { - entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight)); + entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); entry.push_back(Pair("time", (boost::int64_t)pindex->nTime)); entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime)); } @@ -203,6 +171,7 @@ Value listunspent(const Array& params, bool fHelp) Array results; vector<COutput> vecOutputs; + assert(pwalletMain != NULL); pwalletMain->AvailableCoins(vecOutputs, false); BOOST_FOREACH(const COutput& out, vecOutputs) { @@ -334,6 +303,29 @@ Value decoderawtransaction(const Array& params, bool fHelp) return result; } +Value decodescript(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "decodescript <hex string>\n" + "Decode a hex-encoded script."); + + RPCTypeCheck(params, list_of(str_type)); + + Object r; + CScript script; + if (params[0].get_str().size() > 0){ + vector<unsigned char> scriptData(ParseHexV(params[0], "argument")); + script = CScript(scriptData.begin(), scriptData.end()); + } else { + // Empty scripts are valid + } + ScriptPubKeyToJSON(script, r, false); + + r.push_back(Pair("p2sh", CBitcoinAddress(script.GetID()).ToString())); + return r; +} + Value signrawtransaction(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 4) @@ -467,7 +459,7 @@ Value signrawtransaction(const Array& params, bool fHelp) } } - const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain); + const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); int nHashType = SIGHASH_ALL; if (params.size() > 3 && params[3].type() != null_type) @@ -527,9 +519,9 @@ Value signrawtransaction(const Array& params, bool fHelp) Value sendrawtransaction(const Array& params, bool fHelp) { - if (fHelp || params.size() < 1 || params.size() > 1) + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "sendrawtransaction <hex string>\n" + "sendrawtransaction <hex string> [allowhighfees=false]\n" "Submits raw transaction (serialized, hex-encoded) to local node and network."); // parse hex string from parameter @@ -537,6 +529,10 @@ Value sendrawtransaction(const Array& params, bool fHelp) CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; + bool fOverrideFees = false; + if (params.size() > 1) + fOverrideFees = params[1].get_bool(); + // deserialize binary data stream try { ssData >> tx; @@ -554,7 +550,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) if (!fHave) { // push to local node CValidationState state; - if (!mempool.accept(state, tx, false, NULL)) + if (!mempool.accept(state, tx, false, NULL, !fOverrideFees)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state } } diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 14b4956a15..f7341f7b69 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -21,7 +21,7 @@ static CCriticalSection cs_nWalletUnlockTime; std::string HelpRequiringPassphrase() { - return pwalletMain->IsCrypted() + return pwalletMain && pwalletMain->IsCrypted() ? "\nrequires wallet passphrase to be set with walletpassphrase first" : ""; } @@ -72,18 +72,22 @@ Value getinfo(const Array& params, bool fHelp) Object obj; obj.push_back(Pair("version", (int)CLIENT_VERSION)); obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); - obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - obj.push_back(Pair("blocks", (int)nBestHeight)); + if (pwalletMain) { + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + } + obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("testnet", TestNet())); - obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + if (pwalletMain) { + obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + } obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); - if (pwalletMain->IsCrypted()) + if (pwalletMain && pwalletMain->IsCrypted()) obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); obj.push_back(Pair("errors", GetWarnings("statusbar"))); return obj; @@ -738,7 +742,7 @@ static CScript _createmultisig(const Array& params) // Case 1: Bitcoin address and we have full public key: CBitcoinAddress address(ks); - if (address.IsValid()) + if (pwalletMain && address.IsValid()) { CKeyID keyID; if (!address.GetKeyID(keyID)) @@ -1165,7 +1169,9 @@ Value listsinceblock(const Array& params, bool fHelp) uint256 blockId = 0; blockId.SetHex(params[0].get_str()); - pindex = CBlockLocator(blockId).GetBlockIndex(); + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(blockId); + if (it != mapBlockIndex.end()) + pindex = it->second; } if (params.size() > 1) @@ -1176,7 +1182,7 @@ Value listsinceblock(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); } - int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; + int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; Array transactions; @@ -1188,23 +1194,8 @@ Value listsinceblock(const Array& params, bool fHelp) ListTransactions(tx, "*", 0, true, transactions); } - uint256 lastblock; - - if (target_confirms == 1) - { - lastblock = hashBestChain; - } - else - { - int target_height = pindexBest->nHeight + 1 - target_confirms; - - CBlockIndex *block; - for (block = pindexBest; - block && block->nHeight > target_height; - block = block->pprev) { } - - lastblock = block ? block->GetBlockHash() : 0; - } + CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; + uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : 0; Object ret; ret.push_back(Pair("transactions", transactions)); @@ -1447,6 +1438,7 @@ public: int nRequired; ExtractDestinations(subscript, whichType, addresses, nRequired); obj.push_back(Pair("script", GetTxnOutputType(whichType))); + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); Array a; BOOST_FOREACH(const CTxDestination& addr, addresses) a.push_back(CBitcoinAddress(addr).ToString()); @@ -1474,13 +1466,13 @@ Value validateaddress(const Array& params, bool fHelp) CTxDestination dest = address.Get(); string currentAddress = address.ToString(); ret.push_back(Pair("address", currentAddress)); - bool fMine = IsMine(*pwalletMain, dest); + bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false; ret.push_back(Pair("ismine", fMine)); if (fMine) { Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest); ret.insert(ret.end(), detail.begin(), detail.end()); } - if (pwalletMain->mapAddressBook.count(dest)) + if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); } return ret; diff --git a/src/script.cpp b/src/script.cpp index 3c3e9cf3bf..ad2d35d94c 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -971,62 +971,118 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co +namespace { +/** Wrapper that serializes like CTransaction, but with the modifications + * required for the signature hash done in-place + */ +class CTransactionSignatureSerializer { +private: + const CTransaction &txTo; // reference to the spending transaction (the one being serialized) + const CScript &scriptCode; // output script being consumed + const unsigned int nIn; // input index of txTo being signed + const bool fAnyoneCanPay; // whether the hashtype has the SIGHASH_ANYONECANPAY flag set + const bool fHashSingle; // whether the hashtype is SIGHASH_SINGLE + const bool fHashNone; // whether the hashtype is SIGHASH_NONE - -uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) -{ - if (nIn >= txTo.vin.size()) - { - printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); - return 1; +public: + CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : + txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn), + fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)), + fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE), + fHashNone((nHashTypeIn & 0x1f) == SIGHASH_NONE) {} + + /** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */ + template<typename S> + void SerializeScriptCode(S &s, int nType, int nVersion) const { + CScript::const_iterator it = scriptCode.begin(); + CScript::const_iterator itBegin = it; + opcodetype opcode; + unsigned int nCodeSeparators = 0; + while (scriptCode.GetOp(it, opcode)) { + if (opcode == OP_CODESEPARATOR) + nCodeSeparators++; + } + ::WriteCompactSize(s, scriptCode.size() - nCodeSeparators); + it = itBegin; + while (scriptCode.GetOp(it, opcode)) { + if (opcode == OP_CODESEPARATOR) { + s.write((char*)&itBegin[0], it-itBegin-1); + itBegin = it; + } + } + s.write((char*)&itBegin[0], it-itBegin); } - CTransaction txTmp(txTo); - // In case concatenating two scripts ends up with two codeseparators, - // or an extra one at the end, this prevents all those possible incompatibilities. - scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + /** Serialize an input of txTo */ + template<typename S> + void SerializeInput(S &s, unsigned int nInput, int nType, int nVersion) const { + // In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized + if (fAnyoneCanPay) + nInput = nIn; + // Serialize the prevout + ::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion); + // Serialize the script + if (nInput != nIn) + // Blank out other inputs' signatures + ::Serialize(s, CScript(), nType, nVersion); + else + SerializeScriptCode(s, nType, nVersion); + // Serialize the nSequence + if (nInput != nIn && (fHashSingle || fHashNone)) + // let the others update at will + ::Serialize(s, (int)0, nType, nVersion); + else + ::Serialize(s, txTo.vin[nInput].nSequence, nType, nVersion); + } - // Blank out other inputs' signatures - for (unsigned int i = 0; i < txTmp.vin.size(); i++) - txTmp.vin[i].scriptSig = CScript(); - txTmp.vin[nIn].scriptSig = scriptCode; + /** Serialize an output of txTo */ + template<typename S> + void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const { + if (fHashSingle && nOutput != nIn) + // Do not lock-in the txout payee at other indices as txin + ::Serialize(s, CTxOut(), nType, nVersion); + else + ::Serialize(s, txTo.vout[nOutput], nType, nVersion); + } - // Blank out some of the outputs - if ((nHashType & 0x1f) == SIGHASH_NONE) - { - // Wildcard payee - txTmp.vout.clear(); + /** Serialize txTo */ + template<typename S> + void Serialize(S &s, int nType, int nVersion) const { + // Serialize nVersion + ::Serialize(s, txTo.nVersion, nType, nVersion); + // Serialize vin + unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size(); + ::WriteCompactSize(s, nInputs); + for (unsigned int nInput = 0; nInput < nInputs; nInput++) + SerializeInput(s, nInput, nType, nVersion); + // Serialize vout + unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size()); + ::WriteCompactSize(s, nOutputs); + for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++) + SerializeOutput(s, nOutput, nType, nVersion); + // Serialie nLockTime + ::Serialize(s, txTo.nLockTime, nType, nVersion); + } +}; +} - // Let the others update at will - for (unsigned int i = 0; i < txTmp.vin.size(); i++) - if (i != nIn) - txTmp.vin[i].nSequence = 0; +uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + if (nIn >= txTo.vin.size()) { + LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + return 1; } - else if ((nHashType & 0x1f) == SIGHASH_SINGLE) - { - // Only lock-in the txout payee at same index as txin - unsigned int nOut = nIn; - if (nOut >= txTmp.vout.size()) - { - printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + + // Check for invalid use of SIGHASH_SINGLE + if ((nHashType & 0x1f) == SIGHASH_SINGLE) { + if (nIn >= txTo.vout.size()) { + LogPrintf("ERROR: SignatureHash() : nOut=%d out of range\n", nIn); return 1; } - txTmp.vout.resize(nOut+1); - for (unsigned int i = 0; i < nOut; i++) - txTmp.vout[i].SetNull(); - - // Let the others update at will - for (unsigned int i = 0; i < txTmp.vin.size(); i++) - if (i != nIn) - txTmp.vin[i].nSequence = 0; } - // Blank out other inputs completely, not recommended for open transactions - if (nHashType & SIGHASH_ANYONECANPAY) - { - txTmp.vin[0] = txTmp.vin[nIn]; - txTmp.vin.resize(1); - } + // Wrapper to serialize only the necessary parts of the transaction being signed + CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType); // Serialize and hash CHashWriter ss(SER_GETHASH, 0); diff --git a/src/script.h b/src/script.h index 0ab47678e8..842b8512eb 100644 --- a/src/script.h +++ b/src/script.h @@ -553,6 +553,13 @@ public: return true; } + // Returns whether the script is guaranteed to fail at execution, + // regardless of the initial stack. This allows outputs to be pruned + // instantly when entering the UTXO set. + bool IsUnspendable() const + { + return (size() > 0 && *begin() == OP_RETURN); + } void SetDestination(const CTxDestination& address); void SetMultisig(int nRequired, const std::vector<CPubKey>& keys); @@ -560,7 +567,7 @@ public: void PrintHex() const { - printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); + LogPrintf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); } std::string ToString() const @@ -588,7 +595,7 @@ public: void print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } CScriptID GetID() const diff --git a/src/sync.cpp b/src/sync.cpp index 1ac4403beb..29a455f9b2 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -10,8 +10,8 @@ #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine) { - printf("LOCKCONTENTION: %s\n", pszName); - printf("Locker: %s:%d\n", pszFile, nLine); + LogPrintf("LOCKCONTENTION: %s\n", pszName); + LogPrintf("Locker: %s:%d\n", pszFile, nLine); } #endif /* DEBUG_LOCKCONTENTION */ @@ -56,20 +56,20 @@ static boost::thread_specific_ptr<LockStack> lockstack; static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2) { - printf("POTENTIAL DEADLOCK DETECTED\n"); - printf("Previous lock order was:\n"); + LogPrintf("POTENTIAL DEADLOCK DETECTED\n"); + LogPrintf("Previous lock order was:\n"); BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s2) { - if (i.first == mismatch.first) printf(" (1)"); - if (i.first == mismatch.second) printf(" (2)"); - printf(" %s\n", i.second.ToString().c_str()); + if (i.first == mismatch.first) LogPrintf(" (1)"); + if (i.first == mismatch.second) LogPrintf(" (2)"); + LogPrintf(" %s\n", i.second.ToString().c_str()); } - printf("Current lock order is:\n"); + LogPrintf("Current lock order is:\n"); BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)& i, s1) { - if (i.first == mismatch.first) printf(" (1)"); - if (i.first == mismatch.second) printf(" (2)"); - printf(" %s\n", i.second.ToString().c_str()); + if (i.first == mismatch.first) LogPrintf(" (1)"); + if (i.first == mismatch.second) LogPrintf(" (2)"); + LogPrintf(" %s\n", i.second.ToString().c_str()); } } @@ -78,7 +78,7 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) if (lockstack.get() == NULL) lockstack.reset(new LockStack); - if (fDebug) printf("Locking: %s\n", locklocation.ToString().c_str()); + if (fDebug) LogPrintf("Locking: %s\n", locklocation.ToString().c_str()); dd_mutex.lock(); (*lockstack).push_back(std::make_pair(c, locklocation)); @@ -108,7 +108,7 @@ static void pop_lock() if (fDebug) { const CLockLocation& locklocation = (*lockstack).rbegin()->second; - printf("Unlocked: %s\n", locklocation.ToString().c_str()); + LogPrintf("Unlocked: %s\n", locklocation.ToString().c_str()); } dd_mutex.lock(); (*lockstack).pop_back(); diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 80571aa295..c3495095d9 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -2,7 +2,7 @@ include $(top_srcdir)/src/Makefile.include AM_CPPFLAGS = $(INCLUDES) -I$(top_builddir)/src/obj \ -I$(top_srcdir)/src/leveldb/include -I$(top_srcdir)/src/leveldb/helpers \ - -I$(top_srcdir)/src $(BOOST_INCLUDES) + -I$(top_srcdir)/src $(BOOST_INCLUDES) $(BDB_CPPFLAGS) AM_LDFLAGS = $(PTHREAD_CFLAGS) @@ -10,21 +10,22 @@ bin_PROGRAMS = test_bitcoin TESTS = test_bitcoin -TEST_DATA_DIR=$(top_srcdir)/src/test/data +JSON_TEST_FILES= data/script_valid.json \ + data/base58_keys_valid.json data/sig_canonical.json \ + data/sig_noncanonical.json \ + data/base58_encode_decode.json \ + data/base58_keys_invalid.json \ + data/script_invalid.json data/tx_invalid.json \ + data/tx_valid.json -TEST_DATA_FILES= $(TEST_DATA_DIR)/script_valid.json \ - $(TEST_DATA_DIR)/base58_keys_valid.json $(TEST_DATA_DIR)/sig_canonical.json \ - $(TEST_DATA_DIR)/sig_noncanonical.json \ - $(TEST_DATA_DIR)/base58_encode_decode.json $(TEST_DATA_DIR)/alertTests \ - $(TEST_DATA_DIR)/base58_keys_invalid.json \ - $(TEST_DATA_DIR)/script_invalid.json $(TEST_DATA_DIR)/tx_invalid.json \ - $(TEST_DATA_DIR)/tx_valid.json +RAW_TEST_FILES = data/alertTests.raw + +BUILT_SOURCES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) # test_bitcoin binary # -test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(TESTDEFS) \ - -DTEST_DATA_DIR=$(TEST_DATA_DIR) +test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(TESTDEFS) test_bitcoin_LDADD = $(LIBBITCOIN) $(LIBLEVELDB) $(LIBMEMENV) \ - $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) + $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(BDB_LIBS) test_bitcoin_SOURCES = accounting_tests.cpp alert_tests.cpp \ allocator_tests.cpp base32_tests.cpp base58_tests.cpp base64_tests.cpp \ bignum_tests.cpp bloom_tests.cpp canonical_tests.cpp checkblock_tests.cpp \ @@ -33,6 +34,8 @@ test_bitcoin_SOURCES = accounting_tests.cpp alert_tests.cpp \ netbase_tests.cpp pmt_tests.cpp rpc_tests.cpp script_P2SH_tests.cpp \ script_tests.cpp serialize_tests.cpp sigopcount_tests.cpp test_bitcoin.cpp \ transaction_tests.cpp uint160_tests.cpp uint256_tests.cpp util_tests.cpp \ - wallet_tests.cpp $(TEST_DATA_FILES) + wallet_tests.cpp sighash_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES) + +nodist_test_bitcoin_SOURCES = $(BUILT_SOURCES) -CLEANFILES = *.gcda *.gcno +CLEANFILES = *.gcda *.gcno $(BUILT_SOURCES) diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index f7a11376d3..cb941943f7 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -9,6 +9,7 @@ #include "alert.h" #include "serialize.h" #include "util.h" +#include "data/alertTests.raw.h" #if 0 // @@ -71,27 +72,13 @@ struct ReadAlerts { ReadAlerts() { - std::string filename("alertTests"); - namespace fs = boost::filesystem; - fs::path testFile = fs::current_path() / "test" / "data" / filename; -#ifdef TEST_DATA_DIR - if (!fs::exists(testFile)) - { - testFile = fs::path(BOOST_PP_STRINGIZE(TEST_DATA_DIR)) / filename; - } -#endif - FILE* fp = fopen(testFile.string().c_str(), "rb"); - if (!fp) return; - - - CAutoFile filein = CAutoFile(fp, SER_DISK, CLIENT_VERSION); - if (!filein) return; - + std::vector<unsigned char> vch(alert_tests::alertTests, alert_tests::alertTests + sizeof(alert_tests::alertTests)); + CDataStream stream(vch, SER_DISK, CLIENT_VERSION); try { - while (!feof(filein)) + while (stream.good()) { CAlert alert; - filein >> alert; + stream >> alert; alerts.push_back(alert); } } @@ -125,6 +112,9 @@ BOOST_AUTO_TEST_CASE(AlertApplies) { BOOST_CHECK(alert.CheckSignature()); } + + BOOST_CHECK(alerts.size() >= 3); + // Matches: BOOST_CHECK(alerts[0].AppliesTo(1, "")); BOOST_CHECK(alerts[0].AppliesTo(70001, "")); diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index af65416485..05675685bd 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -2,20 +2,21 @@ #include "json/json_spirit_reader_template.h" #include "json/json_spirit_writer_template.h" #include "json/json_spirit_utils.h" +#include "data/base58_encode_decode.json.h" +#include "data/base58_keys_invalid.json.h" +#include "data/base58_keys_valid.json.h" #include "base58.h" #include "util.h" - using namespace json_spirit; -extern Array read_json(const std::string& filename); +extern Array read_json(const std::string& jsondata); BOOST_AUTO_TEST_SUITE(base58_tests) // Goal: test low-level base58 encoding functionality BOOST_AUTO_TEST_CASE(base58_EncodeBase58) { - Array tests = read_json("base58_encode_decode.json"); - + Array tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode))); BOOST_FOREACH(Value& tv, tests) { Array test = tv.get_array(); @@ -36,7 +37,7 @@ BOOST_AUTO_TEST_CASE(base58_EncodeBase58) // Goal: test low-level base58 decoding functionality BOOST_AUTO_TEST_CASE(base58_DecodeBase58) { - Array tests = read_json("base58_encode_decode.json"); + Array tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode))); std::vector<unsigned char> result; BOOST_FOREACH(Value& tv, tests) @@ -104,7 +105,7 @@ public: // Goal: check that parsed keys match test payload BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { - Array tests = read_json("base58_keys_valid.json"); + Array tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); std::vector<unsigned char> result; CBitcoinSecret secret; CBitcoinAddress addr; @@ -163,7 +164,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) // Goal: check that generated keys match test vectors BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) { - Array tests = read_json("base58_keys_valid.json"); + Array tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); std::vector<unsigned char> result; BOOST_FOREACH(Value& tv, tests) { @@ -231,7 +232,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) // Goal: check that base58 parsing code is robust against a variety of corrupted data BOOST_AUTO_TEST_CASE(base58_keys_invalid) { - Array tests = read_json("base58_keys_invalid.json"); // Negative testcases + Array tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases std::vector<unsigned char> result; CBitcoinSecret secret; CBitcoinAddress addr; diff --git a/src/test/canonical_tests.cpp b/src/test/canonical_tests.cpp index 09988da259..ec32ceb8a4 100644 --- a/src/test/canonical_tests.cpp +++ b/src/test/canonical_tests.cpp @@ -8,13 +8,15 @@ #include "key.h" #include "script.h" #include "util.h" +#include "data/sig_noncanonical.json.h" +#include "data/sig_canonical.json.h" using namespace std; using namespace json_spirit; // In script_tests.cpp -extern Array read_json(const std::string& filename); +extern Array read_json(const std::string& jsondata); BOOST_AUTO_TEST_SUITE(canonical_tests) @@ -58,7 +60,7 @@ bool static IsCanonicalSignature_OpenSSL(const std::vector<unsigned char> &vchSi BOOST_AUTO_TEST_CASE(script_canon) { - Array tests = read_json("sig_canonical.json"); + Array tests = read_json(std::string(json_tests::sig_canonical, json_tests::sig_canonical + sizeof(json_tests::sig_canonical))); BOOST_FOREACH(Value &tv, tests) { string test = tv.get_str(); @@ -72,7 +74,7 @@ BOOST_AUTO_TEST_CASE(script_canon) BOOST_AUTO_TEST_CASE(script_noncanon) { - Array tests = read_json("sig_noncanonical.json"); + Array tests = read_json(std::string(json_tests::sig_noncanonical, json_tests::sig_noncanonical + sizeof(json_tests::sig_noncanonical))); BOOST_FOREACH(Value &tv, tests) { string test = tv.get_str(); diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index 5675c40e76..e34680db99 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -19,7 +19,7 @@ bool read_block(const std::string& filename, CBlock& block) { namespace fs = boost::filesystem; - fs::path testFile = fs::current_path() / "test" / "data" / filename; + fs::path testFile = fs::current_path() / "data" / filename; #ifdef TEST_DATA_DIR if (!fs::exists(testFile)) { diff --git a/src/test/data/alertTests b/src/test/data/alertTests.raw Binary files differindex 7fc4528961..7fc4528961 100644 --- a/src/test/data/alertTests +++ b/src/test/data/alertTests.raw diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 8183504147..67165760b2 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -65,10 +65,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 1; - pblock->nTime = pindexBest->GetMedianTimePast()+1; + pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1; pblock->vtx[0].vin[0].scriptSig = CScript(); pblock->vtx[0].vin[0].scriptSig.push_back(blockinfo[i].extranonce); - pblock->vtx[0].vin[0].scriptSig.push_back(pindexBest->nHeight); + pblock->vtx[0].vin[0].scriptSig.push_back(chainActive.Height()); pblock->vtx[0].vout[0].scriptPubKey = CScript(); if (txFirst.size() < 2) txFirst.push_back(new CTransaction(pblock->vtx[0])); @@ -83,6 +83,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // Just to make sure we can still make simple blocks BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + delete pblocktemplate; // block sigops > limit: 1000 CHECKMULTISIG + 1 tx.vin.resize(1); @@ -192,14 +193,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.clear(); // subsidy changing - int nHeight = pindexBest->nHeight; - pindexBest->nHeight = 209999; + int nHeight = chainActive.Height(); + chainActive.Tip()->nHeight = 209999; BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; - pindexBest->nHeight = 210000; + chainActive.Tip()->nHeight = 210000; BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; - pindexBest->nHeight = nHeight; + chainActive.Tip()->nHeight = nHeight; + + BOOST_FOREACH(CTransaction *tx, txFirst) + delete tx; } BOOST_AUTO_TEST_CASE(sha256transform_equality) diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 9ef932b5b4..7f6f141c62 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -19,7 +19,7 @@ using namespace boost::assign; typedef vector<unsigned char> valtype; -extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); BOOST_AUTO_TEST_SUITE(multisig_tests) diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index c1f6f178db..32be914414 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -14,12 +14,14 @@ #include "main.h" #include "wallet.h" +#include "data/script_invalid.json.h" +#include "data/script_valid.json.h" using namespace std; using namespace json_spirit; using namespace boost::algorithm; -extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); static const unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; @@ -90,34 +92,15 @@ ParseScript(string s) } Array -read_json(const std::string& filename) +read_json(const std::string& jsondata) { - namespace fs = boost::filesystem; - fs::path testFile = fs::current_path() / "test" / "data" / filename; - -#ifdef TEST_DATA_DIR - if (!fs::exists(testFile)) - { - testFile = fs::path(BOOST_PP_STRINGIZE(TEST_DATA_DIR)) / filename; - } -#endif - - ifstream ifs(testFile.string().c_str(), ifstream::in); Value v; - if (!read_stream(ifs, v)) - { - if (ifs.fail()) - BOOST_ERROR("Cound not find/open " << filename); - else - BOOST_ERROR("JSON syntax error in " << filename); - return Array(); - } - if (v.type() != array_type) + + if (!read_string(jsondata, v) || v.type() != array_type) { - BOOST_ERROR(filename << " does not contain a json array"); + BOOST_ERROR("Parse error."); return Array(); } - return v.get_array(); } @@ -130,7 +113,7 @@ BOOST_AUTO_TEST_CASE(script_valid) // Inner arrays are [ "scriptSig", "scriptPubKey" ] // ... where scriptSig and scriptPubKey are stringified // scripts. - Array tests = read_json("script_valid.json"); + Array tests = read_json(std::string(json_tests::script_valid, json_tests::script_valid + sizeof(json_tests::script_valid))); BOOST_FOREACH(Value& tv, tests) { @@ -154,7 +137,7 @@ BOOST_AUTO_TEST_CASE(script_valid) BOOST_AUTO_TEST_CASE(script_invalid) { // Scripts that should evaluate as invalid - Array tests = read_json("script_invalid.json"); + Array tests = read_json(std::string(json_tests::script_invalid, json_tests::script_invalid + sizeof(json_tests::script_invalid))); BOOST_FOREACH(Value& tv, tests) { diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp new file mode 100644 index 0000000000..f098d46186 --- /dev/null +++ b/src/test/sighash_tests.cpp @@ -0,0 +1,120 @@ +#include <boost/test/unit_test.hpp> + +#include "main.h" +#include "util.h" + +extern uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); + +// Old script.cpp SignatureHash function +uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +{ + if (nIn >= txTo.vin.size()) + { + printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + return 1; + } + CTransaction txTmp(txTo); + + // In case concatenating two scripts ends up with two codeseparators, + // or an extra one at the end, this prevents all those possible incompatibilities. + scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); + + // Blank out other inputs' signatures + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + txTmp.vin[i].scriptSig = CScript(); + txTmp.vin[nIn].scriptSig = scriptCode; + + // Blank out some of the outputs + if ((nHashType & 0x1f) == SIGHASH_NONE) + { + // Wildcard payee + txTmp.vout.clear(); + + // Let the others update at will + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + else if ((nHashType & 0x1f) == SIGHASH_SINGLE) + { + // Only lock-in the txout payee at same index as txin + unsigned int nOut = nIn; + if (nOut >= txTmp.vout.size()) + { + printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + return 1; + } + txTmp.vout.resize(nOut+1); + for (unsigned int i = 0; i < nOut; i++) + txTmp.vout[i].SetNull(); + + // Let the others update at will + for (unsigned int i = 0; i < txTmp.vin.size(); i++) + if (i != nIn) + txTmp.vin[i].nSequence = 0; + } + + // Blank out other inputs completely, not recommended for open transactions + if (nHashType & SIGHASH_ANYONECANPAY) + { + txTmp.vin[0] = txTmp.vin[nIn]; + txTmp.vin.resize(1); + } + + // Serialize and hash + CHashWriter ss(SER_GETHASH, 0); + ss << txTmp << nHashType; + return ss.GetHash(); +} + +void static RandomScript(CScript &script) { + static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR}; + script = CScript(); + int ops = (insecure_rand() % 10); + for (int i=0; i<ops; i++) + script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))]; +} + +void static RandomTransaction(CTransaction &tx, bool fSingle) { + tx.nVersion = insecure_rand(); + tx.vin.clear(); + tx.vout.clear(); + tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0; + int ins = (insecure_rand() % 4) + 1; + int outs = fSingle ? ins : (insecure_rand() % 4) + 1; + for (int in = 0; in < ins; in++) { + tx.vin.push_back(CTxIn()); + CTxIn &txin = tx.vin.back(); + txin.prevout.hash = GetRandHash(); + txin.prevout.n = insecure_rand() % 4; + RandomScript(txin.scriptSig); + txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1; + } + for (int out = 0; out < outs; out++) { + tx.vout.push_back(CTxOut()); + CTxOut &txout = tx.vout.back(); + txout.nValue = insecure_rand() % 100000000; + RandomScript(txout.scriptPubKey); + } +} + +BOOST_AUTO_TEST_SUITE(sighash_tests) + +BOOST_AUTO_TEST_CASE(sighash_test) +{ + seed_insecure_rand(false); + + for (int i=0; i<50000; i++) { + int nHashType = insecure_rand(); + CTransaction txTo; + RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE); + CScript scriptCode; + RandomScript(scriptCode); + int nIn = insecure_rand() % txTo.vin.size(); + BOOST_CHECK(SignatureHash(scriptCode, txTo, nIn, nHashType) == + SignatureHashOld(scriptCode, txTo, nIn, nHashType)); + } +} + +BOOST_AUTO_TEST_SUITE_END() + diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 0c7475b4f2..416b93ab33 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -5,12 +5,14 @@ #include "main.h" #include "wallet.h" +#include "data/tx_invalid.json.h" +#include "data/tx_valid.json.h" using namespace std; using namespace json_spirit; // In script_tests.cpp -extern Array read_json(const std::string& filename); +extern Array read_json(const std::string& jsondata); extern CScript ParseScript(string s); BOOST_AUTO_TEST_SUITE(transaction_tests) @@ -22,7 +24,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) // Inner arrays are either [ "comment" ] // or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, enforceP2SH // ... where all scripts are stringified scripts. - Array tests = read_json("tx_valid.json"); + Array tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid))); BOOST_FOREACH(Value& tv, tests) { @@ -91,7 +93,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) // Inner arrays are either [ "comment" ] // or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, enforceP2SH // ... where all scripts are stringified scripts. - Array tests = read_json("tx_invalid.json"); + Array tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid))); BOOST_FOREACH(Value& tv, tests) { diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp index a14f6b2b70..51f3b27c8b 100644 --- a/src/test/wallet_tests.cpp +++ b/src/test/wallet_tests.cpp @@ -289,6 +289,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK_NE(fails, RANDOM_REPEATS); } } + empty_wallet(); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/txdb.cpp b/src/txdb.cpp index 34836eaa97..24ee8ec3e8 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -55,7 +55,7 @@ bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { } bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { - printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); + LogPrint("coindb", "Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); CLevelDBBatch batch; for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) @@ -223,10 +223,6 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - // Watch for genesis block - if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == Params().HashGenesisBlock()) - pindexGenesisBlock = pindexNew; - if (!pindexNew->CheckIndex()) return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str()); diff --git a/src/uint256.h b/src/uint256.h index 2a252c94f3..45ab8abb5e 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -654,39 +654,39 @@ inline int Testuint256AdHoc(std::vector<std::string> vArg) uint256 g(0); - printf("%s\n", g.ToString().c_str()); - g--; printf("g--\n"); - printf("%s\n", g.ToString().c_str()); - g--; printf("g--\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); - g++; printf("g++\n"); - printf("%s\n", g.ToString().c_str()); + LogPrintf("%s\n", g.ToString().c_str()); + g--; LogPrintf("g--\n"); + LogPrintf("%s\n", g.ToString().c_str()); + g--; LogPrintf("g--\n"); + LogPrintf("%s\n", g.ToString().c_str()); + g++; LogPrintf("g++\n"); + LogPrintf("%s\n", g.ToString().c_str()); + g++; LogPrintf("g++\n"); + LogPrintf("%s\n", g.ToString().c_str()); + g++; LogPrintf("g++\n"); + LogPrintf("%s\n", g.ToString().c_str()); + g++; LogPrintf("g++\n"); + LogPrintf("%s\n", g.ToString().c_str()); uint256 a(7); - printf("a=7\n"); - printf("%s\n", a.ToString().c_str()); + LogPrintf("a=7\n"); + LogPrintf("%s\n", a.ToString().c_str()); uint256 b; - printf("b undefined\n"); - printf("%s\n", b.ToString().c_str()); + LogPrintf("b undefined\n"); + LogPrintf("%s\n", b.ToString().c_str()); int c = 3; a = c; a.pn[3] = 15; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); uint256 k(c); a = 5; a.pn[3] = 15; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); b = 1; b <<= 52; @@ -694,86 +694,86 @@ inline int Testuint256AdHoc(std::vector<std::string> vArg) a ^= 0x500; - printf("a %s\n", a.ToString().c_str()); + LogPrintf("a %s\n", a.ToString().c_str()); a = a | b | (uint256)0x1000; - printf("a %s\n", a.ToString().c_str()); - printf("b %s\n", b.ToString().c_str()); + LogPrintf("a %s\n", a.ToString().c_str()); + LogPrintf("b %s\n", b.ToString().c_str()); a = 0xfffffffe; a.pn[4] = 9; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); a++; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); a++; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); a++; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); a++; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); a--; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); a--; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); a--; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); uint256 d = a--; - printf("%s\n", d.ToString().c_str()); - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", d.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); a--; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); a--; - printf("%s\n", a.ToString().c_str()); + LogPrintf("%s\n", a.ToString().c_str()); d = a; - printf("%s\n", d.ToString().c_str()); - for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n"); + LogPrintf("%s\n", d.ToString().c_str()); + for (int i = uint256::WIDTH-1; i >= 0; i--) LogPrintf("%08x", d.pn[i]); LogPrintf("\n"); uint256 neg = d; neg = ~neg; - printf("%s\n", neg.ToString().c_str()); + LogPrintf("%s\n", neg.ToString().c_str()); uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); - printf("\n"); - printf("%s\n", e.ToString().c_str()); + LogPrintf("\n"); + LogPrintf("%s\n", e.ToString().c_str()); - printf("\n"); + LogPrintf("\n"); uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); uint256 x2; - printf("%s\n", x1.ToString().c_str()); + LogPrintf("%s\n", x1.ToString().c_str()); for (int i = 0; i < 270; i += 4) { x2 = x1 << i; - printf("%s\n", x2.ToString().c_str()); + LogPrintf("%s\n", x2.ToString().c_str()); } - printf("\n"); - printf("%s\n", x1.ToString().c_str()); + LogPrintf("\n"); + LogPrintf("%s\n", x1.ToString().c_str()); for (int i = 0; i < 270; i += 4) { x2 = x1; x2 >>= i; - printf("%s\n", x2.ToString().c_str()); + LogPrintf("%s\n", x2.ToString().c_str()); } for (int i = 0; i < 100; i++) { uint256 k = (~uint256(0) >> i); - printf("%s\n", k.ToString().c_str()); + LogPrintf("%s\n", k.ToString().c_str()); } for (int i = 0; i < 100; i++) { uint256 k = (~uint256(0) << i); - printf("%s\n", k.ToString().c_str()); + LogPrintf("%s\n", k.ToString().c_str()); } return (0); diff --git a/src/util.cpp b/src/util.cpp index 136a035485..cfaf5bdf8c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -78,7 +78,6 @@ bool fPrintToConsole = false; bool fPrintToDebugger = false; bool fDaemon = false; bool fServer = false; -bool fCommandLine = false; string strMiscWarning; bool fNoListen = false; bool fLogTimestamps = false; @@ -166,7 +165,7 @@ void RandAddSeedPerfmon() { RAND_add(pdata, nSize, nSize/100.0); OPENSSL_cleanse(pdata, nSize); - printf("RandAddSeed() %lu bytes\n", nSize); + LogPrint("rand", "RandAddSeed() %lu bytes\n", nSize); } #endif } @@ -198,15 +197,7 @@ uint256 GetRandHash() return hash; } - - - - - - -// -// OutputDebugStringF (aka printf -- there is a #define that we really -// should get rid of one day) has been broken a couple of times now +// 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, @@ -233,8 +224,16 @@ static void DebugPrintInit() mutexDebugLog = new boost::mutex(); } -int OutputDebugStringF(const char* pszFormat, ...) +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()) + return 0; + } + int ret = 0; // Returns total number of characters written if (fPrintToConsole) { @@ -360,7 +359,7 @@ bool error(const char *format, ...) va_start(arg_ptr, format); std::string str = vstrprintf(format, arg_ptr); va_end(arg_ptr); - printf("ERROR: %s\n", str.c_str()); + LogPrintf("ERROR: %s\n", str.c_str()); return false; } @@ -994,13 +993,13 @@ static std::string FormatException(std::exception* pex, const char* pszThread) void LogException(std::exception* pex, const char* pszThread) { std::string message = FormatException(pex, pszThread); - printf("\n%s", message.c_str()); + LogPrintf("\n%s", message.c_str()); } void PrintException(std::exception* pex, const char* pszThread) { std::string message = FormatException(pex, pszThread); - printf("\n\n************************\n%s\n", message.c_str()); + LogPrintf("\n\n************************\n%s\n", message.c_str()); fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); strMiscWarning = message; throw; @@ -1009,7 +1008,7 @@ void PrintException(std::exception* pex, const char* pszThread) void PrintExceptionContinue(std::exception* pex, const char* pszThread) { std::string message = FormatException(pex, pszThread); - printf("\n\n************************\n%s\n", message.c_str()); + LogPrintf("\n\n************************\n%s\n", message.c_str()); fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); strMiscWarning = message; } @@ -1057,7 +1056,7 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) fs::path &path = pathCached[nNet]; - // This can be called during exceptions by printf, so we cache the + // 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; @@ -1156,6 +1155,8 @@ void FileCommit(FILE *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 @@ -1320,7 +1321,7 @@ void AddTimeData(const CNetAddr& ip, int64 nTime) // Add data vTimeOffsets.input(nOffsetSample); - printf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); + 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(); @@ -1348,17 +1349,17 @@ void AddTimeData(const CNetAddr& ip, int64 nTime) fDone = true; string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin will not work properly."); strMiscWarning = strMessage; - printf("*** %s\n", strMessage.c_str()); + LogPrintf("*** %s\n", strMessage.c_str()); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); } } } if (fDebug) { BOOST_FOREACH(int64 n, vSorted) - printf("%+"PRI64d" ", n); - printf("| "); + LogPrintf("%+"PRI64d" ", n); + LogPrintf("| "); } - printf("nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60); + LogPrintf("nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60); } } @@ -1420,7 +1421,7 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) return fs::path(pszPath); } - printf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); + LogPrintf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); return fs::path(""); } #endif @@ -1440,7 +1441,7 @@ boost::filesystem::path GetTempPath() { path = boost::filesystem::path("/tmp"); #endif if (path.empty() || !boost::filesystem::is_directory(path)) { - printf("GetTempPath(): failed to find temp path\n"); + LogPrintf("GetTempPath(): failed to find temp path\n"); return boost::filesystem::path(""); } return path; @@ -1451,7 +1452,7 @@ void runCommand(std::string strCommand) { int nErr = ::system(strCommand.c_str()); if (nErr) - printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr); + LogPrintf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr); } void RenameThread(const char* name) diff --git a/src/util.h b/src/util.h index 62eb7fad9e..c3fb01dbf0 100644 --- a/src/util.h +++ b/src/util.h @@ -145,7 +145,6 @@ extern bool fPrintToConsole; extern bool fPrintToDebugger; extern bool fDaemon; extern bool fServer; -extern bool fCommandLine; extern std::string strMiscWarning; extern bool fNoListen; extern bool fLogTimestamps; @@ -153,7 +152,10 @@ extern volatile bool fReopenDebugLog; void RandAddSeed(); void RandAddSeedPerfmon(); -int ATTR_WARN_PRINTF(1,2) OutputDebugStringF(const char* pszFormat, ...); + +// 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: @@ -173,14 +175,6 @@ std::string vstrprintf(const char *format, va_list ap); bool ATTR_WARN_PRINTF(1,2) error(const char *format, ...); -/* Redefine printf so that it directs output to debug.log - * - * Do this *after* defining the other printf-like functions, because otherwise the - * __attribute__((format(printf,X,Y))) gets expanded to __attribute__((format(OutputDebugStringF,X,Y))) - * which confuses gcc. - */ -#define printf OutputDebugStringF - void LogException(std::exception* pex, const char* pszThread); void PrintException(std::exception* pex, const char* pszThread); void PrintExceptionContinue(std::exception* pex, const char* pszThread); @@ -317,12 +311,12 @@ inline std::string HexStr(const T& vch, bool fSpaces=false) template<typename T> void PrintHex(const T pbegin, const T pend, const char* pszFormat="%s", bool fSpaces=true) { - printf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str()); + LogPrintf(pszFormat, HexStr(pbegin, pend, fSpaces).c_str()); } inline void PrintHex(const std::vector<unsigned char>& vch, const char* pszFormat="%s", bool fSpaces=true) { - printf(pszFormat, HexStr(vch, fSpaces).c_str()); + LogPrintf(pszFormat, HexStr(vch, fSpaces).c_str()); } inline int64 GetPerformanceCounter() @@ -522,7 +516,7 @@ inline void SetThreadPriority(int nPriority) // PRIO_MAX is not defined on Solaris #ifndef PRIO_MAX - #define PRIO_MAX 20 +#define PRIO_MAX 20 #endif #define THREAD_PRIORITY_LOWEST PRIO_MAX #define THREAD_PRIORITY_BELOW_NORMAL 2 @@ -560,7 +554,7 @@ template <typename Callable> void LoopForever(const char* name, Callable func, { std::string s = strprintf("bitcoin-%s", name); RenameThread(s.c_str()); - printf("%s thread start\n", name); + LogPrintf("%s thread start\n", name); try { while (1) @@ -571,7 +565,7 @@ template <typename Callable> void LoopForever(const char* name, Callable func, } catch (boost::thread_interrupted) { - printf("%s thread stop\n", name); + LogPrintf("%s thread stop\n", name); throw; } catch (std::exception& e) { @@ -588,13 +582,13 @@ template <typename Callable> void TraceThread(const char* name, Callable func) RenameThread(s.c_str()); try { - printf("%s thread start\n", name); + LogPrintf("%s thread start\n", name); func(); - printf("%s thread exit\n", name); + LogPrintf("%s thread exit\n", name); } catch (boost::thread_interrupted) { - printf("%s thread interrupt\n", name); + LogPrintf("%s thread interrupt\n", name); throw; } catch (std::exception& e) { diff --git a/src/wallet.cpp b/src/wallet.cpp index 54ede12a50..0f0ce7e631 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -159,7 +159,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, if (pMasterKey.second.nDeriveIterations < 25000) pMasterKey.second.nDeriveIterations = 25000; - printf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); + LogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; @@ -267,7 +267,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) if (kMasterKey.nDeriveIterations < 25000) kMasterKey.nDeriveIterations = 25000; - printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); + LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) return false; @@ -368,10 +368,10 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx) { CWalletTx& wtx = (*mi).second; if (txin.prevout.n >= wtx.vout.size()) - printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str()); + LogPrintf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str()); else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) { - printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + LogPrintf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkSpent(txin.prevout.n); wtx.WriteToDisk(); NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); @@ -446,9 +446,9 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } else - printf("AddToWallet() : found %s in block %s not in index\n", - wtxIn.GetHash().ToString().c_str(), - wtxIn.hashBlock.ToString().c_str()); + LogPrintf("AddToWallet() : found %s in block %s not in index\n", + wtxIn.GetHash().ToString().c_str(), + wtxIn.hashBlock.ToString().c_str()); } } @@ -476,7 +476,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) } //// debug print - printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); // Write to disk if (fInsertedNew || fUpdated) @@ -677,8 +677,8 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64> >& listReceived, vector<unsigned char> vchPubKey; if (!ExtractDestination(txout.scriptPubKey, address)) { - printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", - this->GetHash().ToString().c_str()); + LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().ToString().c_str()); } // Don't report 'change' txouts @@ -799,7 +799,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) // no need to read and scan block, if block was created before // our wallet birthday (as adjusted for block time variability) if (nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200))) { - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); continue; } @@ -810,7 +810,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) ret++; } - pindex = pindex->GetNextInMainChain(); + pindex = chainActive.Next(pindex); } } return ret; @@ -849,7 +849,7 @@ void CWallet::ReacceptWalletTransactions() } if (fUpdated) { - printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); + LogPrintf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkDirty(); wtx.WriteToDisk(); } @@ -864,7 +864,7 @@ void CWallet::ReacceptWalletTransactions() if (fMissing) { // TODO: optimize this to scan just part of the block chain? - if (ScanForWalletTransactions(pindexGenesisBlock)) + if (ScanForWalletTransactions(chainActive.Genesis())) fRepeat = true; // Found missing transactions: re-do re-accept. } } @@ -882,7 +882,7 @@ void CWalletTx::RelayWalletTransaction() { if (GetDepthInMainChain() == 0) { uint256 hash = GetHash(); - printf("Relaying wtx %s\n", hash.ToString().c_str()); + LogPrintf("Relaying wtx %s\n", hash.ToString().c_str()); RelayTransaction((CTransaction)*this, hash); } } @@ -905,7 +905,7 @@ void CWallet::ResendWalletTransactions() nLastResend = GetTime(); // Rebroadcast any of our txes that aren't in a block yet - printf("ResendWalletTransactions()\n"); + LogPrintf("ResendWalletTransactions()\n"); { LOCK(cs_wallet); // Sort them in chronological order @@ -1145,12 +1145,11 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe nValueRet += vValue[i].first; } - //// debug print - printf("SelectCoins() best subset: "); + LogPrint("selectcoins", "SelectCoins() best subset: "); for (unsigned int i = 0; i < vValue.size(); i++) if (vfBest[i]) - printf("%s ", FormatMoney(vValue[i].first).c_str()); - printf("total %s\n", FormatMoney(nBest).c_str()); + LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first).c_str()); + LogPrint("selectcoins", "total %s\n", FormatMoney(nBest).c_str()); } return true; @@ -1336,7 +1335,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) { { LOCK2(cs_main, cs_wallet); - printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); + LogPrintf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); { // This is only to keep the database open to defeat the auto-flush for the // duration of this scope. This is the only place where this optimization @@ -1372,7 +1371,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) if (!wtxNew.AcceptToMemoryPool(false)) { // This must not fail. The transaction has already been signed and recorded. - printf("CommitTransaction() : Error: Transaction not valid"); + LogPrintf("CommitTransaction() : Error: Transaction not valid"); return false; } wtxNew.RelayWalletTransaction(); @@ -1391,7 +1390,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, if (IsLocked()) { string strError = _("Error: Wallet locked, unable to create transaction!"); - printf("SendMoney() : %s", strError.c_str()); + LogPrintf("SendMoney() : %s", strError.c_str()); return strError; } string strError; @@ -1399,7 +1398,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, { if (nValue + nFeeRequired > GetBalance()) strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"), FormatMoney(nFeeRequired).c_str()); - printf("SendMoney() : %s\n", strError.c_str()); + LogPrintf("SendMoney() : %s\n", strError.c_str()); return strError; } @@ -1490,10 +1489,10 @@ void CWallet::PrintWallet(const CBlock& block) if (mapWallet.count(block.vtx[0].GetHash())) { CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; - printf(" mine: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); + LogPrintf(" mine: %d %d %"PRI64d"", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); } } - printf("\n"); + LogPrintf("\n"); } bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) @@ -1552,7 +1551,7 @@ bool CWallet::NewKeyPool() walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); setKeyPool.insert(nIndex); } - printf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys); + LogPrintf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys); } return true; } @@ -1582,7 +1581,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) throw runtime_error("TopUpKeyPool() : writing generated key failed"); setKeyPool.insert(nEnd); - printf("keypool added key %"PRI64d", size=%"PRIszu"\n", nEnd, setKeyPool.size()); + LogPrintf("keypool added key %"PRI64d", size=%"PRIszu"\n", nEnd, setKeyPool.size()); } } return true; @@ -1611,7 +1610,7 @@ void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) if (!HaveKey(keypool.vchPubKey.GetID())) throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); assert(keypool.vchPubKey.IsValid()); - printf("keypool reserve %"PRI64d"\n", nIndex); + LogPrintf("keypool reserve %"PRI64d"\n", nIndex); } } @@ -1638,7 +1637,7 @@ void CWallet::KeepKey(int64 nIndex) CWalletDB walletdb(strWalletFile); walletdb.ErasePool(nIndex); } - printf("keypool keep %"PRI64d"\n", nIndex); + LogPrintf("keypool keep %"PRI64d"\n", nIndex); } void CWallet::ReturnKey(int64 nIndex) @@ -1648,7 +1647,7 @@ void CWallet::ReturnKey(int64 nIndex) LOCK(cs_wallet); setKeyPool.insert(nIndex); } - printf("keypool return %"PRI64d"\n", nIndex); + LogPrintf("keypool return %"PRI64d"\n", nIndex); } bool CWallet::GetKeyFromPool(CPubKey& result) @@ -1836,7 +1835,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey) vchPubKey = keypool.vchPubKey; else { if (pwallet->vchDefaultKey.IsValid()) { - printf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); + LogPrintf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); vchPubKey = pwallet->vchDefaultKey; } else return false; @@ -1934,7 +1933,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const { mapKeyBirth[it->first] = it->second.nCreateTime; // map in which we'll infer heights of other keys - CBlockIndex *pindexMax = FindBlockByHeight(std::max(0, nBestHeight - 144)); // the tip can be reorganised; use a 144-block safety margin + CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganised; use a 144-block safety margin std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock; std::set<CKeyID> setKeys; GetKeys(setKeys); @@ -1954,7 +1953,7 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const { // iterate over all wallet transactions... const CWalletTx &wtx = (*it).second; std::map<uint256, CBlockIndex*>::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); - if (blit != mapBlockIndex.end() && blit->second->IsInMainChain()) { + if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { // ... which are already in a block int nHeight = blit->second->nHeight; BOOST_FOREACH(const CTxOut &txout, wtx.vout) { diff --git a/src/wallet.h b/src/wallet.h index b529d5f28b..51bc9f67c4 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -745,7 +745,7 @@ public: void print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } }; diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 554d140024..efcd59d5f1 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -271,8 +271,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, wss.fAnyUnordered = true; //// debug print - //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); - //printf(" %12"PRI64d" %s %s %s\n", + //LogPrintf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); + //LogPrintf(" %12"PRI64d" %s %s %s\n", // wtx.vout[0].nValue, // DateTimeStrFormat("%Y-%m-%d %H:%M:%S", wtx.GetBlockTime()).c_str(), // wtx.hashBlock.ToString().c_str(), @@ -306,6 +306,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } CKey key; CPrivKey pkey; + uint256 hash = 0; + if (strType == "key") { wss.nKeys++; @@ -315,14 +317,40 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssValue >> wkey; pkey = wkey.vchPrivKey; } - if (!key.SetPrivKey(pkey, vchPubKey.IsCompressed())) + + // Old wallets store keys as "key" [pubkey] => [privkey] + // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key + // using EC operations as a checksum. + // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while + // remaining backwards-compatible. + try { - strErr = "Error reading wallet database: CPrivKey corrupt"; - return false; + ssValue >> hash; + } + catch(...){} + + bool fSkipCheck = false; + + if (hash != 0) + { + // hash pubkey/privkey to accelerate wallet load + std::vector<unsigned char> vchKey; + vchKey.reserve(vchPubKey.size() + pkey.size()); + vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); + vchKey.insert(vchKey.end(), pkey.begin(), pkey.end()); + + if (Hash(vchKey.begin(), vchKey.end()) != hash) + { + strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; + return false; + } + + fSkipCheck = true; } - if (key.GetPubKey() != vchPubKey) + + if (!key.Load(pkey, vchPubKey, fSkipCheck)) { - strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; + strErr = "Error reading wallet database: CPrivKey corrupt"; return false; } if (!pwallet->LoadKey(key, vchPubKey)) @@ -451,7 +479,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) Dbc* pcursor = GetCursor(); if (!pcursor) { - printf("Error getting wallet database cursor\n"); + LogPrintf("Error getting wallet database cursor\n"); return DB_CORRUPT; } @@ -465,7 +493,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) break; else if (ret != 0) { - printf("Error reading next record from wallet database\n"); + LogPrintf("Error reading next record from wallet database\n"); return DB_CORRUPT; } @@ -487,7 +515,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) } } if (!strErr.empty()) - printf("%s\n", strErr.c_str()); + LogPrintf("%s\n", strErr.c_str()); } pcursor->close(); } @@ -506,9 +534,9 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) if (result != DB_LOAD_OK) return result; - printf("nFileVersion = %d\n", wss.nFileVersion); + LogPrintf("nFileVersion = %d\n", wss.nFileVersion); - printf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", + LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); // nTimeFirstKey is only reliable if all keys have metadata @@ -576,7 +604,7 @@ void ThreadFlushWalletDB(const string& strFile) map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile); if (mi != bitdb.mapFileUseCount.end()) { - printf("Flushing wallet.dat\n"); + LogPrint("db", "Flushing wallet.dat\n"); nLastFlushed = nWalletDBUpdated; int64 nStart = GetTimeMillis(); @@ -585,7 +613,7 @@ void ThreadFlushWalletDB(const string& strFile) bitdb.CheckpointLSN(strFile); bitdb.mapFileUseCount.erase(mi++); - printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); + LogPrint("db", "Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); } } } @@ -620,10 +648,10 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) #else filesystem::copy_file(pathSrc, pathDest); #endif - printf("copied wallet.dat to %s\n", pathDest.string().c_str()); + LogPrintf("copied wallet.dat to %s\n", pathDest.string().c_str()); return true; } catch(const filesystem::filesystem_error &e) { - printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what()); + LogPrintf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what()); return false; } } @@ -651,10 +679,10 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL, newFilename.c_str(), DB_AUTO_COMMIT); if (result == 0) - printf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str()); + LogPrintf("Renamed %s to %s\n", filename.c_str(), newFilename.c_str()); else { - printf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str()); + LogPrintf("Failed to rename %s to %s\n", filename.c_str(), newFilename.c_str()); return false; } @@ -662,10 +690,10 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) bool allOK = dbenv.Salvage(newFilename, true, salvagedData); if (salvagedData.empty()) { - printf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str()); + LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename.c_str()); return false; } - printf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size()); + LogPrintf("Salvage(aggressive) found %"PRIszu" records\n", salvagedData.size()); bool fSuccess = allOK; Db* pdbCopy = new Db(&dbenv.dbenv, 0); @@ -677,7 +705,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) 0); if (ret > 0) { - printf("Cannot create database file %s\n", filename.c_str()); + LogPrintf("Cannot create database file %s\n", filename.c_str()); return false; } CWallet dummyWallet; @@ -697,7 +725,7 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) continue; if (!fReadOK) { - printf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str()); + LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType.c_str(), strErr.c_str()); continue; } } diff --git a/src/walletdb.h b/src/walletdb.h index 09ebebe5ac..2d01a5cf74 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -93,8 +93,14 @@ public: if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) return false; - - return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); + + // hash pubkey/privkey to accelerate wallet load + std::vector<unsigned char> vchKey; + vchKey.reserve(vchPubKey.size() + vchPrivKey.size()); + vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); + vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); + + return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); } bool WriteCryptedKey(const CPubKey& vchPubKey, |