diff options
Diffstat (limited to 'src')
247 files changed, 9870 insertions, 5890 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..24a95eed84 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,11 +1,11 @@ -AM_CPPFLAGS = $(INCLUDES) -I$(top_builddir)/src/obj \ - -I$(top_srcdir)/src/leveldb/include -I$(top_srcdir)/src/leveldb/helpers \ - $(BOOST_INCLUDES) -AM_LDFLAGS = $(PTHREAD_CFLAGS) +include Makefile.include + +AM_CPPFLAGS += -I$(top_srcdir)/src/leveldb/helpers/memenv \ + -I$(builddir) noinst_LIBRARIES = libbitcoin.a -bin_PROGRAMS = bitcoind +bin_PROGRAMS = bitcoind bitcoin-cli SUBDIRS = . $(BUILD_QT) $(BUILD_TEST) DIST_SUBDIRS = . qt test @@ -13,10 +13,10 @@ DIST_SUBDIRS = . qt test # bitcoin core # BITCOIN_CORE_H = addrman.h alert.h allocators.h base58.h bignum.h \ bitcoinrpc.h bloom.h chainparams.h checkpoints.h checkqueue.h \ - clientversion.h compat.h core.h crypter.h db.h hash.h init.h \ - key.h keystore.h leveldb.h limitedmap.h main.h miner.h mruset.h \ - netbase.h net.h protocol.h script.h serialize.h sync.h threadsafety.h \ - txdb.h ui_interface.h uint256.h util.h version.h walletdb.h wallet.h + clientversion.h compat.h core.h coins.h crypter.h db.h hash.h init.h \ + key.h keystore.h leveldbwrapper.h limitedmap.h main.h miner.h mruset.h \ + netbase.h net.h noui.h protocol.h script.h serialize.h sync.h threadsafety.h \ + txdb.h txmempool.h ui_interface.h uint256.h util.h version.h walletdb.h wallet.h JSON_H = json/json_spirit.h json/json_spirit_error_position.h \ json/json_spirit_reader.h json/json_spirit_reader_template.h \ @@ -30,12 +30,12 @@ obj/build.h: FORCE $(abs_top_srcdir) version.o: obj/build.h -libbitcoin_a_SOURCES = addrman.cpp alert.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 \ +libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp bitcoinrpc.cpp bloom.cpp \ + chainparams.cpp checkpoints.cpp core.cpp coins.cpp crypter.cpp db.cpp hash.cpp \ + init.cpp key.cpp keystore.cpp leveldbwrapper.cpp main.cpp miner.cpp \ netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \ rpcmining.cpp rpcnet.cpp rpcrawtransaction.cpp rpcwallet.cpp script.cpp \ - sync.cpp txdb.cpp util.cpp version.cpp wallet.cpp walletdb.cpp $(JSON_H) \ + sync.cpp txdb.cpp txmempool.cpp util.cpp version.cpp wallet.cpp walletdb.cpp $(JSON_H) \ $(BITCOIN_CORE_H) nodist_libbitcoin_a_SOURCES = $(top_srcdir)/src/obj/build.h @@ -47,13 +47,34 @@ 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 + +AM_CPPFLAGS += $(BDB_CPPFLAGS) +bitcoind_LDADD += $(BDB_LIBS) + +# bitcoin-cli binary # +bitcoin_cli_LDADD = libbitcoin.a leveldb/libleveldb.a leveldb/libmemenv.a \ + $(BOOST_LIBS) +bitcoin_cli_SOURCES = bitcoin-cli.cpp +# + +if TARGET_WINDOWS +bitcoin_cli_SOURCES += bitcoin-cli-res.rc +endif +bitcoin_cli_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)" -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 +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..8c2274a287 100644 --- a/src/Makefile.include +++ b/src/Makefile.include @@ -1,4 +1,9 @@ -.PHONY: FORCE +AM_CPPFLAGS = $(INCLUDES) \ + -I$(top_builddir)/src/obj \ + -I$(top_srcdir)/src/leveldb/include \ + $(BDB_CPPFLAGS) \ + $(BOOST_INCLUDES) +AM_LDFLAGS = $(PTHREAD_CFLAGS) LIBBITCOIN=$(top_builddir)/src/libbitcoin.a LIBLEVELDB=$(top_builddir)/src/leveldb/libleveldb.a @@ -46,3 +51,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..815da07c9b 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -3,7 +3,9 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "addrman.h" + #include "hash.h" +#include "serialize.h" using namespace std; @@ -12,12 +14,12 @@ int CAddrInfo::GetTriedBucket(const std::vector<unsigned char> &nKey) const CDataStream ss1(SER_GETHASH, 0); std::vector<unsigned char> vchKey = GetKey(); ss1 << nKey << vchKey; - uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64(); + uint64_t hash1 = Hash(ss1.begin(), ss1.end()).Get64(); CDataStream ss2(SER_GETHASH, 0); std::vector<unsigned char> vchGroupKey = GetGroup(); ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP); - uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64(); + uint64_t hash2 = Hash(ss2.begin(), ss2.end()).Get64(); return hash2 % ADDRMAN_TRIED_BUCKET_COUNT; } @@ -27,15 +29,15 @@ int CAddrInfo::GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAd std::vector<unsigned char> vchGroupKey = GetGroup(); std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(); ss1 << nKey << vchGroupKey << vchSourceGroupKey; - uint64 hash1 = Hash(ss1.begin(), ss1.end()).Get64(); + uint64_t hash1 = Hash(ss1.begin(), ss1.end()).Get64(); CDataStream ss2(SER_GETHASH, 0); ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP); - uint64 hash2 = Hash(ss2.begin(), ss2.end()).Get64(); + uint64_t hash2 = Hash(ss2.begin(), ss2.end()).Get64(); return hash2 % ADDRMAN_NEW_BUCKET_COUNT; } -bool CAddrInfo::IsTerrible(int64 nNow) const +bool CAddrInfo::IsTerrible(int64_t nNow) const { if (nLastTry && nLastTry >= nNow-60) // never remove things tried the last minute return false; @@ -55,12 +57,12 @@ bool CAddrInfo::IsTerrible(int64 nNow) const return false; } -double CAddrInfo::GetChance(int64 nNow) const +double CAddrInfo::GetChance(int64_t nNow) const { double fChance = 1.0; - int64 nSinceLastSeen = nNow - nTime; - int64 nSinceLastTry = nNow - nLastTry; + int64_t nSinceLastSeen = nNow - nTime; + int64_t nSinceLastTry = nNow - nLastTry; if (nSinceLastSeen < 0) nSinceLastSeen = 0; if (nSinceLastTry < 0) nSinceLastTry = 0; @@ -129,7 +131,7 @@ int CAddrMan::SelectTried(int nKBucket) // random shuffle the first few elements (using the entire list) // find the least recently tried among them - int64 nOldest = -1; + int64_t nOldest = -1; int nOldestPos = -1; for (unsigned int i = 0; i < ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT && i < vTried.size(); i++) { @@ -259,10 +261,8 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin) return; } -void CAddrMan::Good_(const CService &addr, int64 nTime) +void CAddrMan::Good_(const CService &addr, int64_t nTime) { -// printf("Good: addr=%s\n", addr.ToString().c_str()); - int nId; CAddrInfo *pinfo = Find(addr, &nId); @@ -304,13 +304,13 @@ 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); } -bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty) +bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) { if (!addr.IsRoutable()) return false; @@ -323,9 +323,9 @@ bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePen { // periodically update nTime bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); - int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); + int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty)) - pinfo->nTime = max((int64)0, addr.nTime - nTimePenalty); + pinfo->nTime = max((int64_t)0, addr.nTime - nTimePenalty); // add services pinfo->nServices |= addr.nServices; @@ -350,8 +350,7 @@ bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePen return false; } 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); + pinfo->nTime = max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty); nNew++; fNew = true; } @@ -368,7 +367,7 @@ bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePen return fNew; } -void CAddrMan::Attempt_(const CService &addr, int64 nTime) +void CAddrMan::Attempt_(const CService &addr, int64_t nTime) { CAddrInfo *pinfo = Find(addr); @@ -507,7 +506,7 @@ void CAddrMan::GetAddr_(std::vector<CAddress> &vAddr) } } -void CAddrMan::Connected_(const CService &addr, int64 nTime) +void CAddrMan::Connected_(const CService &addr, int64_t nTime) { CAddrInfo *pinfo = Find(addr); @@ -522,7 +521,7 @@ void CAddrMan::Connected_(const CService &addr, int64 nTime) return; // update info - int64 nUpdateInterval = 20 * 60; + int64_t nUpdateInterval = 20 * 60; if (nTime - info.nTime > nUpdateInterval) info.nTime = nTime; } diff --git a/src/addrman.h b/src/addrman.h index 7af6afd78f..e2b0cb1093 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -1,21 +1,22 @@ // Copyright (c) 2012 Pieter Wuille // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef _BITCOIN_ADDRMAN #define _BITCOIN_ADDRMAN 1 #include "netbase.h" #include "protocol.h" -#include "util.h" #include "sync.h" - +#include "util.h" #include <map> +#include <set> +#include <stdint.h> #include <vector> #include <openssl/rand.h> - /** Extended statistics about a CAddress */ class CAddrInfo : public CAddress { @@ -24,10 +25,10 @@ private: CNetAddr source; // last successful connection by us - int64 nLastSuccess; + int64_t nLastSuccess; // last try whatsoever by us: - // int64 CAddress::nLastTry + // int64_t CAddress::nLastTry // connection attempts since last successful attempt int nAttempts; @@ -86,10 +87,10 @@ public: } // Determine whether the statistics about this entry are bad enough so that it can just be deleted - bool IsTerrible(int64 nNow = GetAdjustedTime()) const; + bool IsTerrible(int64_t nNow = GetAdjustedTime()) const; // Calculate the relative chance this entry should be given when selecting nodes to connect to - double GetChance(int64 nNow = GetAdjustedTime()) const; + double GetChance(int64_t nNow = GetAdjustedTime()) const; }; @@ -220,13 +221,13 @@ protected: void MakeTried(CAddrInfo& info, int nId, int nOrigin); // Mark an entry "good", possibly moving it from "new" to "tried". - void Good_(const CService &addr, int64 nTime); + void Good_(const CService &addr, int64_t nTime); // Add an entry to the "new" table. - bool Add_(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty); + bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty); // Mark an entry as attempted to connect. - void Attempt_(const CService &addr, int64 nTime); + void Attempt_(const CService &addr, int64_t nTime); // Select an address to connect to. // nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100) @@ -241,7 +242,7 @@ protected: void GetAddr_(std::vector<CAddress> &vAddr); // Mark an entry as currently-connected-to. - void Connected_(const CService &addr, int64 nTime); + void Connected_(const CService &addr, int64_t nTime); public: @@ -403,13 +404,13 @@ 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 } // Add a single address. - bool Add(const CAddress &addr, const CNetAddr& source, int64 nTimePenalty = 0) + bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0) { bool fRet = false; { @@ -419,12 +420,12 @@ 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("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort().c_str(), source.ToString().c_str(), nTried, nNew); return fRet; } // Add multiple addresses. - bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64 nTimePenalty = 0) + bool Add(const std::vector<CAddress> &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0) { int nAdd = 0; { @@ -435,12 +436,12 @@ public: Check(); } if (nAdd) - printf("Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew); + LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString().c_str(), nTried, nNew); return nAdd > 0; } // Mark an entry as accessible. - void Good(const CService &addr, int64 nTime = GetAdjustedTime()) + void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) { { LOCK(cs); @@ -451,7 +452,7 @@ public: } // Mark an entry as connection attempted to. - void Attempt(const CService &addr, int64 nTime = GetAdjustedTime()) + void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime()) { { LOCK(cs); @@ -489,7 +490,7 @@ public: } // Mark an entry as currently-connected-to. - void Connected(const CService &addr, int64 nTime = GetAdjustedTime()) + void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) { { LOCK(cs); diff --git a/src/alert.cpp b/src/alert.cpp index e00847aadb..7f7e59ee10 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -2,17 +2,20 @@ // Alert system // -#include <algorithm> -#include <boost/algorithm/string/classification.hpp> -#include <boost/algorithm/string/replace.hpp> -#include <boost/foreach.hpp> -#include <map> - #include "alert.h" + #include "key.h" #include "net.h" -#include "sync.h" #include "ui_interface.h" +#include "util.h" + +#include <algorithm> +#include <inttypes.h> +#include <map> + +#include <boost/algorithm/string/classification.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/foreach.hpp> using namespace std; @@ -48,8 +51,8 @@ std::string CUnsignedAlert::ToString() const return strprintf( "CAlert(\n" " nVersion = %d\n" - " nRelayUntil = %"PRI64d"\n" - " nExpiration = %"PRI64d"\n" + " nRelayUntil = %"PRId64"\n" + " nExpiration = %"PRId64"\n" " nID = %d\n" " nCancel = %d\n" " setCancel = %s\n" @@ -76,7 +79,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 +203,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 +223,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; } } @@ -238,15 +241,7 @@ bool CAlert::ProcessAlert(bool fThread) // be safe we first strip anything not in safeChars, then add single quotes around // the whole string before passing it to the shell: std::string singleQuote("'"); - // safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything - // even possibly remotely dangerous like & or > - std::string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); - std::string safeStatus; - for (std::string::size_type i = 0; i < strStatusBar.size(); i++) - { - if (safeChars.find(strStatusBar[i]) != std::string::npos) - safeStatus.push_back(strStatusBar[i]); - } + std::string safeStatus = SanitizeString(strStatusBar); safeStatus = singleQuote+safeStatus+singleQuote; boost::replace_all(strCmd, "%s", safeStatus); @@ -258,6 +253,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/alert.h b/src/alert.h index 25e140f573..da140be5e5 100644 --- a/src/alert.h +++ b/src/alert.h @@ -1,18 +1,25 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef _BITCOINALERT_H_ #define _BITCOINALERT_H_ 1 +#include "serialize.h" +#include "sync.h" + +#include <map> #include <set> +#include <stdint.h> #include <string> -#include "uint256.h" -#include "util.h" - +class CAlert; class CNode; +class uint256; + +extern std::map<uint256, CAlert> mapAlerts; +extern CCriticalSection cs_mapAlerts; /** Alerts are for notifying old versions if they become too obsolete and * need to upgrade. The message is displayed in the status bar. @@ -24,8 +31,8 @@ class CUnsignedAlert { public: int nVersion; - int64 nRelayUntil; // when newer nodes stop relaying to newer nodes - int64 nExpiration; + int64_t nRelayUntil; // when newer nodes stop relaying to newer nodes + int64_t nExpiration; int nID; int nCancel; std::set<int> setCancel; diff --git a/src/allocators.cpp b/src/allocators.cpp new file mode 100644 index 0000000000..15f34aa2c8 --- /dev/null +++ b/src/allocators.cpp @@ -0,0 +1,67 @@ +// 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 + +LockedPageManager* LockedPageManager::_instance = NULL; +boost::once_flag LockedPageManager::init_flag = BOOST_ONCE_INIT; + +/** 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..7012ef7e2a 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -1,36 +1,19 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_ALLOCATORS_H #define BITCOIN_ALLOCATORS_H -#include <string.h> +#include <map> #include <string> +#include <string.h> + #include <boost/thread/mutex.hpp> -#include <map> +#include <boost/thread/once.hpp> #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. * @@ -53,6 +36,12 @@ public: page_mask = ~(page_size - 1); } + ~LockedPageManagerBase() + { + assert(this->GetLockedPageCount() == 0); + } + + // For all pages in affected range, increase lock count void LockRange(void *p, size_t size) { @@ -115,21 +104,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,39 +115,49 @@ 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); }; /** * Singleton class to keep track of locked (ie, non-swappable) memory pages, for use in * std::allocator templates. + * + * Some implementations of the STL allocate memory in some constructors (i.e., see + * MSVC's vector<T> implementation where it allocates 1 byte of memory in the allocator.) + * Due to the unpredictable order of static initializers, we have to make sure the + * LockedPageManager instance exists before any other STL-based objects that use + * secure_allocator are created. So instead of having LockedPageManager also be + * static-intialized, it is created on demand. */ class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker> { public: - static LockedPageManager instance; // instantiated in util.cpp + static LockedPageManager& Instance() + { + boost::call_once(LockedPageManager::CreateInstance, LockedPageManager::init_flag); + return *LockedPageManager::_instance; + } + private: - LockedPageManager(): - LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize()) - {} + LockedPageManager(); + + static void CreateInstance() + { + // Using a local static instance guarantees that the object is initialized + // when it's first needed and also deinitialized after all objects that use + // it are done with it. I can think of one unlikely scenario where we may + // have a static deinitialization order/problem, but the check in + // LockedPageManagerBase's destructor helps us detect if that ever happens. + static LockedPageManager instance; + LockedPageManager::_instance = &instance; + } + + static LockedPageManager* _instance; + static boost::once_flag init_flag; }; // @@ -181,12 +165,12 @@ private: // Intended for non-dynamically allocated structures. // template<typename T> void LockObject(const T &t) { - LockedPageManager::instance.LockRange((void*)(&t), sizeof(T)); + LockedPageManager::Instance().LockRange((void*)(&t), sizeof(T)); } template<typename T> void UnlockObject(const T &t) { OPENSSL_cleanse((void*)(&t), sizeof(T)); - LockedPageManager::instance.UnlockRange((void*)(&t), sizeof(T)); + LockedPageManager::Instance().UnlockRange((void*)(&t), sizeof(T)); } // @@ -218,7 +202,7 @@ struct secure_allocator : public std::allocator<T> T *p; p = std::allocator<T>::allocate(n, hint); if (p != NULL) - LockedPageManager::instance.LockRange(p, sizeof(T) * n); + LockedPageManager::Instance().LockRange(p, sizeof(T) * n); return p; } @@ -227,7 +211,7 @@ struct secure_allocator : public std::allocator<T> if (p != NULL) { OPENSSL_cleanse(p, sizeof(T) * n); - LockedPageManager::instance.UnlockRange(p, sizeof(T) * n); + LockedPageManager::Instance().UnlockRange(p, sizeof(T) * n); } std::allocator<T>::deallocate(p, n); } diff --git a/src/base58.h b/src/base58.h index aabae8de88..ebe5376825 100644 --- a/src/base58.h +++ b/src/base58.h @@ -1,9 +1,8 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin Developers +// 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. - // // Why base-58 instead of standard base-64 encoding? // - Don't want 0OIl characters that look the same in some fonts and @@ -15,14 +14,18 @@ #ifndef BITCOIN_BASE58_H #define BITCOIN_BASE58_H -#include <string> -#include <vector> - -#include "chainparams.h" #include "bignum.h" +#include "chainparams.h" +#include "hash.h" #include "key.h" #include "script.h" -#include "allocators.h" +#include "uint256.h" + +#include <string> +#include <vector> + +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; diff --git a/src/bignum.h b/src/bignum.h index 0881807d70..0259338b31 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -1,15 +1,20 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_BIGNUM_H #define BITCOIN_BIGNUM_H +#include "serialize.h" +#include "uint256.h" +#include "version.h" + #include <stdexcept> +#include <stdint.h> #include <vector> -#include <openssl/bn.h> -#include "util.h" // for uint64 +#include <openssl/bn.h> /** Errors thrown by the bignum class */ class bignum_error : public std::runtime_error @@ -79,17 +84,17 @@ public: } //CBigNum(char n) is not portable. Use 'signed char' or 'unsigned char'. - CBigNum(signed char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int64 n) { BN_init(this); setint64(n); } - CBigNum(unsigned char n) { BN_init(this); setulong(n); } - CBigNum(unsigned short n) { BN_init(this); setulong(n); } - CBigNum(unsigned int n) { BN_init(this); setulong(n); } - CBigNum(unsigned long n) { BN_init(this); setulong(n); } - CBigNum(uint64 n) { BN_init(this); setuint64(n); } - explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } + CBigNum(signed char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(long long n) { BN_init(this); setint64(n); } + CBigNum(unsigned char n) { BN_init(this); setulong(n); } + CBigNum(unsigned short n) { BN_init(this); setulong(n); } + CBigNum(unsigned int n) { BN_init(this); setulong(n); } + CBigNum(unsigned long n) { BN_init(this); setulong(n); } + CBigNum(unsigned long long n) { BN_init(this); setuint64(n); } + explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } explicit CBigNum(const std::vector<unsigned char>& vch) { @@ -122,14 +127,14 @@ public: return (n > (unsigned long)std::numeric_limits<int>::max() ? std::numeric_limits<int>::min() : -(int)n); } - void setint64(int64 sn) + void setint64(int64_t sn) { unsigned char pch[sizeof(sn) + 6]; unsigned char* p = pch + 4; bool fNegative; - uint64 n; + uint64_t n; - if (sn < (int64)0) + if (sn < (int64_t)0) { // Since the minimum signed integer cannot be represented as positive so long as its type is signed, // and it's not well-defined what happens if you make it unsigned before negating it, @@ -167,7 +172,7 @@ public: BN_mpi2bn(pch, p - pch, this); } - void setuint64(uint64 n) + void setuint64(uint64_t n) { unsigned char pch[sizeof(n) + 6]; unsigned char* p = pch + 4; @@ -347,13 +352,13 @@ public: psz++; // hex string to bignum - static const signed char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; *this = 0; - while (isxdigit(*psz)) + int n; + while ((n = HexDigit(*psz)) != -1) { *this <<= 4; - int n = phexdigit[(unsigned char)*psz++]; *this += n; + ++psz; } if (fNegative) *this = 0 - *this; diff --git a/src/bitcoin-cli-res.rc b/src/bitcoin-cli-res.rc new file mode 100644 index 0000000000..337897753e --- /dev/null +++ b/src/bitcoin-cli-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", "Bitcoin-cli (OSS RPC client for Bitcoin)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "bitcoin-cli" + 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", "bitcoin-cli.exe" + VALUE "ProductName", "Bitcoin-cli" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp new file mode 100644 index 0000000000..aa6ce27c52 --- /dev/null +++ b/src/bitcoin-cli.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "util.h" +#include "init.h" +#include "bitcoinrpc.h" +#include "ui_interface.h" /* for _(...) */ + +#include <boost/filesystem/operations.hpp> + +////////////////////////////////////////////////////////////////////////////// +// +// Start +// +static bool AppInitRPC(int argc, char* argv[]) +{ + // + // Parameters + // + ParseParameters(argc, argv); + if (!boost::filesystem::is_directory(GetDataDir(false))) + { + fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str()); + return false; + } + ReadConfigFile(mapArgs, mapMultiArgs); + + if (argc<2 || mapArgs.count("-?") || mapArgs.count("--help")) + { + // First part of help message is specific to RPC client + std::string strUsage = _("Bitcoin RPC client version") + " " + FormatFullVersion() + "\n\n" + + _("Usage:") + "\n" + + " bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin server") + "\n" + + " bitcoin-cli [options] help " + _("List commands") + "\n" + + " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n"; + + strUsage += "\n" + HelpMessage(HMM_BITCOIN_CLI); + + fprintf(stdout, "%s", strUsage.c_str()); + return false; + } + return true; +} + +int main(int argc, char* argv[]) +{ + try + { + if(!AppInitRPC(argc, argv)) + return 1; + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "AppInitRPC()"); + } catch (...) { + PrintExceptionContinue(NULL, "AppInitRPC()"); + } + + try + { + if(!CommandLineRPC(argc, argv)) + return 1; + } + catch (std::exception& e) { + PrintExceptionContinue(&e, "CommandLineRPC()"); + } catch (...) { + PrintExceptionContinue(NULL, "CommandLineRPC()"); + } + return 0; +} 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..c1b26812b5 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -1,23 +1,34 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "init.h" + + #include "bitcoinrpc.h" +#include "init.h" +#include "main.h" +#include "noui.h" +#include "ui_interface.h" +#include "util.h" + #include <boost/algorithm/string/predicate.hpp> +#include <boost/filesystem.hpp> 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(); + } } ////////////////////////////////////////////////////////////////////////////// @@ -54,18 +65,20 @@ bool AppInit(int argc, char* argv[]) // First part of help message is specific to bitcoind / RPC client std::string strUsage = _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + _("Usage:") + "\n" + - " bitcoind [options] " + "\n" + - " bitcoind [options] <command> [params] " + _("Send command to -server or bitcoind") + "\n" + + " bitcoind [options] " + _("Start Bitcoin server") + "\n" + + _("Usage (deprecated, use bitcoin-cli):") + "\n" + + " bitcoind [options] <command> [params] " + _("Send command to Bitcoin server") + "\n" + " bitcoind [options] help " + _("List commands") + "\n" + " bitcoind [options] help <command> " + _("Get help for a command") + "\n"; - strUsage += "\n" + HelpMessage(); + strUsage += "\n" + HelpMessage(HMM_BITCOIND); fprintf(stdout, "%s", strUsage.c_str()); return false; } // 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; @@ -100,17 +113,23 @@ bool AppInit(int argc, char* argv[]) #endif detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup)); - fRet = AppInit2(threadGroup); + fRet = AppInit2(threadGroup, true); } catch (std::exception& e) { PrintExceptionContinue(&e, "AppInit()"); } 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) @@ -124,11 +143,9 @@ bool AppInit(int argc, char* argv[]) return fRet; } -extern void noui_connect(); int main(int argc, char* argv[]) { bool fRet = false; - fHaveGUI = false; // Connect bitcoind signal handlers noui_connect(); diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 47c7383564..6ecb70309b 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1,30 +1,29 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "chainparams.h" +#include "bitcoinrpc.h" + +#include "base58.h" #include "init.h" +#include "main.h" #include "util.h" -#include "sync.h" -#include "ui_interface.h" -#include "base58.h" -#include "bitcoinrpc.h" -#include "db.h" +#include "wallet.h" + +#include <stdint.h> #include <boost/algorithm/string.hpp> #include <boost/asio.hpp> -#include <boost/asio/ip/v6_only.hpp> #include <boost/asio/ssl.hpp> #include <boost/bind.hpp> #include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> #include <boost/foreach.hpp> #include <boost/iostreams/concepts.hpp> #include <boost/iostreams/stream.hpp> #include <boost/lexical_cast.hpp> #include <boost/shared_ptr.hpp> -#include <list> +#include "json/json_spirit_writer_template.h" using namespace std; using namespace boost; @@ -87,18 +86,18 @@ void RPCTypeCheck(const Object& o, } } -int64 AmountFromValue(const Value& value) +int64_t AmountFromValue(const Value& value) { double dAmount = value.get_real(); if (dAmount <= 0.0 || dAmount > 21000000.0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - int64 nAmount = roundint64(dAmount * COIN); + int64_t nAmount = roundint64(dAmount * COIN); if (!MoneyRange(nAmount)) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); return nAmount; } -Value ValueFromAmount(int64 amount) +Value ValueFromAmount(int64_t amount) { return (double)amount / (double)COIN; } @@ -113,6 +112,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 +159,9 @@ string CRPCTable::help(string strCommand) const continue; if (strCommand != "" && strMethod != strCommand) continue; + if (pcmd->reqWallet && !pwalletMain) + continue; + try { Array params; @@ -190,75 +220,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,14 +801,14 @@ 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"); + string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); } @@ -847,7 +881,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; @@ -859,7 +894,7 @@ void RPCRunHandler(const boost::system::error_code& err, boost::function<void(vo func(); } -void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64 nSeconds) +void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds) { assert(rpc_io_service != NULL); @@ -902,7 +937,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 +1011,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 +1070,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 +1086,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 +1199,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 +1236,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 +1333,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..46e3ba4f1d 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -1,23 +1,24 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef _BITCOINRPC_H_ #define _BITCOINRPC_H_ 1 -#include <string> +#include "uint256.h" + #include <list> #include <map> - -class CBlockIndex; -class CReserveKey; +#include <stdint.h> +#include <string> #include "json/json_spirit_reader_template.h" -#include "json/json_spirit_writer_template.h" #include "json/json_spirit_utils.h" +#include "json/json_spirit_writer_template.h" -#include "util.h" +class CBlockIndex; +class CReserveKey; // HTTP status codes enum HTTPStatusCode @@ -49,10 +50,13 @@ enum RPCErrorCode RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter RPC_DATABASE_ERROR = -20, // Database error RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format + RPC_SERVER_NOT_STARTED = -18, // RPC server was not started (StartRPCThreads() not called) // P2P client errors RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks + RPC_CLIENT_NODE_ALREADY_ADDED = -23, // Node is already added + RPC_CLIENT_NODE_NOT_ADDED = -24, // Node has not been added before // Wallet errors RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.) @@ -93,7 +97,7 @@ void RPCTypeCheck(const json_spirit::Object& o, Run func nSeconds from now. Uses boost deadline timers. Overrides previous timer <name> (if any). */ -void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64 nSeconds); +void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds); typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp); @@ -104,6 +108,7 @@ public: rpcfn_type actor; bool okSafeMode; bool threadSafe; + bool reqWallet; }; /** @@ -130,12 +135,21 @@ 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(); -extern int64 nWalletUnlockTime; -extern int64 AmountFromValue(const json_spirit::Value& value); -extern json_spirit::Value ValueFromAmount(int64 amount); +extern int64_t nWalletUnlockTime; +extern int64_t AmountFromValue(const json_spirit::Value& value); +extern json_spirit::Value ValueFromAmount(int64_t amount); extern double GetDifficulty(const CBlockIndex* blockindex = NULL); extern std::string HexBits(unsigned int nBits); extern std::string HelpRequiringPassphrase(); @@ -143,8 +157,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 +169,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 +215,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/bloom.cpp b/src/bloom.cpp index 8e8d8fa06b..cbb8cf4a82 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -1,13 +1,15 @@ // Copyright (c) 2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <math.h> -#include <stdlib.h> #include "bloom.h" + #include "core.h" #include "script.h" +#include <math.h> +#include <stdlib.h> + #define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455 #define LN2 0.6931471805599453094172321214581765680755001343602552 diff --git a/src/bloom.h b/src/bloom.h index f482bfcc10..75e3f38c55 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -1,16 +1,17 @@ // Copyright (c) 2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_BLOOM_H #define BITCOIN_BLOOM_H -#include <vector> - -#include "uint256.h" #include "serialize.h" +#include <vector> + class COutPoint; class CTransaction; +class uint256; // 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001% static const unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 0795f09765..14da3860ad 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -1,11 +1,11 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "assert.h" - #include "chainparams.h" + +#include "assert.h" #include "core.h" #include "protocol.h" #include "util.h" @@ -158,7 +158,7 @@ public: // it'll get a pile of addresses with newer timestamps. // Seed nodes are given a random 'last seen time' of between one and two // weeks ago. - const int64 nOneWeek = 7*24*60*60; + const int64_t nOneWeek = 7*24*60*60; struct in_addr ip; memcpy(&ip, &pnSeed[i], sizeof(ip)); CAddress addr(CService(ip, GetDefaultPort())); diff --git a/src/chainparams.h b/src/chainparams.h index ce3c14306d..542afeaf92 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -8,7 +8,6 @@ #include "bignum.h" #include "uint256.h" -#include "util.h" #include <vector> @@ -68,7 +67,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/checkpoints.cpp b/src/checkpoints.cpp index bb551501f1..40dd30d1a4 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -1,15 +1,17 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 <boost/assign/list_of.hpp> // for 'map_list_of()' -#include <boost/foreach.hpp> - #include "checkpoints.h" #include "main.h" #include "uint256.h" +#include <stdint.h> + +#include <boost/assign/list_of.hpp> // for 'map_list_of()' +#include <boost/foreach.hpp> + namespace Checkpoints { typedef std::map<int, uint256> MapCheckpoints; @@ -23,8 +25,8 @@ namespace Checkpoints struct CCheckpointData { const MapCheckpoints *mapCheckpoints; - int64 nTimeLastCheckpoint; - int64 nTransactionsLastCheckpoint; + int64_t nTimeLastCheckpoint; + int64_t nTransactionsLastCheckpoint; double fTransactionsPerDay; }; @@ -105,7 +107,7 @@ namespace Checkpoints if (pindex==NULL) return 0.0; - int64 nNow = time(NULL); + int64_t nNow = time(NULL); double fWorkBefore = 0.0; // Amount of work done before pindex double fWorkAfter = 0.0; // Amount of work left after pindex (estimated) diff --git a/src/checkpoints.h b/src/checkpoints.h index a49a908a38..3724c57533 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -1,13 +1,14 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_CHECKPOINT_H #define BITCOIN_CHECKPOINT_H #include <map> -class uint256; class CBlockIndex; +class uint256; /** Block-chain checkpoints are compiled-in sanity checks. * They are updated every release or three. diff --git a/src/checkqueue.h b/src/checkqueue.h index eba424fbaa..ef7b4ca422 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -1,15 +1,17 @@ // Copyright (c) 2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef CHECKQUEUE_H #define CHECKQUEUE_H -#include <boost/thread/mutex.hpp> -#include <boost/thread/locks.hpp> -#include <boost/thread/condition_variable.hpp> - -#include <vector> #include <algorithm> +#include <vector> + +#include <boost/foreach.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/locks.hpp> +#include <boost/thread/mutex.hpp> template<typename T> class CCheckQueueControl; diff --git a/src/coins.cpp b/src/coins.cpp new file mode 100644 index 0000000000..ed82c9df8b --- /dev/null +++ b/src/coins.cpp @@ -0,0 +1,180 @@ +// Copyright (c) 2012-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 "coins.h" + +#include <assert.h> + +// calculate number of bytes for the bitmask, and its number of non-zero bytes +// each bit in the bitmask represents the availability of one output, but the +// availabilities of the first two outputs are encoded separately +void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { + unsigned int nLastUsedByte = 0; + for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { + bool fZero = true; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { + if (!vout[2+b*8+i].IsNull()) { + fZero = false; + continue; + } + } + if (!fZero) { + nLastUsedByte = b + 1; + nNonzeroBytes++; + } + } + nBytes += nLastUsedByte; +} + +bool CCoins::Spend(const COutPoint &out, CTxInUndo &undo) { + if (out.n >= vout.size()) + return false; + if (vout[out.n].IsNull()) + return false; + undo = CTxInUndo(vout[out.n]); + vout[out.n].SetNull(); + Cleanup(); + if (vout.size() == 0) { + undo.nHeight = nHeight; + undo.fCoinBase = fCoinBase; + undo.nVersion = this->nVersion; + } + return true; +} + +bool CCoins::Spend(int nPos) { + CTxInUndo undo; + COutPoint out(0, nPos); + return Spend(out, undo); +} + + +bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } +bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } +bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } +uint256 CCoinsView::GetBestBlock() { return uint256(0); } +bool CCoinsView::SetBestBlock(const uint256 &hashBlock) { return false; } +bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { return false; } +bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } + + +CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } +bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } +bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } +bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } +uint256 CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } +bool CCoinsViewBacked::SetBestBlock(const uint256 &hashBlock) { return base->SetBestBlock(hashBlock); } +void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } +bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } +bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } + +CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { } + +bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { + if (cacheCoins.count(txid)) { + coins = cacheCoins[txid]; + return true; + } + if (base->GetCoins(txid, coins)) { + cacheCoins[txid] = coins; + return true; + } + return false; +} + +std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { + std::map<uint256,CCoins>::iterator it = cacheCoins.lower_bound(txid); + if (it != cacheCoins.end() && it->first == txid) + return it; + CCoins tmp; + if (!base->GetCoins(txid,tmp)) + return cacheCoins.end(); + std::map<uint256,CCoins>::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); + tmp.swap(ret->second); + return ret; +} + +CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { + std::map<uint256,CCoins>::iterator it = FetchCoins(txid); + assert(it != cacheCoins.end()); + return it->second; +} + +bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { + cacheCoins[txid] = coins; + return true; +} + +bool CCoinsViewCache::HaveCoins(const uint256 &txid) { + return FetchCoins(txid) != cacheCoins.end(); +} + +uint256 CCoinsViewCache::GetBestBlock() { + if (hashBlock == uint256(0)) + hashBlock = base->GetBestBlock(); + return hashBlock; +} + +bool CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { + hashBlock = hashBlockIn; + return true; +} + +bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlockIn) { + for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) + cacheCoins[it->first] = it->second; + hashBlock = hashBlockIn; + return true; +} + +bool CCoinsViewCache::Flush() { + bool fOk = base->BatchWrite(cacheCoins, hashBlock); + if (fOk) + cacheCoins.clear(); + return fOk; +} + +unsigned int CCoinsViewCache::GetCacheSize() { + return cacheCoins.size(); +} + +const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) +{ + const CCoins &coins = GetCoins(input.prevout.hash); + assert(coins.IsAvailable(input.prevout.n)); + return coins.vout[input.prevout.n]; +} + +int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) +{ + if (tx.IsCoinBase()) + return 0; + + int64_t nResult = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + nResult += GetOutputFor(tx.vin[i]).nValue; + + return nResult; +} + +bool CCoinsViewCache::HaveInputs(const CTransaction& tx) +{ + if (!tx.IsCoinBase()) { + // first check whether information about the prevout hash is available + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + if (!HaveCoins(prevout.hash)) + return false; + } + + // then check whether the actual outputs are available + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins &coins = GetCoins(prevout.hash); + if (!coins.IsAvailable(prevout.n)) + return false; + } + } + return true; +} diff --git a/src/coins.h b/src/coins.h new file mode 100644 index 0000000000..2c72ee88e1 --- /dev/null +++ b/src/coins.h @@ -0,0 +1,356 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_COINS_H +#define BITCOIN_COINS_H + +#include "core.h" +#include "serialize.h" +#include "uint256.h" + +#include <assert.h> +#include <stdint.h> + +#include <boost/foreach.hpp> + +/** pruned version of CTransaction: only retains metadata and unspent transaction outputs + * + * Serialized format: + * - VARINT(nVersion) + * - VARINT(nCode) + * - unspentness bitvector, for vout[2] and further; least significant byte first + * - the non-spent CTxOuts (via CTxOutCompressor) + * - VARINT(nHeight) + * + * The nCode value consists of: + * - bit 1: IsCoinBase() + * - bit 2: vout[0] is not spent + * - bit 4: vout[1] is not spent + * - The higher bits encode N, the number of non-zero bytes in the following bitvector. + * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at + * least one non-spent output). + * + * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e + * <><><--------------------------------------------><----> + * | \ | / + * version code vout[1] height + * + * - version = 1 + * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) + * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 + * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 + * * 8358: compact amount representation for 60000000000 (600 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 + * - height = 203998 + * + * + * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b + * <><><--><--------------------------------------------------><----------------------------------------------><----> + * / \ \ | | / + * version code unspentness vout[4] vout[16] height + * + * - version = 1 + * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, + * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) + * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent + * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee + * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 + * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 + * * bbd123: compact amount representation for 110397 (0.001 BTC) + * * 00: special txout type pay-to-pubkey-hash + * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 + * - height = 120891 + */ +class CCoins +{ +public: + // whether transaction is a coinbase + bool fCoinBase; + + // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped + std::vector<CTxOut> vout; + + // at which height this transaction was included in the active block chain + int nHeight; + + // version of the CTransaction; accesses to this value should probably check for nHeight as well, + // as new tx version will probably only be introduced at certain heights + 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) { + ClearUnspendable(); + } + + // empty constructor + CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } + + // remove spent outputs at the end of vout + void Cleanup() { + while (vout.size() > 0 && vout.back().IsNull()) + vout.pop_back(); + if (vout.empty()) + 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); + std::swap(to.nHeight, nHeight); + std::swap(to.nVersion, nVersion); + } + + // equality test + friend bool operator==(const CCoins &a, const CCoins &b) { + // Empty CCoins objects are always equal. + if (a.IsPruned() && b.IsPruned()) + return true; + return a.fCoinBase == b.fCoinBase && + a.nHeight == b.nHeight && + a.nVersion == b.nVersion && + a.vout == b.vout; + } + friend bool operator!=(const CCoins &a, const CCoins &b) { + return !(a == b); + } + + void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const; + + bool IsCoinBase() const { + return fCoinBase; + } + + unsigned int GetSerializeSize(int nType, int nVersion) const { + unsigned int nSize = 0; + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); + // size of header code + nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); + // spentness bitmask + nSize += nMaskSize; + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) + if (!vout[i].IsNull()) + nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); + // height + nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); + return nSize; + } + + template<typename Stream> + void Serialize(Stream &s, int nType, int nVersion) const { + unsigned int nMaskSize = 0, nMaskCode = 0; + CalcMaskSize(nMaskSize, nMaskCode); + bool fFirst = vout.size() > 0 && !vout[0].IsNull(); + bool fSecond = vout.size() > 1 && !vout[1].IsNull(); + assert(fFirst || fSecond || nMaskCode); + unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); + // version + ::Serialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Serialize(s, VARINT(nCode), nType, nVersion); + // spentness bitmask + for (unsigned int b = 0; b<nMaskSize; b++) { + unsigned char chAvail = 0; + for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) + if (!vout[2+b*8+i].IsNull()) + chAvail |= (1 << i); + ::Serialize(s, chAvail, nType, nVersion); + } + // txouts themself + for (unsigned int i = 0; i < vout.size(); i++) { + if (!vout[i].IsNull()) + ::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion); + } + // coinbase height + ::Serialize(s, VARINT(nHeight), nType, nVersion); + } + + template<typename Stream> + void Unserialize(Stream &s, int nType, int nVersion) { + unsigned int nCode = 0; + // version + ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); + // header code + ::Unserialize(s, VARINT(nCode), nType, nVersion); + fCoinBase = nCode & 1; + std::vector<bool> vAvail(2, false); + vAvail[0] = nCode & 2; + vAvail[1] = nCode & 4; + unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); + // spentness bitmask + while (nMaskCode > 0) { + unsigned char chAvail = 0; + ::Unserialize(s, chAvail, nType, nVersion); + for (unsigned int p = 0; p < 8; p++) { + bool f = (chAvail & (1 << p)) != 0; + vAvail.push_back(f); + } + if (chAvail != 0) + nMaskCode--; + } + // txouts themself + vout.assign(vAvail.size(), CTxOut()); + for (unsigned int i = 0; i < vAvail.size(); i++) { + if (vAvail[i]) + ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); + } + // coinbase height + ::Unserialize(s, VARINT(nHeight), nType, nVersion); + Cleanup(); + } + + // mark an outpoint spent, and construct undo information + bool Spend(const COutPoint &out, CTxInUndo &undo); + + // mark a vout spent + bool Spend(int nPos); + + // check whether a particular output is still available + bool IsAvailable(unsigned int nPos) const { + return (nPos < vout.size() && !vout[nPos].IsNull()); + } + + // check whether the entire CCoins is spent + // note that only !IsPruned() CCoins can be serialized + bool IsPruned() const { + BOOST_FOREACH(const CTxOut &out, vout) + if (!out.IsNull()) + return false; + return true; + } +}; + + +struct CCoinsStats +{ + int nHeight; + uint256 hashBlock; + uint64_t nTransactions; + uint64_t nTransactionOutputs; + uint64_t nSerializedSize; + uint256 hashSerialized; + int64_t nTotalAmount; + + CCoinsStats() : nHeight(0), hashBlock(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), hashSerialized(0), nTotalAmount(0) {} +}; + + +/** Abstract view on the open txout dataset. */ +class CCoinsView +{ +public: + // Retrieve the CCoins (unspent transaction outputs) for a given txid + virtual bool GetCoins(const uint256 &txid, CCoins &coins); + + // Modify the CCoins for a given txid + virtual bool SetCoins(const uint256 &txid, const CCoins &coins); + + // Just check whether we have data for a given txid. + // This may (but cannot always) return true for fully spent transactions + virtual bool HaveCoins(const uint256 &txid); + + // Retrieve the block hash whose state this CCoinsView currently represents + virtual uint256 GetBestBlock(); + + // Modify the currently active block hash + virtual bool SetBestBlock(const uint256 &hashBlock); + + // Do a bulk modification (multiple SetCoins + one SetBestBlock) + virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + + // Calculate statistics about the unspent transaction output set + virtual bool GetStats(CCoinsStats &stats); + + // As we use CCoinsViews polymorphically, have a virtual destructor + virtual ~CCoinsView() {} +}; + + +/** CCoinsView backed by another CCoinsView */ +class CCoinsViewBacked : public CCoinsView +{ +protected: + CCoinsView *base; + +public: + CCoinsViewBacked(CCoinsView &viewIn); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); + uint256 GetBestBlock(); + bool SetBestBlock(const uint256 &hashBlock); + void SetBackend(CCoinsView &viewIn); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + bool GetStats(CCoinsStats &stats); +}; + + +/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ +class CCoinsViewCache : public CCoinsViewBacked +{ +protected: + uint256 hashBlock; + std::map<uint256,CCoins> cacheCoins; + +public: + CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); + + // Standard CCoinsView methods + bool GetCoins(const uint256 &txid, CCoins &coins); + bool SetCoins(const uint256 &txid, const CCoins &coins); + bool HaveCoins(const uint256 &txid); + uint256 GetBestBlock(); + bool SetBestBlock(const uint256 &hashBlock); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); + + // Return a modifiable reference to a CCoins. Check HaveCoins first. + // Many methods explicitly require a CCoinsViewCache because of this method, to reduce + // copying. + CCoins &GetCoins(const uint256 &txid); + + // Push the modifications applied to this cache to its base. + // Failure to call this method before destruction will cause the changes to be forgotten. + bool Flush(); + + // Calculate the size of the cache (in number of transactions) + unsigned int GetCacheSize(); + + /** Amount of bitcoins coming in to a transaction + Note that lightweight clients may not know anything besides the hash of previous transactions, + so may not be able to calculate this. + + @param[in] tx transaction for which we are checking input total + @return Sum of value of all inputs (scriptSigs) + @see CTransaction::FetchInputs + */ + int64_t 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); + + const CTxOut &GetOutputFor(const CTxIn& input); + +private: + std::map<uint256,CCoins>::iterator FetchCoins(const uint256 &txid); +}; + +#endif diff --git a/src/compat.h b/src/compat.h index 9caf5e4810..1deef493c2 100644 --- a/src/compat.h +++ b/src/compat.h @@ -1,28 +1,42 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef _BITCOIN_COMPAT_H #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 <winsock2.h> // Must be included before mswsock.h and windows.h + +#include <mswsock.h> +#include <windows.h> #include <ws2tcpip.h> #else -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/fcntl.h> #include <arpa/inet.h> -#include <netdb.h> +#include <ifaddrs.h> +#include <limits.h> #include <net/if.h> +#include <netdb.h> #include <netinet/in.h> -#include <ifaddrs.h> +#include <sys/fcntl.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> #endif #ifdef WIN32 diff --git a/src/core.cpp b/src/core.cpp index afba0959cf..80cdcb0849 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -4,8 +4,11 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "core.h" + #include "util.h" +#include <stdint.h> + std::string COutPoint::ToString() const { return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10).c_str(), n); @@ -13,7 +16,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,10 +50,10 @@ 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) +CTxOut::CTxOut(int64_t nValueIn, CScript scriptPubKeyIn) { nValue = nValueIn; scriptPubKey = scriptPubKeyIn; @@ -63,14 +66,12 @@ uint256 CTxOut::GetHash() const std::string CTxOut::ToString() const { - if (scriptPubKey.size() < 6) - return "CTxOut(error)"; - return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); + return strprintf("CTxOut(nValue=%"PRId64".%08"PRId64", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); } void CTxOut::print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } uint256 CTransaction::GetHash() const @@ -125,7 +126,7 @@ std::string CTransaction::ToString() const void CTransaction::print() const { - printf("%s", ToString().c_str()); + LogPrintf("%s", ToString().c_str()); } // Amount compression: @@ -137,7 +138,7 @@ void CTransaction::print() const // * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9 // (this is decodable, as d is in [1-9] and e is in [0-9]) -uint64 CTxOutCompressor::CompressAmount(uint64 n) +uint64_t CTxOutCompressor::CompressAmount(uint64_t n) { if (n == 0) return 0; @@ -156,7 +157,7 @@ uint64 CTxOutCompressor::CompressAmount(uint64 n) } } -uint64 CTxOutCompressor::DecompressAmount(uint64 x) +uint64_t CTxOutCompressor::DecompressAmount(uint64_t x) { // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9 if (x == 0) @@ -165,7 +166,7 @@ uint64 CTxOutCompressor::DecompressAmount(uint64 x) // x = 10*(9*n + d - 1) + e int e = x % 10; x /= 10; - uint64 n = 0; + uint64_t n = 0; if (e < 9) { // x = 9*n + d - 1 int d = (x % 9) + 1; @@ -182,49 +183,6 @@ uint64 CTxOutCompressor::DecompressAmount(uint64 x) return n; } -// calculate number of bytes for the bitmask, and its number of non-zero bytes -// each bit in the bitmask represents the availability of one output, but the -// availabilities of the first two outputs are encoded separately -void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { - unsigned int nLastUsedByte = 0; - for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { - bool fZero = true; - for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { - if (!vout[2+b*8+i].IsNull()) { - fZero = false; - continue; - } - } - if (!fZero) { - nLastUsedByte = b + 1; - nNonzeroBytes++; - } - } - nBytes += nLastUsedByte; -} - -bool CCoins::Spend(const COutPoint &out, CTxInUndo &undo) { - if (out.n >= vout.size()) - return false; - if (vout[out.n].IsNull()) - return false; - undo = CTxInUndo(vout[out.n]); - vout[out.n].SetNull(); - Cleanup(); - if (vout.size() == 0) { - undo.nHeight = nHeight; - undo.fCoinBase = fCoinBase; - undo.nVersion = this->nVersion; - } - return true; -} - -bool CCoins::Spend(int nPos) { - CTxInUndo undo; - COutPoint out(0, nPos); - return Spend(out, undo); -} - uint256 CBlockHeader::GetHash() const { return Hash(BEGIN(nVersion), END(nNonce)); @@ -282,7 +240,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 +249,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..e31a7e6582 100644 --- a/src/core.h +++ b/src/core.h @@ -2,14 +2,15 @@ // Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_CORE_H #define BITCOIN_CORE_H -#include "uint256.h" -#include "serialize.h" #include "script.h" +#include "serialize.h" +#include "uint256.h" -#include <stdio.h> +#include <stdint.h> class CTransaction; @@ -114,7 +115,7 @@ public: class CTxOut { public: - int64 nValue; + int64_t nValue; CScript scriptPubKey; CTxOut() @@ -122,7 +123,7 @@ public: SetNull(); } - CTxOut(int64 nValueIn, CScript scriptPubKeyIn); + CTxOut(int64_t nValueIn, CScript scriptPubKeyIn); IMPLEMENT_SERIALIZE ( @@ -143,7 +144,7 @@ public: uint256 GetHash() const; - bool IsDust(int64 nMinRelayTxFee) const + bool IsDust(int64_t nMinRelayTxFee) const { // "Dust" is defined in terms of CTransaction::nMinRelayTxFee, // which has units satoshis-per-kilobyte. @@ -178,8 +179,8 @@ public: class CTransaction { public: - static int64 nMinTxFee; - static int64 nMinRelayTxFee; + static int64_t nMinTxFee; + static int64_t nMinRelayTxFee; static const int CURRENT_VERSION=1; int nVersion; std::vector<CTxIn> vin; @@ -246,17 +247,17 @@ private: CTxOut &txout; public: - static uint64 CompressAmount(uint64 nAmount); - static uint64 DecompressAmount(uint64 nAmount); + static uint64_t CompressAmount(uint64_t nAmount); + static uint64_t DecompressAmount(uint64_t nAmount); CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } IMPLEMENT_SERIALIZE(({ if (!fRead) { - uint64 nVal = CompressAmount(txout.nValue); + uint64_t nVal = CompressAmount(txout.nValue); READWRITE(VARINT(nVal)); } else { - uint64 nVal = 0; + uint64_t nVal = 0; READWRITE(VARINT(nVal)); txout.nValue = DecompressAmount(nVal); } @@ -321,219 +322,6 @@ public: }; -/** pruned version of CTransaction: only retains metadata and unspent transaction outputs - * - * Serialized format: - * - VARINT(nVersion) - * - VARINT(nCode) - * - unspentness bitvector, for vout[2] and further; least significant byte first - * - the non-spent CTxOuts (via CTxOutCompressor) - * - VARINT(nHeight) - * - * The nCode value consists of: - * - bit 1: IsCoinBase() - * - bit 2: vout[0] is not spent - * - bit 4: vout[1] is not spent - * - The higher bits encode N, the number of non-zero bytes in the following bitvector. - * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at - * least one non-spent output). - * - * Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e - * <><><--------------------------------------------><----> - * | \ | / - * version code vout[1] height - * - * - version = 1 - * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) - * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 - * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 - * * 8358: compact amount representation for 60000000000 (600 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 - * - height = 203998 - * - * - * Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b - * <><><--><--------------------------------------------------><----------------------------------------------><----> - * / \ \ | | / - * version code unspentness vout[4] vout[16] height - * - * - version = 1 - * - code = 9 (coinbase, neither vout[0] or vout[1] are unspent, - * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) - * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent - * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee - * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 - * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 - * * bbd123: compact amount representation for 110397 (0.001 BTC) - * * 00: special txout type pay-to-pubkey-hash - * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 - * - height = 120891 - */ -class CCoins -{ -public: - // whether transaction is a coinbase - bool fCoinBase; - - // unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped - std::vector<CTxOut> vout; - - // at which height this transaction was included in the active block chain - int nHeight; - - // version of the CTransaction; accesses to this value should probably check for nHeight as well, - // as new tx version will probably only be introduced at certain heights - 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) { } - - // empty constructor - CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { } - - // remove spent outputs at the end of vout - void Cleanup() { - while (vout.size() > 0 && vout.back().IsNull()) - vout.pop_back(); - if (vout.empty()) - std::vector<CTxOut>().swap(vout); - } - - void swap(CCoins &to) { - std::swap(to.fCoinBase, fCoinBase); - to.vout.swap(vout); - std::swap(to.nHeight, nHeight); - std::swap(to.nVersion, nVersion); - } - - // equality test - friend bool operator==(const CCoins &a, const CCoins &b) { - return a.fCoinBase == b.fCoinBase && - a.nHeight == b.nHeight && - a.nVersion == b.nVersion && - a.vout == b.vout; - } - friend bool operator!=(const CCoins &a, const CCoins &b) { - return !(a == b); - } - - void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const; - - bool IsCoinBase() const { - return fCoinBase; - } - - unsigned int GetSerializeSize(int nType, int nVersion) const { - unsigned int nSize = 0; - unsigned int nMaskSize = 0, nMaskCode = 0; - CalcMaskSize(nMaskSize, nMaskCode); - bool fFirst = vout.size() > 0 && !vout[0].IsNull(); - bool fSecond = vout.size() > 1 && !vout[1].IsNull(); - assert(fFirst || fSecond || nMaskCode); - unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); - // version - nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion); - // size of header code - nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion); - // spentness bitmask - nSize += nMaskSize; - // txouts themself - for (unsigned int i = 0; i < vout.size(); i++) - if (!vout[i].IsNull()) - nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion); - // height - nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion); - return nSize; - } - - template<typename Stream> - void Serialize(Stream &s, int nType, int nVersion) const { - unsigned int nMaskSize = 0, nMaskCode = 0; - CalcMaskSize(nMaskSize, nMaskCode); - bool fFirst = vout.size() > 0 && !vout[0].IsNull(); - bool fSecond = vout.size() > 1 && !vout[1].IsNull(); - assert(fFirst || fSecond || nMaskCode); - unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0); - // version - ::Serialize(s, VARINT(this->nVersion), nType, nVersion); - // header code - ::Serialize(s, VARINT(nCode), nType, nVersion); - // spentness bitmask - for (unsigned int b = 0; b<nMaskSize; b++) { - unsigned char chAvail = 0; - for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) - if (!vout[2+b*8+i].IsNull()) - chAvail |= (1 << i); - ::Serialize(s, chAvail, nType, nVersion); - } - // txouts themself - for (unsigned int i = 0; i < vout.size(); i++) { - if (!vout[i].IsNull()) - ::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion); - } - // coinbase height - ::Serialize(s, VARINT(nHeight), nType, nVersion); - } - - template<typename Stream> - void Unserialize(Stream &s, int nType, int nVersion) { - unsigned int nCode = 0; - // version - ::Unserialize(s, VARINT(this->nVersion), nType, nVersion); - // header code - ::Unserialize(s, VARINT(nCode), nType, nVersion); - fCoinBase = nCode & 1; - std::vector<bool> vAvail(2, false); - vAvail[0] = nCode & 2; - vAvail[1] = nCode & 4; - unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1); - // spentness bitmask - while (nMaskCode > 0) { - unsigned char chAvail = 0; - ::Unserialize(s, chAvail, nType, nVersion); - for (unsigned int p = 0; p < 8; p++) { - bool f = (chAvail & (1 << p)) != 0; - vAvail.push_back(f); - } - if (chAvail != 0) - nMaskCode--; - } - // txouts themself - vout.assign(vAvail.size(), CTxOut()); - for (unsigned int i = 0; i < vAvail.size(); i++) { - if (vAvail[i]) - ::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion); - } - // coinbase height - ::Unserialize(s, VARINT(nHeight), nType, nVersion); - Cleanup(); - } - - // mark an outpoint spent, and construct undo information - bool Spend(const COutPoint &out, CTxInUndo &undo); - - // mark a vout spent - bool Spend(int nPos); - - // check whether a particular output is still available - bool IsAvailable(unsigned int nPos) const { - return (nPos < vout.size() && !vout[nPos].IsNull()); - } - - // check whether the entire CCoins is spent - // note that only !IsPruned() CCoins can be serialized - bool IsPruned() const { - BOOST_FOREACH(const CTxOut &out, vout) - if (!out.IsNull()) - return false; - return true; - } -}; - - /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work * requirements. When they solve the proof-of-work, they broadcast the block @@ -586,9 +374,9 @@ public: uint256 GetHash() const; - int64 GetBlockTime() const + int64_t GetBlockTime() const { - return (int64)nTime; + return (int64_t)nTime; } }; @@ -651,4 +439,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/crypter.cpp b/src/crypter.cpp index 754de536a9..10a34ae24a 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -1,13 +1,14 @@ -// Copyright (c) 2009-2012 The Bitcoin Developers +// 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 <openssl/aes.h> -#include <openssl/evp.h> -#include <vector> +#include "crypter.h" + #include <string> +#include <vector> -#include "crypter.h" +#include <openssl/aes.h> +#include <openssl/evp.h> bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) { diff --git a/src/crypter.h b/src/crypter.h index 4134c1b49b..861c4f9441 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -1,13 +1,15 @@ -// Copyright (c) 2009-2012 The Bitcoin Developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef __CRYPTER_H__ #define __CRYPTER_H__ -#include "allocators.h" /* for SecureString */ -#include "key.h" +#include "allocators.h" #include "serialize.h" +class uint256; + const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; const unsigned int WALLET_CRYPTO_SALT_SIZE = 8; @@ -88,16 +90,16 @@ public: // Try to keep the key data out of swap (and be a bit over-careful to keep the IV that we don't even use out of swap) // Note that this does nothing about suspend-to-disk (which will put all our key data on disk) // Note as well that at no point in this program is any attempt made to prevent stealing of keys by reading the memory of the running process. - LockedPageManager::instance.LockRange(&chKey[0], sizeof chKey); - LockedPageManager::instance.LockRange(&chIV[0], sizeof chIV); + LockedPageManager::Instance().LockRange(&chKey[0], sizeof chKey); + LockedPageManager::Instance().LockRange(&chIV[0], sizeof chIV); } ~CCrypter() { CleanKey(); - LockedPageManager::instance.UnlockRange(&chKey[0], sizeof chKey); - LockedPageManager::instance.UnlockRange(&chIV[0], sizeof chIV); + LockedPageManager::Instance().UnlockRange(&chKey[0], sizeof chKey); + LockedPageManager::Instance().UnlockRange(&chIV[0], sizeof chIV); } }; diff --git a/src/db.cpp b/src/db.cpp index 03f46f3c26..a286d9f726 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -1,21 +1,26 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "chainparams.h" #include "db.h" -#include "util.h" -#include "hash.h" + #include "addrman.h" -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> -#include <openssl/rand.h> +#include "hash.h" +#include "protocol.h" +#include "util.h" + +#include <inttypes.h> +#include <stdint.h> #ifndef WIN32 -#include "sys/stat.h" +#include <sys/stat.h> #endif +#include <boost/filesystem.hpp> +#include <boost/version.hpp> +#include <openssl/rand.h> + using namespace std; using namespace boost; @@ -38,7 +43,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 +75,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 +116,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 +173,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 +353,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 +367,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 +423,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; } } @@ -430,10 +435,10 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) void CDBEnv::Flush(bool fShutdown) { - int64 nStart = GetTimeMillis(); + int64_t 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 +448,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"PRId64"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart); if (fShutdown) { char** listp; @@ -1,31 +1,30 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_DB_H #define BITCOIN_DB_H -#include "sync.h" #include "serialize.h" +#include "sync.h" +#include "version.h" #include <map> #include <string> #include <vector> -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp> #include <db_cxx.h> class CAddrMan; -class CBlockLocator; +struct CBlockLocator; class CDiskBlockIndex; -class CMasterKey; class COutPoint; -class CWallet; extern unsigned int nWalletDBUpdated; void ThreadFlushWalletDB(const std::string& strWalletFile); -bool BackupWallet(const CWallet& wallet, const std::string& strDest); class CDBEnv diff --git a/src/hash.h b/src/hash.h index 880468a2d2..7dbf1b6448 100644 --- a/src/hash.h +++ b/src/hash.h @@ -1,17 +1,20 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_HASH_H #define BITCOIN_HASH_H -#include "uint256.h" #include "serialize.h" +#include "uint256.h" +#include "version.h" -#include <openssl/sha.h> -#include <openssl/ripemd.h> #include <vector> +#include <openssl/ripemd.h> +#include <openssl/sha.h> + template<typename T1> inline uint256 Hash(const T1 pbegin, const T1 pend) { diff --git a/src/init.cpp b/src/init.cpp index 3c1ee24e2d..6749064ed5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -8,29 +8,30 @@ #endif #include "init.h" -#include "main.h" -#include "core.h" -#include "chainparams.h" -#include "txdb.h" -#include "walletdb.h" + +#include "addrman.h" #include "bitcoinrpc.h" -#include "net.h" -#include "util.h" +#include "checkpoints.h" #include "miner.h" +#include "net.h" +#include "txdb.h" #include "ui_interface.h" -#include "checkpoints.h" +#include "util.h" +#include "wallet.h" +#include "walletdb.h" -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> -#include <boost/filesystem/convenience.hpp> -#include <boost/interprocess/sync/file_lock.hpp> -#include <boost/algorithm/string/predicate.hpp> -#include <openssl/crypto.h> +#include <inttypes.h> +#include <stdint.h> #ifndef WIN32 #include <signal.h> #endif +#include <boost/algorithm/string/predicate.hpp> +#include <boost/filesystem.hpp> +#include <boost/interprocess/sync/file_lock.hpp> +#include <openssl/crypto.h> + using namespace std; using namespace boost; @@ -100,21 +101,23 @@ static CCoinsViewDB *pcoinsdbview; void Shutdown() { + LogPrintf("Shutdown : In progress...\n"); static CCriticalSection cs_Shutdown; TRY_LOCK(cs_Shutdown, lockShutdown); if (!lockShutdown) return; RenameThread("bitcoin-shutoff"); - nTransactionsUpdated++; + mempool.AddTransactionsUpdated(1); 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 +126,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"); } // @@ -166,94 +172,130 @@ bool static Bind(const CService &addr, unsigned int flags) { return true; } -// Core-specific options shared between UI and daemon -std::string HelpMessage() +// Core-specific options shared between UI, daemon and RPC client +std::string HelpMessage(HelpMessageMode hmm) { string strUsage = _("Options:") + "\n"; strUsage += " -? " + _("This help message") + "\n"; strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; - strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n"; - strUsage += " -gen " + _("Generate coins (default: 0)") + "\n"; strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; - strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n"; - strUsage += " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n"; - strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n"; - strUsage += " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n"; - strUsage += " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n"; - strUsage += " -tor=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"; - strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n"; - strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n"; - strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n"; - strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n"; - strUsage += " -connect=<ip> " + _("Connect only to the specified node(s)") + "\n"; - strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n"; - strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n"; - strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n"; - strUsage += " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n"; - strUsage += " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n"; - strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n"; - strUsage += " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n"; - strUsage += " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n"; - strUsage += " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n"; - strUsage += " -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n"; - strUsage += " -maxreceivebuffer=<n> " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)") + "\n"; - strUsage += " -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n"; + strUsage += " -testnet " + _("Use the test network") + "\n"; + + if(hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) + { + strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n"; + strUsage += " -gen " + _("Generate coins (default: 0)") + "\n"; + strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n"; + strUsage += " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n"; + strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n"; + strUsage += " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n"; + strUsage += " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n"; + strUsage += " -onion=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"; + strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n"; + strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n"; + strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n"; + strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n"; + strUsage += " -connect=<ip> " + _("Connect only to the specified node(s)") + "\n"; + strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n"; + strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n"; + strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n"; + strUsage += " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n"; + strUsage += " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n"; + strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n"; + strUsage += " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n"; + strUsage += " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n"; + strUsage += " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n"; + strUsage += " -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n"; + strUsage += " -maxreceivebuffer=<n> " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)") + "\n"; + strUsage += " -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n"; #ifdef USE_UPNP #if USE_UPNP - strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n"; + strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n"; #else - strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n"; + strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n"; +#endif #endif + strUsage += " -paytxfee=<amt> " + _("Fee per KB to add to transactions you send") + "\n"; + strUsage += " -debug=<category> " + _("Output debugging information (default: 0, supplying <category> is optional)") + "\n"; + strUsage += _("If <category> is not supplied, output all debugging information.") + "\n"; + strUsage += _("<category> can be:"); + strUsage += " addrman, alert, coindb, db, lock, rand, rpc, selectcoins, mempool, net"; // Don't translate these and qt below + if (hmm == HMM_BITCOIN_QT) + { + strUsage += ", qt.\n"; + } + else + { + strUsage += ".\n"; + } + strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n"; + strUsage += " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n"; + strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; + strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " + "solved instantly. This is intended for regression testing tools and app development.") + "\n"; +#ifdef WIN32 + strUsage += " -printtodebugger " + _("Send trace/debug info to debugger") + "\n"; #endif - strUsage += " -paytxfee=<amt> " + _("Fee per KB to add to transactions you send") + "\n"; - if (fHaveGUI) + } + + if (hmm == HMM_BITCOIN_QT) + { strUsage += " -server " + _("Accept command line and JSON-RPC commands") + "\n"; + } + + if (hmm == HMM_BITCOIND) + { #if !defined(WIN32) - if (fHaveGUI) strUsage += " -daemon " + _("Run in the background as a daemon and accept commands") + "\n"; #endif - strUsage += " -testnet " + _("Use the test network") + "\n"; - strUsage += " -debug " + _("Output extra debugging information. Implies all other -debug* options") + "\n"; - strUsage += " -debugnet " + _("Output extra network debugging information") + "\n"; - strUsage += " -logtimestamps " + _("Prepend debug output with timestamp") + "\n"; - strUsage += " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n"; - strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; - strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " - "solved instantly. This is intended for regression testing tools and app development.") + "\n"; -#ifdef WIN32 - strUsage += " -printtodebugger " + _("Send trace/debug info to debugger") + "\n"; -#endif + } + + if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_CLI) + { + strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n"; + } + strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n"; strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n"; - strUsage += " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n"; - strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n"; - if (!fHaveGUI) - strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n"; - strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n"; - strUsage += " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n"; - strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; - strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n"; - strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n"; - strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; - strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n"; - strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n"; - strUsage += " -checkblocks=<n> " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n"; - strUsage += " -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n"; - strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n"; - strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n"; - strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n"; - strUsage += " -par=<n> " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n"; - - strUsage += "\n" + _("Block creation options:") + "\n"; - strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; - strUsage += " -blockmaxsize=<n> " + _("Set maximum block size in bytes (default: 250000)") + "\n"; - strUsage += " -blockprioritysize=<n> " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n"; + if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) + { + strUsage += " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n"; + } else { + strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n"; + } + + if(hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) + { + strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n"; + strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n"; + strUsage += " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n"; + strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; + strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n"; + strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n"; + strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; + strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n"; + strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n"; + strUsage += " -checkblocks=<n> " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n"; + strUsage += " -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n"; + strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n"; + strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n"; + strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n"; + strUsage += " -par=<n> " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n"; + + strUsage += "\n" + _("Block creation options:") + "\n"; + strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; + strUsage += " -blockmaxsize=<n> " + _("Set maximum block size in bytes (default: 250000)") + "\n"; + strUsage += " -blockprioritysize=<n> " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n"; + } strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; - strUsage += " -rpcsslcertificatechainfile=<file.cert> " + _("Server certificate file (default: server.cert)") + "\n"; - strUsage += " -rpcsslprivatekeyfile=<file.pem> " + _("Server private key (default: server.pem)") + "\n"; - strUsage += " -rpcsslciphers=<ciphers> " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)") + "\n"; + if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) + { + strUsage += " -rpcsslcertificatechainfile=<file.cert> " + _("Server certificate file (default: server.cert)") + "\n"; + strUsage += " -rpcsslprivatekeyfile=<file.pem> " + _("Server private key (default: server.pem)") + "\n"; + strUsage += " -rpcsslciphers=<ciphers> " + _("Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)") + "\n"; + } return strUsage; } @@ -284,13 +326,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 +344,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 +355,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); } } @@ -322,7 +364,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) /** Initialize bitcoin. * @pre Parameters should be parsed and config file should be read. */ -bool AppInit2(boost::thread_group& threadGroup) +bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) { // ********************************************************* Step 1: setup #ifdef _MSC_VER @@ -339,8 +381,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,9 +468,18 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 3: parameter-to-internal-flags - fDebug = GetBoolArg("-debug", false); + fDebug = !mapMultiArgs["-debug"].empty(); + // Special-case: if -debug=0/-nodebug is set, turn off debugging messages + const vector<string>& categories = mapMultiArgs["-debug"]; + if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), string("0")) != categories.end()) + fDebug = false; + + // Check for -debugnet (deprecated) + if (GetBoolArg("-debugnet", false)) + InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net")); + fBenchmark = GetBoolArg("-benchmark", false); - mempool.fChecks = GetBoolArg("-checkmempool", RegTest()); + mempool.setSanityCheck(GetBoolArg("-checkmempool", RegTest())); Checkpoints::fEnabled = GetBoolArg("-checkpoints", true); // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency @@ -440,23 +491,14 @@ bool AppInit2(boost::thread_group& threadGroup) else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; - // -debug implies fDebug* - if (fDebug) - fDebugNet = true; - else - fDebugNet = GetBoolArg("-debugnet", false); - - if (fDaemon) + if (fDaemon || fForceServer) fServer = true; else fServer = GetBoolArg("-server", false); - /* force fServer when running without GUI */ - if (!fHaveGUI) - fServer = true; fPrintToConsole = GetBoolArg("-printtoconsole", false); fPrintToDebugger = GetBoolArg("-printtodebugger", false); - fLogTimestamps = GetBoolArg("-logtimestamps", false); + fLogTimestamps = GetBoolArg("-logtimestamps", true); if (mapArgs.count("-timeout")) { @@ -479,7 +521,7 @@ bool AppInit2(boost::thread_group& threadGroup) // cost to you of processing a transaction. if (mapArgs.count("-mintxfee")) { - int64 n = 0; + int64_t n = 0; if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) CTransaction::nMinTxFee = n; else @@ -487,7 +529,7 @@ bool AppInit2(boost::thread_group& threadGroup) } if (mapArgs.count("-minrelaytxfee")) { - int64 n = 0; + int64_t n = 0; if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0) CTransaction::nMinRelayTxFee = n; else @@ -522,26 +564,26 @@ 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); } - int64 nStart; + int64_t nStart; // ********************************************************* Step 5: verify wallet database integrity @@ -551,10 +593,10 @@ bool AppInit2(boost::thread_group& threadGroup) { // try moving the database env out of the way boost::filesystem::path pathDatabase = GetDataDir() / "database"; - boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%"PRI64d".bak", GetTime()); + boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%"PRId64".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) } @@ -637,15 +679,20 @@ bool AppInit2(boost::thread_group& threadGroup) fProxy = true; } - // -tor can override normal proxy, -notor disables tor entirely - if (!(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && (fProxy || mapArgs.count("-tor"))) { + // -onion can override normal proxy, -noonion disables tor entirely + // -tor here is a temporary backwards compatibility measure + if (mapArgs.count("-tor")) + printf("Notice: option -tor has been replaced with -onion and will be removed in a later version.\n"); + if (!(mapArgs.count("-onion") && mapArgs["-onion"] == "0") && + !(mapArgs.count("-tor") && mapArgs["-tor"] == "0") && + (fProxy || mapArgs.count("-onion") || mapArgs.count("-tor"))) { CService addrOnion; - if (!mapArgs.count("-tor")) + if (!mapArgs.count("-onion") && !mapArgs.count("-tor")) addrOnion = addrProxy; else - addrOnion = CService(mapArgs["-tor"], 9050); + addrOnion = mapArgs.count("-onion")?CService(mapArgs["-onion"], 9050):CService(mapArgs["-tor"], 9050); if (!addrOnion.IsValid()) - return InitError(strprintf(_("Invalid -tor address: '%s'"), mapArgs["-tor"].c_str())); + return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs.count("-onion")?mapArgs["-onion"].c_str():mapArgs["-tor"].c_str())); SetProxy(NET_TOR, addrOnion, 5); SetReachable(NET_TOR); } @@ -705,12 +752,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; } } @@ -761,7 +808,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) @@ -778,12 +825,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; } @@ -801,7 +848,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 { @@ -815,10 +862,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"PRId64"ms\n", GetTimeMillis() - nStart); if (GetBoolArg("-printblockindex", false) || GetBoolArg("-printblocktree", false)) { @@ -840,12 +887,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; } @@ -872,7 +919,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 @@ -884,12 +931,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); @@ -907,34 +954,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"PRId64"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"PRId64"ms\n", GetTimeMillis() - nStart); + pwalletMain->SetBestChain(chainActive.GetLocator()); nWalletDBUpdated++; } @@ -962,10 +1009,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 %"PRId64"ms\n", addrman.size(), GetTimeMillis() - nStart); // ********************************************************* Step 11: start node @@ -979,11 +1026,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); @@ -993,17 +1040,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/init.h b/src/init.h index a4d5a67252..864f2f4614 100644 --- a/src/init.h +++ b/src/init.h @@ -1,11 +1,18 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_INIT_H #define BITCOIN_INIT_H -#include "wallet.h" +#include <string> + +class CWallet; + +namespace boost { + class thread_group; +}; extern std::string strWalletFile; extern CWallet* pwalletMain; @@ -13,7 +20,16 @@ extern CWallet* pwalletMain; void StartShutdown(); bool ShutdownRequested(); void Shutdown(); -bool AppInit2(boost::thread_group& threadGroup); -std::string HelpMessage(); +bool AppInit2(boost::thread_group& threadGroup, bool fForceServer); + +/* The help message mode determines what help message to show */ +enum HelpMessageMode +{ + HMM_BITCOIND, + HMM_BITCOIN_QT, + HMM_BITCOIN_CLI +}; + +std::string HelpMessage(HelpMessageMode mode); #endif diff --git a/src/key.cpp b/src/key.cpp index 85dc9cda2b..2fd68fa56b 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,14 +1,13 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "key.h" + #include <openssl/bn.h> #include <openssl/ecdsa.h> -#include <openssl/rand.h> #include <openssl/obj_mac.h> - -#include "key.h" - +#include <openssl/rand.h> // anonymous namespace with local implementation code (OpenSSL interaction) namespace { @@ -166,9 +165,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 +201,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 +413,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; @@ -2,15 +2,17 @@ // Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_KEY_H #define BITCOIN_KEY_H -#include <vector> - #include "allocators.h" +#include "hash.h" #include "serialize.h" #include "uint256.h" -#include "hash.h" + +#include <stdexcept> +#include <vector> // secp256k1: // const unsigned int PRIVATE_KEY_SIZE = 279; @@ -205,7 +207,8 @@ public: } friend bool operator==(const CKey &a, const CKey &b) { - return a.fCompressed == b.fCompressed && memcmp(&a.vch[0], &b.vch[0], 32); + return a.fCompressed == b.fCompressed && a.size() == b.size() && + memcmp(&a.vch[0], &b.vch[0], a.size()) == 0; } // Initialize using begin and end iterators to byte data. @@ -261,6 +264,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/keystore.cpp b/src/keystore.cpp index 808f8c24ef..05427291e0 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -1,11 +1,16 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "keystore.h" + +#include "crypter.h" +#include "key.h" #include "script.h" +#include <boost/foreach.hpp> + bool CKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const { CKey key; diff --git a/src/keystore.h b/src/keystore.h index 49a7bf569d..8d936bcab7 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -1,12 +1,14 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_KEYSTORE_H #define BITCOIN_KEYSTORE_H -#include "crypter.h" +#include "key.h" #include "sync.h" + #include <boost/signals2/signal.hpp> class CScript; @@ -88,8 +90,10 @@ public: virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; }; +typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial; typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap; + /** Keystore which keeps the private keys encrypted. * It derives from the basic key store, which is used if no encryption is active. */ 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/leveldb.cpp b/src/leveldbwrapper.cpp index 5e3fa08f5e..aff1ec0283 100644 --- a/src/leveldb.cpp +++ b/src/leveldbwrapper.cpp @@ -1,21 +1,21 @@ -// Copyright (c) 2012 The Bitcoin developers +// Copyright (c) 2012-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 "leveldb.h" +#include "leveldbwrapper.h" + #include "util.h" -#include <leveldb/env.h> +#include <boost/filesystem.hpp> #include <leveldb/cache.h> +#include <leveldb/env.h> #include <leveldb/filter_policy.h> -#include <memenv/memenv.h> - -#include <boost/filesystem.hpp> +#include <memenv.h> 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()) @@ -35,7 +35,7 @@ static leveldb::Options GetOptions(size_t nCacheSize) { return options; } -CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) { +CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) { penv = NULL; readoptions.verify_checksums = true; iteroptions.verify_checksums = true; @@ -48,18 +48,18 @@ 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() { +CLevelDBWrapper::~CLevelDBWrapper() { delete pdb; pdb = NULL; delete options.filter_policy; @@ -70,7 +70,7 @@ CLevelDB::~CLevelDB() { options.env = NULL; } -bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) { +bool CLevelDBWrapper::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) { leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); HandleError(status); return true; diff --git a/src/leveldb.h b/src/leveldbwrapper.h index 79262edbb5..53e9e439bd 100644 --- a/src/leveldb.h +++ b/src/leveldbwrapper.h @@ -1,16 +1,18 @@ -// Copyright (c) 2012 The Bitcoin developers +// Copyright (c) 2012-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_LEVELDB_H -#define BITCOIN_LEVELDB_H + +#ifndef BITCOIN_LEVELDBWRAPPER_H +#define BITCOIN_LEVELDBWRAPPER_H #include "serialize.h" +#include "util.h" +#include "version.h" +#include <boost/filesystem/path.hpp> #include <leveldb/db.h> #include <leveldb/write_batch.h> -#include <boost/filesystem/path.hpp> - class leveldb_error : public std::runtime_error { public: @@ -19,10 +21,10 @@ public: void HandleError(const leveldb::Status &status) throw(leveldb_error); -// Batch of changes queued to be written to a CLevelDB +// Batch of changes queued to be written to a CLevelDBWrapper class CLevelDBBatch { - friend class CLevelDB; + friend class CLevelDBWrapper; private: leveldb::WriteBatch batch; @@ -52,7 +54,7 @@ public: } }; -class CLevelDB +class CLevelDBWrapper { private: // custom environment this database is using (may be NULL in case of default environment) @@ -77,8 +79,8 @@ private: leveldb::DB *pdb; public: - CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); - ~CLevelDB(); + CLevelDBWrapper(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); + ~CLevelDBWrapper(); template<typename K, typename V> bool Read(const K& key, V& value) throw(leveldb_error) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); @@ -91,7 +93,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 +122,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; @@ -150,4 +152,4 @@ public: } }; -#endif // BITCOIN_LEVELDB_H +#endif // BITCOIN_LEVELDBWRAPPER_H diff --git a/src/limitedmap.h b/src/limitedmap.h index 7049d68e5a..1623a372b3 100644 --- a/src/limitedmap.h +++ b/src/limitedmap.h @@ -1,11 +1,12 @@ // Copyright (c) 2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_LIMITEDMAP_H #define BITCOIN_LIMITEDMAP_H +#include <assert.h> // TODO: remove #include <map> -#include <deque> /** STL-like map container that only keeps the N elements with the highest value. */ template <typename K, typename V> class limitedmap 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..8aef91d829 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,17 +1,26 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "main.h" + +#include "addrman.h" #include "alert.h" +#include "chainparams.h" #include "checkpoints.h" -#include "db.h" -#include "txdb.h" -#include "net.h" +#include "checkqueue.h" #include "init.h" +#include "net.h" +#include "txdb.h" +#include "txmempool.h" #include "ui_interface.h" -#include "checkqueue.h" -#include "chainparams.h" +#include "util.h" + +#include <inttypes.h> +#include <sstream> +#include <stdint.h> + #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> @@ -23,38 +32,26 @@ using namespace boost; // Global state // -CCriticalSection cs_setpwalletRegistered; -set<CWallet*> setpwalletRegistered; - CCriticalSection cs_main; CTxMemPool mempool; -unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; -std::vector<CBlockIndex*> vBlockIndexByHeight; -CBlockIndex* pindexGenesisBlock = NULL; -int nBestHeight = -1; -uint256 nBestChainWork = 0; -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; +CChain chainActive; +int64_t nTimeBestReceived = 0; int nScriptCheckThreads = 0; bool fImporting = false; bool fReindex = false; bool fBenchmark = false; bool fTxIndex = false; unsigned int nCoinCacheSize = 5000; -bool fHaveGUI = false; /** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ -int64 CTransaction::nMinTxFee = 10000; // Override with -mintxfee +int64_t CTransaction::nMinTxFee = 10000; // Override with -mintxfee /** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ -int64 CTransaction::nMinRelayTxFee = 10000; +int64_t CTransaction::nMinRelayTxFee = 10000; -CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have +static CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have map<uint256, CBlock*> mapOrphanBlocks; multimap<uint256, CBlock*> mapOrphanBlocksByPrev; @@ -67,108 +64,85 @@ CScript COINBASE_FLAGS; const string strMessageMagic = "Bitcoin Signed Message:\n"; -double dHashesPerSec = 0.0; -int64 nHPSTimerStart = 0; - // Settings -int64 nTransactionFee = 0; - - - -////////////////////////////////////////////////////////////////////////////// -// -// dispatching functions -// - -// These functions dispatch to one or all registered wallets +int64_t nTransactionFee = 0; - -void RegisterWallet(CWallet* pwalletIn) -{ - { - LOCK(cs_setpwalletRegistered); - setpwalletRegistered.insert(pwalletIn); - } -} - -void UnregisterWallet(CWallet* pwalletIn) -{ - { - LOCK(cs_setpwalletRegistered); - setpwalletRegistered.erase(pwalletIn); - } -} - -void UnregisterAllWallets() +// Internal stuff +namespace { +struct CBlockIndexWorkComparator { - LOCK(cs_setpwalletRegistered); - setpwalletRegistered.clear(); -} + bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + if (pa->nChainWork > pb->nChainWork) return false; + if (pa->nChainWork < pb->nChainWork) return true; -// get the wallet transaction with the given hash (if it exists) -bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - if (pwallet->GetTransaction(hashTx,wtx)) - return true; - return false; -} + if (pa->GetBlockHash() < pb->GetBlockHash()) return false; + if (pa->GetBlockHash() > pb->GetBlockHash()) return true; -// erases transaction with the given hash from all wallets -void static EraseFromWallets(uint256 hash) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->EraseFromWallet(hash); -} - -// make sure all wallets know about the given transaction, in the given block -void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate); -} + return false; // identical blocks + } +}; -// notify wallets about a new best chain -void static SetBestChain(const CBlockLocator& loc) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->SetBestChain(loc); -} +CBlockIndex *pindexBestInvalid; +set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed -// notify wallets about an updated transaction -void static UpdatedTransaction(const uint256& hashTx) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->UpdatedTransaction(hashTx); +CCriticalSection cs_LastBlockFile; +CBlockFileInfo infoLastBlockFile; +int nLastBlockFile = 0; } -// dump all wallets -void static PrintWallets(const CBlock& block) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->PrintWallet(block); -} +////////////////////////////////////////////////////////////////////////////// +// +// dispatching functions +// -// notify wallets about an incoming inventory (for request counts) -void static Inventory(const uint256& hash) -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->Inventory(hash); -} +// These functions dispatch to one or all registered wallets -// ask wallets to resend their transactions -void static ResendWalletTransactions() -{ - LOCK(cs_setpwalletRegistered); - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->ResendWalletTransactions(); +namespace { +struct CMainSignals { + // Notifies listeners of updated transaction data (passing hash, transaction, and optionally the block it is found in. + boost::signals2::signal<void (const uint256 &, const CTransaction &, const CBlock *)> SyncTransaction; + // Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). + boost::signals2::signal<void (const uint256 &)> EraseTransaction; + // Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). + boost::signals2::signal<void (const uint256 &)> UpdatedTransaction; + // Notifies listeners of a new active block chain. + boost::signals2::signal<void (const CBlockLocator &)> SetBestChain; + // Notifies listeners about an inventory item being seen on the network. + boost::signals2::signal<void (const uint256 &)> Inventory; + // Tells listeners to broadcast their data. + boost::signals2::signal<void ()> Broadcast; +} g_signals; +} + +void RegisterWallet(CWalletInterface* pwalletIn) { + g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3)); + g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); + g_signals.UpdatedTransaction.connect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); + g_signals.SetBestChain.connect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); + g_signals.Inventory.connect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1)); + g_signals.Broadcast.connect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn)); +} + +void UnregisterWallet(CWalletInterface* pwalletIn) { + g_signals.Broadcast.disconnect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn)); + g_signals.Inventory.disconnect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1)); + g_signals.SetBestChain.disconnect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); + g_signals.UpdatedTransaction.disconnect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); + g_signals.EraseTransaction.disconnect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); + g_signals.SyncTransaction.disconnect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3)); +} + +void UnregisterAllWallets() { + g_signals.Broadcast.disconnect_all_slots(); + g_signals.Inventory.disconnect_all_slots(); + g_signals.SetBestChain.disconnect_all_slots(); + g_signals.UpdatedTransaction.disconnect_all_slots(); + g_signals.EraseTransaction.disconnect_all_slots(); + g_signals.SyncTransaction.disconnect_all_slots(); +} + +void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) { + g_signals.SyncTransaction(hash, tx, pblock); } ////////////////////////////////////////////////////////////////////////////// @@ -176,219 +150,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; -} - -////////////////////////////////////////////////////////////////////////////// -// -// CCoinsView implementations -// - -bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } -bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } -bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } -CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } -bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } -bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; } -bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } - - -CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } -bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } -bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } -bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } -CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } -bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } -void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } -bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } -bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } - -CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } - -bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { - if (cacheCoins.count(txid)) { - coins = cacheCoins[txid]; - return true; - } - if (base->GetCoins(txid, coins)) { - cacheCoins[txid] = coins; - return true; - } - return false; -} - -std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { - std::map<uint256,CCoins>::iterator it = cacheCoins.lower_bound(txid); - if (it != cacheCoins.end() && it->first == txid) - return it; - CCoins tmp; - if (!base->GetCoins(txid,tmp)) - return cacheCoins.end(); - std::map<uint256,CCoins>::iterator ret = cacheCoins.insert(it, std::make_pair(txid, CCoins())); - tmp.swap(ret->second); - return ret; -} - -CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { - std::map<uint256,CCoins>::iterator it = FetchCoins(txid); - assert(it != cacheCoins.end()); - return it->second; -} - -bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { - cacheCoins[txid] = coins; - return true; -} - -bool CCoinsViewCache::HaveCoins(const uint256 &txid) { - return FetchCoins(txid) != cacheCoins.end(); -} - -CBlockIndex *CCoinsViewCache::GetBestBlock() { - if (pindexTip == NULL) - pindexTip = base->GetBestBlock(); - return pindexTip; -} - -bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) { - pindexTip = pindex; - return true; -} - -bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { - for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) - cacheCoins[it->first] = it->second; - pindexTip = pindex; - return true; -} - -bool CCoinsViewCache::Flush() { - bool fOk = base->BatchWrite(cacheCoins, pindexTip); - if (fOk) - cacheCoins.clear(); - return fOk; -} - -unsigned int CCoinsViewCache::GetCacheSize() { - return cacheCoins.size(); -} - -/** CCoinsView that brings transactions from a memorypool into view. - It does not check for spendings by memory pool transactions. */ -CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } - -bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { - if (base->GetCoins(txid, coins)) - return true; - if (mempool.exists(txid)) { - const CTransaction &tx = mempool.lookup(txid); - coins = CCoins(tx, MEMPOOL_HEIGHT); - return true; - } - return false; -} - -bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { - return mempool.exists(txid) || base->HaveCoins(txid); + return Genesis(); } CCoinsViewCache *pcoinsTip = NULL; @@ -415,7 +253,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 +261,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 +304,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; } @@ -500,30 +338,41 @@ bool IsStandardTx(const CTransaction& tx, string& reason) return false; } } + + unsigned int nDataOut = 0; + txnouttype whichType; BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey)) { + if (!::IsStandard(txout.scriptPubKey, whichType)) { reason = "scriptpubkey"; return false; } - if (txout.IsDust(CTransaction::nMinRelayTxFee)) { + if (whichType == TX_NULL_DATA) + nDataOut++; + else if (txout.IsDust(CTransaction::nMinRelayTxFee)) { reason = "dust"; return false; } } + // only one OP_RETURN txout is permitted + if (nDataOut > 1) { + reason = "mucho-data"; + return false; + } + return true; } -bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64 nBlockTime) +bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { // Time based nLockTime implemented in 0.1.6 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)) + if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, tx.vin) if (!txin.IsFinal()) @@ -534,9 +383,9 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64 nBlockTime) /** Amount of bitcoins spent by the transaction. @return sum of all outputs (note: does not include fees) */ -int64 GetValueOut(const CTransaction& tx) +int64_t GetValueOut(const CTransaction& tx) { - int64 nValueOut = 0; + int64_t nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { nValueOut += txout.nValue; @@ -647,7 +496,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 +517,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 +530,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; } @@ -697,24 +546,30 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context if (tx.vin.empty()) - return state.DoS(10, error("CheckTransaction() : vin empty")); + return state.DoS(10, error("CheckTransaction() : vin empty"), + REJECT_INVALID, "vin empty"); if (tx.vout.empty()) - return state.DoS(10, error("CheckTransaction() : vout empty")); + return state.DoS(10, error("CheckTransaction() : vout empty"), + REJECT_INVALID, "vout empty"); // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); + return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed"), + REJECT_INVALID, "oversize"); // Check for negative or overflow output values - int64 nValueOut = 0; + int64_t nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (txout.nValue < 0) - return state.DoS(100, error("CheckTransaction() : txout.nValue negative")); + return state.DoS(100, error("CheckTransaction() : txout.nValue negative"), + REJECT_INVALID, "vout negative"); if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CheckTransaction() : txout.nValue too high")); + return state.DoS(100, error("CheckTransaction() : txout.nValue too high"), + REJECT_INVALID, "vout too large"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"), + REJECT_INVALID, "txout total too large"); } // Check for duplicate inputs @@ -722,46 +577,52 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); + return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs"), + REJECT_INVALID, "duplicate inputs"); vInOutPoints.insert(txin.prevout); } if (tx.IsCoinBase()) { if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CheckTransaction() : coinbase script size")); + return state.DoS(100, error("CheckTransaction() : coinbase script size"), + REJECT_INVALID, "coinbase script too large"); } else { BOOST_FOREACH(const CTxIn& txin, tx.vin) if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction() : prevout is null")); + return state.DoS(10, error("CheckTransaction() : prevout is null"), + REJECT_INVALID, "prevout null"); } return true; } -int64 GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mode) +int64_t GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mode) { // Base fee is either nMinTxFee or nMinRelayTxFee - int64 nBaseFee = (mode == GMF_RELAY) ? tx.nMinRelayTxFee : tx.nMinTxFee; + int64_t nBaseFee = (mode == GMF_RELAY) ? tx.nMinRelayTxFee : tx.nMinTxFee; unsigned int nBytes = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; + int64_t nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; if (fAllowFree) { // There is a free transaction area in blocks created by most miners, // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 - // to be considered to fall into this category - // * If we are creating a transaction we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 17000 - // (= 10000) to be considered safe and assume they can likely make it into this section - if (nBytes < (mode == GMF_SEND ? (DEFAULT_BLOCK_PRIORITY_SIZE - 17000) : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))) + // to be considered to fall into this category. We don't want to encourage sending + // multiple transactions instead of one big transaction to avoid fees. + // * If we are creating a transaction we allow transactions up to 1,000 bytes + // to be considered safe and assume they can likely make it into this section. + if (nBytes < (mode == GMF_SEND ? 1000 : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))) nMinFee = 0; } - // To limit dust spam, require base fee if any output is less than 0.01 - if (nMinFee < nBaseFee) + // This code can be removed after enough miners have upgraded to version 0.9. + // Until then, be safe when sending and require a fee if any output + // is less than CENT: + if (nMinFee < nBaseFee && mode == GMF_SEND) { BOOST_FOREACH(const CTxOut& txout, tx.vout) if (txout.nValue < CENT) @@ -773,56 +634,41 @@ int64 GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mod return nMinFee; } -void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) -{ - LOCK(cs); - - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); - // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx - while (it != mapNextTx.end() && it->first.hash == hashTx) { - coins.Spend(it->first.n); // and remove those outputs from coins - it++; - } -} - -bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs) +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, + bool* pfMissingInputs, bool fRejectInsaneFee) { if (pfMissingInputs) *pfMissingInputs = false; if (!CheckTransaction(tx, state)) - return error("CTxMemPool::accept() : CheckTransaction failed"); + return error("AcceptToMemoryPool: : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); - - // To help v0.1.5 clients who would see it as a negative number - if ((int64)tx.nLockTime > std::numeric_limits<int>::max()) - return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); + return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx"), + REJECT_INVALID, "coinbase"); // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason)) - return error("CTxMemPool::accept() : nonstandard transaction: %s", - reason.c_str()); + return state.DoS(0, + error("AcceptToMemoryPool : nonstandard transaction: %s", reason.c_str()), + REJECT_NONSTANDARD, reason); // is it already in the memory pool? uint256 hash = tx.GetHash(); - { - LOCK(cs); - if (mapTx.count(hash)) - return false; - } + if (pool.exists(hash)) + return false; // Check for conflicts with in-memory transactions CTransaction* ptxOld = NULL; + { + LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; - if (mapNextTx.count(outpoint)) + if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now return false; @@ -830,7 +676,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // Allow replacing with a newer version of the same transaction if (i != 0) return false; - ptxOld = mapNextTx[outpoint].ptx; + ptxOld = pool.mapNextTx[outpoint].ptx; if (IsFinalTx(*ptxOld)) return false; if (!tx.IsNewerThan(*ptxOld)) @@ -838,20 +684,21 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; - if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) + if (!pool.mapNextTx.count(outpoint) || pool.mapNextTx[outpoint].ptx != ptxOld) return false; } break; } } + } { CCoinsView dummy; CCoinsViewCache view(dummy); { - LOCK(cs); - CCoinsViewMemPool viewMemPool(*pcoinsTip, *this); + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(*pcoinsTip, pool); view.SetBackend(viewMemPool); // do we already have it? @@ -871,7 +718,8 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // are the actual inputs available? if (!view.HaveInputs(tx)) - return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); + return state.Invalid(error("AcceptToMemoryPool : inputs already spent"), + REJECT_DUPLICATE, "inputs spent"); // Bring the best block into scope view.GetBestBlock(); @@ -882,32 +730,33 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // Check for non-standard pay-to-script-hash in inputs if (Params().NetworkID() == CChainParams::MAIN && !AreInputsStandard(tx, view)) - return error("CTxMemPool::accept() : nonstandard transaction input"); + return error("AcceptToMemoryPool: : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. - int64 nFees = view.GetValueIn(tx)-GetValueOut(tx); + int64_t nFees = view.GetValueIn(tx)-GetValueOut(tx); unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block - int64 txMinFee = GetMinFee(tx, true, GMF_RELAY); + int64_t txMinFee = GetMinFee(tx, true, GMF_RELAY); if (fLimitFree && nFees < txMinFee) - return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, - hash.ToString().c_str(), - nFees, txMinFee); + return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %"PRId64" < %"PRId64, + hash.ToString().c_str(), nFees, txMinFee), + REJECT_INSUFFICIENTFEE, "insufficient fee"); // Continuously rate-limit free transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. if (fLimitFree && nFees < CTransaction::nMinRelayTxFee) { + static CCriticalSection csFreeLimiter; static double dFreeCount; - static int64 nLastTime; - int64 nNow = GetTime(); + static int64_t nLastTime; + int64_t nNow = GetTime(); - LOCK(cs); + LOCK(csFreeLimiter); // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); @@ -915,157 +764,48 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); - if (fDebug) - printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), + REJECT_INSUFFICIENTFEE, "insufficient priority"); + LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } + if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000) + return error("AcceptToMemoryPool: : insane fees %s, %"PRId64" > %"PRId64, + hash.ToString().c_str(), + nFees, CTransaction::nMinRelayTxFee * 10000); + // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) { - return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str()); + return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString().c_str()); } } // Store transaction in memory { - LOCK(cs); if (ptxOld) { - printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); - remove(*ptxOld); + LogPrint("mempool", "AcceptToMemoryPool: : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); + pool.remove(*ptxOld); } - addUnchecked(hash, tx); + pool.addUnchecked(hash, tx); } ///// are we sure this is ok when loading transactions or restoring block txes // If updated, erase old tx from wallet if (ptxOld) - EraseFromWallets(ptxOld->GetHash()); - SyncWithWallets(hash, tx, NULL, true); - - printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", - hash.ToString().c_str(), - mapTx.size()); - return true; -} - + g_signals.EraseTransaction(ptxOld->GetHash()); + g_signals.SyncTransaction(hash, tx, NULL); -bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx) -{ - // Add to memory pool without checking anything. Don't call this directly, - // call CTxMemPool::accept to properly check the transaction first. - { - mapTx[hash] = tx; - for (unsigned int i = 0; i < tx.vin.size(); i++) - mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); - nTransactionsUpdated++; - } + LogPrint("mempool", "AcceptToMemoryPool: : accepted %s (poolsz %"PRIszu")\n", + hash.ToString().c_str(), + pool.mapTx.size()); return true; } -bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) -{ - // Remove transaction from memory pool - { - LOCK(cs); - uint256 hash = tx.GetHash(); - if (fRecursive) { - for (unsigned int i = 0; i < tx.vout.size(); i++) { - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i)); - if (it != mapNextTx.end()) - remove(*it->second.ptx, true); - } - } - if (mapTx.count(hash)) - { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapNextTx.erase(txin.prevout); - mapTx.erase(hash); - nTransactionsUpdated++; - } - } - return true; -} - -bool CTxMemPool::removeConflicts(const CTransaction &tx) -{ - // Remove transactions which depend on inputs of tx, recursively - LOCK(cs); - BOOST_FOREACH(const CTxIn &txin, tx.vin) { - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); - if (it != mapNextTx.end()) { - const CTransaction &txConflict = *it->second.ptx; - if (txConflict != tx) - remove(txConflict, true); - } - } - return true; -} - -void CTxMemPool::clear() -{ - LOCK(cs); - mapTx.clear(); - mapNextTx.clear(); - ++nTransactionsUpdated; -} - -bool CTxMemPool::fChecks = false; - -void CTxMemPool::check(CCoinsViewCache *pcoins) const -{ - if (!fChecks) - return; - - printf("Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); - - LOCK(cs); - for (std::map<uint256, CTransaction>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { - unsigned int i = 0; - BOOST_FOREACH(const CTxIn &txin, it->second.vin) { - // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. - std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(txin.prevout.hash); - if (it2 != mapTx.end()) { - assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull()); - } else { - CCoins &coins = pcoins->GetCoins(txin.prevout.hash); - assert(coins.IsAvailable(txin.prevout.n)); - } - // Check whether its inputs are marked in mapNextTx. - std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout); - assert(it3 != mapNextTx.end()); - assert(it3->second.ptx == &it->second); - assert(it3->second.n == i); - i++; - } - } - for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { - uint256 hash = it->second.ptx->GetHash(); - std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(hash); - assert(it2 != mapTx.end()); - assert(&it2->second == it->second.ptx); - assert(it2->second.vin.size() > it->second.n); - assert(it->first == it->second.ptx->vin[it->second.n].prevout); - } -} - -void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) -{ - vtxid.clear(); - - LOCK(cs); - vtxid.reserve(mapTx.size()); - for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) - vtxid.push_back((*mi).first); -} - - - - int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const { if (hashBlock == 0 || nIndex == -1) @@ -1076,7 +816,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 +828,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const } pindexRet = pindex; - return pindexBest->nHeight - pindex->nHeight + 1; + return chainActive.Height() - pindex->nHeight + 1; } @@ -1096,35 +836,14 @@ int CMerkleTx::GetBlocksToMaturity() const { if (!IsCoinBase()) return 0; - return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); + return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); } bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree) { CValidationState state; - return mempool.accept(state, *this, fLimitFree, NULL); -} - - - -bool CWalletTx::AcceptWalletTransaction() -{ - { - LOCK(mempool.cs); - // Add previous supporting transactions first - BOOST_FOREACH(CMerkleTx& tx, vtxPrev) - { - if (!tx.IsCoinBase()) - { - uint256 hash = tx.GetHash(); - if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) - tx.AcceptToMemoryPool(false); - } - } - return AcceptToMemoryPool(false); - } - return false; + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL); } @@ -1135,10 +854,8 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock { LOCK(cs_main); { - LOCK(mempool.cs); - if (mempool.exists(hash)) + if (mempool.lookup(hash, txOut)) { - txOut = mempool.lookup(hash); return true; } } @@ -1171,7 +888,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock nHeight = coins.nHeight; } if (nHeight > 0) - pindexSlow = FindBlockByHeight(nHeight); + pindexSlow = chainActive[nHeight]; } } @@ -1201,14 +918,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 @@ -1276,9 +985,9 @@ uint256 static GetOrphanRoot(const CBlockHeader* pblock) return pblock->GetHash(); } -int64 GetBlockValue(int nHeight, int64 nFees) +int64_t GetBlockValue(int nHeight, int64_t nFees) { - int64 nSubsidy = 50 * COIN; + int64_t nSubsidy = 50 * COIN; // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years. nSubsidy >>= (nHeight / Params().SubsidyHalvingInterval()); @@ -1286,15 +995,15 @@ int64 GetBlockValue(int nHeight, int64 nFees) return nSubsidy + nFees; } -static const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks -static const int64 nTargetSpacing = 10 * 60; -static const int64 nInterval = nTargetTimespan / nTargetSpacing; +static const int64_t nTargetTimespan = 14 * 24 * 60 * 60; // two weeks +static const int64_t nTargetSpacing = 10 * 60; +static const int64_t nInterval = nTargetTimespan / nTargetSpacing; // // minimum amount of work that could possibly be required nTime after // minimum work required was nBase // -unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) +unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime) { const CBigNum &bnLimit = Params().ProofOfWorkLimit(); // Testnet has min-difficulty blocks @@ -1353,8 +1062,8 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead assert(pindexFirst); // Limit adjustment step - int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); - printf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan); + int64_t nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); + LogPrintf(" nActualTimespan = %"PRId64" before bounds\n", nActualTimespan); if (nActualTimespan < nTargetTimespan/4) nActualTimespan = nTargetTimespan/4; if (nActualTimespan > nTargetTimespan*4) @@ -1370,10 +1079,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 = %"PRId64" nActualTimespan = %"PRId64"\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 +1111,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 int64_t 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 +1130,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 || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (chainActive.Tip()->GetBlockWork() * 6).getuint256())) { if (!fLargeWorkForkFound) { @@ -1441,14 +1155,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 +1177,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 +1196,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; @@ -1493,19 +1207,22 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) void static InvalidChainFound(CBlockIndex* pindexNew) { - if (pindexNew->nChainWork > nBestInvalidWork) + if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) { - nBestInvalidWork = pindexNew->nChainWork; - pblocktree->WriteBestInvalidWork(CBigNum(nBestInvalidWork)); + pindexBestInvalid = pindexNew; + // The current code doesn't actually read the BestInvalidWork entry in + // the block database anymore, as it is derived from the flags in block + // index entry. We only write it for backward compatibility. + pblocktree->WriteBestInvalidWork(CBigNum(pindexBestInvalid->nChainWork)); 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 +1231,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 +1248,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 +1268,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(); @@ -1591,25 +1308,6 @@ void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) -const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) -{ - const CCoins &coins = GetCoins(input.prevout.hash); - assert(coins.IsAvailable(input.prevout.n)); - return coins.vout[input.prevout.n]; -} - -int64 CCoinsViewCache::GetValueIn(const CTransaction& tx) -{ - if (tx.IsCoinBase()) - return 0; - - int64 nResult = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) - nResult += GetOutputFor(tx.vin[i]).nValue; - - return nResult; -} - void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) { // mark inputs spent @@ -1626,27 +1324,6 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach assert(inputs.SetCoins(txhash, CCoins(tx, nHeight))); } -bool CCoinsViewCache::HaveInputs(const CTransaction& tx) -{ - if (!tx.IsCoinBase()) { - // first check whether information about the prevout hash is available - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = tx.vin[i].prevout; - if (!HaveCoins(prevout.hash)) - return false; - } - - // then check whether the actual outputs are available - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = tx.vin[i].prevout; - const CCoins &coins = GetCoins(prevout.hash); - if (!coins.IsAvailable(prevout.n)) - return false; - } - } - return true; -} - bool CScriptCheck::operator()() const { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType)) @@ -1673,9 +1350,10 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. - int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; - int64 nValueIn = 0; - int64 nFees = 0; + CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + int nSpendHeight = pindexPrev->nHeight + 1; + int64_t nValueIn = 0; + int64_t nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { const COutPoint &prevout = tx.vin[i].prevout; @@ -1684,26 +1362,32 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach // If prev is coinbase, check that it's matured if (coins.IsCoinBase()) { if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) - return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight)); + return state.Invalid( + error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight), + REJECT_INVALID, "premature spend of coinbase"); } // Check for negative or overflow input values nValueIn += coins.vout[prevout.n].nValue; if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, error("CheckInputs() : txin values out of range")); + return state.DoS(100, error("CheckInputs() : txin values out of range"), + REJECT_INVALID, "input values out of range"); } if (nValueIn < GetValueOut(tx)) - return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str())); + return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()), + REJECT_INVALID, "in < out"); // Tally transaction fees - int64 nTxFee = nValueIn - GetValueOut(tx); + int64_t nTxFee = nValueIn - GetValueOut(tx); if (nTxFee < 0) - return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str())); + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()), + REJECT_INVALID, "fee < 0"); nFees += nTxFee; if (!MoneyRange(nFees)) - return state.DoS(100, error("CheckInputs() : nFees out of range")); + return state.DoS(100, error("CheckInputs() : nFees out of range"), + REJECT_INVALID, "fee out of range"); // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. @@ -1728,9 +1412,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach // encodings or not; if so, don't trigger DoS protection. CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); if (check()) - return state.Invalid(); + return state.Invalid(false, REJECT_NONSTANDARD, "non-canonical"); } - return state.DoS(100,false); + return state.DoS(100,false, REJECT_NONSTANDARD, "non-canonical"); } } } @@ -1743,7 +1427,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { - assert(pindex == view.GetBestBlock()); + assert(pindex->GetBlockHash() == view.GetBestBlock()); if (pfClean) *pfClean = false; @@ -1765,14 +1449,20 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex const CTransaction &tx = block.vtx[i]; uint256 hash = tx.GetHash(); - // check that all outputs are available - if (!view.HaveCoins(hash)) { - fClean = fClean && error("DisconnectBlock() : outputs still spent? database corrupted"); - view.SetCoins(hash, CCoins()); - } - CCoins &outs = view.GetCoins(hash); + // Check that all outputs are available and match the outputs in the block itself + // exactly. Note that transactions with only provably unspendable outputs won't + // have outputs available even in the block itself, so we handle that case + // specially with outsEmpty. + CCoins outsEmpty; + CCoins &outs = view.HaveCoins(hash) ? view.GetCoins(hash) : outsEmpty; + outs.ClearUnspendable(); CCoins outsBlock = CCoins(tx, pindex->nHeight); + // The CCoins serialization does not serialize negative numbers. + // No network rules currently depend on the version here, so an inconsistency is harmless + // but it must be corrected before txout nversion ever influences a network rule. + if (outsBlock.nVersion < 0) + outs.nVersion = outsBlock.nVersion; if (outs != outsBlock) fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); @@ -1813,7 +1503,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } // move best block pointer to prevout block - view.SetBestBlock(pindex->pprev); + view.SetBestBlock(pindex->pprev->GetBlockHash()); if (pfClean) { *pfClean = fClean; @@ -1862,13 +1552,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return false; // verify that the view's current state corresponds to the previous block - assert(pindex->pprev == view.GetBestBlock()); + uint256 hashPrevBlock = pindex->pprev == NULL ? uint256(0) : pindex->pprev->GetBlockHash(); + assert(hashPrevBlock == view.GetBestBlock()); // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) if (block.GetHash() == Params().HashGenesisBlock()) { - view.SetBestBlock(pindex); - pindexGenesisBlock = pindex; + view.SetBestBlock(pindex->GetBlockHash()); return true; } @@ -1893,12 +1583,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C for (unsigned int i = 0; i < block.vtx.size(); i++) { uint256 hash = block.GetTxHash(i); if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) - return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction")); + return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"), + REJECT_INVALID, "BIP30"); } } // BIP16 didn't become active until Apr 1 2012 - int64 nBIP16SwitchTime = 1333238400; + int64_t nBIP16SwitchTime = 1333238400; bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime); unsigned int flags = SCRIPT_VERIFY_NOCACHE | @@ -1908,8 +1599,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); - int64 nStart = GetTimeMicros(); - int64 nFees = 0; + int64_t nStart = GetTimeMicros(); + int64_t nFees = 0; int nInputs = 0; unsigned int nSigOps = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); @@ -1922,12 +1613,14 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops"), + REJECT_INVALID, "too many sigops"); if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) - return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); + return state.DoS(100, error("ConnectBlock() : inputs missing/spent"), + REJECT_INVALID, "inputs missing/spent"); if (fStrictPayToScriptHash) { @@ -1936,7 +1629,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C // an incredibly-expensive-to-validate block. nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops"), + REJECT_INVALID, "too many sigops"); } nFees += view.GetValueIn(tx)-GetValueOut(tx); @@ -1955,18 +1649,21 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C vPos.push_back(std::make_pair(block.GetTxHash(i), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } - int64 nTime = GetTimeMicros() - nStart; + int64_t 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))); + return state.DoS(100, + error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", + GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)), + REJECT_INVALID, "coinbase too large"); if (!control.Wait()) return state.DoS(100, false); - int64 nTime2 = GetTimeMicros() - nStart; + int64_t 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; @@ -1998,11 +1695,11 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return state.Abort(_("Failed to write transaction index")); // add this block to the view's block chain - assert(view.SetBestBlock(pindex)); + assert(view.SetBestBlock(pindex->GetBlockHash())); // Watch for transactions paying to me for (unsigned int i = 0; i < block.vtx.size(); i++) - SyncWithWallets(block.GetTxHash(i), block.vtx[i], &block, true); + g_signals.SyncTransaction(block.GetTxHash(i), block.vtx[i], &block); return true; } @@ -2016,7 +1713,9 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) CCoinsViewCache view(*pcoinsTip, true); // Find the fork (typically, there is none) - CBlockIndex* pfork = view.GetBestBlock(); + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(view.GetBestBlock()); + CBlockIndex* ptip = (it != mapBlockIndex.end()) ? it->second : NULL; + CBlockIndex* pfork = ptip; CBlockIndex* plonger = pindexNew; while (pfork && pfork != plonger) { @@ -2032,7 +1731,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // List of what to disconnect (typically nothing) vector<CBlockIndex*> vDisconnect; - for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev) + for (CBlockIndex* pindex = ptip; pindex != pfork; pindex = pindex->pprev) vDisconnect.push_back(pindex); // List of what to connect (typically only pindexNew) @@ -2042,8 +1741,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 @@ -2052,11 +1751,11 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) CBlock block; if (!ReadBlockFromDisk(block, pindex)) return state.Abort(_("Failed to read block")); - int64 nStart = GetTimeMicros(); + int64_t nStart = GetTimeMicros(); 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 @@ -2072,7 +1771,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) CBlock block; if (!ReadBlockFromDisk(block, pindex)) return state.Abort(_("Failed to read block")); - int64 nStart = GetTimeMicros(); + int64_t nStart = GetTimeMicros(); if (!ConnectBlock(block, state, pindex, view)) { if (state.IsInvalid()) { InvalidChainFound(pindexNew); @@ -2081,7 +1780,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) @@ -2089,12 +1788,12 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) } // Flush changes to global coin state - int64 nStart = GetTimeMicros(); + int64_t nStart = GetTimeMicros(); int nModified = view.GetCacheSize(); assert(view.Flush()); - int64 nTime = GetTimeMicros() - nStart; + int64_t 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,15 +1815,13 @@ 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) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - if (!mempool.accept(stateDummy, tx, false, NULL)) + if (!AcceptToMemoryPool(mempool,stateDummy, tx, false, NULL)) mempool.remove(tx, true); } @@ -2138,29 +1835,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); - } + g_signals.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)); + mempool.AddTransactionsUpdated(1); + 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 +1857,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 +1867,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,13 +1909,13 @@ 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(); // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; - UpdatedTransaction(hashPrevBestCoinBase); + g_signals.UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = block.GetTxHash(0); } else CheckForkWarningConditionsOnNewFork(pindexNew); @@ -2239,7 +1928,7 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos } -bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) +bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) { bool fUpdatedLast = false; @@ -2254,7 +1943,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 +1964,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 +2010,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); } @@ -2341,22 +2030,27 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckBlock() : size limits failed")); + return state.DoS(100, error("CheckBlock() : size limits failed"), + REJECT_INVALID, "block size too large"); // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits)) - return state.DoS(50, error("CheckBlock() : proof of work failed")); + return state.DoS(50, error("CheckBlock() : proof of work failed"), + REJECT_INVALID, "invalid pow"); // Check timestamp if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); + return state.Invalid(error("CheckBlock() : block timestamp too far in the future"), + REJECT_INVALID, "time in future"); // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); + return state.DoS(100, error("CheckBlock() : first tx is not coinbase"), + REJECT_INVALID, "no coinbase"); for (unsigned int i = 1; i < block.vtx.size(); i++) if (block.vtx[i].IsCoinBase()) - return state.DoS(100, error("CheckBlock() : more than one coinbase")); + return state.DoS(100, error("CheckBlock() : more than one coinbase"), + REJECT_INVALID, "duplicate coinbase"); // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2375,7 +2069,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo uniqueTx.insert(block.GetTxHash(i)); } if (uniqueTx.size() != block.vtx.size()) - return state.DoS(100, error("CheckBlock() : duplicate transaction")); + return state.DoS(100, error("CheckBlock() : duplicate transaction"), + REJECT_INVALID, "duplicate transaction", true); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2383,11 +2078,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo nSigOps += GetLegacySigOpCount(tx); } if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); + return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"), + REJECT_INVALID, "sig op count", true); // Check merkle root if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back()) - return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); + return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), + REJECT_INVALID, "bad merkle root", true); return true; } @@ -2411,20 +2108,24 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block)) - return state.DoS(100, error("AcceptBlock() : incorrect proof of work")); + return state.DoS(100, error("AcceptBlock() : incorrect proof of work"), + REJECT_INVALID, "bad pow"); // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(error("AcceptBlock() : block's timestamp is too early")); + return state.Invalid(error("AcceptBlock() : block's timestamp is too early"), + REJECT_INVALID, "timestamp too early"); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) - return state.DoS(10, error("AcceptBlock() : contains a non-final transaction")); + return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"), + REJECT_INVALID, "non-final tx"); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(nHeight, hash)) - return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); + return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight), + REJECT_CHECKPOINT, "checkpoint mismatch"); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (block.nVersion < 2) @@ -2432,7 +2133,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) { - return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); + return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"), + REJECT_OBSOLETE, "version 1 blocks obsolete"); } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height @@ -2445,7 +2147,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) - return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), + REJECT_INVALID, "height incorrect in coinbase"); } } } @@ -2469,11 +2172,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 +2195,18 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } +int64_t 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 +2215,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,13 +2232,14 @@ 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; + int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; if (deltaTime < 0) { - return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint")); + return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"), + REJECT_CHECKPOINT, "timestamp before checkpoint"); } CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); @@ -2531,7 +2247,8 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); if (bnNewBlock > bnRequired) { - return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work")); + return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"), + REJECT_INVALID, "invalid pow"); } } @@ -2539,7 +2256,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 +2265,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 +2295,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl mapOrphanBlocksByPrev.erase(hashPrev); } - printf("ProcessBlock: ACCEPTED\n"); + LogPrintf("ProcessBlock: ACCEPTED\n"); return true; } @@ -2744,15 +2461,15 @@ 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; } -bool CheckDiskSpace(uint64 nAdditionalBytes) +bool CheckDiskSpace(uint64_t nAdditionalBytes) { - uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; + uint64_t nFreeBytesAvailable = filesystem::space(GetDataDir()).available; // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) @@ -2761,10 +2478,6 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) return true; } -CCriticalSection cs_LastBlockFile; -CBlockFileInfo infoLastBlockFile; -int nLastBlockFile = 0; - FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) { if (pos.IsNull()) @@ -2775,12 +2488,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; } @@ -2839,18 +2552,15 @@ bool static LoadBlockIndexDB() pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK)) setBlockIndexValid.insert(pindex); + if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) + pindexBestInvalid = pindex; } // 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()); - - // Load nBestInvalidWork, OK if it doesn't exist - CBigNum bnBestInvalidWork; - pblocktree->ReadBestInvalidWork(bnBestInvalidWork); - nBestInvalidWork = bnBestInvalidWork.getuint256(); + LogPrintf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString().c_str()); // Check whether we need to continue reindexing bool fReindexing = false; @@ -2859,51 +2569,41 @@ 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) + // Load pointer to end of best chain + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + if (it == mapBlockIndex.end()) 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()); + chainActive.SetTip(it->second); + 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 +2635,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 +2651,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 +2660,8 @@ void UnloadBlockIndex() { mapBlockIndex.clear(); setBlockIndexValid.clear(); - pindexGenesisBlock = NULL; - nBestHeight = 0; - nBestChainWork = 0; - nBestInvalidWork = 0; - hashBestChain = 0; - pindexBest = NULL; + chainActive.SetTip(NULL); + pindexBestInvalid = NULL; } bool LoadBlockIndex() @@ -2979,13 +2675,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 +2721,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,37 +2734,35 @@ 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(), block.vtx.size()); - PrintWallets(block); - // put the main time-chain first 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; @@ -3083,12 +2777,12 @@ void PrintBlockTree() bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) { - int64 nStart = GetTimeMillis(); + int64_t nStart = GetTimeMillis(); int nLoaded = 0; try { CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); - uint64 nStartByte = 0; + uint64_t nStartByte = 0; if (dbp) { // (try to) skip already indexed part CBlockFileInfo info; @@ -3097,7 +2791,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) blkdat.Seek(info.nSize); } } - uint64 nRewind = blkdat.GetPos(); + uint64_t nRewind = blkdat.GetPos(); while (blkdat.good() && !blkdat.eof()) { boost::this_thread::interruption_point(); @@ -3123,7 +2817,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } try { // read block - uint64 nBlockPos = blkdat.GetPos(); + uint64_t nBlockPos = blkdat.GetPos(); blkdat.SetLimit(nBlockPos + nSize); CBlock block; blkdat >> block; @@ -3141,7 +2835,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 +2843,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 %"PRId64"ms\n", nLoaded, GetTimeMillis() - nStart); return nLoaded > 0; } @@ -3167,9 +2861,6 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) // CAlert // -extern map<uint256, CAlert> mapAlerts; -extern CCriticalSection cs_mapAlerts; - string GetWarnings(string strFor) { int nPriority = 0; @@ -3242,10 +2933,7 @@ bool static AlreadyHave(const CInv& inv) case MSG_TX: { bool txInMap = false; - { - LOCK(mempool.cs); - txInMap = mempool.exists(inv.hash); - } + txInMap = mempool.exists(inv.hash); return txInMap || mapOrphanTransactions.count(inv.hash) || pcoinsTip->HaveCoins(inv.hash); } @@ -3259,13 +2947,14 @@ bool static AlreadyHave(const CInv& inv) - void static ProcessGetData(CNode* pfrom) { std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); vector<CInv> vNotFound; + LOCK(cs_main); + while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) @@ -3315,7 +3004,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; } @@ -3334,9 +3023,8 @@ void static ProcessGetData(CNode* pfrom) } } if (!pushed && inv.type == MSG_TX) { - LOCK(mempool.cs); - if (mempool.exists(inv.hash)) { - CTransaction tx = mempool.lookup(inv.hash); + CTransaction tx; + if (mempool.lookup(inv.hash, tx)) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(1000); ss << tx; @@ -3350,7 +3038,10 @@ void static ProcessGetData(CNode* pfrom) } // Track requests for our stuff. - Inventory(inv.hash); + g_signals.Inventory(inv.hash); + + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + break; } } @@ -3371,11 +3062,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; } @@ -3388,20 +3078,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Each connection can only send one version message if (pfrom->nVersion != 0) { + pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); pfrom->Misbehaving(1); return false; } - int64 nTime; + int64_t nTime; CAddress addrMe; CAddress addrFrom; - uint64 nNonce = 1; + uint64_t nNonce = 1; vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; - if (pfrom->nVersion < MIN_PROTO_VERSION) + if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) { - // 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); + // disconnect from peers older than this proto version + LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)); pfrom->fDisconnect = true; return false; } @@ -3428,7 +3120,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; } @@ -3439,7 +3131,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - AddTimeData(pfrom->addr, nTime); // Change version pfrom->PushMessage("verack"); @@ -3479,8 +3170,11 @@ 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()); + AddTimeData(pfrom->addr, nTime); + + LOCK(cs_main); cPeerBlockCounts.input(pfrom->nStartingHeight); } @@ -3515,8 +3209,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Store the new addresses vector<CAddress> vAddrOk; - int64 nNow = GetAdjustedTime(); - int64 nSince = nNow - 10 * 60; + int64_t nNow = GetAdjustedTime(); + int64_t nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) { boost::this_thread::interruption_point(); @@ -3535,7 +3229,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) static uint256 hashSalt; if (hashSalt == 0) hashSalt = GetRandHash(); - uint64 hashAddr = addr.GetHash(); + uint64_t hashAddr = addr.GetHash(); uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); multimap<uint256, CNode*> mapMix; @@ -3584,6 +3278,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) break; } } + + LOCK(cs_main); + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; @@ -3592,25 +3289,24 @@ 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 - Inventory(inv.hash); + g_signals.Inventory(inv.hash); } } @@ -3625,11 +3321,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return error("message getdata size() = %"PRIszu"", vInv.size()); } - if (fDebugNet || (vInv.size() != 1)) - printf("received getdata (%"PRIszu" invsz)\n", vInv.size()); + if (fDebug || (vInv.size() != 1)) + 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()); + if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) + LogPrint("net", "received getdata for: %s\n", vInv[0].ToString().c_str()); pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(pfrom); @@ -3642,19 +3338,21 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) uint256 hashStop; vRecv >> locator >> hashStop; + LOCK(cs_main); + // 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 +3360,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; } @@ -3676,6 +3374,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) uint256 hashStop; vRecv >> locator >> hashStop; + LOCK(cs_main); + CBlockIndex* pindex = NULL; if (locator.IsNull()) { @@ -3688,16 +3388,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) @@ -3711,16 +3411,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { vector<uint256> vWorkQueue; vector<uint256> vEraseQueue; - CDataStream vMsg(vRecv); CTransaction tx; vRecv >> tx; CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); + LOCK(cs_main); + bool fMissingInputs = false; CValidationState state; - if (mempool.accept(state, tx, true, &fMissingInputs)) + if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { mempool.check(pcoinsTip); RelayTransaction(tx, inv.hash); @@ -3744,9 +3445,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // anyone relaying LegitTxX banned) CValidationState stateDummy; - if (mempool.accept(stateDummy, orphanTx, true, &fMissingInputs2)) + if (AcceptToMemoryPool(mempool, 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 +3457,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 +3473,16 @@ 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); + { + pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + state.GetRejectReason(), inv.hash); + if (nDoS > 0) + pfrom->Misbehaving(nDoS); + } } @@ -3785,18 +3491,25 @@ 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()); pfrom->AddInventoryKnown(inv); + LOCK(cs_main); + CValidationState state; - if (ProcessBlock(state, pfrom, &block)) + if (ProcessBlock(state, pfrom, &block) || state.CorruptionPossible()) mapAlreadyAskedFor.erase(inv); - int nDoS; + int nDoS = 0; if (state.IsInvalid(nDoS)) - pfrom->Misbehaving(nDoS); + { + pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + state.GetRejectReason(), inv.hash); + if (nDoS > 0) + pfrom->Misbehaving(nDoS); + } } @@ -3811,17 +3524,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "mempool") { + LOCK2(cs_main, pfrom->cs_filter); + std::vector<uint256> vtxid; - LOCK2(mempool.cs, pfrom->cs_filter); mempool.queryHashes(vtxid); vector<CInv> vInv; BOOST_FOREACH(uint256& hash, vtxid) { CInv inv(MSG_TX, hash); - if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(mempool.lookup(hash), hash)) || + CTransaction tx; + bool fInMemPool = mempool.lookup(hash, tx); + if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... + if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx, hash)) || (!pfrom->pfilter)) vInv.push_back(inv); - if (vInv.size() == MAX_INV_SZ) - break; + if (vInv.size() == MAX_INV_SZ) { + pfrom->PushMessage("inv", vInv); + vInv.clear(); + } } if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); @@ -3832,7 +3551,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { if (pfrom->nVersion > BIP0031_VERSION) { - uint64 nonce = 0; + uint64_t nonce = 0; vRecv >> nonce; // Echo the message back with the nonce. This allows for two useful features: // @@ -3850,6 +3569,63 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + else if (strCommand == "pong") + { + int64_t pingUsecEnd = GetTimeMicros(); + uint64_t 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_t 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, %"PRIx64" expected, %"PRIx64" 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; @@ -3929,6 +3705,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + else if (strCommand == "reject") + { + if (fDebug) + { + string strMsg; unsigned char ccode; string strReason; + vRecv >> strMsg >> ccode >> strReason; + + ostringstream ss; + ss << strMsg << " code " << itostr(ccode) << ": " << strReason; + + if (strMsg == "block" || strMsg == "tx") + { + uint256 hash; + vRecv >> hash; + ss << ": hash " << hash.ToString(); + } + // Truncate to reasonable length and sanitize before printing: + string s = ss.str(); + if (s.size() > 111) s.erase(111, string::npos); + LogPrint("net", "Reject %s\n", SanitizeString(s).c_str()); + } + } + else { // Ignore unknown commands for extensibility @@ -3948,7 +3747,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 @@ -3962,7 +3761,10 @@ bool ProcessMessages(CNode* pfrom) if (!pfrom->vRecvGetData.empty()) ProcessGetData(pfrom); - + + // this maintains the order of responses + if (!pfrom->vRecvGetData.empty()) return fOk; + std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin(); while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway @@ -3973,7 +3775,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 +3788,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 +3797,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 +3812,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; } @@ -4019,23 +3821,21 @@ bool ProcessMessages(CNode* pfrom) bool fRet = false; try { - { - LOCK(cs_main); - fRet = ProcessMessage(pfrom, strCommand, vRecv); - } + fRet = ProcessMessage(pfrom, strCommand, vRecv); boost::this_thread::interruption_point(); } catch (std::ios_base::failure& e) { + pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message")); 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 +3852,9 @@ 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); + + break; } // In case the connection got shut down, its receive buffer was wiped @@ -4065,38 +3867,43 @@ bool ProcessMessages(CNode* pfrom) bool SendMessages(CNode* pto, bool fSendTrickle) { - TRY_LOCK(cs_main, lockMain); - if (lockMain) { + { // Don't send anything until we get their version message 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()) { - uint64 nonce = 0; - if (pto->nVersion > BIP0031_VERSION) + // Ping automatically sent as a keepalive + pingSend = true; + } + if (pingSend) { + uint64_t nonce = 0; + while (nonce == 0) { + RAND_bytes((unsigned char*)&nonce, sizeof(nonce)); + } + pto->nPingNonceSent = nonce; + pto->fPingQueued = false; + if (pto->nVersion > BIP0031_VERSION) { + // Take timestamp as close as possible before transmitting ping + pto->nPingUsecStart = GetTimeMicros(); pto->PushMessage("ping", nonce); - else + } 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)); - } - - // Resend wallet transactions that haven't gotten in a block yet - // Except during reindex, importing and IBD, when old wallet - // transactions become unconfirmed and spams other nodes. - if (!fReindex && !fImporting && !IsInitialBlockDownload()) - { - ResendWalletTransactions(); + } } // Address refresh broadcast - static int64 nLastRebroadcast; + static int64_t nLastRebroadcast; if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) { { @@ -4145,6 +3952,23 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("addr", vAddr); } + TRY_LOCK(cs_main, lockMain); + if (!lockMain) + return true; + + // Start block sync + if (pto->fStartSync && !fImporting && !fReindex) { + pto->fStartSync = false; + PushGetBlocks(pto, chainActive.Tip(), uint256(0)); + } + + // Resend wallet transactions that haven't gotten in a block yet + // Except during reindex, importing and IBD, when old wallet + // transactions become unconfirmed and spams other nodes. + if (!fReindex && !fImporting && !IsInitialBlockDownload()) + { + g_signals.Broadcast(); + } // // Message: inventory @@ -4171,15 +3995,6 @@ bool SendMessages(CNode* pto, bool fSendTrickle) hashRand = Hash(BEGIN(hashRand), END(hashRand)); bool fTrickleWait = ((hashRand & 3) != 0); - // always trickle our own transactions - if (!fTrickleWait) - { - CWalletTx wtx; - if (GetTransaction(inv.hash, wtx)) - if (wtx.fFromMe) - fTrickleWait = true; - } - if (fTrickleWait) { vInvWait.push_back(inv); @@ -4208,14 +4023,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Message: getdata // vector<CInv> vGetData; - int64 nNow = GetTime() * 1000000; + int64_t nNow = GetTime() * 1000000; while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { const CInv& inv = (*pto->mapAskFor.begin()).second; if (!AlreadyHave(inv)) { - if (fDebugNet) - printf("sending getdata: %s\n", inv.ToString().c_str()); + if (fDebug) + 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..b02aa60665 100644 --- a/src/main.h +++ b/src/main.h @@ -1,7 +1,8 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_MAIN_H #define BITCOIN_MAIN_H @@ -9,25 +10,28 @@ #include "bitcoin-config.h" #endif -#include "core.h" #include "bignum.h" -#include "sync.h" +#include "chainparams.h" +#include "coins.h" +#include "core.h" #include "net.h" #include "script.h" +#include "sync.h" +#include "txmempool.h" +#include "uint256.h" + +#include <algorithm> +#include <exception> +#include <map> +#include <set> +#include <stdint.h> +#include <string> +#include <utility> +#include <vector> -#include <list> - -class CWallet; -class CBlock; class CBlockIndex; -class CKeyItem; -class CReserveKey; - -class CAddress; +class CBloomFilter; class CInv; -class CNode; - -struct CBlockIndexWorkComparator; /** The maximum allowed size for a serialized block, in bytes (network rule) */ static const unsigned int MAX_BLOCK_SIZE = 1000000; @@ -45,11 +49,9 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB -/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ -static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; /** No amount larger than this (in satoshi) is valid */ -static const int64 MAX_MONEY = 21000000 * COIN; -inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } +static const int64_t MAX_MONEY = 21000000 * COIN; +inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; /** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */ @@ -64,6 +66,16 @@ static const int fHaveUPnP = true; static const int fHaveUPnP = false; #endif +/** "reject" message codes **/ +static const unsigned char REJECT_MALFORMED = 0x01; +static const unsigned char REJECT_INVALID = 0x10; +static const unsigned char REJECT_OBSOLETE = 0x11; +static const unsigned char REJECT_DUPLICATE = 0x12; +static const unsigned char REJECT_NONSTANDARD = 0x40; +static const unsigned char REJECT_DUST = 0x41; +static const unsigned char REJECT_INSUFFICIENTFEE = 0x42; +static const unsigned char REJECT_CHECKPOINT = 0x43; + extern CScript COINBASE_FLAGS; @@ -73,24 +85,12 @@ extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; +extern CTxMemPool mempool; 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 uint64_t nLastBlockTx; +extern uint64_t nLastBlockSize; extern const std::string strMessageMagic; -extern double dHashesPerSec; -extern int64 nHPSTimerStart; -extern int64 nTimeBestReceived; -extern CCriticalSection cs_setpwalletRegistered; -extern std::set<CWallet*> setpwalletRegistered; +extern int64_t nTimeBestReceived; extern bool fImporting; extern bool fReindex; extern bool fBenchmark; @@ -100,33 +100,30 @@ extern unsigned int nCoinCacheSize; extern bool fHaveGUI; // Settings -extern int64 nTransactionFee; +extern int64_t nTransactionFee; // Minimum disk space required - used in CheckDiskSpace() -static const uint64 nMinDiskSpace = 52428800; +static const uint64_t nMinDiskSpace = 52428800; -class CReserveKey; class CCoinsDB; class CBlockTreeDB; struct CDiskBlockPos; -class CCoins; class CTxUndo; -class CCoinsView; -class CCoinsViewCache; class CScriptCheck; class CValidationState; +class CWalletInterface; struct CBlockTemplate; /** Register a wallet to receive updates from core */ -void RegisterWallet(CWallet* pwalletIn); +void RegisterWallet(CWalletInterface* pwalletIn); /** Unregister a wallet from core */ -void UnregisterWallet(CWallet* pwalletIn); +void UnregisterWallet(CWalletInterface* pwalletIn); /** Unregister all wallets from core */ void UnregisterAllWallets(); /** Push an updated transaction to all registered wallets */ -void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL); /** Register with a network node to receive its signals */ void RegisterNodeSignals(CNodeSignals& nodeSignals); @@ -138,7 +135,7 @@ void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd); /** Process an incoming block */ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); /** Check whether enough disk space is available for an incoming block */ -bool CheckDiskSpace(uint64 nAdditionalBytes = 0); +bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); /** Open a block file (blk?????.dat) */ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); /** Open an undo file (rev?????.dat) */ @@ -155,8 +152,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 */ @@ -166,7 +161,7 @@ void ThreadScriptCheck(); /** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ bool CheckProofOfWork(uint256 hash, unsigned int nBits); /** Calculate the minimum amount of work a received block needs, without knowing its direct parent */ -unsigned int ComputeMinWork(unsigned int nBase, int64 nTime); +unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime); /** Get the number of active peers */ int GetNumBlocksOfPeers(); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ @@ -179,7 +174,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, b bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew); /** Find the best known block, and make it the tip of the block chain */ bool ConnectBestBlock(CValidationState &state); -int64 GetBlockValue(int nHeight, int64 nFees); +int64_t GetBlockValue(int nHeight, int64_t nFees); unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock); void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev); @@ -191,6 +186,9 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in /** Abort with a message */ bool AbortNode(const std::string &msg); +/** (try to) add transaction to memory pool **/ +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, + bool* pfMissingInputs, bool fRejectInsaneFee=false); @@ -199,10 +197,6 @@ bool AbortNode(const std::string &msg); - - -bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); - struct CDiskBlockPos { int nFile; @@ -264,7 +258,7 @@ enum GetMinFee_mode GMF_SEND, }; -int64 GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mode); +int64_t GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mode); // // Check transaction inputs, and make sure any @@ -324,12 +318,12 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state); */ bool IsStandardTx(const CTransaction& tx, std::string& reason); -bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64 nBlockTime = 0); +bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0); /** Amount of bitcoins spent by the transaction. @return sum of all outputs (note: does not include fees) */ -int64 GetValueOut(const CTransaction& tx); +int64_t GetValueOut(const CTransaction& tx); /** Undo information for a CBlock */ class CBlockUndo @@ -617,8 +611,8 @@ public: unsigned int nUndoSize; // number of used bytes in the undo file unsigned int nHeightFirst; // lowest height of block in file unsigned int nHeightLast; // highest height of block in file - uint64 nTimeFirst; // earliest time of block in file - uint64 nTimeLast; // latest time of block in file + uint64_t nTimeFirst; // earliest time of block in file + uint64_t nTimeLast; // latest time of block in file IMPLEMENT_SERIALIZE( READWRITE(VARINT(nBlocks)); @@ -649,23 +643,19 @@ public: } // update statistics (does not update nSize) - void AddBlock(unsigned int nHeightIn, uint64 nTimeIn) { + void AddBlock(unsigned int nHeightIn, uint64_t nTimeIn) { if (nBlocks==0 || nHeightFirst > nHeightIn) nHeightFirst = nHeightIn; if (nBlocks==0 || nTimeFirst > nTimeIn) nTimeFirst = nTimeIn; nBlocks++; - if (nHeightIn > nHeightFirst) + if (nHeightIn > nHeightLast) nHeightLast = nHeightIn; if (nTimeIn > nTimeLast) nTimeLast = nTimeIn; } }; -extern CCriticalSection cs_LastBlockFile; -extern CBlockFileInfo infoLastBlockFile; -extern int nLastBlockFile; - enum BlockStatus { BLOCK_VALID_UNKNOWN = 0, BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future @@ -807,9 +797,9 @@ public: return *phashBlock; } - int64 GetBlockTime() const + int64_t GetBlockTime() const { - return (int64)nTime; + return (int64_t)nTime; } CBigNum GetBlockWork() const @@ -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); @@ -837,11 +818,11 @@ public: enum { nMedianTimeSpan=11 }; - int64 GetMedianTimePast() const + int64_t GetMedianTimePast() const { - int64 pmedian[nMedianTimeSpan]; - int64* pbegin = &pmedian[nMedianTimeSpan]; - int64* pend = &pmedian[nMedianTimeSpan]; + int64_t pmedian[nMedianTimeSpan]; + int64_t* pbegin = &pmedian[nMedianTimeSpan]; + int64_t* pend = &pmedian[nMedianTimeSpan]; const CBlockIndex* pindex = this; for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) @@ -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_t GetMedianTime() const; /** * Returns true if there are nRequired or more blocks of minVersion or above @@ -872,28 +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()); - } -}; - -struct CBlockIndexWorkComparator -{ - bool operator()(CBlockIndex *pa, CBlockIndex *pb) { - if (pa->nChainWork > pb->nChainWork) return false; - if (pa->nChainWork < pb->nChainWork) return true; - - if (pa->GetBlockHash() < pb->GetBlockHash()) return false; - if (pa->GetBlockHash() > pb->GetBlockHash()) return true; - - return false; // identical blocks + LogPrintf("%s\n", ToString().c_str()); } }; @@ -962,7 +920,7 @@ public: void print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } }; @@ -975,17 +933,26 @@ private: MODE_ERROR, // run-time error } mode; int nDoS; + std::string strRejectReason; + unsigned char chRejectCode; + bool corruptionPossible; public: CValidationState() : mode(MODE_VALID), nDoS(0) {} - bool DoS(int level, bool ret = false) { + bool DoS(int level, bool ret = false, + unsigned char chRejectCodeIn=0, std::string strRejectReasonIn="", + bool corruptionIn=false) { + chRejectCode = chRejectCodeIn; + strRejectReason = strRejectReasonIn; + corruptionPossible = corruptionIn; if (mode == MODE_ERROR) return ret; nDoS += level; mode = MODE_INVALID; return ret; } - bool Invalid(bool ret = false) { - return DoS(0, ret); + bool Invalid(bool ret = false, + unsigned char _chRejectCode=0, std::string _strRejectReason="") { + return DoS(0, ret, _chRejectCode, _strRejectReason); } bool Error() { mode = MODE_ERROR; @@ -1011,231 +978,73 @@ public: } return false; } + bool CorruptionPossible() { + return corruptionPossible; + } + unsigned char GetRejectCode() const { return chRejectCode; } + std::string GetRejectReason() const { return strRejectReason; } }; +/** 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() {} - - explicit CBlockLocator(const CBlockIndex* pindex) - { - Set(pindex); + /** 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(uint256 hashBlock); - - CBlockLocator(const std::vector<uint256>& vHaveIn) - { - vHave = vHaveIn; + /** 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; } - IMPLEMENT_SERIALIZE - ( - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vHave); - ) - - void SetNull() - { - vHave.clear(); + /** 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]; } - bool IsNull() - { - return vHave.empty(); + /** 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]; } - /** 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(); -}; - - - - - - - - -class CTxMemPool -{ -public: - static bool fChecks; - mutable CCriticalSection cs; - std::map<uint256, CTransaction> mapTx; - std::map<COutPoint, CInPoint> mapNextTx; - - bool accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs); - bool addUnchecked(const uint256& hash, const CTransaction &tx); - bool remove(const CTransaction &tx, bool fRecursive = false); - bool removeConflicts(const CTransaction &tx); - void clear(); - void queryHashes(std::vector<uint256>& vtxid); - void pruneSpent(const uint256& hash, CCoins &coins); - void check(CCoinsViewCache *pcoins) const; - - unsigned long size() - { - LOCK(cs); - return mapTx.size(); + /** Efficiently check whether a block is present in this chain. */ + bool Contains(const CBlockIndex *pindex) const { + return (*this)[pindex->nHeight] == pindex; } - bool exists(uint256 hash) - { - return (mapTx.count(hash) != 0); + /** 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; } - CTransaction& lookup(uint256 hash) - { - return mapTx[hash]; + /** Return the maximal height in the chain. Is equal to chain.Tip() ? chain.Tip()->nHeight : -1. */ + int Height() const { + return vChain.size() - 1; } -}; - -extern CTxMemPool mempool; - -struct CCoinsStats -{ - int nHeight; - uint256 hashBlock; - uint64 nTransactions; - uint64 nTransactionOutputs; - uint64 nSerializedSize; - uint256 hashSerialized; - int64 nTotalAmount; - - CCoinsStats() : nHeight(0), hashBlock(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), hashSerialized(0), nTotalAmount(0) {} -}; - -/** Abstract view on the open txout dataset. */ -class CCoinsView -{ -public: - // Retrieve the CCoins (unspent transaction outputs) for a given txid - virtual bool GetCoins(const uint256 &txid, CCoins &coins); - - // Modify the CCoins for a given txid - virtual bool SetCoins(const uint256 &txid, const CCoins &coins); - - // Just check whether we have data for a given txid. - // This may (but cannot always) return true for fully spent transactions - virtual bool HaveCoins(const uint256 &txid); - // Retrieve the block index whose state this CCoinsView currently represents - virtual CBlockIndex *GetBestBlock(); + /** Set/initialize a chain with a given tip. Returns the forking point. */ + CBlockIndex *SetTip(CBlockIndex *pindex); - // Modify the currently active block index - virtual bool SetBestBlock(CBlockIndex *pindex); + /** Return a CBlockLocator that refers to a block in this chain (by default the tip). */ + CBlockLocator GetLocator(const CBlockIndex *pindex = NULL) const; - // Do a bulk modification (multiple SetCoins + one SetBestBlock) - virtual bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); - - // Calculate statistics about the unspent transaction output set - virtual bool GetStats(CCoinsStats &stats); - - // As we use CCoinsViews polymorphically, have a virtual destructor - virtual ~CCoinsView() {} -}; - -/** CCoinsView backed by another CCoinsView */ -class CCoinsViewBacked : public CCoinsView -{ -protected: - CCoinsView *base; - -public: - CCoinsViewBacked(CCoinsView &viewIn); - bool GetCoins(const uint256 &txid, CCoins &coins); - bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - void SetBackend(CCoinsView &viewIn); - bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); - bool GetStats(CCoinsStats &stats); -}; - -/** CCoinsView that adds a memory cache for transactions to another CCoinsView */ -class CCoinsViewCache : public CCoinsViewBacked -{ -protected: - CBlockIndex *pindexTip; - std::map<uint256,CCoins> cacheCoins; - -public: - CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); - - // Standard CCoinsView methods - bool GetCoins(const uint256 &txid, CCoins &coins); - bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); - - // Return a modifiable reference to a CCoins. Check HaveCoins first. - // Many methods explicitly require a CCoinsViewCache because of this method, to reduce - // copying. - CCoins &GetCoins(const uint256 &txid); - - // Push the modifications applied to this cache to its base. - // Failure to call this method before destruction will cause the changes to be forgotten. - bool Flush(); - - // Calculate the size of the cache (in number of transactions) - unsigned int GetCacheSize(); - - /** Amount of bitcoins coming in to a transaction - Note that lightweight clients may not know anything besides the hash of previous transactions, - so may not be able to calculate this. - - @param[in] tx transaction for which we are checking input total - @return Sum of value of all inputs (scriptSigs) - @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); - - const CTxOut &GetOutputFor(const CTxIn& input); - -private: - std::map<uint256,CCoins>::iterator FetchCoins(const uint256 &txid); + /** Find the last common block between this chain and a locator. */ + CBlockIndex *FindFork(const CBlockLocator &locator) const; }; -/** CCoinsView that brings transactions from a memorypool into view. - It does not check for spendings by memory pool transactions. */ -class CCoinsViewMemPool : public CCoinsViewBacked -{ -protected: - CTxMemPool &mempool; +/** The currently-connected chain of blocks. */ +extern CChain chainActive; -public: - CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); - bool GetCoins(const uint256 &txid, CCoins &coins); - bool HaveCoins(const uint256 &txid); -}; /** Global variable that points to the active CCoinsView (protected by cs_main) */ extern CCoinsViewCache *pcoinsTip; @@ -1282,4 +1091,18 @@ public: ) }; + +class CWalletInterface { +protected: + virtual void SyncTransaction(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) =0; + virtual void EraseFromWallet(const uint256 &hash) =0; + virtual void SetBestChain(const CBlockLocator &locator) =0; + virtual void UpdatedTransaction(const uint256 &hash) =0; + virtual void Inventory(const uint256 &hash) =0; + virtual void ResendWalletTransactions() =0; + friend void ::RegisterWallet(CWalletInterface*); + friend void ::UnregisterWallet(CWalletInterface*); + friend void ::UnregisterAllWallets(); +}; + #endif diff --git a/src/miner.cpp b/src/miner.cpp index 3ecf1609e1..397c95c624 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -1,17 +1,19 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "core.h" -#include "wallet.h" #include "miner.h" -#include "main.h" - - +#include "core.h" +#include "main.h" +#include "net.h" +#include "wallet.h" +#include <stdint.h> +double dHashesPerSec = 0.0; +int64_t nHPSTimerStart = 0; ////////////////////////////////////////////////////////////////////////////// // @@ -106,16 +108,16 @@ 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()); } }; -uint64 nLastBlockTx = 0; -uint64 nLastBlockSize = 0; +uint64_t nLastBlockTx = 0; +uint64_t nLastBlockSize = 0; // We want to sort transactions by priority and fee, so: typedef boost::tuple<double, double, CTransaction*> TxPriority; @@ -177,10 +179,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); // Collect memory pool transactions into the block - int64 nFees = 0; + int64_t nFees = 0; { LOCK2(cs_main, mempool.cs); - CBlockIndex* pindexPrev = pindexBest; + CBlockIndex* pindexPrev = chainActive.Tip(); CCoinsViewCache view(*pcoinsTip, true); // Priority order to process transactions @@ -199,7 +201,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) COrphan* porphan = NULL; double dPriority = 0; - int64 nTotalIn = 0; + int64_t nTotalIn = 0; bool fMissingInputs = false; BOOST_FOREACH(const CTxIn& txin, tx.vin) { @@ -211,7 +213,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) @@ -233,7 +235,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) } const CCoins &coins = view.GetCoins(txin.prevout.hash); - int64 nValueIn = coins.vout[txin.prevout.n].nValue; + int64_t nValueIn = coins.vout[txin.prevout.n].nValue; nTotalIn += nValueIn; int nConf = pindexPrev->nHeight - coins.nHeight + 1; @@ -242,9 +244,21 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) } if (fMissingInputs) continue; - // Priority is sum(valuein * age) / txsize + // Priority is sum(valuein * age) / modified_txsize unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - dPriority /= nTxSize; + unsigned int nTxSizeMod = nTxSize; + // In order to avoid disincentivizing cleaning up the UTXO set we don't count + // the constant overhead for each txin and up to 110 bytes of scriptSig (which + // is enough to cover a compressed pubkey p2sh redemption) for priority. + // Providing any more cleanup incentive than making additional inputs free would + // risk encouraging people to create junk outputs to redeem later. + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + unsigned int offset = 41U + min(110U, (unsigned int)txin.scriptSig.size()); + if (nTxSizeMod > offset) + nTxSizeMod -= offset; + } + dPriority /= nTxSizeMod; // This is a more accurate fee-per-kilobyte than is used by the client code, because the // client code rounds up the size to the nearest 1K. That's good, because it gives an @@ -261,8 +275,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) } // Collect transactions into block - uint64 nBlockSize = 1000; - uint64 nBlockTx = 0; + uint64_t nBlockSize = 1000; + uint64_t nBlockTx = 0; int nBlockSigOps = 100; bool fSortedByFee = (nBlockPrioritySize <= 0); @@ -306,7 +320,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) if (!view.HaveInputs(tx)) continue; - int64 nTxFees = view.GetValueIn(tx)-GetValueOut(tx); + int64_t nTxFees = view.GetValueIn(tx)-GetValueOut(tx); nTxSigOps += GetP2SHSigOpCount(tx, view); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) @@ -331,7 +345,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 +369,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; - printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); + LogPrintf("CreateNewBlock(): total size %"PRIu64"\n", nBlockSize); pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); pblocktemplate->vTxFees[0] = -nFees; @@ -463,15 +477,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 +508,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"); @@ -513,8 +527,8 @@ void static BitcoinMiner(CWallet *pwallet) // // Create new block // - unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrev = pindexBest; + unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + CBlockIndex* pindexPrev = chainActive.Tip(); auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); if (!pblocktemplate.get()) @@ -522,7 +536,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)); // @@ -542,7 +556,7 @@ void static BitcoinMiner(CWallet *pwallet) // // Search // - int64 nStart = GetTime(); + int64_t nStart = GetTime(); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); uint256 hashbuf[2]; uint256& hash = *alignup<16>(hashbuf); @@ -581,7 +595,7 @@ void static BitcoinMiner(CWallet *pwallet) } // Meter hashes/sec - static int64 nHashCounter; + static int64_t nHashCounter; if (nHPSTimerStart == 0) { nHPSTimerStart = GetTimeMillis(); @@ -599,11 +613,11 @@ void static BitcoinMiner(CWallet *pwallet) dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); nHPSTimerStart = GetTimeMillis(); nHashCounter = 0; - static int64 nLogTime; + static int64_t nLogTime; 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); } } } @@ -615,9 +629,9 @@ void static BitcoinMiner(CWallet *pwallet) break; if (nBlockNonce >= 0xffff0000) break; - if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) break; - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) break; // Update nTime every few seconds @@ -633,7 +647,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..4879f55d51 100644 --- a/src/miner.h +++ b/src/miner.h @@ -1,12 +1,19 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_MINER_H #define BITCOIN_MINER_H -#include "core.h" -#include "wallet.h" +#include <stdint.h> + +class CBlock; +class CBlockIndex; +struct CBlockTemplate; +class CReserveKey; +class CScript; +class CWallet; /** Run the miner threads */ void GenerateBitcoins(bool fGenerate, CWallet* pwallet); @@ -22,4 +29,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_t nHPSTimerStart; + #endif // BITCOIN_MINER_H diff --git a/src/mruset.h b/src/mruset.h index a52735182d..c36a0c8f37 100644 --- a/src/mruset.h +++ b/src/mruset.h @@ -1,11 +1,13 @@ // Copyright (c) 2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_MRUSET_H #define BITCOIN_MRUSET_H -#include <set> #include <deque> +#include <set> +#include <utility> /** STL-like set container that only keeps the most recent N elements. */ template <typename T> class mruset diff --git a/src/net.cpp b/src/net.cpp index 02b99fd049..954fe5947c 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -7,32 +7,34 @@ #include "bitcoin-config.h" #endif -#include "chainparams.h" -#include "db.h" #include "net.h" -#include "core.h" + #include "addrman.h" +#include "chainparams.h" +#include "core.h" +#include "db.h" #include "ui_interface.h" -#include "script.h" + +#include <inttypes.h> +#include <stdint.h> #ifdef WIN32 #include <string.h> -#endif - -#ifndef WIN32 +#else #include <fcntl.h> #endif #ifdef USE_UPNP -#include <miniupnpc/miniwget.h> #include <miniupnpc/miniupnpc.h> +#include <miniupnpc/miniwget.h> #include <miniupnpc/upnpcommands.h> #include <miniupnpc/upnperrors.h> #endif // Dump addresses to peers.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 -#if !defined(HAVE_MSG_NOSIGNAL) + +#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif @@ -53,14 +55,14 @@ struct LocalServiceInfo { // Global state variables // bool fDiscover = true; -uint64 nLocalServices = NODE_NETWORK; +uint64_t nLocalServices = NODE_NETWORK; static CCriticalSection cs_mapLocalHost; static map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfReachable[NET_MAX] = {}; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; static CNode* pnodeSync = NULL; -uint64 nLocalHostNonce = 0; +uint64_t nLocalHostNonce = 0; static std::vector<SOCKET> vhListenSocket; CAddrMan addrman; int nMaxConnections = 125; @@ -68,9 +70,9 @@ int nMaxConnections = 125; vector<CNode*> vNodes; CCriticalSection cs_vNodes; map<CInv, CDataStream> mapRelay; -deque<pair<int64, CInv> > vRelayExpiration; +deque<pair<int64_t, CInv> > vRelayExpiration; CCriticalSection cs_mapRelay; -limitedmap<CInv, int64> mapAlreadyAskedFor(MAX_INV_SZ); +limitedmap<CInv, int64_t> mapAlreadyAskedFor(MAX_INV_SZ); static deque<string> vOneShots; CCriticalSection cs_vOneShots; @@ -173,14 +175,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 +228,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 +336,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 +411,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 +428,10 @@ void AddressCurrentlyConnected(const CService& addr) - - +uint64_t CNode::nTotalBytesRecv = 0; +uint64_t CNode::nTotalBytesSent = 0; +CCriticalSection CNode::cs_totalBytesRecv; +CCriticalSection CNode::cs_totalBytesSent; CNode* FindNode(const CNetAddr& ip) { @@ -473,7 +477,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 +487,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 +522,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 +544,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()); + int64_t 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); } @@ -555,7 +560,7 @@ void CNode::PushVersion() -std::map<CNetAddr, int64> CNode::setBanned; +std::map<CNetAddr, int64_t> CNode::setBanned; CCriticalSection CNode::cs_setBanned; void CNode::ClearBanned() @@ -568,10 +573,10 @@ bool CNode::IsBanned(CNetAddr ip) bool fResult = false; { LOCK(cs_setBanned); - std::map<CNetAddr, int64>::iterator i = setBanned.find(ip); + std::map<CNetAddr, int64_t>::iterator i = setBanned.find(ip); if (i != setBanned.end()) { - int64 t = (*i).second; + int64_t t = (*i).second; if (GetTime() < t) fResult = true; } @@ -583,15 +588,15 @@ 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; } nMisbehavior += 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); + int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban + LogPrintf("Misbehaving: %s (%d -> %d) DISCONNECTING\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); { LOCK(cs_setBanned); if (setBanned[addr] < banTime) @@ -600,7 +605,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 +626,24 @@ 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_t 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); + + // Leave string empty if addrLocal invalid (not filled in yet) + stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : ""; } #undef X @@ -717,6 +740,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 +755,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(); } } @@ -782,7 +806,8 @@ void ThreadSocketHandler() vNodesDisconnected.push_back(pnode); } } - + } + { // Delete disconnected nodes list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected; BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) @@ -812,10 +837,9 @@ void ThreadSocketHandler() } } } - if (vNodes.size() != nPrevNodeCount) - { + if(vNodes.size() != nPrevNodeCount) { nPrevNodeCount = vNodes.size(); - uiInterface.NotifyNumConnectionsChanged(vNodes.size()); + uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); } @@ -891,7 +915,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 +943,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 +956,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 +968,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 +1018,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 +1034,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 +1063,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 +1125,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 +1153,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 +1164,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 +1215,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 +1238,7 @@ void ThreadDNSAddressSeed() } } - printf("%d addresses found from DNS seeds\n", found); + LogPrintf("%d addresses found from DNS seeds\n", found); } @@ -1229,12 +1254,12 @@ void ThreadDNSAddressSeed() void DumpAddresses() { - int64 nStart = GetTimeMillis(); + int64_t nStart = GetTimeMillis(); CAddrDB adb; adb.Write(addrman); - printf("Flushed %d addresses to peers.dat %"PRI64d"ms\n", + LogPrint("net", "Flushed %d addresses to peers.dat %"PRId64"ms\n", addrman.size(), GetTimeMillis() - nStart); } @@ -1261,7 +1286,7 @@ void ThreadOpenConnections() // Connect to specific addresses if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { - for (int64 nLoop = 0;; nLoop++) + for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"]) @@ -1278,7 +1303,7 @@ void ThreadOpenConnections() } // Initiate network connections - int64 nStart = GetTime(); + int64_t nStart = GetTime(); while (true) { ProcessOneShot(); @@ -1292,7 +1317,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; } @@ -1317,7 +1342,7 @@ void ThreadOpenConnections() } } - int64 nANow = GetAdjustedTime(); + int64_t nANow = GetAdjustedTime(); int nTries = 0; while (true) @@ -1468,6 +1493,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 @@ -1515,6 +1542,9 @@ void ThreadMessageHandler() CNode* pnodeTrickle = NULL; if (!vNodesCopy.empty()) pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; + + bool fSleep = true; + BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (pnode->fDisconnect) @@ -1524,8 +1554,18 @@ void ThreadMessageHandler() { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) + { if (!g_signals.ProcessMessages(pnode)) pnode->CloseSocketDisconnect(); + + if (pnode->nSendSize < SendBufferSize()) + { + if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) + { + fSleep = false; + } + } + } } boost::this_thread::interruption_point(); @@ -1543,8 +1583,9 @@ void ThreadMessageHandler() BOOST_FOREACH(CNode* pnode, vNodesCopy) pnode->Release(); } - - MilliSleep(100); + + if (fSleep) + MilliSleep(100); } } @@ -1568,7 +1609,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 +1617,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 +1641,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 +1672,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 +1728,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 +1736,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 +1767,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 +1794,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 +1820,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 +1886,62 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt pnode->PushInventory(inv); } } + +void CNode::RecordBytesRecv(uint64_t bytes) +{ + LOCK(cs_totalBytesRecv); + nTotalBytesRecv += bytes; +} + +void CNode::RecordBytesSent(uint64_t bytes) +{ + LOCK(cs_totalBytesSent); + nTotalBytesSent += bytes; +} + +uint64_t CNode::GetTotalBytesRecv() +{ + LOCK(cs_totalBytesRecv); + return nTotalBytesRecv; +} + +uint64_t CNode::GetTotalBytesSent() +{ + LOCK(cs_totalBytesSent); + return nTotalBytesSent; +} + +void CNode::Fuzz(int nChance) +{ + if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake + if (GetRand(nChance) != 0) return; // Fuzz 1 of every nChance messages + + switch (GetRand(3)) + { + case 0: + // xor a random byte with a random value: + if (!ssSend.empty()) { + CDataStream::size_type pos = GetRand(ssSend.size()); + ssSend[pos] ^= (unsigned char)(GetRand(256)); + } + break; + case 1: + // delete a random byte: + if (!ssSend.empty()) { + CDataStream::size_type pos = GetRand(ssSend.size()); + ssSend.erase(ssSend.begin()+pos); + } + break; + case 2: + // insert a random byte at a random position + { + CDataStream::size_type pos = GetRand(ssSend.size()); + char ch = (char)GetRand(256); + ssSend.insert(ssSend.begin()+pos, ch); + } + break; + } + // Chance of more than one change half the time: + // (more changes exponentially less likely): + Fuzz(2); +} @@ -1,36 +1,45 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_NET_H #define BITCOIN_NET_H +#include "bloom.h" +#include "compat.h" +#include "hash.h" +#include "limitedmap.h" +#include "mruset.h" +#include "netbase.h" +#include "protocol.h" +#include "sync.h" +#include "uint256.h" +#include "util.h" + #include <deque> -#include <boost/array.hpp> -#include <boost/foreach.hpp> -#include <boost/signals2/signal.hpp> -#include <openssl/rand.h> +#include <inttypes.h> +#include <stdint.h> #ifndef WIN32 #include <arpa/inet.h> #endif -#include "mruset.h" -#include "limitedmap.h" -#include "netbase.h" -#include "protocol.h" -#include "addrman.h" -#include "hash.h" -#include "bloom.h" - -/** The maximum number of entries in an 'inv' protocol message */ -static const unsigned int MAX_INV_SZ = 50000; +//#include <boost/array.hpp> +#include <boost/foreach.hpp> +#include <boost/signals2/signal.hpp> +#include <openssl/rand.h> -class CNode; +class CAddrMan; class CBlockIndex; -extern int nBestHeight; +class CNode; +namespace boost { + class thread_group; +} +/** The maximum number of entries in an 'inv' protocol message */ +static const unsigned int MAX_INV_SZ = 50000; inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } @@ -52,6 +61,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; }; @@ -85,17 +95,17 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); extern bool fDiscover; -extern uint64 nLocalServices; -extern uint64 nLocalHostNonce; +extern uint64_t nLocalServices; +extern uint64_t nLocalHostNonce; extern CAddrMan addrman; extern int nMaxConnections; extern std::vector<CNode*> vNodes; extern CCriticalSection cs_vNodes; extern std::map<CInv, CDataStream> mapRelay; -extern std::deque<std::pair<int64, CInv> > vRelayExpiration; +extern std::deque<std::pair<int64_t, CInv> > vRelayExpiration; extern CCriticalSection cs_mapRelay; -extern limitedmap<CInv, int64> mapAlreadyAskedFor; +extern limitedmap<CInv, int64_t> mapAlreadyAskedFor; extern std::vector<std::string> vAddedNodes; extern CCriticalSection cs_vAddedNodes; @@ -106,19 +116,22 @@ extern CCriticalSection cs_vAddedNodes; class CNodeStats { public: - uint64 nServices; - int64 nLastSend; - int64 nLastRecv; - int64 nTimeConnected; + uint64_t nServices; + int64_t nLastSend; + int64_t nLastRecv; + int64_t nTimeConnected; std::string addrName; int nVersion; std::string strSubVer; bool fInbound; int nStartingHeight; int nMisbehavior; - uint64 nSendBytes; - uint64 nRecvBytes; + uint64_t nSendBytes; + uint64_t nRecvBytes; bool fSyncNode; + double dPingTime; + double dPingWait; + std::string addrLocal; }; @@ -168,25 +181,25 @@ class CNode { public: // socket - uint64 nServices; + uint64_t nServices; SOCKET hSocket; CDataStream ssSend; size_t nSendSize; // total size of all vSendMsg entries size_t nSendOffset; // offset inside the first vSendMsg already sent - uint64 nSendBytes; + uint64_t nSendBytes; std::deque<CSerializeData> vSendMsg; CCriticalSection cs_vSend; std::deque<CInv> vRecvGetData; std::deque<CNetMessage> vRecvMsg; CCriticalSection cs_vRecvMsg; - uint64 nRecvBytes; + uint64_t nRecvBytes; int nRecvVersion; - int64 nLastSend; - int64 nLastRecv; - int64 nLastSendEmpty; - int64 nTimeConnected; + int64_t nLastSend; + int64_t nLastRecv; + int64_t nLastSendEmpty; + int64_t nTimeConnected; CAddress addr; std::string addrName; CService addrLocal; @@ -211,10 +224,13 @@ protected: // Denial-of-service detection/prevention // Key is IP address, value is banned-until-time - static std::map<CNetAddr, int64> setBanned; + static std::map<CNetAddr, int64_t> setBanned; static CCriticalSection cs_setBanned; int nMisbehavior; + // Basic fuzz-testing + void Fuzz(int nChance); // modifies ssSend + public: uint256 hashContinue; CBlockIndex* pindexLastGetBlocksBegin; @@ -232,13 +248,19 @@ public: mruset<CInv> setInventoryKnown; std::vector<CInv> vInventoryToSend; CCriticalSection cs_inventory; - std::multimap<int64, CInv> mapAskFor; - - CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, MIN_PROTO_VERSION) + std::multimap<int64_t, CInv> mapAskFor; + + // Ping time measurement + uint64_t nPingNonceSent; + int64_t nPingUsecStart; + int64_t nPingUsecTime; + bool fPingQueued; + + CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, INIT_PROTO_VERSION) { nServices = 0; hSocket = hSocketIn; - nRecvVersion = MIN_PROTO_VERSION; + nRecvVersion = INIT_PROTO_VERSION; nLastSend = 0; nLastRecv = 0; nSendBytes = 0; @@ -268,6 +290,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 +312,15 @@ public: } private: + // Network usage totals + static CCriticalSection cs_totalBytesRecv; + static CCriticalSection cs_totalBytesSent; + static uint64_t nTotalBytesRecv; + static uint64_t nTotalBytesSent; + CNode(const CNode&); void operator=(const CNode&); + public: @@ -301,7 +334,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; } @@ -366,18 +399,17 @@ public: { // We're using mapAskFor as a priority queue, // the key is the earliest time the request can be sent - int64 nRequestTime; - limitedmap<CInv, int64>::const_iterator it = mapAlreadyAskedFor.find(inv); + int64_t nRequestTime; + limitedmap<CInv, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv); if (it != mapAlreadyAskedFor.end()) 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 %"PRId64" (%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; - static int64 nLastTime; + int64_t nNow = (GetTime() - 1) * 1000000; + static int64_t nLastTime; ++nLastTime; nNow = std::max(nNow, nLastTime); nLastTime = nNow; @@ -399,8 +431,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,19 +441,23 @@ 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? void EndMessage() UNLOCK_FUNCTION(cs_vSend) { - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) + // The -*messagestest options are intentionally not documented in the help message, + // since they are only used during development to debug the networking code and are + // not intended for end-users. + if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) { - printf("dropmessages DROPPING SEND MESSAGE\n"); + LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n"); AbortMessage(); return; } + if (mapArgs.count("-fuzzmessagestest")) + Fuzz(GetArg("-fuzzmessagestest", 10)); if (ssSend.size() == 0) return; @@ -438,9 +473,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 +672,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_t bytes); + static void RecordBytesSent(uint64_t bytes); + + static uint64_t GetTotalBytesRecv(); + static uint64_t GetTotalBytesSent(); }; diff --git a/src/netbase.cpp b/src/netbase.cpp index d02490ad4a..1392fa8233 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -1,12 +1,16 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "netbase.h" -#include "util.h" -#include "sync.h" + #include "hash.h" +#include "sync.h" +#include "uint256.h" +#include "util.h" + +#include <stdint.h> #ifndef WIN32 #include <fcntl.h> @@ -15,7 +19,7 @@ #include <boost/algorithm/string/case_conv.hpp> // for to_lower() #include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith() -#if !defined(HAVE_MSG_NOSIGNAL) +#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif @@ -169,7 +173,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 +208,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 +313,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 +328,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 +367,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 to %s timeout\n", addrConnect.ToString().c_str()); closesocket(hSocket); return false; } if (nRet == SOCKET_ERROR) { - printf("select() for connection failed: %i\n",WSAGetLastError()); + LogPrintf("select() for %s failed: %i\n", addrConnect.ToString().c_str(), WSAGetLastError()); closesocket(hSocket); return false; } @@ -380,13 +384,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 %s failed: %i\n", addrConnect.ToString().c_str(), WSAGetLastError()); closesocket(hSocket); return false; } if (nRet != 0) { - printf("connect() failed after select(): %s\n",strerror(nRet)); + LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString().c_str(), strerror(nRet)); closesocket(hSocket); return false; } @@ -397,7 +401,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe else #endif { - printf("connect() failed: %i\n",WSAGetLastError()); + LogPrintf("connect() to %s failed: %i\n", addrConnect.ToString().c_str(), WSAGetLastError()); closesocket(hSocket); return false; } @@ -864,7 +868,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 @@ -883,17 +887,17 @@ std::vector<unsigned char> CNetAddr::GetGroup() const return vchRet; } -uint64 CNetAddr::GetHash() const +uint64_t CNetAddr::GetHash() const { uint256 hash = Hash(&ip[0], &ip[16]); - uint64 nRet; + uint64_t nRet; memcpy(&nRet, &hash, sizeof(nRet)); return nRet; } 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 +1138,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..af40aee94b 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -1,6 +1,7 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_NETBASE_H #define BITCOIN_NETBASE_H @@ -8,12 +9,13 @@ #include "bitcoin-config.h" #endif +#include "compat.h" +#include "serialize.h" + +#include <stdint.h> #include <string> #include <vector> -#include "serialize.h" -#include "compat.h" - extern int nConnectTimeout; #ifdef WIN32 @@ -54,7 +56,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) @@ -69,7 +71,7 @@ class CNetAddr std::string ToString() const; std::string ToStringIP() const; unsigned int GetByte(int n) const; - uint64 GetHash() const; + uint64_t GetHash() const; bool GetInAddr(struct in_addr* pipv4Addr) const; std::vector<unsigned char> GetGroup() const; int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const; diff --git a/src/noui.cpp b/src/noui.cpp index c0e00c4715..fd285c5719 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -1,12 +1,14 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "noui.h" + #include "ui_interface.h" -#include "init.h" -#include "bitcoinrpc.h" +#include "util.h" +#include <stdint.h> #include <string> static bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) @@ -27,19 +29,19 @@ 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; } -static bool noui_ThreadSafeAskFee(int64 /*nFeeRequired*/) +static bool noui_ThreadSafeAskFee(int64_t /*nFeeRequired*/) { return true; } 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/noui.h b/src/noui.h new file mode 100644 index 0000000000..755d987fdd --- /dev/null +++ b/src/noui.h @@ -0,0 +1,10 @@ +// Copyright (c) 2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NOUI_H +#define BITCOIN_NOUI_H + +extern void noui_connect(); + +#endif diff --git a/src/protocol.cpp b/src/protocol.cpp index 745b4338e4..798227581e 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1,11 +1,13 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "protocol.h" + #include "util.h" -#include "netbase.h" + +#include <stdint.h> #ifndef WIN32 # include <arpa/inet.h> @@ -67,7 +69,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; } @@ -81,7 +83,7 @@ CAddress::CAddress() : CService() Init(); } -CAddress::CAddress(CService ipIn, uint64 nServicesIn) : CService(ipIn) +CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn) { Init(); nServices = nServicesIn; @@ -146,6 +148,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/protocol.h b/src/protocol.h index ae541dfdba..86e08ddcfa 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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. @@ -11,11 +11,13 @@ #define __INCLUDED_PROTOCOL_H__ #include "chainparams.h" -#include "serialize.h" #include "netbase.h" -#include <string> +#include "serialize.h" #include "uint256.h" +#include <stdint.h> +#include <string> + /** Message header. * (4) message start. * (12) command. @@ -67,7 +69,7 @@ class CAddress : public CService { public: CAddress(); - explicit CAddress(CService ipIn, uint64 nServicesIn=NODE_NETWORK); + explicit CAddress(CService ipIn, uint64_t nServicesIn=NODE_NETWORK); void Init(); @@ -90,13 +92,13 @@ class CAddress : public CService // TODO: make private (improves encapsulation) public: - uint64 nServices; + uint64_t nServices; // disk and network only unsigned int nTime; // memory only - int64 nLastTry; + int64_t nLastTry; }; /** inv message data */ diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index c6c4cb37a3..a19cdc25a5 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -1,11 +1,10 @@ include $(top_srcdir)/src/Makefile.include -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) \ +AM_CPPFLAGS += -I$(top_srcdir)/src \ + -I$(top_builddir)/src/qt \ + -I$(top_builddir)/src/qt/forms \ + $(PROTOBUF_CFLAGS) \ $(QR_CFLAGS) -AM_LDFLAGS = $(PTHREAD_CFLAGS) bin_PROGRAMS = bitcoin-qt noinst_LIBRARIES = libbitcoinqt.a SUBDIRS = $(BUILD_TEST_QT) @@ -34,7 +33,9 @@ QT_TS = locale/bitcoin_ach.ts locale/bitcoin_af_ZA.ts locale/bitcoin_ar.ts \ QT_FORMS_UI = forms/aboutdialog.ui forms/addressbookpage.ui \ forms/askpassphrasedialog.ui forms/editaddressdialog.ui forms/intro.ui \ - forms/optionsdialog.ui forms/overviewpage.ui forms/qrcodedialog.ui \ + forms/openuridialog.ui \ + forms/optionsdialog.ui forms/overviewpage.ui forms/receiverequestdialog.ui \ + forms/receivecoinsdialog.ui \ forms/rpcconsole.ui forms/sendcoinsdialog.ui forms/sendcoinsentry.ui \ forms/signverifymessagedialog.ui forms/transactiondescdialog.ui @@ -44,14 +45,19 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \ moc_bitcoingui.cpp moc_bitcoinunits.cpp moc_clientmodel.cpp \ moc_csvmodelwriter.cpp moc_editaddressdialog.cpp moc_guiutil.cpp \ moc_intro.cpp moc_macdockiconhandler.cpp moc_macnotificationhandler.cpp \ - moc_monitoreddatamapper.cpp moc_notificator.cpp moc_optionsdialog.cpp \ + moc_monitoreddatamapper.cpp moc_notificator.cpp \ + moc_openuridialog.cpp \ + moc_optionsdialog.cpp \ moc_optionsmodel.cpp moc_overviewpage.cpp moc_paymentserver.cpp \ - moc_qrcodedialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ + moc_receiverequestdialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ + moc_receivecoinsdialog.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 + moc_walletmodel.cpp moc_walletview.cpp + +BITCOIN_MM = macdockiconhandler.mm macnotificationhandler.mm QT_MOC = intro.moc overviewpage.moc rpcconsole.moc @@ -66,13 +72,16 @@ BITCOIN_QT_H = aboutdialog.h addressbookpage.h addresstablemodel.h \ askpassphrasedialog.h bitcoinaddressvalidator.h bitcoinamountfield.h \ bitcoingui.h bitcoinunits.h clientmodel.h csvmodelwriter.h \ editaddressdialog.h guiconstants.h guiutil.h intro.h macdockiconhandler.h \ - macnotificationhandler.h monitoreddatamapper.h notificator.h optionsdialog.h \ + macnotificationhandler.h monitoreddatamapper.h notificator.h \ + openuridialog.h \ + optionsdialog.h \ optionsmodel.h overviewpage.h paymentrequestplus.h paymentserver.h \ - qrcodedialog.h qvalidatedlineedit.h qvaluecombobox.h rpcconsole.h \ + receivecoinsdialog.h \ + receiverequestdialog.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 + walletmodel.h walletmodeltransaction.h walletview.h RES_ICONS = res/icons/bitcoin.png res/icons/address-book.png \ res/icons/quit.png res/icons/send.png res/icons/toolbar.png \ @@ -91,6 +100,21 @@ 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 \ + openuridialog.cpp \ + optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \ + paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \ + receivecoinsdialog.cpp receiverequestdialog.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 walletview.cpp + RES_IMAGES = res/images/about.png res/images/splash.png \ res/images/splash_testnet.png @@ -100,20 +124,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,14 +138,11 @@ $(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 -endif # # bitcoin-qt binary # @@ -141,7 +150,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 +158,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) + @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/aboutdialog.cpp b/src/qt/aboutdialog.cpp index cea8e98425..797ebf97ed 100644 --- a/src/qt/aboutdialog.cpp +++ b/src/qt/aboutdialog.cpp @@ -1,7 +1,12 @@ +// Copyright (c) 2011-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 "aboutdialog.h" #include "ui_aboutdialog.h" #include "clientmodel.h" + #include "clientversion.h" AboutDialog::AboutDialog(QWidget *parent) : diff --git a/src/qt/aboutdialog.h b/src/qt/aboutdialog.h index 33b1437674..b02be74844 100644 --- a/src/qt/aboutdialog.h +++ b/src/qt/aboutdialog.h @@ -1,12 +1,17 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef ABOUTDIALOG_H #define ABOUTDIALOG_H #include <QDialog> +class ClientModel; + namespace Ui { class AboutDialog; } -class ClientModel; /** "About" dialog box */ class AboutDialog : public QDialog diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 5b8d44481e..5e82d4d17d 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #if defined(HAVE_CONFIG_H) #include "bitcoin-config.h" #endif @@ -6,26 +10,20 @@ #include "ui_addressbookpage.h" #include "addresstablemodel.h" -#include "optionsmodel.h" #include "bitcoingui.h" -#include "editaddressdialog.h" #include "csvmodelwriter.h" +#include "editaddressdialog.h" #include "guiutil.h" -#ifdef USE_QRCODE -#include "qrcodedialog.h" -#endif - -#include <QSortFilterProxyModel> -#include <QClipboard> -#include <QMessageBox> +#include <QIcon> #include <QMenu> +#include <QMessageBox> +#include <QSortFilterProxyModel> AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : QDialog(parent), ui(new Ui::AddressBookPage), model(0), - optionsModel(0), mode(mode), tab(tab) { @@ -35,25 +33,29 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : ui->newAddress->setIcon(QIcon()); ui->copyAddress->setIcon(QIcon()); ui->deleteAddress->setIcon(QIcon()); - ui->verifyMessage->setIcon(QIcon()); - ui->signMessage->setIcon(QIcon()); ui->exportButton->setIcon(QIcon()); #endif -#ifndef USE_QRCODE - ui->showQRCode->setVisible(false); -#endif - switch(mode) { - case ForSending: + case ForSelection: + switch(tab) + { + case SendingTab: setWindowTitle(tr("Choose the address to send coins to")); break; + case ReceivingTab: setWindowTitle(tr("Choose the address to receive coins with")); break; + } connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept())); ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->tableView->setFocus(); + ui->closeButton->setText(tr("C&hoose")); ui->exportButton->hide(); break; case ForEditing: - ui->buttonBox->setVisible(false); + switch(tab) + { + case SendingTab: setWindowTitle(tr("Sending addresses")); break; + case ReceivingTab: setWindowTitle(tr("Receiving addresses")); break; + } break; } switch(tab) @@ -61,23 +63,17 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : case SendingTab: ui->labelExplanation->setText(tr("These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.")); ui->deleteAddress->setVisible(true); - ui->signMessage->setVisible(false); break; case ReceivingTab: - ui->labelExplanation->setText(tr("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.")); + ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.")); ui->deleteAddress->setVisible(false); - ui->signMessage->setVisible(true); break; } // Context menu actions - QAction *copyAddressAction = new QAction(ui->copyAddress->text(), this); + QAction *copyAddressAction = new QAction(tr("&Copy Address"), this); QAction *copyLabelAction = new QAction(tr("Copy &Label"), this); QAction *editAction = new QAction(tr("&Edit"), this); - QAction *sendCoinsAction = new QAction(tr("Send &Coins"), this); - QAction *showQRCodeAction = new QAction(ui->showQRCode->text(), this); - QAction *signMessageAction = new QAction(ui->signMessage->text(), this); - QAction *verifyMessageAction = new QAction(ui->verifyMessage->text(), this); deleteAction = new QAction(ui->deleteAddress->text(), this); // Build context menu @@ -88,30 +84,16 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) : if(tab == SendingTab) contextMenu->addAction(deleteAction); contextMenu->addSeparator(); - if(tab == SendingTab) - contextMenu->addAction(sendCoinsAction); -#ifdef USE_QRCODE - contextMenu->addAction(showQRCodeAction); -#endif - if(tab == ReceivingTab) - contextMenu->addAction(signMessageAction); - else if(tab == SendingTab) - contextMenu->addAction(verifyMessageAction); // Connect signals for context menu actions connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyAddress_clicked())); connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction())); connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction())); connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteAddress_clicked())); - connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(onSendCoinsAction())); - connect(showQRCodeAction, SIGNAL(triggered()), this, SLOT(on_showQRCode_clicked())); - connect(signMessageAction, SIGNAL(triggered()), this, SLOT(on_signMessage_clicked())); - connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(on_verifyMessage_clicked())); connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); - // Pass through accept action from button box - connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(accept())); } AddressBookPage::~AddressBookPage() @@ -164,11 +146,6 @@ void AddressBookPage::setModel(AddressTableModel *model) selectionChanged(); } -void AddressBookPage::setOptionsModel(OptionsModel *optionsModel) -{ - this->optionsModel = optionsModel; -} - void AddressBookPage::on_copyAddress_clicked() { GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address); @@ -197,42 +174,6 @@ void AddressBookPage::onEditAction() dlg.exec(); } -void AddressBookPage::on_signMessage_clicked() -{ - QTableView *table = ui->tableView; - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QString address = index.data().toString(); - emit signMessage(address); - } -} - -void AddressBookPage::on_verifyMessage_clicked() -{ - QTableView *table = ui->tableView; - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QString address = index.data().toString(); - emit verifyMessage(address); - } -} - -void AddressBookPage::onSendCoinsAction() -{ - QTableView *table = ui->tableView; - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QString address = index.data().toString(); - emit sendCoins(address); - } -} - void AddressBookPage::on_newAddress_clicked() { if(!model) @@ -278,32 +219,20 @@ void AddressBookPage::selectionChanged() ui->deleteAddress->setEnabled(true); ui->deleteAddress->setVisible(true); deleteAction->setEnabled(true); - ui->signMessage->setEnabled(false); - ui->signMessage->setVisible(false); - ui->verifyMessage->setEnabled(true); - ui->verifyMessage->setVisible(true); break; case ReceivingTab: // Deleting receiving addresses, however, is not allowed ui->deleteAddress->setEnabled(false); ui->deleteAddress->setVisible(false); deleteAction->setEnabled(false); - ui->signMessage->setEnabled(true); - ui->signMessage->setVisible(true); - ui->verifyMessage->setEnabled(false); - ui->verifyMessage->setVisible(false); break; } ui->copyAddress->setEnabled(true); - ui->showQRCode->setEnabled(true); } else { ui->deleteAddress->setEnabled(false); - ui->showQRCode->setEnabled(false); ui->copyAddress->setEnabled(false); - ui->signMessage->setEnabled(false); - ui->verifyMessage->setEnabled(false); } } @@ -312,9 +241,6 @@ void AddressBookPage::done(int retval) QTableView *table = ui->tableView; if(!table->selectionModel() || !table->model()) return; - // When this is a tab/widget and not a model dialog, ignore "done" - if(mode == ForEditing) - return; // Figure out which address was selected, and return it QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); @@ -339,7 +265,7 @@ void AddressBookPage::on_exportButton_clicked() // CSV is currently the only supported format QString filename = GUIUtil::getSaveFileName( this, - tr("Export Address Book Data"), QString(), + tr("Export Address List"), QString(), tr("Comma separated file (*.csv)")); if (filename.isNull()) return; @@ -358,25 +284,6 @@ void AddressBookPage::on_exportButton_clicked() } } -void AddressBookPage::on_showQRCode_clicked() -{ -#ifdef USE_QRCODE - QTableView *table = ui->tableView; - QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); - - foreach (QModelIndex index, indexes) - { - QString address = index.data().toString(); - QString label = index.sibling(index.row(), 0).data(Qt::EditRole).toString(); - - QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this); - dialog->setModel(optionsModel); - dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->show(); - } -#endif -} - void AddressBookPage::contextualMenu(const QPoint &point) { QModelIndex index = ui->tableView->indexAt(point); diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 34465aa65f..a9192efc84 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -1,20 +1,25 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef ADDRESSBOOKPAGE_H #define ADDRESSBOOKPAGE_H #include <QDialog> +class AddressTableModel; +class OptionsModel; + namespace Ui { class AddressBookPage; } -class AddressTableModel; -class OptionsModel; QT_BEGIN_NAMESPACE -class QTableView; class QItemSelection; -class QSortFilterProxyModel; class QMenu; class QModelIndex; +class QSortFilterProxyModel; +class QTableView; QT_END_NAMESPACE /** Widget that shows a list of sending or receiving addresses. @@ -30,7 +35,7 @@ public: }; enum Mode { - ForSending, /**< Open address book to pick address for sending */ + ForSelection, /**< Open address book to pick address */ ForEditing /**< Open address book for editing */ }; @@ -38,7 +43,6 @@ public: ~AddressBookPage(); void setModel(AddressTableModel *model); - void setOptionsModel(OptionsModel *optionsModel); const QString &getReturnValue() const { return returnValue; } public slots: @@ -47,7 +51,6 @@ public slots: private: Ui::AddressBookPage *ui; AddressTableModel *model; - OptionsModel *optionsModel; Mode mode; Tabs tab; QString returnValue; @@ -63,14 +66,6 @@ private slots: void on_newAddress_clicked(); /** Copy address of currently selected address entry to clipboard */ void on_copyAddress_clicked(); - /** Open the sign message tab in the Sign/Verify Message dialog with currently selected address */ - void on_signMessage_clicked(); - /** Open the verify message tab in the Sign/Verify Message dialog with currently selected address */ - void on_verifyMessage_clicked(); - /** Open send coins dialog for currently selected address (no button) */ - void onSendCoinsAction(); - /** Generate a QR Code from the currently selected address */ - void on_showQRCode_clicked(); /** Copy label of currently selected address entry to clipboard (no button) */ void onCopyLabelAction(); /** Edit currently selected address entry (no button) */ @@ -86,8 +81,6 @@ private slots: void selectNewAddress(const QModelIndex &parent, int begin, int /*end*/); signals: - void signMessage(QString addr); - void verifyMessage(QString addr); void sendCoins(QString addr); }; diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 921c4443a9..d686cd4fd8 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -1,10 +1,14 @@ +// Copyright (c) 2011-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 "addresstablemodel.h" #include "guiutil.h" #include "walletmodel.h" -#include "wallet.h" #include "base58.h" +#include "wallet.h" #include <QFont> #include <QDebug> diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 6f532087fe..71691f5a26 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef ADDRESSTABLEMODEL_H #define ADDRESSTABLEMODEL_H @@ -5,9 +9,10 @@ #include <QStringList> class AddressTablePriv; -class CWallet; class WalletModel; +class CWallet; + /** Qt model of the address book in the core. This allows views to access and modify the address book. */ @@ -79,9 +84,6 @@ private: /** Notify listeners that data changed. */ void emitDataChanged(int index); -signals: - void defaultAddressChanged(const QString &address); - public slots: /* Update address list from core. */ diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index f165c11cb1..2a6d6abc35 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -1,12 +1,18 @@ +// Copyright (c) 2011-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 "askpassphrasedialog.h" #include "ui_askpassphrasedialog.h" #include "guiconstants.h" #include "walletmodel.h" +#include "allocators.h" + +#include <QKeyEvent> #include <QMessageBox> #include <QPushButton> -#include <QKeyEvent> AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : QDialog(parent), @@ -16,6 +22,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/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index 9df002da2c..4c92afcd54 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -1,12 +1,17 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef ASKPASSPHRASEDIALOG_H #define ASKPASSPHRASEDIALOG_H #include <QDialog> +class WalletModel; + namespace Ui { class AskPassphraseDialog; } -class WalletModel; /** Multifunctional dialog to ask for passphrases. Used for encryption, unlocking, and changing the passphrase. */ @@ -37,6 +42,8 @@ private: private slots: void textChanged(); + +protected: bool event(QEvent *event); bool eventFilter(QObject *object, QEvent *event); }; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index a4d589e167..54f96f4426 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -1,30 +1,37 @@ -/* - * W.J. van der Laan 2011-2012 - */ +// Copyright (c) 2011-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 "bitcoingui.h" + #include "clientmodel.h" -#include "walletmodel.h" -#include "optionsmodel.h" -#include "guiutil.h" #include "guiconstants.h" -#include "init.h" -#include "util.h" -#include "ui_interface.h" +#include "guiutil.h" +#include "intro.h" +#include "optionsmodel.h" #include "paymentserver.h" #include "splashscreen.h" -#include "intro.h" +#include "walletmodel.h" +#include "init.h" +#include "main.h" +#include "ui_interface.h" +#include "util.h" + +#include <stdint.h> + +#include <boost/filesystem/operations.hpp> #include <QApplication> +#include <QLibraryInfo> +#include <QLocale> #include <QMessageBox> +#include <QSettings> +#include <QTimer> +#include <QTranslator> + #if QT_VERSION < 0x050000 #include <QTextCodec> #endif -#include <QLocale> -#include <QTimer> -#include <QTranslator> -#include <QLibraryInfo> -#include <QSettings> #if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED) #define _BITCOIN_QT_PLUGINS_INCLUDED @@ -62,13 +69,13 @@ 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; } } -static bool ThreadSafeAskFee(int64 nFeeRequired) +static bool ThreadSafeAskFee(int64_t nFeeRequired) { if(!guiref) return false; @@ -91,7 +98,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 +162,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 @@ -170,8 +180,6 @@ int main(int argc, char *argv[]) bool fMissingDatadir = false; bool fSelParFromCLFailed = false; - fHaveGUI = true; - // Command-line options take precedence: ParseParameters(argc, argv); // ... then bitcoin.conf: @@ -290,7 +298,7 @@ int main(int argc, char *argv[]) QObject::connect(pollShutdownTimer, SIGNAL(timeout()), guiref, SLOT(detectShutdown())); pollShutdownTimer->start(200); - if(AppInit2(threadGroup)) + if(AppInit2(threadGroup, false)) { { // Put this in a block, so that the Model objects are cleaned up before @@ -300,7 +308,6 @@ int main(int argc, char *argv[]) PaymentServer::LoadRootCAs(); paymentServer->setOptionsModel(&optionsModel); - paymentServer->initNetManager(); if (splashref) splash.finish(&window); @@ -326,12 +333,12 @@ int main(int argc, char *argv[]) // bitcoin: URIs or payment requests: QObject::connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), &window, SLOT(handlePaymentRequest(SendCoinsRecipient))); + QObject::connect(&window, SIGNAL(receivedURI(QString)), + paymentServer, SLOT(handleURIOrFile(QString))); QObject::connect(&walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)), paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray))); - QObject::connect(paymentServer, SIGNAL(receivedPaymentACK(QString)), - &window, SLOT(showPaymentACK(QString))); - QObject::connect(paymentServer, SIGNAL(reportError(QString, QString, unsigned int)), - guiref, SLOT(message(QString, QString, unsigned int))); + QObject::connect(paymentServer, SIGNAL(message(QString,QString,unsigned int)), + guiref, SLOT(message(QString,QString,unsigned int))); QTimer::singleShot(100, paymentServer, SLOT(uiReady())); app.exec(); diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index 5136ea0c40..604f24192b 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-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 "bitcoinaddressvalidator.h" /* Base58 characters are: diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h index b7f4dfee96..91d248abd1 100644 --- a/src/qt/bitcoinaddressvalidator.h +++ b/src/qt/bitcoinaddressvalidator.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOINADDRESSVALIDATOR_H #define BITCOINADDRESSVALIDATOR_H diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index d9d4e3b23d..6b083331d3 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -1,14 +1,17 @@ +// Copyright (c) 2011-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 "bitcoinamountfield.h" -#include "qvaluecombobox.h" #include "bitcoinunits.h" #include "guiconstants.h" +#include "qvaluecombobox.h" #include <QApplication> +#include <QDoubleSpinBox> #include <QHBoxLayout> #include <QKeyEvent> -#include <QDoubleSpinBox> - #include <qmath.h> // for qPow() BitcoinAmountField::BitcoinAmountField(QWidget *parent): @@ -130,9 +133,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/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 9c8be5a26a..d54f536b1f 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOINAMOUNTFIELD_H #define BITCOINAMOUNTFIELD_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bb9eb60e5b..8b0aba1b5b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1,57 +1,58 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011-2012 - * The Bitcoin Developers 2011-2012 - */ +// Copyright (c) 2011-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 "bitcoingui.h" -#include "transactiontablemodel.h" -#include "optionsdialog.h" #include "aboutdialog.h" -#include "clientmodel.h" -#include "walletmodel.h" -#include "walletframe.h" -#include "optionsmodel.h" -#include "transactiondescdialog.h" #include "bitcoinunits.h" +#include "clientmodel.h" #include "guiconstants.h" -#include "notificator.h" #include "guiutil.h" +#include "notificator.h" +#include "optionsdialog.h" +#include "optionsmodel.h" #include "rpcconsole.h" -#include "ui_interface.h" -#include "wallet.h" -#include "init.h" +#include "walletframe.h" +#include "walletmodel.h" +#include "openuridialog.h" #ifdef Q_OS_MAC #include "macdockiconhandler.h" #endif +#include "init.h" +#include "ui_interface.h" + +#include <iostream> + #include <QApplication> -#include <QMenuBar> -#include <QMenu> +#include <QDateTime> +#include <QDesktopWidget> +#include <QDragEnterEvent> #include <QIcon> -#include <QVBoxLayout> -#include <QToolBar> -#include <QStatusBar> #include <QLabel> +#include <QListWidget> +#include <QMenu> +#include <QMenuBar> #include <QMessageBox> +#include <QMimeData> +#include <QMovie> #include <QProgressBar> +#include <QSettings> #include <QStackedWidget> -#include <QDateTime> -#include <QMovie> +#include <QStatusBar> +#include <QStyle> #include <QTimer> -#include <QDragEnterEvent> +#include <QToolBar> +#include <QVBoxLayout> + #if QT_VERSION < 0x050000 #include <QUrl> #include <QTextDocument> +#else +#include <QUrlQuery> #endif -#include <QMimeData> -#include <QStyle> -#include <QListWidget> - -#include <iostream> const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; @@ -157,6 +158,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); @@ -192,7 +195,7 @@ void BitcoinGUI::createActions(bool fIsTestnet) tabGroup->addAction(sendCoinsAction); receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive"), this); - receiveCoinsAction->setStatusTip(tr("Show the list of addresses for receiving payments")); + receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and bitcoin: URIs)")); receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); receiveCoinsAction->setCheckable(true); receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); @@ -205,13 +208,6 @@ void BitcoinGUI::createActions(bool fIsTestnet) historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); tabGroup->addAction(historyAction); - addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Addresses"), this); - addressBookAction->setStatusTip(tr("Edit the list of stored addresses and labels")); - addressBookAction->setToolTip(addressBookAction->statusTip()); - addressBookAction->setCheckable(true); - addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); - tabGroup->addAction(addressBookAction); - connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); @@ -220,8 +216,6 @@ void BitcoinGUI::createActions(bool fIsTestnet) connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); - connect(addressBookAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage())); quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this); quitAction->setStatusTip(tr("Quit application")); @@ -233,7 +227,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); @@ -260,6 +258,14 @@ void BitcoinGUI::createActions(bool fIsTestnet) openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this); openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); + usedSendingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("&Used sending addresses..."), this); + usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels")); + usedReceivingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("Used &receiving addresses..."), this); + usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels")); + + openAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_FileIcon), tr("Open URI..."), this); + openAction->setStatusTip(tr("Open a bitcoin: URI or payment request")); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked())); connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); @@ -270,6 +276,9 @@ void BitcoinGUI::createActions(bool fIsTestnet) connect(changePassphraseAction, SIGNAL(triggered()), walletFrame, SLOT(changePassphrase())); connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab())); connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); + connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedSendingAddresses())); + connect(usedReceivingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedReceivingAddresses())); + connect(openAction, SIGNAL(triggered()), this, SLOT(openClicked())); } void BitcoinGUI::createMenuBar() @@ -284,10 +293,14 @@ void BitcoinGUI::createMenuBar() // Configure the menus QMenu *file = appMenuBar->addMenu(tr("&File")); + file->addAction(openAction); file->addAction(backupWalletAction); file->addAction(signMessageAction); file->addAction(verifyMessageAction); file->addSeparator(); + file->addAction(usedSendingAddressesAction); + file->addAction(usedReceivingAddressesAction); + file->addSeparator(); file->addAction(quitAction); QMenu *settings = appMenuBar->addMenu(tr("&Settings")); @@ -311,7 +324,6 @@ void BitcoinGUI::createToolBars() toolbar->addAction(sendCoinsAction); toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); - toolbar->addAction(addressBookAction); } void BitcoinGUI::setClientModel(ClientModel *clientModel) @@ -439,28 +451,36 @@ void BitcoinGUI::aboutClicked() dlg.exec(); } +void BitcoinGUI::openClicked() +{ + OpenURIDialog dlg; + if(dlg.exec()) + { + emit receivedURI(dlg.getURI()); + } +} + void BitcoinGUI::gotoOverviewPage() { + overviewAction->setChecked(true); if (walletFrame) walletFrame->gotoOverviewPage(); } void BitcoinGUI::gotoHistoryPage() { + historyAction->setChecked(true); if (walletFrame) walletFrame->gotoHistoryPage(); } -void BitcoinGUI::gotoAddressBookPage() -{ - if (walletFrame) walletFrame->gotoAddressBookPage(); -} - void BitcoinGUI::gotoReceiveCoinsPage() { + receiveCoinsAction->setChecked(true); if (walletFrame) walletFrame->gotoReceiveCoinsPage(); } void BitcoinGUI::gotoSendCoinsPage(QString addr) { + sendCoinsAction->setChecked(true); if (walletFrame) walletFrame->gotoSendCoinsPage(addr); } @@ -590,21 +610,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 +652,9 @@ 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); + // Ensure we get users attention + showNormalIfMinimized(); + QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this); int r = mBox.exec(); if (ret != NULL) *ret = r == QMessageBox::Ok; @@ -676,9 +705,8 @@ void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) QString strMessage = tr("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?").arg(BitcoinUnits::formatWithUnit(clientModel->getOptionsModel()->getDisplayUnit(), nFeeRequired)); - QMessageBox::StandardButton retval = QMessageBox::question( - this, tr("Confirm transaction fee"), strMessage, - QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm transaction fee"), strMessage, + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); *payFee = (retval == QMessageBox::Yes); } @@ -707,23 +735,11 @@ void BitcoinGUI::dropEvent(QDropEvent *event) { if(event->mimeData()->hasUrls()) { - int nValidUrisFound = 0; - QList<QUrl> uris = event->mimeData()->urls(); - foreach(const QUrl &uri, uris) + foreach(const QUrl &uri, event->mimeData()->urls()) { - SendCoinsRecipient r; - if (GUIUtil::parseBitcoinURI(uri, &r) && walletFrame->handlePaymentRequest(r)) - nValidUrisFound++; + emit receivedURI(uri.toString()); } - - // if valid URIs were found - if (nValidUrisFound) - walletFrame->gotoSendCoinsPage(); - else - message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."), - CClientUIInterface::ICON_WARNING); } - event->acceptProposedAction(); } @@ -739,18 +755,17 @@ bool BitcoinGUI::eventFilter(QObject *object, QEvent *event) return QMainWindow::eventFilter(object, event); } -void BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient) +bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient) { - walletFrame->handlePaymentRequest(recipient); -} - -void BitcoinGUI::showPaymentACK(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 + // URI has to be valid + if (walletFrame->handlePaymentRequest(recipient)) + { + showNormalIfMinimized(); + gotoSendCoinsPage(); + return true; + } + else + return false; } void BitcoinGUI::setEncryptionStatus(int status) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index fc25e867fc..acbc38c894 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -1,36 +1,27 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOINGUI_H #define BITCOINGUI_H #include <QMainWindow> -#include <QSystemTrayIcon> #include <QMap> +#include <QSystemTrayIcon> -class TransactionTableModel; -class WalletFrame; -class WalletView; class ClientModel; -class WalletModel; -class WalletStack; -class TransactionView; -class OverviewPage; -class AddressBookPage; -class SendCoinsDialog; -class SendCoinsRecipient; -class SignVerifyMessageDialog; class Notificator; class RPCConsole; +class SendCoinsRecipient; +class WalletFrame; +class WalletModel; class CWallet; QT_BEGIN_NAMESPACE +class QAction; class QLabel; -class QModelIndex; class QProgressBar; -class QStackedWidget; -class QUrl; -class QListWidget; -class QPushButton; -class QAction; QT_END_NAMESPACE /** @@ -51,24 +42,16 @@ public: The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic. */ void setClientModel(ClientModel *clientModel); + /** Set the wallet model. The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending functionality. */ - bool addWallet(const QString& name, WalletModel *walletModel); bool setCurrentWallet(const QString& name); void removeAllWallets(); - /** Used by WalletView to allow access to needed QActions */ - // Todo: Use Qt signals for these - QAction * getOverviewAction() { return overviewAction; } - QAction * getHistoryAction() { return historyAction; } - QAction * getAddressBookAction() { return addressBookAction; } - QAction * getReceiveCoinsAction() { return receiveCoinsAction; } - QAction * getSendCoinsAction() { return sendCoinsAction; } - protected: void changeEvent(QEvent *e); void closeEvent(QCloseEvent *event); @@ -91,7 +74,8 @@ private: QAction *historyAction; QAction *quitAction; QAction *sendCoinsAction; - QAction *addressBookAction; + QAction *usedSendingAddressesAction; + QAction *usedReceivingAddressesAction; QAction *signMessageAction; QAction *verifyMessageAction; QAction *aboutAction; @@ -103,10 +87,10 @@ private: QAction *changePassphraseAction; QAction *aboutQtAction; QAction *openRPCConsoleAction; + QAction *openAction; QSystemTrayIcon *trayIcon; Notificator *notificator; - TransactionView *transactionView; RPCConsole *rpcConsole; QMovie *syncIconMovie; @@ -124,6 +108,10 @@ private: /** Create system tray menu (or setup the dock menu) */ void createTrayIconMenu(); +signals: + /** Signal raised when a URI was entered or dragged to the GUI */ + void receivedURI(const QString &uri); + public slots: /** Set number of connections shown in the UI */ void setNumConnections(int count); @@ -143,6 +131,7 @@ public slots: @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) */ void message(const QString &title, const QString &message, unsigned int style, bool *ret = NULL); + /** Asks the user whether to pay the transaction fee or to cancel the transaction. It is currently not possible to pass a return value to another thread through BlockingQueuedConnection, so an indirected pointer is used. @@ -153,8 +142,7 @@ public slots: */ void askFee(qint64 nFeeRequired, bool *payFee); - void handlePaymentRequest(const SendCoinsRecipient& recipient); - void showPaymentACK(QString msg); + bool handlePaymentRequest(const SendCoinsRecipient& recipient); /** Show incoming transaction notification for new transactions. */ void incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address); @@ -164,8 +152,6 @@ private slots: void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); - /** Switch to address book page */ - void gotoAddressBookPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ @@ -184,6 +170,8 @@ private slots: /** Handle tray icon clicked */ void trayIconActivated(QSystemTrayIcon::ActivationReason reason); #endif + /** Show open dialog */ + void openClicked(); /** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */ void showNormalIfMinimized(bool fToggleHidden = false); diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index bfe9494b17..45a9bf6a41 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -1,4 +1,7 @@ + + #include <QtGlobal> + // Automatically generated by extract_strings.py #ifdef __GNUC__ #define UNUSED __attribute__((unused)) @@ -19,8 +22,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "It is also recommended to set alertnotify so you are notified of problems;\n" "for example: alertnotify=echo %%s | mail -s \"Bitcoin Alert\" admin@foo.com\n"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:" -"@STRENGTH)"), +"Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!" +"3DES:@STRENGTH)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "An error occurred while setting up the RPC port %u for listening on IPv4: %s"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -98,6 +101,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"), QT_TRANSLATE_NOOP("bitcoin-core", "Allow JSON-RPC connections from specified IP address"), QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet.dat"), +QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin RPC client version"), QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin version"), QT_TRANSLATE_NOOP("bitcoin-core", "Block creation options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"), @@ -106,6 +110,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -externalip address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write default address"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node(s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect through socks proxy"), +QT_TRANSLATE_NOOP("bitcoin-core", "Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, and disconnect"), QT_TRANSLATE_NOOP("bitcoin-core", "Corrupted block database detected"), QT_TRANSLATE_NOOP("bitcoin-core", "Discover own IP address (default: 1 when listening and no -externalip)"), @@ -143,8 +148,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat fil QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"), QT_TRANSLATE_NOOP("bitcoin-core", "Information"), QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -tor address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -minrelaytxfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -mintxfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s'"), @@ -172,7 +177,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."), QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"), QT_TRANSLATE_NOOP("bitcoin-core", "SSL options: (see the Bitcoin Wiki for SSL setup instructions)"), QT_TRANSLATE_NOOP("bitcoin-core", "Select the version of socks proxy to use (4-5, default: 5)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Send command to -server or bitcoind"), +QT_TRANSLATE_NOOP("bitcoin-core", "Send command to Bitcoin server"), QT_TRANSLATE_NOOP("bitcoin-core", "Send commands to node running on <ip> (default: 127.0.0.1)"), QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"), QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to debugger"), @@ -191,6 +196,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: bitcoind.pid)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Start Bitcoin server"), QT_TRANSLATE_NOOP("bitcoin-core", "System error: "), QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: 100)"), @@ -202,6 +208,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind r QT_TRANSLATE_NOOP("bitcoin-core", "Unknown -socks proxy version requested: %i"), QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Upgrade wallet to latest format"), +QT_TRANSLATE_NOOP("bitcoin-core", "Usage (deprecated, use bitcoin-cli):"), QT_TRANSLATE_NOOP("bitcoin-core", "Usage:"), QT_TRANSLATE_NOOP("bitcoin-core", "Use OpenSSL (https) for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default: 0)"), @@ -217,4 +224,4 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Warning"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade required!"), QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the database using -reindex to change -txindex"), QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"), -};
\ No newline at end of file +}; diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index ae9791123d..2fed443cf2 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-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 "bitcoinunits.h" #include <QStringList> diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index f6fdf6c7be..46517fc07b 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -1,8 +1,12 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOINUNITS_H #define BITCOINUNITS_H -#include <QString> #include <QAbstractListModel> +#include <QString> /** Bitcoin unit definitions. Encapsulates parsing and formatting and serves as list model for drop-down selection boxes. diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index f7dd8adb6b..c64e411bca 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -1,21 +1,25 @@ +// Copyright (c) 2011-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 "clientmodel.h" #include "guiconstants.h" -#include "optionsmodel.h" -#include "addresstablemodel.h" -#include "transactiontablemodel.h" -#include "chainparams.h" #include "alert.h" -#include "main.h" +#include "chainparams.h" #include "checkpoints.h" +#include "main.h" +#include "net.h" #include "ui_interface.h" +#include <stdint.h> + #include <QDateTime> -#include <QTimer> #include <QDebug> +#include <QTimer> -static const int64 nClientStartupTime = GetTime(); +static const int64_t nClientStartupTime = GetTime(); ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : QObject(parent), optionsModel(optionsModel), @@ -24,9 +28,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 +46,7 @@ int ClientModel::getNumConnections() const int ClientModel::getNumBlocks() const { - return nBestHeight; + return chainActive.Height(); } int ClientModel::getNumBlocksAtStartup() @@ -52,19 +55,27 @@ 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()); - else if(!isTestNet()) - return QDateTime::fromTime_t(1231006505); // Genesis block's time + if (chainActive.Tip()) + return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime()); else - return QDateTime::fromTime_t(1296688602); // Genesis block's time (testnet) + return QDateTime::fromTime_t(Params().GenesisBlock().nTime); // Genesis block's time of current network } double ClientModel::getVerificationProgress() const { - return Checkpoints::GuessVerificationProgress(pindexBest); + return Checkpoints::GuessVerificationProgress(chainActive.Tip()); } void ClientModel::updateTimer() @@ -86,6 +97,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..dcc528f4fd 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -1,11 +1,16 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef CLIENTMODEL_H #define CLIENTMODEL_H #include <QObject> -class OptionsModel; class AddressTableModel; +class OptionsModel; class TransactionTableModel; + class CWallet; QT_BEGIN_NAMESPACE @@ -35,6 +40,9 @@ public: int getNumBlocks() const; int getNumBlocksAtStartup(); + quint64 getTotalBytesRecv() const; + quint64 getTotalBytesSent() const; + double getVerificationProgress() const; QDateTime getLastBlockDate() const; @@ -74,6 +82,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..ac8c6b41d4 100644 --- a/src/qt/csvmodelwriter.cpp +++ b/src/qt/csvmodelwriter.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-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 "csvmodelwriter.h" #include <QAbstractItemModel> @@ -85,4 +89,3 @@ bool CSVModelWriter::write() return file.error() == QFile::NoError; } - diff --git a/src/qt/csvmodelwriter.h b/src/qt/csvmodelwriter.h index c4504ee91a..29de251efe 100644 --- a/src/qt/csvmodelwriter.h +++ b/src/qt/csvmodelwriter.h @@ -1,8 +1,12 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef CSVMODELWRITER_H #define CSVMODELWRITER_H -#include <QObject> #include <QList> +#include <QObject> QT_BEGIN_NAMESPACE class QAbstractItemModel; diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 9abad7647c..618567218f 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-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 "editaddressdialog.h" #include "ui_editaddressdialog.h" diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h index 44e5023d25..a448c4b23f 100644 --- a/src/qt/editaddressdialog.h +++ b/src/qt/editaddressdialog.h @@ -1,12 +1,17 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef EDITADDRESSDIALOG_H #define EDITADDRESSDIALOG_H #include <QDialog> +class AddressTableModel; + namespace Ui { class EditAddressDialog; } -class AddressTableModel; QT_BEGIN_NAMESPACE class QDataWidgetMapper; diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index a2a7da34dd..49221f41c1 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -10,9 +10,6 @@ <height>380</height> </rect> </property> - <property name="windowTitle"> - <string>Address Book</string> - </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QLabel" name="labelExplanation"> @@ -60,7 +57,7 @@ <string>Create a new address</string> </property> <property name="text"> - <string>&New Address</string> + <string>&New</string> </property> <property name="icon"> <iconset resource="../bitcoin.qrc"> @@ -74,7 +71,7 @@ <string>Copy the currently selected address to the system clipboard</string> </property> <property name="text"> - <string>&Copy Address</string> + <string>&Copy</string> </property> <property name="icon"> <iconset resource="../bitcoin.qrc"> @@ -83,45 +80,6 @@ </widget> </item> <item> - <widget class="QPushButton" name="showQRCode"> - <property name="text"> - <string>Show &QR Code</string> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/qrcode</normaloff>:/icons/qrcode</iconset> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="signMessage"> - <property name="toolTip"> - <string>Sign a message to prove you own a Bitcoin address</string> - </property> - <property name="text"> - <string>Sign &Message</string> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/edit</normaloff>:/icons/edit</iconset> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="verifyMessage"> - <property name="toolTip"> - <string>Verify a message to ensure it was signed with a specified Bitcoin address</string> - </property> - <property name="text"> - <string>&Verify Message</string> - </property> - <property name="icon"> - <iconset resource="../bitcoin.qrc"> - <normaloff>:/icons/transaction_0</normaloff>:/icons/transaction_0</iconset> - </property> - </widget> - </item> - <item> <widget class="QPushButton" name="deleteAddress"> <property name="toolTip"> <string>Delete the currently selected address from the list</string> @@ -163,15 +121,9 @@ </widget> </item> <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Ok</set> + <widget class="QPushButton" name="closeButton"> + <property name="text"> + <string>C&lose</string> </property> </widget> </item> diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui index b4a4c1b1e9..8ff3805226 100644 --- a/src/qt/forms/editaddressdialog.ui +++ b/src/qt/forms/editaddressdialog.ui @@ -32,7 +32,7 @@ <item row="0" column="1"> <widget class="QLineEdit" name="labelEdit"> <property name="toolTip"> - <string>The label associated with this address book entry</string> + <string>The label associated with this address list entry</string> </property> </widget> </item> @@ -49,7 +49,7 @@ <item row="1" column="1"> <widget class="QLineEdit" name="addressEdit"> <property name="toolTip"> - <string>The address associated with this address book entry. This can only be modified for sending addresses.</string> + <string>The address associated with this address list entry. This can only be modified for sending addresses.</string> </property> </widget> </item> diff --git a/src/qt/forms/openuridialog.ui b/src/qt/forms/openuridialog.ui new file mode 100644 index 0000000000..cd09ed0246 --- /dev/null +++ b/src/qt/forms/openuridialog.ui @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>OpenURIDialog</class> + <widget class="QDialog" name="OpenURIDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>564</width> + <height>109</height> + </rect> + </property> + <property name="windowTitle"> + <string>Open URI</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Open payment request from URI or file</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>URI:</string> + </property> + </widget> + </item> + <item> + <widget class="QValidatedLineEdit" name="uriEdit"> + </widget> + </item> + <item> + <widget class="QPushButton" name="selectFileButton"> + <property name="toolTip"> + <string>Select payment request file</string> + </property> + <property name="text"> + <string notr="true">…</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QValidatedLineEdit</class> + <extends>QLineEdit</extends> + <header>qvalidatedlineedit.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>OpenURIDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>OpenURIDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> 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 deleted file mode 100644 index 52e9db3762..0000000000 --- a/src/qt/forms/qrcodedialog.ui +++ /dev/null @@ -1,212 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>QRCodeDialog</class> - <widget class="QDialog" name="QRCodeDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>340</width> - <height>530</height> - </rect> - </property> - <property name="windowTitle"> - <string>QR Code Dialog</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <widget class="QLabel" name="lblQRCode"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>300</width> - <height>300</height> - </size> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - <property name="wordWrap"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QPlainTextEdit" name="outUri"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>50</height> - </size> - </property> - <property name="tabChangesFocus"> - <bool>true</bool> - </property> - <property name="textInteractionFlags"> - <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> - <item> - <widget class="QWidget" name="widget" native="true"> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QCheckBox" name="chkReqPayment"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="text"> - <string>Request Payment</string> - </property> - </widget> - </item> - <item> - <layout class="QFormLayout" name="formLayout"> - <property name="fieldGrowthPolicy"> - <enum>QFormLayout::AllNonFixedFieldsGrow</enum> - </property> - <item row="1" column="0"> - <widget class="QLabel" name="lblLabel"> - <property name="text"> - <string>Label:</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>lnLabel</cstring> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="lnLabel"/> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="lblMessage"> - <property name="text"> - <string>Message:</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>lnMessage</cstring> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLineEdit" name="lnMessage"/> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="lblAmount"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Amount:</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>lnReqAmount</cstring> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="BitcoinAmountField" name="lnReqAmount"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="btnSaveAs"> - <property name="text"> - <string>&Save As...</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>BitcoinAmountField</class> - <extends>QSpinBox</extends> - <header>bitcoinamountfield.h</header> - </customwidget> - </customwidgets> - <resources/> - <connections> - <connection> - <sender>chkReqPayment</sender> - <signal>clicked(bool)</signal> - <receiver>lnReqAmount</receiver> - <slot>setEnabled(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>92</x> - <y>285</y> - </hint> - <hint type="destinationlabel"> - <x>98</x> - <y>311</y> - </hint> - </hints> - </connection> - </connections> -</ui> diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui new file mode 100644 index 0000000000..6d1a72ecd2 --- /dev/null +++ b/src/qt/forms/receivecoinsdialog.ui @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ReceiveCoinsDialog</class> + <widget class="QWidget" name="ReceiveCoinsDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>776</width> + <height>343</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="3" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>&Amount:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>reqAmount</cstring> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="BitcoinAmountField" name="reqAmount"> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="toolTip"> + <string>The amount to request</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>&Label:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>reqLabel</cstring> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="reqLabel"> + <property name="toolTip"> + <string>The label to associate with the receiving address</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>&Message:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>reqMessage</cstring> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QLineEdit" name="reqMessage"> + <property name="toolTip"> + <string>The message to attach to payment request</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QCheckBox" name="reuseAddress"> + <property name="toolTip"> + <string>Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before.</string> + </property> + <property name="text"> + <string>R&euse an existing receiving address (not recommended)</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Use this form to request payments. All fields are optional.</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="clearButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Clear all fields of the form.</string> + </property> + <property name="text"> + <string>Clear</string> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> + </property> + <property name="autoRepeatDelay"> + <number>300</number> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="receiveButton"> + <property name="minimumSize"> + <size> + <width>150</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>&Request payment</string> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/receiving_addresses</normaloff>:/icons/receiving_addresses</iconset> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>BitcoinAmountField</class> + <extends>QLineEdit</extends> + <header>bitcoinamountfield.h</header> + </customwidget> + </customwidgets> + <resources> + <include location="../bitcoin.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui new file mode 100644 index 0000000000..c9cb3de69f --- /dev/null +++ b/src/qt/forms/receiverequestdialog.ui @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ReceiveRequestDialog</class> + <widget class="QDialog" name="ReceiveRequestDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>487</width> + <height>597</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QRImageWidget" name="lblQRCode"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>300</height> + </size> + </property> + <property name="toolTip"> + <string>QR Code</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QTextEdit" name="outUri"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>50</height> + </size> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="tabChangesFocus"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="btnCopyURI"> + <property name="text"> + <string>Copy &URI</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnCopyAddress"> + <property name="text"> + <string>Copy &Address</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnCopyImage"> + <property name="text"> + <string>&Copy Image</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnSaveAs"> + <property name="text"> + <string>&Save Image...</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Close</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QRImageWidget</class> + <extends>QLabel</extends> + <header>receiverequestdialog.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ReceiveRequestDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>452</x> + <y>573</y> + </hint> + <hint type="destinationlabel"> + <x>243</x> + <y>298</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ReceiveRequestDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>452</x> + <y>573</y> + </hint> + <hint type="destinationlabel"> + <x>243</x> + <y>298</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index d1d8ab42a0..c77c5a384c 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Bitcoin - Debug window</string> + <string>Debug window</string> </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> @@ -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..7547931ff1 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -28,8 +28,17 @@ <height>165</height> </rect> </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <property name="margin"> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -84,7 +93,7 @@ </sizepolicy> </property> <property name="toolTip"> - <string>Remove all transaction fields</string> + <string>Clear all fields of the form.</string> </property> <property name="text"> <string>Clear &All</string> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index a2ef9a0a38..199a14598a 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -10,18 +10,18 @@ <height>150</height> </rect> </property> - <property name="windowTitle"> - <string>StackedWidget</string> + <property name="focusPolicy"> + <enum>Qt::TabFocus</enum> </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> + <widget class="QFrame" name="SendCoins"> + <property name="toolTip"> + <string>This is a normal payment.</string> </property> <property name="frameShape"> <enum>QFrame::StyledPanel</enum> @@ -34,7 +34,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 +47,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 +63,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> @@ -93,7 +93,7 @@ <item> <widget class="QToolButton" name="addressBookButton"> <property name="toolTip"> - <string>Choose address from address book</string> + <string>Choose previously used address</string> </property> <property name="text"> <string/> @@ -143,13 +143,501 @@ <item row="4" column="1"> <widget class="QValidatedLineEdit" name="addAsLabel"> <property name="toolTip"> - <string>Enter a label for this address to add it to your address book</string> + <string>Enter a label for this address to add it to the list of used addresses</string> </property> </widget> </item> </layout> </widget> - <widget class="QFrame" name="SendCoinsSecure"> + <widget class="QFrame" name="SendCoins_InsecurePaymentRequest"> + <property name="palette"> + <palette> + <active> + <colorrole role="WindowText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + <colorrole role="Button"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>127</blue> + </color> + </brush> + </colorrole> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Midlight"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>191</blue> + </color> + </brush> + </colorrole> + <colorrole role="Dark"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>127</red> + <green>127</green> + <blue>63</blue> + </color> + </brush> + </colorrole> + <colorrole role="Mid"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>170</red> + <green>170</green> + <blue>84</blue> + </color> + </brush> + </colorrole> + <colorrole role="Text"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + <colorrole role="BrightText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="ButtonText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>127</blue> + </color> + </brush> + </colorrole> + <colorrole role="Shadow"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + <colorrole role="AlternateBase"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>191</blue> + </color> + </brush> + </colorrole> + <colorrole role="ToolTipBase"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>220</blue> + </color> + </brush> + </colorrole> + <colorrole role="ToolTipText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="WindowText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + <colorrole role="Button"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>127</blue> + </color> + </brush> + </colorrole> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Midlight"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>191</blue> + </color> + </brush> + </colorrole> + <colorrole role="Dark"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>127</red> + <green>127</green> + <blue>63</blue> + </color> + </brush> + </colorrole> + <colorrole role="Mid"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>170</red> + <green>170</green> + <blue>84</blue> + </color> + </brush> + </colorrole> + <colorrole role="Text"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + <colorrole role="BrightText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="ButtonText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>127</blue> + </color> + </brush> + </colorrole> + <colorrole role="Shadow"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + <colorrole role="AlternateBase"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>191</blue> + </color> + </brush> + </colorrole> + <colorrole role="ToolTipBase"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>220</blue> + </color> + </brush> + </colorrole> + <colorrole role="ToolTipText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="WindowText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>127</red> + <green>127</green> + <blue>63</blue> + </color> + </brush> + </colorrole> + <colorrole role="Button"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>127</blue> + </color> + </brush> + </colorrole> + <colorrole role="Light"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Midlight"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>191</blue> + </color> + </brush> + </colorrole> + <colorrole role="Dark"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>127</red> + <green>127</green> + <blue>63</blue> + </color> + </brush> + </colorrole> + <colorrole role="Mid"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>170</red> + <green>170</green> + <blue>84</blue> + </color> + </brush> + </colorrole> + <colorrole role="Text"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>127</red> + <green>127</green> + <blue>63</blue> + </color> + </brush> + </colorrole> + <colorrole role="BrightText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="ButtonText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>127</red> + <green>127</green> + <blue>63</blue> + </color> + </brush> + </colorrole> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>127</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>127</blue> + </color> + </brush> + </colorrole> + <colorrole role="Shadow"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + <colorrole role="AlternateBase"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>127</blue> + </color> + </brush> + </colorrole> + <colorrole role="ToolTipBase"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>220</blue> + </color> + </brush> + </colorrole> + <colorrole role="ToolTipText"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="toolTip"> + <string>This is an unverified payment request.</string> + </property> + <property name="autoFillBackground"> + <bool>true</bool> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Sunken</enum> + </property> + <layout class="QGridLayout" name="gridLayout_is"> + <property name="spacing"> + <number>12</number> + </property> + <item row="4" column="0"> + <widget class="QLabel" name="memoLabel_is"> + <property name="text"> + <string>Memo:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="amountLabel_is"> + <property name="text"> + <string>Amount:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="payToLabel_is"> + <property name="text"> + <string>Pay To:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="2"> + <widget class="BitcoinAmountField" name="payAmount_is"> + <property name="acceptDrops"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="3" column="2"> + <layout class="QHBoxLayout" name="payToLayout_is"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="payTo_is"/> + </item> + </layout> + </item> + <item row="4" column="2"> + <widget class="QLabel" name="memoTextLabel_is"> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QFrame" name="SendCoins_SecurePaymentRequest"> <property name="palette"> <palette> <active> @@ -592,8 +1080,8 @@ </disabled> </palette> </property> - <property name="windowTitle"> - <string>SecureSend</string> + <property name="toolTip"> + <string>This is a verified payment request.</string> </property> <property name="autoFillBackground"> <bool>true</bool> @@ -609,55 +1097,40 @@ <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> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> - <property name="buddy"> - <cstring>addAsLabel</cstring> - </property> </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> + <string>Amount:</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> - <property name="buddy"> - <cstring>payAmount</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> + <string>Pay To:</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> - <property name="buddy"> - <cstring>payTo_s</cstring> - </property> </widget> </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 +1140,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> @@ -686,7 +1162,6 @@ <class>BitcoinAmountField</class> <extends>QLineEdit</extends> <header>bitcoinamountfield.h</header> - <container>1</container> </customwidget> <customwidget> <class>QValidatedLineEdit</class> diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui index 279b2a5052..04d614a1cd 100644 --- a/src/qt/forms/signverifymessagedialog.ui +++ b/src/qt/forms/signverifymessagedialog.ui @@ -58,7 +58,7 @@ <item> <widget class="QPushButton" name="addressBookButton_SM"> <property name="toolTip"> - <string>Choose an address from the address book</string> + <string>Choose previously used address</string> </property> <property name="text"> <string/> @@ -271,7 +271,7 @@ <item> <widget class="QPushButton" name="addressBookButton_VM"> <property name="toolTip"> - <string>Choose an address from the address book</string> + <string>Choose previously used address</string> </property> <property name="text"> <string/> diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 2c72c01021..6c9fa6a755 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef GUICONSTANTS_H #define GUICONSTANTS_H diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index c951b21b80..85eeab2cbc 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1,33 +1,16 @@ +// Copyright (c) 2011-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 "guiutil.h" #include "bitcoinaddressvalidator.h" -#include "walletmodel.h" #include "bitcoinunits.h" +#include "walletmodel.h" -#include "util.h" +#include "core.h" #include "init.h" - -#include <QApplication> -#include <QDateTime> -#include <QDoubleValidator> -#include <QFont> -#include <QLineEdit> -#if QT_VERSION >= 0x050000 -#include <QUrlQuery> -#else -#include <QUrl> -#endif -#include <QTextDocument> // for Qt::mightBeRichText -#include <QAbstractItemView> -#include <QClipboard> -#include <QFileDialog> -#include <QDesktopServices> -#include <QThread> -#include <QSettings> -#include <QDesktopWidget> - -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> +#include "util.h" #ifdef WIN32 #ifdef _WIN32_WINNT @@ -42,9 +25,31 @@ #ifndef NOMINMAX #define NOMINMAX #endif -#include "shlwapi.h" -#include "shlobj.h" #include "shellapi.h" +#include "shlobj.h" +#include "shlwapi.h" +#endif + +#include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> +#include <QAbstractItemView> +#include <QApplication> +#include <QClipboard> +#include <QDateTime> +#include <QDesktopServices> +#include <QDesktopWidget> +#include <QDoubleValidator> +#include <QFileDialog> +#include <QFont> +#include <QLineEdit> +#include <QSettings> +#include <QTextDocument> // for Qt::mightBeRichText +#include <QThread> + +#if QT_VERSION < 0x050000 +#include <QUrl> +#else +#include <QUrlQuery> #endif namespace GUIUtil { @@ -112,6 +117,11 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) rv.label = i->second; fShouldReturnFalse = false; } + if (i->first == "message") + { + rv.message = i->second; + fShouldReturnFalse = false; + } else if (i->first == "amount") { if(!i->second.isEmpty()) @@ -148,6 +158,34 @@ bool parseBitcoinURI(QString uri, SendCoinsRecipient *out) return parseBitcoinURI(uriInstance, out); } +QString formatBitcoinURI(const SendCoinsRecipient &info) +{ + QString ret = QString("bitcoin:%1").arg(info.address); + int paramCount = 0; + + if (info.amount) + { + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount)); + paramCount++; + } + + if (!info.label.isEmpty()) + { + QString lbl(QUrl::toPercentEncoding(info.label)); + ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl); + paramCount++; + } + + if (!info.message.isEmpty()) + { + QString msg(QUrl::toPercentEncoding(info.message));; + ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg); + paramCount++; + } + + return ret; +} + bool isDust(const QString& address, qint64 amount) { CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); @@ -190,10 +228,9 @@ void copyEntryData(QAbstractItemView *view, int column, int role) } } -QString getSaveFileName(QWidget *parent, const QString &caption, - const QString &dir, - const QString &filter, - QString *selectedSuffixOut) +QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, + QString *selectedSuffixOut) { QString selectedFilter; QString myDir; @@ -209,7 +246,8 @@ QString getSaveFileName(QWidget *parent, const QString &caption, { myDir = dir; } - QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter); + /* Directly convert path to native OS path separators */ + QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter)); /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); @@ -240,6 +278,41 @@ QString getSaveFileName(QWidget *parent, const QString &caption, return result; } +QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, + QString *selectedSuffixOut) +{ + QString selectedFilter; + QString myDir; + if(dir.isEmpty()) // Default to user documents location + { +#if QT_VERSION < 0x050000 + myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + } + else + { + myDir = dir; + } + /* Directly convert path to native OS path separators */ + QString result = QDir::toNativeSeparators(QFileDialog::getOpenFileName(parent, caption, myDir, filter, &selectedFilter)); + + if(selectedSuffixOut) + { + /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ + QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); + QString selectedSuffix; + if(filter_re.exactMatch(selectedFilter)) + { + selectedSuffix = filter_re.cap(1); + } + *selectedSuffixOut = selectedSuffix; + } + return result; +} + Qt::ConnectionType blockingGUIThreadConnection() { if(QThread::currentThread() != qApp->thread()) @@ -527,7 +600,7 @@ HelpMessageBox::HelpMessageBox(QWidget *parent) : tr("Usage:") + "\n" + " bitcoin-qt [" + tr("command-line options") + "] " + "\n"; - coreOptions = QString::fromStdString(HelpMessage()); + coreOptions = QString::fromStdString(HelpMessage(HMM_BITCOIN_QT)); uiOptions = tr("UI options") + ":\n" + " -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" + diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 8472700f48..ddff2de4c4 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -1,19 +1,23 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef GUIUTIL_H #define GUIUTIL_H -#include <QString> -#include <QObject> #include <QMessageBox> +#include <QObject> +#include <QString> class SendCoinsRecipient; QT_BEGIN_NAMESPACE +class QAbstractItemView; +class QDateTime; class QFont; class QLineEdit; -class QWidget; -class QDateTime; class QUrl; -class QAbstractItemView; +class QWidget; QT_END_NAMESPACE /** Utility functions used by the Bitcoin Qt UI. @@ -32,9 +36,9 @@ namespace GUIUtil void setupAmountWidget(QLineEdit *widget, QWidget *parent); // Parse "bitcoin:" URI into recipient object, return true on successful parsing - // See Bitcoin URI definition discussion here: https://bitcointalk.org/index.php?topic=33490.0 bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out); bool parseBitcoinURI(QString uri, SendCoinsRecipient *out); + QString formatBitcoinURI(const SendCoinsRecipient &info); // Returns true if given address+amount meets "dust" definition bool isDust(const QString& address, qint64 amount); @@ -65,6 +69,19 @@ namespace GUIUtil const QString &dir=QString(), const QString &filter=QString(), QString *selectedSuffixOut=0); + /** Get open filename, convenience wrapper for QFileDialog::getOpenFileName. + + @param[in] parent Parent window (or 0) + @param[in] caption Window caption (or empty, for default) + @param[in] dir Starting directory (or empty, to default to documents directory) + @param[in] filter Filter specification such as "Comma Separated Files (*.csv)" + @param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0). + Can be useful when choosing the save file format based on suffix. + */ + QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, + QString *selectedSuffixOut); + /** Get connection type to call object slot in GUI thread with invokeMethod. The call will be blocking. @returns If called from the GUI thread, return a Qt::DirectConnection. diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 4a02ff89e7..3ecd96cc42 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -1,16 +1,20 @@ +// Copyright (c) 2011-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 "intro.h" #include "ui_intro.h" + #include "util.h" +#include <boost/filesystem.hpp> #include <QFileDialog> #include <QSettings> #include <QMessageBox> -#include <boost/filesystem.hpp> - /* Minimum free space (in bytes) needed for data directory */ -static const uint64 GB_BYTES = 1000000000LL; -static const uint64 BLOCK_CHAIN_SIZE = 10LL * GB_BYTES; +static const uint64_t GB_BYTES = 1000000000LL; +static const uint64_t BLOCK_CHAIN_SIZE = 10LL * GB_BYTES; /* Check free space asynchronously to prevent hanging the UI thread. @@ -56,7 +60,7 @@ void FreespaceChecker::check() namespace fs = boost::filesystem; QString dataDirStr = intro->getPathToCheck(); fs::path dataDir = fs::path(dataDirStr.toStdString()); - uint64 freeBytesAvailable = 0; + uint64_t freeBytesAvailable = 0; int replyStatus = ST_OK; QString replyMessage = tr("A new data directory will be created."); @@ -177,8 +181,8 @@ void Intro::pickDataDirectory(bool fIsTestnet) fs::create_directory(dataDir.toStdString()); break; } catch(fs::filesystem_error &e) { - QMessageBox::critical(0, QObject::tr("Bitcoin"), - QObject::tr("Error: Specified data directory \"%1\" can not be created.").arg(dataDir)); + QMessageBox::critical(0, tr("Bitcoin"), + tr("Error: Specified data directory \"%1\" can not be created.").arg(dataDir)); /* fall through, back to choosing screen */ } } diff --git a/src/qt/intro.h b/src/qt/intro.h index 8b09847abd..72693d5544 100644 --- a/src/qt/intro.h +++ b/src/qt/intro.h @@ -1,14 +1,19 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef INTRO_H #define INTRO_H #include <QDialog> -#include <QThread> #include <QMutex> +#include <QThread> + +class FreespaceChecker; namespace Ui { class Intro; } -class FreespaceChecker; /** Introduction screen (pre-GUI startup). Allows the user to choose a data directory, diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 7d419b6bd9..0dc56ac5dc 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -42,12 +42,7 @@ This product includes software developed by the OpenSSL Project for use in the O <context> <name>AddressBookPage</name> <message> - <location filename="../forms/addressbookpage.ui" line="+14"/> - <source>Address Book</source> - <translation>Address Book</translation> - </message> - <message> - <location line="+19"/> + <location filename="../forms/addressbookpage.ui" line="+30"/> <source>Double-click to edit address or label</source> <translation>Double-click to edit address or label</translation> </message> @@ -57,42 +52,32 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Create a new address</translation> </message> <message> - <location line="+14"/> + <location line="+3"/> + <source>&New</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+11"/> <source>Copy the currently selected address to the system clipboard</source> <translation>Copy the currently selected address to the system clipboard</translation> </message> <message> - <location line="-11"/> - <source>&New Address</source> - <translation>&New Address</translation> + <location line="+3"/> + <source>&Copy</source> + <translation type="unfinished"></translation> </message> <message> - <location filename="../addressbookpage.cpp" line="+63"/> - <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> + <location line="+52"/> + <source>C&lose</source> + <translation type="unfinished"></translation> </message> <message> - <location filename="../forms/addressbookpage.ui" line="+14"/> + <location filename="../addressbookpage.cpp" line="+72"/> <source>&Copy Address</source> <translation>&Copy Address</translation> </message> <message> - <location line="+11"/> - <source>Show &QR Code</source> - <translation>Show &QR Code</translation> - </message> - <message> - <location line="+11"/> - <source>Sign a message to prove you own a Bitcoin address</source> - <translation>Sign a message to prove you own a Bitcoin address</translation> - </message> - <message> - <location line="+3"/> - <source>Sign &Message</source> - <translation>Sign &Message</translation> - </message> - <message> - <location line="+25"/> + <location filename="../forms/addressbookpage.ui" line="-41"/> <source>Delete the currently selected address from the list</source> <translation>Delete the currently selected address from the list</translation> </message> @@ -107,27 +92,47 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Export</translation> </message> <message> - <location line="-44"/> - <source>Verify a message to ensure it was signed with a specified Bitcoin address</source> - <translation>Verify a message to ensure it was signed with a specified Bitcoin address</translation> + <location line="-27"/> + <source>&Delete</source> + <translation>&Delete</translation> </message> <message> - <location line="+3"/> - <source>&Verify Message</source> - <translation>&Verify Message</translation> + <location filename="../addressbookpage.cpp" line="-30"/> + <source>Choose the address to send coins to</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+14"/> - <source>&Delete</source> - <translation>&Delete</translation> + <location line="+1"/> + <source>Choose the address to receive coins with</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> + <source>C&hoose</source> + <translation type="unfinished"></translation> </message> <message> - <location filename="../addressbookpage.cpp" line="-5"/> + <location line="+6"/> + <source>Sending addresses</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Receiving addresses</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source> <translation>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</translation> </message> <message> - <location line="+13"/> + <location line="+4"/> + <source>These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> <source>Copy &Label</source> <translation>Copy &Label</translation> </message> @@ -137,14 +142,9 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Edit</translation> </message> <message> - <location line="+1"/> - <source>Send &Coins</source> - <translation>Send &Coins</translation> - </message> - <message> - <location line="+265"/> - <source>Export Address Book Data</source> - <translation>Export Address Book Data</translation> + <location line="+197"/> + <source>Export Address List</source> + <translation type="unfinished"></translation> </message> <message> <location line="+1"/> @@ -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,17 +324,17 @@ 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="+250"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> <message> - <location line="+246"/> + <location line="+254"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-321"/> + <location line="-324"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -354,17 +354,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Browse transaction history</translation> </message> <message> - <location line="+7"/> - <source>Edit the list of stored addresses and labels</source> - <translation>Edit the list of stored addresses and labels</translation> - </message> - <message> - <location line="-14"/> - <source>Show the list of addresses for receiving payments</source> - <translation>Show the list of addresses for receiving payments</translation> - </message> - <message> - <location line="+31"/> + <location line="+15"/> <source>E&xit</source> <translation>E&xit</translation> </message> @@ -379,12 +369,13 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Show information about Bitcoin</translation> </message> <message> + <location line="+3"/> <location line="+2"/> <source>About &Qt</source> <translation>About &Qt</translation> </message> <message> - <location line="+1"/> + <location line="+2"/> <source>Show information about Qt</source> <translation>Show information about Qt</translation> </message> @@ -409,7 +400,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Change Passphrase...</translation> </message> <message> - <location line="+251"/> + <location line="+259"/> <source>Importing blocks from disk...</source> <translation>Importing blocks from disk...</translation> </message> @@ -419,12 +410,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Reindexing blocks on disk...</translation> </message> <message> - <location line="-319"/> + <location line="-322"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> <message> - <location line="+52"/> + <location line="+47"/> <source>Modify configuration options for Bitcoin</source> <translation>Modify configuration options for Bitcoin</translation> </message> @@ -454,20 +445,20 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Verify message...</translation> </message> <message> - <location line="-183"/> + <location line="-180"/> <location line="+6"/> - <location line="+508"/> + <location line="+513"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> <message> - <location line="-514"/> + <location line="-519"/> <location line="+6"/> <source>Wallet</source> <translation>Wallet</translation> </message> <message> - <location line="+107"/> + <location line="+109"/> <source>&Send</source> <translation>&Send</translation> </message> @@ -477,18 +468,13 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>&Receive</translation> </message> <message> - <location line="+14"/> - <source>&Addresses</source> - <translation>&Addresses</translation> - </message> - <message> - <location line="+23"/> + <location line="+28"/> <location line="+2"/> <source>&About Bitcoin</source> <translation>&About Bitcoin</translation> </message> <message> - <location line="+10"/> + <location line="+14"/> <location line="+2"/> <source>&Show / Hide</source> <translation>&Show / Hide</translation> @@ -514,12 +500,12 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation> </message> <message> - <location line="+28"/> + <location line="+35"/> <source>&File</source> <translation>&File</translation> </message> <message> - <location line="+7"/> + <location line="+10"/> <source>&Settings</source> <translation>&Settings</translation> </message> @@ -534,19 +520,44 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Tabs toolbar</translation> </message> <message> - <location line="-228"/> - <location line="+288"/> + <location line="-235"/> + <location line="+294"/> <source>[testnet]</source> <translation>[testnet]</translation> </message> <message> - <location line="-5"/> + <location line="-177"/> + <source>Request payments (generates QR codes and bitcoin: URIs)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+63"/> + <source>&Used sending addresses...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Show the list of used sending addresses and labels</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Used &receiving addresses...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Show the list of used receiving addresses and labels</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+106"/> <location line="+5"/> <source>Bitcoin client</source> <translation>Bitcoin client</translation> </message> <message numerus="yes"> - <location line="+121"/> + <location line="+120"/> <source>%n active connection(s) to Bitcoin network</source> <translation> <numerusform>%n active connection to Bitcoin network</numerusform> @@ -608,7 +619,7 @@ This product includes software developed by the OpenSSL Project for use in the O <translation>Transactions after this will not yet be visible.</translation> </message> <message> - <location line="+22"/> + <location line="+27"/> <source>Error</source> <translation>Error</translation> </message> @@ -623,12 +634,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="+77"/> <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="-152"/> <source>Up to date</source> <translation>Up to date</translation> </message> @@ -638,7 +649,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="+124"/> <source>Confirm transaction fee</source> <translation>Confirm transaction fee</translation> </message> @@ -666,19 +677,17 @@ 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="+17"/> + <location line="+45"/> <source>Wallet is <b>encrypted</b> and currently <b>unlocked</b></source> <translation>Wallet is <b>encrypted</b> and currently <b>unlocked</b></translation> </message> @@ -696,7 +705,7 @@ Address: %4 <context> <name>ClientModel</name> <message> - <location filename="../clientmodel.cpp" line="+105"/> + <location filename="../clientmodel.cpp" line="+115"/> <source>Network Alert</source> <translation>Network Alert</translation> </message> @@ -715,18 +724,18 @@ Address: %4 </message> <message> <location line="+10"/> - <source>The label associated with this address book entry</source> - <translation>The label associated with this address book entry</translation> + <source>The label associated with this address list entry</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> - <source>&Address</source> - <translation>&Address</translation> + <location line="+17"/> + <source>The address associated with this address list entry. This can only be modified for sending addresses.</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> - <source>The address associated with this address book entry. This can only be modified for sending addresses.</source> - <translation>The address associated with this address book entry. This can only be modified for sending addresses.</translation> + <location line="-10"/> + <source>&Address</source> + <translation>&Address</translation> </message> <message> <location filename="../editaddressdialog.cpp" line="+21"/> @@ -800,7 +809,7 @@ Address: %4 <context> <name>GUIUtil::HelpMessageBox</name> <message> - <location filename="../guiutil.cpp" line="+517"/> + <location filename="../guiutil.cpp" line="+558"/> <location line="+13"/> <source>Bitcoin-Qt</source> <translation>Bitcoin-Qt</translation> @@ -879,7 +888,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 +1076,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,86 +1186,105 @@ 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="+392"/> + <source>URI handling</source> + <translation type="unfinished">URI handling</translation> </message> -</context> -<context> - <name>QObject</name> <message> - <location filename="../bitcoin.cpp" line="+92"/> - <location filename="../intro.cpp" line="-32"/> - <source>Bitcoin</source> - <translation>Bitcoin</translation> + <location line="+1"/> + <source>URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source> + <translation type="unfinished">URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</translation> + </message> + <message> + <location line="+69"/> + <source>Requested payment amount of %1 is too small (considered dust).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> + <location line="+37"/> + <source>Payment request error</source> + <translation type="unfinished"></translation> </message> <message> <location line="+1"/> - <source>Error: Specified data directory "%1" does not exist.</source> - <translation>Error: Specified data directory "%1" does not exist.</translation> + <source>Insecure requests to custom payment scripts unsupported</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> + <location line="+38"/> + <source>Refund from %1</source> + <translation type="unfinished"></translation> </message> -</context> -<context> - <name>QRCodeDialog</name> <message> - <location filename="../forms/qrcodedialog.ui" line="+14"/> - <source>QR Code Dialog</source> - <translation>QR Code Dialog</translation> + <location line="+42"/> + <source>Error communicating with %1: %2</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+59"/> - <source>Request Payment</source> - <translation>Request Payment</translation> + <location line="+31"/> + <source>Bad response from server %1</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+56"/> - <source>Amount:</source> - <translation>Amount:</translation> + <location line="+32"/> + <source>Payment acknowledged</source> + <translation type="unfinished"></translation> </message> <message> - <location line="-44"/> - <source>Label:</source> - <translation>Label:</translation> + <location line="-58"/> + <location line="+30"/> + <location line="+17"/> + <source>Network request error</source> + <translation type="unfinished"></translation> </message> +</context> +<context> + <name>QObject</name> <message> - <location line="+19"/> - <source>Message:</source> - <translation>Message:</translation> + <location filename="../bitcoin.cpp" line="+114"/> + <location line="+5"/> + <location filename="../intro.cpp" line="-32"/> + <source>Bitcoin</source> + <translation>Bitcoin</translation> </message> <message> - <location line="+71"/> - <source>&Save As...</source> - <translation>&Save As...</translation> + <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 filename="../qrcodedialog.cpp" line="+64"/> - <source>Error encoding URI into QR Code.</source> - <translation>Error encoding URI into QR Code.</translation> + <location line="+4"/> + <source>Error: Invalid combination of -regtest and -testnet.</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+40"/> - <source>The entered amount is invalid, please check.</source> - <translation>The entered amount is invalid, please check.</translation> + <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> +</context> +<context> + <name>QRImageWidget</name> <message> - <location line="+23"/> - <source>Resulting URI too long, try to reduce the text for label / message.</source> - <translation>Resulting URI too long, try to reduce the text for label / message.</translation> + <location filename="../receiverequestdialog.cpp" line="+32"/> + <source>&Save Image...</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+25"/> + <location line="+3"/> + <source>&Copy Image</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+28"/> <source>Save QR Code</source> - <translation>Save QR Code</translation> + <translation type="unfinished">Save QR Code</translation> </message> <message> <location line="+0"/> <source>PNG Images (*.png)</source> - <translation>PNG Images (*.png)</translation> + <translation type="unfinished">PNG Images (*.png)</translation> </message> </context> <context> @@ -1276,7 +1304,7 @@ Address: %4 <location line="+53"/> <location line="+23"/> <location line="+23"/> - <location filename="../rpcconsole.cpp" line="+345"/> + <location filename="../rpcconsole.cpp" line="+352"/> <source>N/A</source> <translation>N/A</translation> </message> @@ -1361,7 +1389,32 @@ Address: %4 <translation>&Console</translation> </message> <message> - <location line="-260"/> + <location line="+72"/> + <source>&Network Traffic</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+52"/> + <source>&Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+13"/> + <source>Totals</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+64"/> + <source>In:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+80"/> + <source>Out:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="-541"/> <source>Build date</source> <translation>Build date</translation> </message> @@ -1405,18 +1458,184 @@ Address: %4 <source>Type <b>help</b> for an overview of available commands.</source> <translation>Type <b>help</b> for an overview of available commands.</translation> </message> + <message> + <location line="+128"/> + <source>%1 B</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>%1 KB</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>%1 MB</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>%1 GB</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>%1 m</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> + <source>%1 h</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>%1 h %2 m</source> + <translation type="unfinished"></translation> + </message> </context> <context> - <name>SendCoinsDialog</name> + <name>ReceiveCoinsDialog</name> + <message> + <location filename="../forms/receivecoinsdialog.ui" line="+22"/> + <source>&Amount:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+16"/> + <source>The amount to request</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>&Label:</source> + <translation type="unfinished">&Label:</translation> + </message> + <message> + <location line="+10"/> + <source>The label to associate with the receiving address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>&Message:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+10"/> + <source>The message to attach to payment request</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+14"/> + <source>Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>R&euse an existing receiving address (not recommended)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>Use this form to request payments. All fields are optional.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+17"/> + <source>Clear all fields of the form.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Clear</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+36"/> + <source>&Request payment</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ReceiveRequestDialog</name> + <message> + <location filename="../forms/receiverequestdialog.ui" line="+29"/> + <source>QR Code</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+46"/> + <source>Copy &URI</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>Copy &Address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>&Copy Image</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>&Save Image...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../receiverequestdialog.cpp" line="+58"/> + <source>Request payment to %1</source> + <translation type="unfinished"></translation> + </message> <message> - <location filename="../forms/sendcoinsdialog.ui" line="+14"/> - <location filename="../sendcoinsdialog.cpp" line="+128"/> - <location line="+5"/> - <location line="+5"/> - <location line="+5"/> <location line="+6"/> + <source>Payment information</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>URI</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Address</source> + <translation type="unfinished">Address</translation> + </message> + <message> + <location line="+2"/> + <source>Amount</source> + <translation type="unfinished">Amount</translation> + </message> + <message> + <location line="+2"/> + <source>Label</source> + <translation type="unfinished">Label</translation> + </message> + <message> + <location line="+2"/> + <source>Message</source> + <translation type="unfinished">Message</translation> + </message> + <message> + <location line="+10"/> + <source>Resulting URI too long, try to reduce the text for label / message.</source> + <translation type="unfinished">Resulting URI too long, try to reduce the text for label / message.</translation> + </message> + <message> <location line="+5"/> - <location line="+5"/> + <source>Error encoding URI into QR Code.</source> + <translation type="unfinished">Error encoding URI into QR Code.</translation> + </message> +</context> +<context> + <name>SendCoinsDialog</name> + <message> + <location filename="../forms/sendcoinsdialog.ui" line="+14"/> + <location filename="../sendcoinsdialog.cpp" line="+140"/> + <location line="+213"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -1432,8 +1651,8 @@ Address: %4 </message> <message> <location line="+20"/> - <source>Remove all transaction fields</source> - <translation>Remove all transaction fields</translation> + <source>Clear all fields of the form.</source> + <translation type="unfinished"></translation> </message> <message> <location line="+3"/> @@ -1461,99 +1680,111 @@ 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="-154"/> <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> - </message> - <message> - <location line="+0"/> - <source> and </source> - <translation> and </translation> + <location line="-90"/> + <location line="+5"/> + <location line="+5"/> + <source>%1 to %2</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+23"/> + <location line="+26"/> <source>The recipient address is not valid, please recheck.</source> <translation>The recipient address is not valid, please recheck.</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>The amount to pay must be larger than 0.</source> <translation>The amount to pay must be larger than 0.</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>The amount exceeds your balance.</source> <translation>The amount exceeds your balance.</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>The total exceeds your balance when the %1 transaction fee is included.</source> <translation>The total exceeds your balance when the %1 transaction fee is included.</translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <source>Duplicate address found, can only send to each address once per send operation.</source> <translation>Duplicate address found, can only send to each address once per send operation.</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>Error: Transaction creation failed!</source> <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="+144"/> + <source>Payment request expired</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+8"/> + <source>Invalid payment address %1</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>SendCoinsEntry</name> <message> - <location filename="../forms/sendcoinsentry.ui" line="+14"/> - <source>Form</source> - <translation>Form</translation> - </message> - <message> - <location line="+15"/> + <location filename="../forms/sendcoinsentry.ui" line="+33"/> + <location line="+585"/> <source>A&mount:</source> <translation>A&mount:</translation> </message> <message> - <location line="+13"/> + <location line="-572"/> + <location line="+585"/> <source>Pay &To:</source> <translation>Pay &To:</translation> </message> <message> - <location line="+34"/> + <location line="-551"/> <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> <message> - <location line="-78"/> + <location filename="../forms/sendcoinsentry.ui" line="-18"/> <source>&Label:</source> <translation>&Label:</translation> </message> <message> <location line="+28"/> - <source>Choose address from address book</source> - <translation>Choose address from address book</translation> + <source>Choose previously used address</source> + <translation type="unfinished"></translation> </message> <message> <location line="+10"/> @@ -1576,6 +1807,16 @@ Address: %4 <translation>Remove this recipient</translation> </message> <message> + <location line="+16"/> + <source>Enter a label for this address to add it to the list of used addresses</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+465"/> + <source>Memo:</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> @@ -1606,8 +1847,8 @@ Address: %4 <message> <location line="+10"/> <location line="+213"/> - <source>Choose an address from the address book</source> - <translation>Choose an address from the address book</translation> + <source>Choose previously used address</source> + <translation type="unfinished"></translation> </message> <message> <location line="-203"/> @@ -1708,7 +1949,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 +2018,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> @@ -1788,9 +2029,17 @@ Address: %4 </message> </context> <context> + <name>TrafficGraphWidget</name> + <message> + <location filename="../trafficgraphwidget.cpp" line="+75"/> + <source>KB/s</source> + <translation type="unfinished"></translation> + </message> +</context> +<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 +2115,12 @@ Address: %4 <location line="+12"/> <location line="+45"/> <location line="+17"/> - <location line="+30"/> + <location line="+48"/> <source>Credit</source> <translation>Credit</translation> </message> <message numerus="yes"> - <location line="-102"/> + <location line="-120"/> <source>matures in %n more block(s)</source> <translation> <numerusform>matures in %n more block</numerusform> @@ -1887,12 +2136,12 @@ Address: %4 <location line="+44"/> <location line="+8"/> <location line="+15"/> - <location line="+30"/> + <location line="+48"/> <source>Debit</source> <translation>Debit</translation> </message> <message> - <location line="-39"/> + <location line="-57"/> <source>Transaction fee</source> <translation>Transaction fee</translation> </message> @@ -1917,12 +2166,17 @@ Address: %4 <translation>Transaction ID</translation> </message> <message> - <location line="+3"/> - <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> + <location line="+13"/> + <source>Merchant</source> + <translation type="unfinished"></translation> </message> <message> <location line="+7"/> + <source>Generated coins must mature %1 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 type="unfinished"></translation> + </message> + <message> + <location line="+8"/> <source>Debug information</source> <translation>Debug information</translation> </message> @@ -1952,7 +2206,7 @@ Address: %4 <translation>false</translation> </message> <message> - <location line="-209"/> + <location line="-227"/> <source>, has not been successfully broadcast yet</source> <translation>, has not been successfully broadcast yet</translation> </message> @@ -1986,7 +2240,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 +2533,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 +2551,7 @@ Address: %4 <translation>Export the data in the current tab to a file</translation> </message> <message> - <location line="+197"/> + <location line="+183"/> <source>Backup Wallet</source> <translation>Backup Wallet</translation> </message> @@ -2330,22 +2584,17 @@ Address: %4 <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+101"/> + <location filename="../bitcoinstrings.cpp" line="+102"/> <source>Bitcoin version</source> <translation>Bitcoin version</translation> </message> <message> - <location line="+104"/> + <location line="+107"/> <source>Usage:</source> <translation>Usage:</translation> </message> <message> - <location line="-30"/> - <source>Send command to -server or bitcoind</source> - <translation>Send command to -server or bitcoind</translation> - </message> - <message> - <location line="-23"/> + <location line="-55"/> <source>List commands</source> <translation>List commands</translation> </message> @@ -2400,12 +2649,12 @@ Address: %4 <translation>Specify your own public address</translation> </message> <message> - <location line="+3"/> + <location line="+4"/> <source>Threshold for disconnecting misbehaving peers (default: 100)</source> <translation>Threshold for disconnecting misbehaving peers (default: 100)</translation> </message> <message> - <location line="-139"/> + <location line="-142"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</translation> </message> @@ -2425,17 +2674,17 @@ Address: %4 <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="+77"/> + <location line="+79"/> <source>Run in the background as a daemon and accept commands</source> <translation>Run in the background as a daemon and accept commands</translation> </message> <message> - <location line="+38"/> + <location line="+40"/> <source>Use the test network</source> <translation>Use the test network</translation> </message> <message> - <location line="-114"/> + <location line="-118"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> @@ -2465,7 +2714,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. </translation> </message> <message> - <location line="+17"/> + <location line="+12"/> + <source>Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> <source>An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s</source> <translation>An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s</translation> </message> @@ -2545,6 +2799,11 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Attempt to recover private keys from a corrupt wallet.dat</translation> </message> <message> + <location line="+1"/> + <source>Bitcoin RPC client version</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+2"/> <source>Block creation options:</source> <translation>Block creation options:</translation> @@ -2555,7 +2814,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Connect only to the specified node(s)</translation> </message> <message> - <location line="+3"/> + <location line="+2"/> + <source>Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Corrupted block database detected</source> <translation>Corrupted block database detected</translation> </message> @@ -2685,7 +2949,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Incorrect or no genesis block found. Wrong datadir for network?</translation> </message> <message> - <location line="+18"/> + <location line="+3"/> + <source>Invalid -onion address: '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+15"/> <source>Not enough file descriptors available.</source> <translation>Not enough file descriptors available.</translation> </message> @@ -2695,7 +2964,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Rebuild block chain index from current blk000??.dat files</translation> </message> <message> - <location line="+16"/> + <location line="+6"/> + <source>Send command to Bitcoin server</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+10"/> <source>Set the number of threads to service RPC calls (default: 4)</source> <translation>Set the number of threads to service RPC calls (default: 4)</translation> </message> @@ -2705,7 +2979,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Specify wallet file (within data directory)</translation> </message> <message> - <location line="+20"/> + <location line="+2"/> + <source>Start Bitcoin server</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+12"/> + <source>Usage (deprecated, use bitcoin-cli):</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+8"/> <source>Verifying blocks...</source> <translation>Verifying blocks...</translation> </message> @@ -2725,12 +3009,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>You need to rebuild the database using -reindex to change -txindex</translation> </message> <message> - <location line="-76"/> + <location line="-78"/> <source>Imports blocks from external blk000??.dat file</source> <translation>Imports blocks from external blk000??.dat file</translation> </message> <message> - <location line="-96"/> + <location line="-98"/> <source>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</source> <translation>Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)</translation> </message> @@ -2740,17 +3024,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)</translation> </message> <message> - <location line="+81"/> + <location line="+83"/> <source>Information</source> <translation>Information</translation> </message> <message> - <location line="+3"/> - <source>Invalid -tor address: '%s'</source> - <translation>Invalid -tor address: '%s'</translation> - </message> - <message> - <location line="+1"/> + <location line="+4"/> <source>Invalid amount for -minrelaytxfee=<amount>: '%s'</source> <translation>Invalid amount for -minrelaytxfee=<amount>: '%s'</translation> </message> @@ -2845,7 +3124,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Specify connection timeout in milliseconds (default: 5000)</translation> </message> <message> - <location line="+5"/> + <location line="+6"/> <source>System error: </source> <translation>System error: </translation> </message> @@ -2865,7 +3144,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Transaction too large</translation> </message> <message> - <location line="+7"/> + <location line="+8"/> <source>Use UPnP to map the listening port (default: 0)</source> <translation>Use UPnP to map the listening port (default: 0)</translation> </message> @@ -2900,32 +3179,32 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>wallet.dat corrupt, salvage failed</translation> </message> <message> - <location line="-52"/> + <location line="-54"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-68"/> + <location line="-70"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Allow JSON-RPC connections from specified IP address</translation> </message> <message> - <location line="+77"/> + <location line="+79"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Send commands to node running on <ip> (default: 127.0.0.1)</translation> </message> <message> - <location line="-124"/> + <location line="-126"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+152"/> + <location line="+155"/> <source>Upgrade wallet to latest format</source> <translation>Upgrade wallet to latest format</translation> </message> <message> - <location line="-22"/> + <location line="-23"/> <source>Set key pool size to <n> (default: 100)</source> <translation>Set key pool size to <n> (default: 100)</translation> </message> @@ -2935,12 +3214,12 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Rescan the block chain for missing wallet transactions</translation> </message> <message> - <location line="+36"/> + <location line="+38"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Use OpenSSL (https) for JSON-RPC connections</translation> </message> <message> - <location line="-27"/> + <location line="-29"/> <source>Server certificate file (default: server.cert)</source> <translation>Server certificate file (default: server.cert)</translation> </message> @@ -2950,12 +3229,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Server private key (default: server.pem)</translation> </message> <message> - <location line="-159"/> - <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> - <translation>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> - </message> - <message> - <location line="+174"/> + <location line="+16"/> <source>This help message</source> <translation>This help message</translation> </message> @@ -2965,17 +3239,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Unable to bind to %s on this computer (bind returned error %d, %s)</translation> </message> <message> - <location line="-93"/> + <location line="-95"/> <source>Connect through socks proxy</source> <translation>Connect through socks proxy</translation> </message> <message> - <location line="-10"/> + <location line="-11"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+56"/> + <location line="+58"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> @@ -2990,17 +3264,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Error loading wallet.dat: Wallet requires newer version of Bitcoin</translation> </message> <message> - <location line="+96"/> + <location line="+98"/> <source>Wallet needed to be rewritten: restart Bitcoin to complete</source> <translation>Wallet needed to be rewritten: restart Bitcoin to complete</translation> </message> <message> - <location line="-98"/> + <location line="-100"/> <source>Error loading wallet.dat</source> <translation>Error loading wallet.dat</translation> </message> <message> - <location line="+29"/> + <location line="+30"/> <source>Invalid -proxy address: '%s'</source> <translation>Invalid -proxy address: '%s'</translation> </message> @@ -3015,7 +3289,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Unknown -socks proxy version requested: %i</translation> </message> <message> - <location line="-98"/> + <location line="-100"/> <source>Cannot resolve -bind address: '%s'</source> <translation>Cannot resolve -bind address: '%s'</translation> </message> @@ -3025,7 +3299,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Cannot resolve -externalip address: '%s'</translation> </message> <message> - <location line="+45"/> + <location line="+46"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> </message> @@ -3045,7 +3319,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Loading block index...</translation> </message> <message> - <location line="-58"/> + <location line="-60"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Add a node to connect to and attempt to keep the connection open</translation> </message> @@ -3055,7 +3329,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Unable to bind to %s on this computer. Bitcoin is probably already running.</translation> </message> <message> - <location line="+67"/> + <location line="+69"/> <source>Fee per KB to add to transactions you send</source> <translation>Fee per KB to add to transactions you send</translation> </message> @@ -3065,7 +3339,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Loading wallet...</translation> </message> <message> - <location line="-53"/> + <location line="-54"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> @@ -3075,7 +3349,7 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Cannot write default address</translation> </message> <message> - <location line="+65"/> + <location line="+66"/> <source>Rescanning...</source> <translation>Rescanning...</translation> </message> @@ -3085,17 +3359,17 @@ for example: alertnotify=echo %%s | mail -s "Bitcoin Alert" admin@foo. <translation>Done loading</translation> </message> <message> - <location line="+84"/> + <location line="+85"/> <source>To use the %s option</source> <translation>To use the %s option</translation> </message> <message> - <location line="-76"/> + <location line="-77"/> <source>Error</source> <translation>Error</translation> </message> <message> - <location line="-31"/> + <location line="-33"/> <source>You must set rpcpassword=<password> in the configuration file: %s If the file does not exist, create it with owner-readable-only file permissions.</source> diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h index 765b004750..b21a61cb07 100644 --- a/src/qt/macdockiconhandler.h +++ b/src/qt/macdockiconhandler.h @@ -1,12 +1,16 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef MACDOCKICONHANDLER_H #define MACDOCKICONHANDLER_H -#include <QObject> #include <QMainWindow> +#include <QObject> QT_BEGIN_NAMESPACE -class QMenu; class QIcon; +class QMenu; class QWidget; QT_END_NAMESPACE diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h index cd8064c61c..bc335edede 100644 --- a/src/qt/macnotificationhandler.h +++ b/src/qt/macnotificationhandler.h @@ -1,5 +1,10 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef MACNOTIFICATIONHANDLER_H #define MACNOTIFICATIONHANDLER_H + #include <QObject> /** Macintosh-specific notification handler (supports UserNotificationCenter and Growl). diff --git a/src/qt/monitoreddatamapper.cpp b/src/qt/monitoreddatamapper.cpp index 4c201834cb..5931c53872 100644 --- a/src/qt/monitoreddatamapper.cpp +++ b/src/qt/monitoreddatamapper.cpp @@ -1,8 +1,12 @@ +// Copyright (c) 2011-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 "monitoreddatamapper.h" -#include <QWidget> #include <QMetaObject> #include <QMetaProperty> +#include <QWidget> MonitoredDataMapper::MonitoredDataMapper(QObject *parent) : QDataWidgetMapper(parent) diff --git a/src/qt/monitoreddatamapper.h b/src/qt/monitoreddatamapper.h index de55c86fcd..b3237d3e09 100644 --- a/src/qt/monitoreddatamapper.h +++ b/src/qt/monitoreddatamapper.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef MONITOREDDATAMAPPER_H #define MONITOREDDATAMAPPER_H diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index 903c54b39d..c9f6834ec2 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -1,26 +1,34 @@ +// Copyright (c) 2011-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 "notificator.h" + #include <QApplication> -#include <QMetaType> -#include <QVariant> +#include <QByteArray> #include <QIcon> +#include <QImageWriter> +#include <QMessageBox> +#include <QMetaType> #include <QStyle> -#include <QByteArray> #include <QSystemTrayIcon> -#include <QMessageBox> #include <QTemporaryFile> -#include <QImageWriter> +#include <QVariant> + +#ifdef Q_OS_MAC +#include "macnotificationhandler.h" + +#include <ApplicationServices/ApplicationServices.h> +#endif #ifdef USE_DBUS -#include <QtDBus> #include <stdint.h> -#endif -#ifdef Q_OS_MAC -#include <ApplicationServices/ApplicationServices.h> -#include "macnotificationhandler.h" +#include <QtDBus> #endif + // https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128 const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128; diff --git a/src/qt/notificator.h b/src/qt/notificator.h index 6c9a46bcf7..d6298c3c0d 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef NOTIFICATOR_H #define NOTIFICATOR_H @@ -5,11 +9,12 @@ #include "bitcoin-config.h" #endif -#include <QObject> #include <QIcon> +#include <QObject> QT_BEGIN_NAMESPACE class QSystemTrayIcon; + #ifdef USE_DBUS class QDBusInterface; #endif diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp new file mode 100644 index 0000000000..803a3c9ddb --- /dev/null +++ b/src/qt/openuridialog.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2011-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 "openuridialog.h" +#include "ui_openuridialog.h" + +#include "guiutil.h" +#include "walletmodel.h" + +#include <QUrl> + +OpenURIDialog::OpenURIDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::OpenURIDialog) +{ + ui->setupUi(this); +#if QT_VERSION >= 0x040700 + ui->uriEdit->setPlaceholderText("bitcoin:"); +#endif +} + +OpenURIDialog::~OpenURIDialog() +{ + delete ui; +} + +QString OpenURIDialog::getURI() +{ + return ui->uriEdit->text(); +} + +void OpenURIDialog::accept() +{ + SendCoinsRecipient rcp; + if(GUIUtil::parseBitcoinURI(getURI(), &rcp)) + { + /* Only accept value URIs */ + QDialog::accept(); + } else { + ui->uriEdit->setValid(false); + } +} + +void OpenURIDialog::on_selectFileButton_clicked() +{ + QString filename = GUIUtil::getOpenFileName(this, tr("Select payment request file to open"), "", "", NULL); + if(filename.isEmpty()) + return; + QUrl fileUri = QUrl::fromLocalFile(filename); + ui->uriEdit->setText("bitcoin:?request=" + QUrl::toPercentEncoding(fileUri.toString())); +} diff --git a/src/qt/openuridialog.h b/src/qt/openuridialog.h new file mode 100644 index 0000000000..3b9ff0a8e1 --- /dev/null +++ b/src/qt/openuridialog.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef OPENURIDIALOG_H +#define OPENURIDIALOG_H + +#include <QDialog> + +namespace Ui { +class OpenURIDialog; +} + +class OpenURIDialog : public QDialog +{ + Q_OBJECT + +public: + explicit OpenURIDialog(QWidget *parent = 0); + ~OpenURIDialog(); + + QString getURI(); + +protected slots: + void accept(); + +private slots: + void on_selectFileButton_clicked(); + +private: + Ui::OpenURIDialog *ui; +}; + +#endif // OPENURIDIALOG_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 7ccda6cdd4..1e91a877a9 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #if defined(HAVE_CONFIG_H) #include "bitcoin-config.h" #endif @@ -7,9 +11,10 @@ #include "bitcoinunits.h" #include "monitoreddatamapper.h" -#include "netbase.h" #include "optionsmodel.h" +#include "netbase.h" + #include <QDir> #include <QIntValidator> #include <QLocale> diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index d64ed0b57f..05234f645b 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -1,14 +1,19 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef OPTIONSDIALOG_H #define OPTIONSDIALOG_H #include <QDialog> +class MonitoredDataMapper; +class OptionsModel; +class QValidatedLineEdit; + namespace Ui { class OptionsDialog; } -class OptionsModel; -class MonitoredDataMapper; -class QValidatedLineEdit; /** Preferences dialog. */ class OptionsDialog : public QDialog diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 95efc58320..65c017f088 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #if defined(HAVE_CONFIG_H) #include "bitcoin-config.h" #endif @@ -5,9 +9,12 @@ #include "optionsmodel.h" #include "bitcoinunits.h" +#include "guiutil.h" + #include "init.h" +#include "main.h" +#include "net.h" #include "walletdb.h" -#include "guiutil.h" #include <QSettings> @@ -193,7 +200,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return QVariant(5); } case Fee: - return QVariant(nTransactionFee); + return QVariant((qint64) nTransactionFee); case DisplayUnit: return QVariant(nDisplayUnit); case DisplayAddresses: @@ -267,7 +274,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case Fee: nTransactionFee = value.toLongLong(); - settings.setValue("nTransactionFee", nTransactionFee); + settings.setValue("nTransactionFee", (qint64) nTransactionFee); break; case DisplayUnit: nDisplayUnit = value.toInt(); @@ -292,7 +299,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in qint64 OptionsModel::getTransactionFee() { - return nTransactionFee; + return (qint64) nTransactionFee; } bool OptionsModel::getProxySettings(QString& proxyIP, quint16 &proxyPort) const diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 173db0fe0a..2d41cf889d 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef OPTIONSMODEL_H #define OPTIONSMODEL_H diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index ddd8891335..016097c5a0 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -1,14 +1,18 @@ +// Copyright (c) 2011-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 "overviewpage.h" #include "ui_overviewpage.h" -#include "clientmodel.h" -#include "walletmodel.h" #include "bitcoinunits.h" +#include "clientmodel.h" +#include "guiconstants.h" +#include "guiutil.h" #include "optionsmodel.h" -#include "transactiontablemodel.h" #include "transactionfilterproxy.h" -#include "guiutil.h" -#include "guiconstants.h" +#include "transactiontablemodel.h" +#include "walletmodel.h" #include <QAbstractItemDelegate> #include <QPainter> diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 59ba3c66bb..2507a3fb31 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -1,15 +1,20 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef OVERVIEWPAGE_H #define OVERVIEWPAGE_H #include <QWidget> +class ClientModel; +class TransactionFilterProxy; +class TxViewDelegate; +class WalletModel; + namespace Ui { class OverviewPage; } -class ClientModel; -class WalletModel; -class TxViewDelegate; -class TransactionFilterProxy; QT_BEGIN_NAMESPACE class QModelIndex; diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index 50fb1bd777..e369734a98 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -1,18 +1,22 @@ +// Copyright (c) 2011-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. + // // Wraps dumb protocol buffer paymentRequest // with some extra methods // -#include <QDateTime> -#include <QDebug> -#include <QSslCertificate> +#include "paymentrequestplus.h" + +#include <stdexcept> #include <openssl/x509.h> #include <openssl/x509_vfy.h> +#include <QDateTime> +#include <QDebug> +#include <QSslCertificate> -#include <stdexcept> - -#include "paymentrequestplus.h" class SSLVerifyError : public std::runtime_error { @@ -75,12 +79,11 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c digestAlgorithm = EVP_sha1(); } else if (paymentRequest.pki_type() == "none") { - if (fDebug) - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none"; + qDebug() << "PaymentRequestPlus::getMerchant : Payment request: pki_type == none"; return false; } else { - qDebug() << "PaymentRequestPlus::getMerchant : Payment request: unknown pki_type " << paymentRequest.pki_type().c_str(); + qDebug() << "PaymentRequestPlus::getMerchant : Payment request: unknown pki_type " << QString::fromStdString(paymentRequest.pki_type()); return false; } @@ -152,7 +155,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/paymentrequestplus.h b/src/qt/paymentrequestplus.h index 846d256aca..8c126b1fad 100644 --- a/src/qt/paymentrequestplus.h +++ b/src/qt/paymentrequestplus.h @@ -1,13 +1,18 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef PAYMENTREQUESTPLUS_H #define PAYMENTREQUESTPLUS_H +#include "paymentrequest.pb.h" + +#include "base58.h" + #include <QByteArray> #include <QList> #include <QString> -#include "base58.h" -#include "paymentrequest.pb.h" - // // Wraps dumb protocol buffer paymentRequest // with extra methods diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 6c10d8a04d..34da0f90ec 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -1,7 +1,24 @@ -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-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 "paymentserver.h" + +#include "bitcoinunits.h" +#include "guiconstants.h" +#include "guiutil.h" +#include "optionsmodel.h" +#include "paymentserver.h" +#include "walletmodel.h" + +#include "base58.h" +#include "ui_interface.h" +#include "wallet.h" + +#include <cstdlib> + +#include <openssl/x509.h> +#include <openssl/x509_vfy.h> #include <QApplication> #include <QByteArray> #include <QDataStream> @@ -13,8 +30,6 @@ #include <QList> #include <QLocalServer> #include <QLocalSocket> -#include <QStringList> -#include <QTextDocument> #include <QNetworkAccessManager> #include <QNetworkProxy> #include <QNetworkReply> @@ -22,34 +37,22 @@ #include <QSslCertificate> #include <QSslError> #include <QSslSocket> +#include <QStringList> +#include <QTextDocument> + #if QT_VERSION < 0x050000 #include <QUrl> #else #include <QUrlQuery> #endif -#include <cstdlib> - -#include <openssl/x509.h> -#include <openssl/x509_vfy.h> - -#include "base58.h" -#include "bitcoinunits.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "paymentserver.h" -#include "ui_interface.h" -#include "util.h" -#include "wallet.h" -#include "walletmodel.h" - 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 +76,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. @@ -88,9 +91,7 @@ static QList<QString> savedPaymentRequests; static void ReportInvalidCertificate(const QSslCertificate& cert) { - if (fDebug) { - qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName); - } + qDebug() << "ReportInvalidCertificate : Payment server found an invalid certificate: " << cert.subjectInfo(QSslCertificate::CommonName); } // @@ -161,8 +162,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) continue; } } - if (fDebug) - qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates"; + qDebug() << "PaymentServer::LoadRootCAs : Loaded " << nRootCerts << " root certificates"; // Project for another day: // Fetch certificate revocation lists, and add them to certStore. @@ -190,7 +190,7 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) if (arg.startsWith("-")) continue; - if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: + if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI { savedPaymentRequests.append(arg); @@ -221,9 +221,9 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) } else { - qDebug() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << argv[i]; // Printing to debug.log is about the best we can do here, the // GUI hasn't started yet so we can't pop up a message box. + qDebug() << "PaymentServer::ipcSendCommandLine : Payment request file does not exist: " << arg; } } @@ -247,17 +247,24 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) delete socket; fResult = true; } + return fResult; } -PaymentServer::PaymentServer(QObject* parent, - bool startLocalServer) : QObject(parent), saveURIs(true) +PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : + QObject(parent), + saveURIs(true), + uriServer(0), + netManager(0), + optionsModel(0) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; - // Install global event filter to catch QFileOpenEvents on the mac (sent when you click bitcoin: links) + // Install global event filter to catch QFileOpenEvents + // on Mac: sent when you click bitcoin: links + // other OSes: helpful when dealing with payment request files (in the future) if (parent) parent->installEventFilter(this); @@ -272,12 +279,11 @@ PaymentServer::PaymentServer(QObject* parent, if (!uriServer->listen(name)) qDebug() << "PaymentServer::PaymentServer : Cannot start bitcoin: click-to-pay handler"; - else + else { connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); + connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString))); + } } - - // netManager is null until uiReady() is called - netManager = NULL; } PaymentServer::~PaymentServer() @@ -286,12 +292,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); @@ -312,10 +318,10 @@ void PaymentServer::initNetManager() if (netManager != NULL) delete netManager; - // netManager is used to fetch paymentrequests given in bitcoin: URI's + // netManager is used to fetch paymentrequests given in bitcoin: URIs netManager = new QNetworkAccessManager(this); - // Use proxy settings from optionsModel: + // Use proxy settings from optionsModel QString proxyIP; quint16 proxyPort; if (optionsModel->getProxySettings(proxyIP, proxyPort)) @@ -335,7 +341,7 @@ void PaymentServer::initNetManager() void PaymentServer::uiReady() { - assert(netManager != NULL); // Must call initNetManager before uiReady() + initNetManager(); saveURIs = false; foreach (const QString& s, savedPaymentRequests) @@ -355,42 +361,46 @@ void PaymentServer::handleURIOrFile(const QString& s) if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: { -#if QT_VERSION >= 0x050000 - QUrlQuery url((QUrl(s))); +#if QT_VERSION < 0x050000 + QUrl uri(s); #else - QUrl url(s); + QUrlQuery uri((QUrl(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); - if (fDebug) - qDebug() << "PaymentServer::handleURIOrFile : fetchRequest(" << fetchUrl << ")"; + qDebug() << "PaymentServer::handleURIOrFile : fetchRequest(" << fetchUrl << ")"; if (fetchUrl.isValid()) fetchRequest(fetchUrl); else - qDebug() << "PaymentServer::handleURIOrFile : Invalid url: " << fetchUrl; + qDebug() << "PaymentServer::handleURIOrFile : Invalid URL: " << fetchUrl; + return; } SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) emit receivedPaymentRequest(recipient); + else + emit message(tr("URI handling"), + tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."), + CClientUIInterface::ICON_WARNING); + return; } if (QFile::exists(s)) { PaymentRequestPlus request; - QList<SendCoinsRecipient> recipients; - if (readPaymentRequest(s, request) && processPaymentRequest(request, recipients)) { - foreach (const SendCoinsRecipient& recipient, recipients){ - emit receivedPaymentRequest(recipient); - } - } + SendCoinsRecipient recipient; + if (readPaymentRequest(s, request) && processPaymentRequest(request, recipient)) + emit receivedPaymentRequest(recipient); + return; } } @@ -410,10 +420,10 @@ void PaymentServer::handleURIConnection() if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) { return; } - QString message; - in >> message; + QString msg; + in >> msg; - handleURIOrFile(message); + handleURIOrFile(msg); } bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPlus& request) @@ -436,76 +446,63 @@ bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPl return request.parse(data); } -bool -PaymentServer::processPaymentRequest(PaymentRequestPlus& request, - QList<SendCoinsRecipient>& recipients) +bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoinsRecipient& recipient) { if (!optionsModel) return false; - QList<std::pair<CScript,qint64> > sendingTos = request.getPayTo(); - qint64 totalAmount = 0; + recipient.paymentRequest = request; + recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo()); + + request.getMerchant(PaymentServer::certStore, recipient.authenticatedMerchant); + + QList<std::pair<CScript, qint64> > sendingTos = request.getPayTo(); + QStringList addresses; + foreach(const PAIRTYPE(CScript, qint64)& sendingTo, sendingTos) { + // Extract and check destination addresses + CTxDestination dest; + if (ExtractDestination(sendingTo.first, dest)) { + // Append destination address + addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString())); + } + else if (!recipient.authenticatedMerchant.isEmpty()){ + // Insecure payments to custom bitcoin addresses are not supported + // (there is no good way to tell the user where they are paying in a way + // they'd have a chance of understanding). + emit message(tr("Payment request error"), + tr("Unverified payment requests to custom payment scripts are unsupported."), + CClientUIInterface::MSG_ERROR); + return false; + } + + // Extract and check amounts CTxOut txOut(sendingTo.second, sendingTo.first); if (txOut.IsDust(CTransaction::nMinRelayTxFee)) { - QString message = QObject::tr("Requested payment amount (%1) too small") + QString msg = tr("Requested payment amount of %1 is too small (considered dust).") .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)); - qDebug() << "PaymentServer::processPaymentRequest : " << message; - emit reportError(tr("Payment request error"), message, CClientUIInterface::MODAL); + qDebug() << "PaymentServer::processPaymentRequest : " << msg; + emit message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR); return false; } - totalAmount += sendingTo.second; + recipient.amount += sendingTo.second; } + // Store addresses and format them to fit nicely into the GUI + recipient.address = addresses.join("<br />"); - recipients.append(SendCoinsRecipient()); - - if (request.getMerchant(PaymentServer::certStore, recipients[0].authenticatedMerchant)) { - recipients[0].paymentRequest = request; - recipients[0].amount = totalAmount; - if (fDebug) - qDebug() << "PaymentServer::processPaymentRequest : Payment request from " << recipients[0].authenticatedMerchant; + if (!recipient.authenticatedMerchant.isEmpty()) { + qDebug() << "PaymentServer::processPaymentRequest : Secure payment request from " << recipient.authenticatedMerchant; } else { - recipients.clear(); - // Insecure payment requests may turn into more than one recipient if - // the merchant is requesting payment to more than one address. - for (int i = 0; i < sendingTos.size(); i++) { - std::pair<CScript, qint64>& sendingTo = sendingTos[i]; - recipients.append(SendCoinsRecipient()); - recipients[i].amount = sendingTo.second; - QString memo = QString::fromStdString(request.getDetails().memo()); -#if QT_VERSION < 0x050000 - recipients[i].label = Qt::escape(memo); -#else - recipients[i].label = memo.toHtmlEscaped(); -#endif - CTxDestination dest; - if (ExtractDestination(sendingTo.first, dest)) { - if (i == 0) // Tie request to first pay-to, we don't want multiple ACKs - recipients[i].paymentRequest = request; - recipients[i].address = QString::fromStdString(CBitcoinAddress(dest).ToString()); - if (fDebug) - qDebug() << "PaymentServer::processPaymentRequest : Payment request, insecure " << recipients[i].address; - } - else { - // Insecure payments to custom bitcoin addresses are not supported - // (there is no good way to tell the user where they are paying in a way - // they'd have a chance of understanding). - emit reportError(tr("Payment request error"), - tr("Insecure requests to custom payment scripts unsupported"), - CClientUIInterface::MODAL); - return false; - } - } + qDebug() << "PaymentServer::processPaymentRequest : Insecure payment request to " << addresses.join(", "); } return true; } -void -PaymentServer::fetchRequest(const QUrl& url) +void PaymentServer::fetchRequest(const QUrl& url) { QNetworkRequest netRequest; netRequest.setAttribute(QNetworkRequest::User, "PaymentRequest"); @@ -515,8 +512,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 +521,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); @@ -534,7 +530,7 @@ PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QB payment.add_transactions(transaction.data(), transaction.size()); // Create a new refund address, or re-use: - QString account = tr("Refund from") + QString(" ") + recipient.authenticatedMerchant; + QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant); std::string strAccount = account.toStdString(); set<CTxDestination> refundAddresses = wallet->GetAccountAddresses(strAccount); if (!refundAddresses.empty()) { @@ -571,17 +567,17 @@ PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QB } } -void -PaymentServer::netRequestFinished(QNetworkReply* reply) +void PaymentServer::netRequestFinished(QNetworkReply* reply) { reply->deleteLater(); if (reply->error() != QNetworkReply::NoError) { - QString message = QObject::tr("Error communicating with %1: %2") + QString msg = tr("Error communicating with %1: %2") .arg(reply->request().url().toString()) .arg(reply->errorString()); - qDebug() << "PaymentServer::netRequestFinished : " << message; - emit reportError(tr("Network request error"), message, CClientUIInterface::MODAL); + + qDebug() << "PaymentServer::netRequestFinished : " << msg; + emit message(tr("Network request error"), msg, CClientUIInterface::MSG_ERROR); return; } @@ -591,14 +587,12 @@ PaymentServer::netRequestFinished(QNetworkReply* reply) if (requestType == "PaymentRequest") { PaymentRequestPlus request; - QList<SendCoinsRecipient> recipients; - if (request.parse(data) && processPaymentRequest(request, recipients)) { - foreach (const SendCoinsRecipient& recipient, recipients){ - emit receivedPaymentRequest(recipient); - } - } + SendCoinsRecipient recipient; + if (request.parse(data) && processPaymentRequest(request, recipient)) + emit receivedPaymentRequest(recipient); else qDebug() << "PaymentServer::netRequestFinished : Error processing payment request"; + return; } else if (requestType == "PaymentACK") @@ -606,29 +600,37 @@ PaymentServer::netRequestFinished(QNetworkReply* reply) payments::PaymentACK paymentACK; if (!paymentACK.ParseFromArray(data.data(), data.size())) { - QString message = QObject::tr("Bad response from server %1") + QString msg = tr("Bad response from server %1") .arg(reply->request().url().toString()); - qDebug() << "PaymentServer::netRequestFinished : " << message; - emit reportError(tr("Network request error"), message, CClientUIInterface::MODAL); + + qDebug() << "PaymentServer::netRequestFinished : " << msg; + emit message(tr("Network request error"), msg, CClientUIInterface::MSG_ERROR); } else { - emit receivedPaymentACK(QString::fromStdString(paymentACK.memo())); + emit receivedPaymentACK(GUIUtil::HtmlEscape(paymentACK.memo())); } } } -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; errString += err.errorString() + "\n"; } - emit reportError(tr("Network request error"), errString, CClientUIInterface::MODAL); + emit message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR); } void PaymentServer::setOptionsModel(OptionsModel *optionsModel) { this->optionsModel = optionsModel; } + +void PaymentServer::handlePaymentACK(const QString& paymentACKMsg) +{ + // currently we don't futher process or store the paymentACK message + emit message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL); +} diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 131ede518e..50f077f3bc 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -1,7 +1,9 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef PAYMENTSERVER_H #define PAYMENTSERVER_H - -// // This class handles payment requests from clicking on // bitcoin: URIs // @@ -28,14 +30,16 @@ // and, if a server is running in another process, // sends them to the server. // -#include <QObject> -#include <QString> #include "paymentrequestplus.h" #include "walletmodel.h" -class CWallet; +#include <QObject> +#include <QString> + class OptionsModel; + +QT_BEGIN_NAMESPACE class QApplication; class QByteArray; class QLocalServer; @@ -43,6 +47,9 @@ class QNetworkAccessManager; class QNetworkReply; class QSslError; class QUrl; +QT_END_NAMESPACE + +class CWallet; class PaymentServer : public QObject { @@ -56,8 +63,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,14 +72,11 @@ 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; } - // Setup networking - void initNetManager(); - // Constructor registers this on the parent QApplication to // receive QEvent::FileOpen events bool eventFilter(QObject *object, QEvent *event); @@ -85,10 +89,10 @@ signals: void receivedPaymentRequest(SendCoinsRecipient); // Fired when a valid PaymentACK is received - void receivedPaymentACK(QString); + void receivedPaymentACK(const QString &paymentACKMsg); - // Fired when an error should be reported to the user - void reportError(QString, QString, unsigned int); + // Fired when a message should be reported to the user + void message(const QString &title, const QString &message, unsigned int style); public slots: // Signal this when the main window's UI is ready @@ -98,17 +102,23 @@ public slots: // Submit Payment message to a merchant, get back PaymentACK: void fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction); + // Handle an incoming URI or file + void handleURIOrFile(const QString& s); + private slots: void handleURIConnection(); void netRequestFinished(QNetworkReply*); void reportSslErrors(QNetworkReply*, const QList<QSslError> &); + void handlePaymentACK(const QString& paymentACKMsg); private: static bool readPaymentRequest(const QString& filename, PaymentRequestPlus& request); - bool processPaymentRequest(PaymentRequestPlus& request, QList<SendCoinsRecipient>& recipients); - void handleURIOrFile(const QString& s); + bool processPaymentRequest(PaymentRequestPlus& request, SendCoinsRecipient& recipient); void fetchRequest(const QUrl& url); + // Setup networking + void initNetManager(); + bool saveURIs; // true during startup QLocalServer* uriServer; diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp deleted file mode 100644 index 6ddcaaf5d9..0000000000 --- a/src/qt/qrcodedialog.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include "qrcodedialog.h" -#include "ui_qrcodedialog.h" - -#include "bitcoinunits.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "optionsmodel.h" - -#include <QPixmap> -#if QT_VERSION < 0x050000 -#include <QUrl> -#endif - -#include <qrencode.h> - -QRCodeDialog::QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent) : - QDialog(parent), - ui(new Ui::QRCodeDialog), - model(0), - address(addr) -{ - ui->setupUi(this); - - setWindowTitle(QString("%1").arg(address)); - - ui->chkReqPayment->setVisible(enableReq); - ui->lblAmount->setVisible(enableReq); - ui->lnReqAmount->setVisible(enableReq); - - ui->lnLabel->setText(label); - - ui->btnSaveAs->setEnabled(false); - - genCode(); -} - -QRCodeDialog::~QRCodeDialog() -{ - delete ui; -} - -void QRCodeDialog::setModel(OptionsModel *model) -{ - this->model = model; - - if (model) - connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - - // update the display unit, to not use the default ("BTC") - updateDisplayUnit(); -} - -void QRCodeDialog::genCode() -{ - QString uri = getURI(); - - if (uri != "") - { - ui->lblQRCode->setText(""); - - QRcode *code = QRcode_encodeString(uri.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); - if (!code) - { - ui->lblQRCode->setText(tr("Error encoding URI into QR Code.")); - return; - } - myImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); - myImage.fill(0xffffff); - unsigned char *p = code->data; - for (int y = 0; y < code->width; y++) - { - for (int x = 0; x < code->width; x++) - { - myImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); - p++; - } - } - QRcode_free(code); - - ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300)); - - ui->outUri->setPlainText(uri); - } -} - -QString QRCodeDialog::getURI() -{ - QString ret = QString("bitcoin:%1").arg(address); - int paramCount = 0; - - ui->outUri->clear(); - - if (ui->chkReqPayment->isChecked()) - { - if (ui->lnReqAmount->validate()) - { - // even if we allow a non BTC unit input in lnReqAmount, we generate the URI with BTC as unit (as defined in BIP21) - ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, ui->lnReqAmount->value())); - paramCount++; - } - else - { - ui->btnSaveAs->setEnabled(false); - ui->lblQRCode->setText(tr("The entered amount is invalid, please check.")); - return QString(""); - } - } - - if (!ui->lnLabel->text().isEmpty()) - { - QString lbl(QUrl::toPercentEncoding(ui->lnLabel->text())); - ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl); - paramCount++; - } - - if (!ui->lnMessage->text().isEmpty()) - { - QString msg(QUrl::toPercentEncoding(ui->lnMessage->text())); - ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg); - paramCount++; - } - - // limit URI length to prevent a DoS against the QR-Code dialog - if (ret.length() > MAX_URI_LENGTH) - { - ui->btnSaveAs->setEnabled(false); - ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message.")); - return QString(""); - } - - ui->btnSaveAs->setEnabled(true); - return ret; -} - -void QRCodeDialog::on_lnReqAmount_textChanged() -{ - genCode(); -} - -void QRCodeDialog::on_lnLabel_textChanged() -{ - genCode(); -} - -void QRCodeDialog::on_lnMessage_textChanged() -{ - genCode(); -} - -void QRCodeDialog::on_btnSaveAs_clicked() -{ - QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Images (*.png)")); - if (!fn.isEmpty()) - myImage.scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE).save(fn); -} - -void QRCodeDialog::on_chkReqPayment_toggled(bool fChecked) -{ - if (!fChecked) - // if chkReqPayment is not active, don't display lnReqAmount as invalid - ui->lnReqAmount->setValid(true); - - genCode(); -} - -void QRCodeDialog::updateDisplayUnit() -{ - if (model) - { - // Update lnReqAmount with the current unit - ui->lnReqAmount->setDisplayUnit(model->getDisplayUnit()); - } -} diff --git a/src/qt/qrcodedialog.h b/src/qt/qrcodedialog.h deleted file mode 100644 index c55c34bce6..0000000000 --- a/src/qt/qrcodedialog.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef QRCODEDIALOG_H -#define QRCODEDIALOG_H - -#include <QDialog> -#include <QImage> - -namespace Ui { - class QRCodeDialog; -} -class OptionsModel; - -class QRCodeDialog : public QDialog -{ - Q_OBJECT - -public: - explicit QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent = 0); - ~QRCodeDialog(); - - void setModel(OptionsModel *model); - -private slots: - void on_lnReqAmount_textChanged(); - void on_lnLabel_textChanged(); - void on_lnMessage_textChanged(); - void on_btnSaveAs_clicked(); - void on_chkReqPayment_toggled(bool fChecked); - - void updateDisplayUnit(); - -private: - Ui::QRCodeDialog *ui; - OptionsModel *model; - QString address; - QImage myImage; - - void genCode(); - QString getURI(); -}; - -#endif // QRCODEDIALOG_H diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp index 8ca230c9d7..1e7596c9c1 100644 --- a/src/qt/qvalidatedlineedit.cpp +++ b/src/qt/qvalidatedlineedit.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-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 "qvalidatedlineedit.h" #include "guiconstants.h" diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h index ec74633c02..53ef04e4a0 100644 --- a/src/qt/qvalidatedlineedit.h +++ b/src/qt/qvalidatedlineedit.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef QVALIDATEDLINEEDIT_H #define QVALIDATEDLINEEDIT_H diff --git a/src/qt/qvaluecombobox.cpp b/src/qt/qvaluecombobox.cpp index d7ce3d0130..ad083f6357 100644 --- a/src/qt/qvaluecombobox.cpp +++ b/src/qt/qvaluecombobox.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-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 "qvaluecombobox.h" QValueComboBox::QValueComboBox(QWidget *parent) : diff --git a/src/qt/qvaluecombobox.h b/src/qt/qvaluecombobox.h index 64a7da9df5..5f2182913c 100644 --- a/src/qt/qvaluecombobox.h +++ b/src/qt/qvaluecombobox.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef QVALUECOMBOBOX_H #define QVALUECOMBOBOX_H diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp new file mode 100644 index 0000000000..45b2325299 --- /dev/null +++ b/src/qt/receivecoinsdialog.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2011-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 "receivecoinsdialog.h" +#include "ui_receivecoinsdialog.h" + +#include "walletmodel.h" +#include "bitcoinunits.h" +#include "addressbookpage.h" +#include "optionsmodel.h" +#include "guiutil.h" +#include "receiverequestdialog.h" +#include "addresstablemodel.h" + +#include <QMessageBox> +#include <QTextDocument> +#include <QScrollBar> + +ReceiveCoinsDialog::ReceiveCoinsDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ReceiveCoinsDialog), + model(0) +{ + ui->setupUi(this); + +#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac + ui->clearButton->setIcon(QIcon()); + ui->receiveButton->setIcon(QIcon()); +#endif + connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); +} + +void ReceiveCoinsDialog::setModel(WalletModel *model) +{ + this->model = model; + + if(model && model->getOptionsModel()) + { + connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + updateDisplayUnit(); + } +} + +ReceiveCoinsDialog::~ReceiveCoinsDialog() +{ + delete ui; +} + +void ReceiveCoinsDialog::clear() +{ + ui->reqAmount->clear(); + ui->reqLabel->setText(""); + ui->reqMessage->setText(""); + ui->reuseAddress->setChecked(false); + updateDisplayUnit(); +} + +void ReceiveCoinsDialog::reject() +{ + clear(); +} + +void ReceiveCoinsDialog::accept() +{ + clear(); +} + +void ReceiveCoinsDialog::updateDisplayUnit() +{ + if(model && model->getOptionsModel()) + { + ui->reqAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); + } +} + +void ReceiveCoinsDialog::on_receiveButton_clicked() +{ + if(!model || !model->getOptionsModel() || !model->getAddressTableModel()) + return; + + QString address; + QString label = ui->reqLabel->text(); + if(ui->reuseAddress->isChecked()) + { + /* Choose existing receiving address */ + AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + address = dlg.getReturnValue(); + if(label.isEmpty()) /* If no label provided, use the previously used label */ + { + label = model->getAddressTableModel()->labelForAddress(address); + } + } else { + return; + } + } else { + /* Generate new receiving address */ + address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, ""); + } + SendCoinsRecipient info(address, label, + ui->reqAmount->value(), ui->reqMessage->text()); + ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); + dialog->setModel(model->getOptionsModel()); + dialog->setInfo(info); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + clear(); +} diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h new file mode 100644 index 0000000000..9980edd1f5 --- /dev/null +++ b/src/qt/receivecoinsdialog.h @@ -0,0 +1,42 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef RECEIVECOINSDIALOG_H +#define RECEIVECOINSDIALOG_H + +#include <QDialog> +#include <QVariant> + +namespace Ui { + class ReceiveCoinsDialog; +} +class WalletModel; +class OptionsModel; + +/** Dialog for requesting payment of bitcoins */ +class ReceiveCoinsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ReceiveCoinsDialog(QWidget *parent = 0); + ~ReceiveCoinsDialog(); + + void setModel(WalletModel *model); + +public slots: + void clear(); + void reject(); + void accept(); + +private: + Ui::ReceiveCoinsDialog *ui; + WalletModel *model; + +private slots: + void on_receiveButton_clicked(); + void updateDisplayUnit(); +}; + +#endif // RECEIVECOINSDIALOG_H diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp new file mode 100644 index 0000000000..ad3abb1845 --- /dev/null +++ b/src/qt/receiverequestdialog.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2011-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 "receiverequestdialog.h" +#include "ui_receiverequestdialog.h" + +#include "bitcoinunits.h" +#include "guiconstants.h" +#include "guiutil.h" +#include "optionsmodel.h" +#include "walletmodel.h" + +#include <QPixmap> +#include <QClipboard> +#include <QMouseEvent> +#include <QDrag> +#include <QMimeData> +#if QT_VERSION < 0x050000 +#include <QUrl> +#endif + +#if defined(HAVE_CONFIG_H) +#include "bitcoin-config.h" /* for USE_QRCODE */ +#endif + +#ifdef USE_QRCODE +#include <qrencode.h> +#endif + +QRImageWidget::QRImageWidget(QWidget *parent): + QLabel(parent) +{ + setContextMenuPolicy(Qt::ActionsContextMenu); + + QAction *saveImageAction = new QAction(tr("&Save Image..."), this); + connect(saveImageAction, SIGNAL(triggered()), this, SLOT(saveImage())); + addAction(saveImageAction); + QAction *copyImageAction = new QAction(tr("&Copy Image"), this); + connect(copyImageAction, SIGNAL(triggered()), this, SLOT(copyImage())); + addAction(copyImageAction); +} + +QImage QRImageWidget::exportImage() +{ + return pixmap()->toImage().scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE); +} + +void QRImageWidget::mousePressEvent(QMouseEvent *event) +{ + if(event->button() == Qt::LeftButton) + { + event->accept(); + QMimeData *mimeData = new QMimeData; + mimeData->setImageData(exportImage()); + + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->exec(); + } else { + QLabel::mousePressEvent(event); + } +} + +void QRImageWidget::saveImage() +{ + QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Images (*.png)")); + if (!fn.isEmpty()) + { + exportImage().save(fn); + } +} + +void QRImageWidget::copyImage() +{ + QApplication::clipboard()->setImage(exportImage()); +} + +ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ReceiveRequestDialog), + model(0) +{ + ui->setupUi(this); + +#ifndef USE_QRCODE + ui->btnSaveAs->setVisible(false); + ui->btnCopyImage->setVisible(false); + ui->lblQRCode->setVisible(false); +#endif + + connect(ui->btnSaveAs, SIGNAL(clicked()), ui->lblQRCode, SLOT(saveImage())); + connect(ui->btnCopyImage, SIGNAL(clicked()), ui->lblQRCode, SLOT(copyImage())); +} + +ReceiveRequestDialog::~ReceiveRequestDialog() +{ + delete ui; +} + +void ReceiveRequestDialog::setModel(OptionsModel *model) +{ + this->model = model; + + if (model) + connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(update())); + + // update the display unit if necessary + update(); +} + +void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &info) +{ + this->info = info; + update(); +} + +void ReceiveRequestDialog::update() +{ + if(!model) + return; + QString target = info.label; + if(target.isEmpty()) + target = info.address; + setWindowTitle(tr("Request payment to %1").arg(target)); + + QString uri = GUIUtil::formatBitcoinURI(info); + ui->btnSaveAs->setEnabled(false); + QString html; + html += "<html><font face='verdana, arial, helvetica, sans-serif'>"; + html += "<b>"+tr("Payment information")+"</b><br>"; + html += "<b>"+tr("URI")+"</b>: "; + html += "<a href=\""+uri+"\">" + GUIUtil::HtmlEscape(uri) + "</a><br>"; + html += "<b>"+tr("Address")+"</b>: " + GUIUtil::HtmlEscape(info.address) + "<br>"; + if(info.amount) + html += "<b>"+tr("Amount")+"</b>: " + BitcoinUnits::formatWithUnit(model->getDisplayUnit(), info.amount) + "<br>"; + if(!info.label.isEmpty()) + html += "<b>"+tr("Label")+"</b>: " + GUIUtil::HtmlEscape(info.label) + "<br>"; + if(!info.message.isEmpty()) + html += "<b>"+tr("Message")+"</b>: " + GUIUtil::HtmlEscape(info.message) + "<br>"; + ui->outUri->setText(html); + +#ifdef USE_QRCODE + ui->lblQRCode->setText(""); + if(!uri.isEmpty()) + { + // limit URI length + if (uri.length() > MAX_URI_LENGTH) + { + ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message.")); + } else { + QRcode *code = QRcode_encodeString(uri.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); + if (!code) + { + ui->lblQRCode->setText(tr("Error encoding URI into QR Code.")); + return; + } + QImage myImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); + myImage.fill(0xffffff); + unsigned char *p = code->data; + for (int y = 0; y < code->width; y++) + { + for (int x = 0; x < code->width; x++) + { + myImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); + p++; + } + } + QRcode_free(code); + + ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300)); + ui->btnSaveAs->setEnabled(true); + } + } +#endif +} + +void ReceiveRequestDialog::on_btnCopyURI_clicked() +{ + QString uri = GUIUtil::formatBitcoinURI(info); + QApplication::clipboard()->setText(uri, QClipboard::Clipboard); + QApplication::clipboard()->setText(uri, QClipboard::Selection); +} + +void ReceiveRequestDialog::on_btnCopyAddress_clicked() +{ + QApplication::clipboard()->setText(info.address, QClipboard::Clipboard); + QApplication::clipboard()->setText(info.address, QClipboard::Selection); +} diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h new file mode 100644 index 0000000000..295a73031d --- /dev/null +++ b/src/qt/receiverequestdialog.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef QRCODEDIALOG_H +#define QRCODEDIALOG_H + +#include "walletmodel.h" + +#include <QDialog> +#include <QImage> +#include <QLabel> + +namespace Ui { + class ReceiveRequestDialog; +} +class OptionsModel; + +/* Label widget for QR code. This image can be dragged, dropped, copied and saved + * to disk. + */ +class QRImageWidget : public QLabel +{ + Q_OBJECT + +public: + explicit QRImageWidget(QWidget *parent = 0); + QImage exportImage(); + +public slots: + void saveImage(); + void copyImage(); + +protected: + virtual void mousePressEvent(QMouseEvent *event); +}; + +class ReceiveRequestDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ReceiveRequestDialog(QWidget *parent = 0); + ~ReceiveRequestDialog(); + + void setModel(OptionsModel *model); + void setInfo(const SendCoinsRecipient &info); + +private slots: + void on_btnCopyURI_clicked(); + void on_btnCopyAddress_clicked(); + + void update(); + +private: + Ui::ReceiveRequestDialog *ui; + OptionsModel *model; + SendCoinsRecipient info; +}; + +#endif // QRCODEDIALOG_H diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 8953c36579..64a3a68ba7 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1,19 +1,25 @@ +// Copyright (c) 2011-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 "rpcconsole.h" #include "ui_rpcconsole.h" #include "clientmodel.h" -#include "bitcoinrpc.h" #include "guiutil.h" -#include <QTime> -#include <QThread> +#include "bitcoinrpc.h" + +#include "json/json_spirit_value.h" +#include <openssl/crypto.h> #include <QKeyEvent> +#include <QScrollBar> +#include <QThread> +#include <QTime> + #if QT_VERSION < 0x050000 #include <QUrl> #endif -#include <QScrollBar> - -#include <openssl/crypto.h> // TODO: add a scrollback limit, as there is currently none // TODO: make it possible to filter out categories (esp debug messages when implemented) @@ -22,6 +28,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; @@ -199,11 +207,13 @@ RPCConsole::RPCConsole(QWidget *parent) : ui->messagesWidget->installEventFilter(this); connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); + connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph, SLOT(clear())); // set OpenSSL version label ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION)); startExecutor(); + setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); clear(); } @@ -253,7 +263,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 +274,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()); @@ -306,7 +320,7 @@ void RPCConsole::clear() ui->messagesWidget->document()->setDefaultStyleSheet( "table { }" "td.time { color: #808080; padding-top: 3px; } " - "td.message { font-family: Monospace; font-size: 12px; } " + "td.message { font-family: monospace; font-size: 12px; } " // Todo: Remove fixed font-size "td.cmd-request { color: #006060; } " "td.cmd-error { color: red; } " "b { color: #006060; } " @@ -431,3 +445,44 @@ 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)); +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 3c38b4b8de..1370d0b103 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -1,12 +1,17 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef RPCCONSOLE_H #define RPCCONSOLE_H #include <QDialog> +class ClientModel; + namespace Ui { class RPCConsole; } -class ClientModel; /** Local Bitcoin RPC console. */ class RPCConsole: public QDialog @@ -37,6 +42,10 @@ 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); public slots: void clear(); @@ -49,12 +58,16 @@ public slots: void browseHistory(int offset); /** Scroll console view to end */ void scrollToEnd(); + signals: // For RPC command executor void stopExecutor(); 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..b9c5eb08d6 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,18 +1,22 @@ +// Copyright (c) 2011-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 "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" -#include "walletmodel.h" #include "bitcoinunits.h" -#include "addressbookpage.h" +#include "guiutil.h" #include "optionsmodel.h" #include "sendcoinsentry.h" -#include "guiutil.h" -#include "askpassphrasedialog.h" +#include "walletmodel.h" + #include "base58.h" +#include "ui_interface.h" #include <QMessageBox> -#include <QTextDocument> #include <QScrollBar> +#include <QTextDocument> SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), @@ -94,27 +98,37 @@ void SendCoinsDialog::on_sendButton_clicked() QStringList formatted; foreach(const SendCoinsRecipient &rcp, recipients) { - QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); - if (rcp.authenticatedMerchant.isEmpty()) - { - QString recipientElement = QString("<b>%1</b> ").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount)); - recipientElement.append(tr("to")); + // 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.label.length() > 0) + if (!rcp.paymentRequest.IsInitialized()) // normal payment + { + 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 if(!rcp.authenticatedMerchant.isEmpty()) // secure payment request { - 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)); } + else // insecure payment request + { + recipientElement = tr("%1 to %2").arg(amount, address); + } + + formatted.append(recipientElement); } fNewRecipientAllowed = false; @@ -131,45 +145,9 @@ void SendCoinsDialog::on_sendButton_clicked() // prepare transaction for getting txFee earlier WalletModelTransaction currentTransaction(recipients); WalletModel::SendCoinsReturn prepareStatus = model->prepareTransaction(currentTransaction); - - 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); - break; - case WalletModel::InvalidAmount: - QMessageBox::warning(this, tr("Send Coins"), - tr("The amount to pay must be larger than 0."), - QMessageBox::Ok, QMessageBox::Ok); - break; - case WalletModel::AmountExceedsBalance: - QMessageBox::warning(this, tr("Send Coins"), - tr("The amount exceeds your balance."), - QMessageBox::Ok, QMessageBox::Ok); - break; - case WalletModel::AmountWithFeeExceedsBalance: - QMessageBox::warning(this, tr("Send Coins"), - 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); - 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); - break; - case WalletModel::TransactionCreationFailed: - QMessageBox::warning(this, tr("Send Coins"), - tr("Error: Transaction creation failed!"), - QMessageBox::Ok, QMessageBox::Ok); - break; - case WalletModel::Aborted: // User aborted, nothing to do - case WalletModel::OK: - case WalletModel::TransactionCommitFailed: - break; - } + // process prepareStatus and on error generate message shown to user + processSendCoinsReturn(prepareStatus, + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())); if(prepareStatus.status != WalletModel::OK) { fNewRecipientAllowed = true; @@ -197,7 +175,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) @@ -207,21 +185,13 @@ void SendCoinsDialog::on_sendButton_clicked() } // now send the prepared transaction - WalletModel::SendCoinsReturn sendstatus = model->sendCoins(currentTransaction); - switch(sendstatus.status) + WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction); + // process sendStatus and on error generate message shown to user + processSendCoinsReturn(sendStatus); + + if (sendStatus.status == WalletModel::OK) { - 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 - break; - case WalletModel::OK: accept(); - break; - default: - break; } fNewRecipientAllowed = true; } @@ -351,22 +321,22 @@ void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) { - if (!rv.authenticatedMerchant.isEmpty()) { + QString strSendCoins = tr("Send Coins"); + if (rv.paymentRequest.IsInitialized()) { // Expired payment request? const payments::PaymentDetails& details = rv.paymentRequest.getDetails(); - if (details.has_expires() && (int64)details.expires() < GetTime()) + if (details.has_expires() && (int64_t)details.expires() < GetTime()) { - QMessageBox::warning(this, tr("Send Coins"), - tr("Payment request expired")); + emit message(strSendCoins, tr("Payment request expired"), + CClientUIInterface::MSG_WARNING); 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)); + emit message(strSendCoins, tr("Invalid payment address %1").arg(rv.address), + CClientUIInterface::MSG_WARNING); return false; } } @@ -390,3 +360,47 @@ void SendCoinsDialog::updateDisplayUnit() { setBalance(model->getBalance(), 0, 0); } + +void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) +{ + QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams; + // Default to a warning message, override if error message is needed + msgParams.second = CClientUIInterface::MSG_WARNING; + + // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn. + // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins() + // all others are used only in WalletModel::prepareTransaction() + switch(sendCoinsReturn.status) + { + case WalletModel::InvalidAddress: + msgParams.first = tr("The recipient address is not valid, please recheck."); + break; + case WalletModel::InvalidAmount: + msgParams.first = tr("The amount to pay must be larger than 0."); + break; + case WalletModel::AmountExceedsBalance: + msgParams.first = tr("The amount exceeds your balance."); + break; + case WalletModel::AmountWithFeeExceedsBalance: + msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg); + break; + case WalletModel::DuplicateAddress: + msgParams.first = tr("Duplicate address found, can only send to each address once per send operation."); + break; + case WalletModel::TransactionCreationFailed: + msgParams.first = tr("Transaction creation failed!"); + msgParams.second = CClientUIInterface::MSG_ERROR; + break; + case WalletModel::TransactionCommitFailed: + msgParams.first = tr("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."); + msgParams.second = CClientUIInterface::MSG_ERROR; + break; + // OK and Aborted are included to prevent a compiler warning. + case WalletModel::OK: + case WalletModel::Aborted: + default: + return; + } + + emit message(tr("Send Coins"), msgParams.first, msgParams.second); +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index f4bffedc9b..9d5f34f0c2 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -1,21 +1,26 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef SENDCOINSDIALOG_H #define SENDCOINSDIALOG_H +#include "walletmodel.h" + #include <QDialog> -#include <QVariant> -namespace Ui { - class SendCoinsDialog; -} -class WalletModel; +class OptionsModel; class SendCoinsEntry; class SendCoinsRecipient; -class OptionsModel; QT_BEGIN_NAMESPACE class QUrl; QT_END_NAMESPACE +namespace Ui { + class SendCoinsDialog; +} + /** Dialog for sending bitcoins */ class SendCoinsDialog : public QDialog { @@ -48,10 +53,19 @@ private: WalletModel *model; bool fNewRecipientAllowed; + // Process WalletModel::SendCoinsReturn and generate a pair consisting + // of a message and message flags for use in emit message(). + // Additional parameter msgArg can be used via .arg(msgArg). + void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg = QString()); + private slots: void on_sendButton_clicked(); void removeEntry(SendCoinsEntry* entry); void updateDisplayUnit(); + +signals: + // Fired when a message should be reported to the user + void message(const QString &title, const QString &message, unsigned int style); }; #endif // SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 75610f199e..2d240f1fe5 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -1,12 +1,15 @@ +// Copyright (c) 2011-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 "sendcoinsentry.h" #include "ui_sendcoinsentry.h" -#include "guiutil.h" -#include "bitcoinunits.h" #include "addressbookpage.h" -#include "walletmodel.h" -#include "optionsmodel.h" #include "addresstablemodel.h" +#include "guiutil.h" +#include "optionsmodel.h" +#include "walletmodel.h" #include <QApplication> #include <QClipboard> @@ -18,7 +21,7 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) : { ui->setupUi(this); - setCurrentWidget(ui->SendCoinsInsecure); + setCurrentWidget(ui->SendCoins); #ifdef Q_OS_MAC ui->payToLayout->setSpacing(4); @@ -28,10 +31,12 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) : ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); #endif - setFocusPolicy(Qt::TabFocus); setFocusProxy(ui->payTo); + // normal bitcoin address field GUIUtil::setupAddressWidget(ui->payTo, this); + // just a label for displaying bitcoin address(es) + ui->payTo_is->setFont(GUIUtil::bitcoinAddressFont()); } SendCoinsEntry::~SendCoinsEntry() @@ -49,7 +54,7 @@ void SendCoinsEntry::on_addressBookButton_clicked() { if(!model) return; - AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); if(dlg.exec()) { @@ -60,19 +65,14 @@ void SendCoinsEntry::on_addressBookButton_clicked() void SendCoinsEntry::on_payTo_textChanged(const QString &address) { - if(!model) - return; - // Fill in label from address book, if address has an associated label - QString associatedLabel = model->getAddressTableModel()->labelForAddress(address); - if(!associatedLabel.isEmpty()) - ui->addAsLabel->setText(associatedLabel); + updateLabel(address); } void SendCoinsEntry::setModel(WalletModel *model) { this->model = model; - if(model && model->getOptionsModel()) + if (model && model->getOptionsModel()) connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); clear(); @@ -85,10 +85,21 @@ void SendCoinsEntry::setRemoveEnabled(bool enabled) void SendCoinsEntry::clear() { + // clear UI elements for normal payment ui->payTo->clear(); ui->addAsLabel->clear(); ui->payAmount->clear(); + // clear UI elements for insecure payment request + ui->payTo_is->clear(); + ui->memoTextLabel_is->clear(); + ui->payAmount_is->clear(); + // clear UI elements for secure payment request + 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(); } @@ -100,20 +111,23 @@ void SendCoinsEntry::on_deleteButton_clicked() bool SendCoinsEntry::validate() { + if (!model) + return false; + // Check input validity bool retval = true; - if (!recipient.authenticatedMerchant.isEmpty()) + // Skip checks for payment request + if (recipient.paymentRequest.IsInitialized()) return retval; - if(!ui->payTo->hasAcceptableInput() || - (model && !model->validateAddress(ui->payTo->text()))) + if (!ui->payTo->hasAcceptableInput() || !model->validateAddress(ui->payTo->text())) { ui->payTo->setValid(false); retval = false; } - if(!ui->payAmount->validate()) + if (!ui->payAmount->validate()) { retval = false; } @@ -129,10 +143,11 @@ bool SendCoinsEntry::validate() SendCoinsRecipient SendCoinsEntry::getValue() { - if (!recipient.authenticatedMerchant.isEmpty()) + // Payment request + if (recipient.paymentRequest.IsInitialized()) return recipient; - // User-entered or non-authenticated: + // Normal payment recipient.address = ui->payTo->text(); recipient.label = ui->addAsLabel->text(); recipient.amount = ui->payAmount->value(); @@ -154,19 +169,30 @@ 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.paymentRequest.IsInitialized()) // payment request { - const payments::PaymentDetails& details = value.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); - setCurrentWidget(ui->SendCoinsSecure); + if (recipient.authenticatedMerchant.isEmpty()) // insecure + { + ui->payTo_is->setText(recipient.address); + ui->memoTextLabel_is->setText(recipient.message); + ui->payAmount_is->setValue(recipient.amount); + ui->payAmount_is->setReadOnly(true); + setCurrentWidget(ui->SendCoins_InsecurePaymentRequest); + } + else // secure + { + ui->payTo_s->setText(recipient.authenticatedMerchant); + ui->memoTextLabel_s->setText(recipient.message); + ui->payAmount_s->setValue(recipient.amount); + ui->payAmount_s->setReadOnly(true); + setCurrentWidget(ui->SendCoins_SecurePaymentRequest); + } + } + else // normal payment + { + ui->payTo->setText(recipient.address); + ui->addAsLabel->setText(recipient.label); + ui->payAmount->setValue(recipient.amount); } } @@ -178,7 +204,7 @@ void SendCoinsEntry::setAddress(const QString &address) bool SendCoinsEntry::isClear() { - return ui->payTo->text().isEmpty() && ui->payTo_s->text().isEmpty(); + return ui->payTo->text().isEmpty() && ui->payTo_is->text().isEmpty() && ui->payTo_s->text().isEmpty(); } void SendCoinsEntry::setFocus() @@ -192,6 +218,23 @@ void SendCoinsEntry::updateDisplayUnit() { // Update payAmount with the current unit ui->payAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); + ui->payAmount_is->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); ui->payAmount_s->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); } } + +bool SendCoinsEntry::updateLabel(const QString &address) +{ + if(!model) + return false; + + // Fill in label from address book, if address has an associated label + QString associatedLabel = model->getAddressTableModel()->labelForAddress(address); + if(!associatedLabel.isEmpty()) + { + ui->addAsLabel->setText(associatedLabel); + return true; + } + + return false; +} diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 9c7bfe9521..6fc36f9787 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -1,14 +1,19 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef SENDCOINSENTRY_H #define SENDCOINSENTRY_H +#include "walletmodel.h" + #include <QStackedWidget> -#include "walletmodel.h" +class WalletModel; namespace Ui { class SendCoinsEntry; } -class WalletModel; /** * A single entry in the dialog for sending bitcoins. @@ -33,7 +38,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); @@ -57,6 +63,8 @@ private: SendCoinsRecipient recipient; Ui::SendCoinsEntry *ui; WalletModel *model; + + bool updateLabel(const QString &address); }; #endif // SENDCOINSENTRY_H diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 1e9c84fe1a..0fa51cb92a 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -1,20 +1,23 @@ +// Copyright (c) 2011-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 "signverifymessagedialog.h" #include "ui_signverifymessagedialog.h" #include "addressbookpage.h" -#include "base58.h" #include "guiutil.h" -#include "init.h" -#include "main.h" -#include "optionsmodel.h" #include "walletmodel.h" -#include "wallet.h" -#include <QClipboard> +#include "base58.h" +#include "init.h" +#include "wallet.h" #include <string> #include <vector> +#include <QClipboard> + SignVerifyMessageDialog::SignVerifyMessageDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SignVerifyMessageDialog), @@ -86,7 +89,7 @@ void SignVerifyMessageDialog::on_addressBookButton_SM_clicked() { if (model && model->getAddressTableModel()) { - AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this); + AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { @@ -178,7 +181,7 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() { if (model && model->getAddressTableModel()) { - AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::SendingTab, this); + AddressBookPage dlg(AddressBookPage::ForSelection, AddressBookPage::SendingTab, this); dlg.setModel(model->getAddressTableModel()); if (dlg.exec()) { diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h index 558f24e232..c741450b8a 100644 --- a/src/qt/signverifymessagedialog.h +++ b/src/qt/signverifymessagedialog.h @@ -1,12 +1,17 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef SIGNVERIFYMESSAGEDIALOG_H #define SIGNVERIFYMESSAGEDIALOG_H #include <QDialog> +class WalletModel; + namespace Ui { class SignVerifyMessageDialog; } -class WalletModel; class SignVerifyMessageDialog : public QDialog { diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 7c9bc46c46..12280bb702 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -1,7 +1,12 @@ +// Copyright (c) 2011-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 "splashscreen.h" + +#include "chainparams.h" #include "clientversion.h" #include "util.h" -#include "chainparams.h" #include <QApplication> #include <QPainter> @@ -66,9 +71,8 @@ SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) : pixPaint.setFont(QFont(font, 10*fontFactor)); pixPaint.drawText(newPixmap.width()-titleTextWidth-paddingRight,paddingTop+titleCopyrightVSpace,copyrightText); - // draw testnet string if -testnet is on - if(QApplication::applicationName().contains(QString("-testnet"))) { - // draw copyright stuff + // draw testnet string if testnet is on + if(TestNet()) { QFont boldFont = QFont(font, 10*fontFactor); boldFont.setWeight(QFont::Bold); pixPaint.setFont(boldFont); diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index 6a6249d80c..ddf040593d 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef SPLASHSCREEN_H #define SPLASHSCREEN_H diff --git a/src/qt/test/Makefile.am b/src/qt/test/Makefile.am index f51ac9bd6d..7891c3e975 100644 --- a/src/qt/test/Makefile.am +++ b/src/qt/test/Makefile.am @@ -1,11 +1,10 @@ include $(top_srcdir)/src/Makefile.include -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) \ +AM_CPPFLAGS += -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/qt \ + -I$(top_builddir)/src/qt \ + $(PROTOBUF_CFLAGS) \ $(QR_CFLAGS) -AM_LDFLAGS = $(PTHREAD_CFLAGS) bin_PROGRAMS = test_bitcoin-qt TESTS = test_bitcoin-qt @@ -20,7 +19,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/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index 34079e94ff..b8f2cc65cb 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -1,17 +1,17 @@ -#include <QCoreApplication> -#include <QDebug> -#include <QTemporaryFile> -#include <QVariant> -#include <QFileOpenEvent> - -#include <openssl/x509.h> -#include <openssl/x509_vfy.h> +#include "paymentservertests.h" #include "optionsmodel.h" -#include "paymentservertests.h" #include "paymentrequestdata.h" + #include "util.h" +#include <openssl/x509.h> +#include <openssl/x509_vfy.h> +#include <QCoreApplication> +#include <QDebug> +#include <QFileOpenEvent> +#include <QTemporaryFile> +#include <QVariant> X509 *parse_b64der_cert(const char* cert_data) @@ -60,7 +60,6 @@ void PaymentServerTests::paymentServerTests() X509_STORE_add_cert(caStore, parse_b64der_cert(caCert_BASE64)); PaymentServer::LoadRootCAs(caStore); server->setOptionsModel(&optionsModel); - server->initNetManager(); server->uiReady(); // Now feed PaymentRequests to server, and observe signals it produces: diff --git a/src/qt/test/paymentservertests.h b/src/qt/test/paymentservertests.h index 5aa24ebaf2..0bff923ad4 100644 --- a/src/qt/test/paymentservertests.h +++ b/src/qt/test/paymentservertests.h @@ -1,11 +1,11 @@ #ifndef PAYMENTSERVERTESTS_H #define PAYMENTSERVERTESTS_H -#include <QTest> -#include <QObject> - #include "../paymentserver.h" +#include <QObject> +#include <QTest> + class PaymentServerTests : public QObject { Q_OBJECT diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index dae4e60412..5c941c6387 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -1,8 +1,10 @@ -#include <QTest> -#include <QObject> -#include "uritests.h" + #include "paymentservertests.h" +#include "uritests.h" + +#include <QObject> +#include <QTest> // This is all you need to run all the tests int main(int argc, char *argv[]) diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp index 802d74719e..5c0f4406a7 100644 --- a/src/qt/test/uritests.cpp +++ b/src/qt/test/uritests.cpp @@ -1,6 +1,7 @@ #include "uritests.h" -#include "../guiutil.h" -#include "../walletmodel.h" + +#include "guiutil.h" +#include "walletmodel.h" #include <QUrl> @@ -50,9 +51,8 @@ void URITests::uriTests() QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); QVERIFY(rv.label == QString()); - // We currently don't implement the message parameter (ok, yea, we break spec...) uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-message=Wikipedia Example Address")); - QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000&label=Wikipedia Example")); QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); diff --git a/src/qt/test/uritests.h b/src/qt/test/uritests.h index 1237516e5f..17d4280a90 100644 --- a/src/qt/test/uritests.h +++ b/src/qt/test/uritests.h @@ -1,8 +1,8 @@ #ifndef URITESTS_H #define URITESTS_H -#include <QTest> #include <QObject> +#include <QTest> class URITests : public QObject { diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp new file mode 100644 index 0000000000..74565bb6d0 --- /dev/null +++ b/src/qt/trafficgraphwidget.cpp @@ -0,0 +1,173 @@ +// Copyright (c) 2011-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 "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..efab6e7fc5 --- /dev/null +++ b/src/qt/trafficgraphwidget.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef 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..8bd2bf564a 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -1,15 +1,21 @@ +// Copyright (c) 2011-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 "transactiondesc.h" -#include "guiutil.h" #include "bitcoinunits.h" -#include "main.h" -#include "wallet.h" -#include "db.h" -#include "ui_interface.h" +#include "guiutil.h" + #include "base58.h" +#include "db.h" +#include "main.h" #include "paymentserver.h" #include "transactionrecord.h" +#include "ui_interface.h" +#include "wallet.h" +#include <stdint.h> #include <string> QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) @@ -17,7 +23,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)); } @@ -42,10 +48,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u strHTML.reserve(4000); strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>"; - int64 nTime = wtx.GetTxTime(); - int64 nCredit = wtx.GetCredit(); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; + int64_t nTime = wtx.GetTxTime(); + int64_t nCredit = wtx.GetCredit(); + int64_t nDebit = wtx.GetDebit(); + int64_t nNet = nCredit - nDebit; strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx); int nRequests = wtx.GetRequestCount(); @@ -125,7 +131,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u // // Coinbase // - int64 nUnmatured = 0; + int64_t nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) nUnmatured += wallet->GetCredit(txout); strHTML += "<b>" + tr("Credit") + ":</b> "; @@ -182,13 +188,13 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u if (fAllToMe) { // Payment to self - int64 nChange = wtx.GetChange(); - int64 nValue = nCredit - nChange; + int64_t nChange = wtx.GetChange(); + int64_t nValue = nCredit - nChange; strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>"; strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>"; } - int64 nTxFee = nDebit - GetValueOut(wtx); + int64_t nTxFee = nDebit - GetValueOut(wtx); if (nTxFee > 0) strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "<br>"; } @@ -234,7 +240,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u } if (wtx.IsCoinBase()) - strHTML += "<br>" + tr("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.") + "<br>"; + { + quint32 numBlocksToMaturity = COINBASE_MATURITY + 1; + strHTML += "<br>" + tr("Generated coins must mature %1 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.").arg(QString::number(numBlocksToMaturity)) + "<br>"; + } // // Debug view diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index 8b3684e961..92d093b3eb 100644 --- a/src/qt/transactiondesc.h +++ b/src/qt/transactiondesc.h @@ -1,8 +1,12 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef TRANSACTIONDESC_H #define TRANSACTIONDESC_H -#include <QString> #include <QObject> +#include <QString> class CWallet; class CWalletTx; diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp index 3bd4808cb6..5ca575fdd4 100644 --- a/src/qt/transactiondescdialog.cpp +++ b/src/qt/transactiondescdialog.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2011-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 "transactiondescdialog.h" #include "ui_transactiondescdialog.h" diff --git a/src/qt/transactiondescdialog.h b/src/qt/transactiondescdialog.h index f7ceacb63d..d4719975b2 100644 --- a/src/qt/transactiondescdialog.h +++ b/src/qt/transactiondescdialog.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef TRANSACTIONDESCDIALOG_H #define TRANSACTIONDESCDIALOG_H diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 068e5550d2..a14e74a469 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -1,11 +1,15 @@ +// Copyright (c) 2011-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 "transactionfilterproxy.h" #include "transactiontablemodel.h" -#include <QDateTime> - #include <cstdlib> +#include <QDateTime> + // Earliest date that can be represented (far in the past) const QDateTime TransactionFilterProxy::MIN_DATE = QDateTime::fromTime_t(0); // Last date that can be represented (far in the future) diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index 1aea85a005..6d1644d48d 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -1,8 +1,12 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef TRANSACTIONFILTERPROXY_H #define TRANSACTIONFILTERPROXY_H -#include <QSortFilterProxyModel> #include <QDateTime> +#include <QSortFilterProxyModel> /** Filter the transaction list according to pre-specified rules. */ class TransactionFilterProxy : public QSortFilterProxyModel diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index ea2c1f0a5c..675daa9c9c 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -1,7 +1,13 @@ +// Copyright (c) 2011-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 "transactionrecord.h" -#include "wallet.h" #include "base58.h" +#include "wallet.h" + +#include <stdint.h> /* Return positive answer if transaction should be shown in list. */ @@ -24,10 +30,10 @@ bool TransactionRecord::showTransaction(const CWalletTx &wtx) QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) { QList<TransactionRecord> parts; - int64 nTime = wtx.GetTxTime(); - int64 nCredit = wtx.GetCredit(true); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; + int64_t nTime = wtx.GetTxTime(); + int64_t nCredit = wtx.GetCredit(true); + int64_t nDebit = wtx.GetDebit(); + int64_t nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(); std::map<std::string, std::string> mapValue = wtx.mapValue; @@ -79,7 +85,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * if (fAllFromMe && fAllToMe) { // Payment to self - int64 nChange = wtx.GetChange(); + int64_t nChange = wtx.GetChange(); parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", -(nDebit - nChange), nCredit - nChange)); @@ -89,7 +95,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * // // Debit // - int64 nTxFee = nDebit - GetValueOut(wtx); + int64_t nTxFee = nDebit - GetValueOut(wtx); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { @@ -118,7 +124,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * sub.address = mapValue["to"]; } - int64 nValue = txout.nValue; + int64_t nValue = txout.nValue; /* Add fee to first output */ if (nTxFee > 0) { @@ -160,14 +166,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 { @@ -194,7 +200,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) // For generated transactions, determine maturity if(type == TransactionRecord::Generated) { - int64 nCredit = wtx.GetCredit(true); + int64_t nCredit = wtx.GetCredit(true); if (nCredit == 0) { status.maturity = TransactionStatus::Immature; @@ -221,7 +227,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/transactionrecord.h b/src/qt/transactionrecord.h index 480e7a7f2c..8a7c9044e3 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef TRANSACTIONRECORD_H #define TRANSACTIONRECORD_H @@ -47,8 +51,8 @@ public: /** @name Reported status @{*/ Status status; - int64 depth; - int64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number + qint64 depth; + qint64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number of additional blocks that need to be mined before finalization */ /**@}*/ @@ -82,15 +86,15 @@ public: { } - TransactionRecord(uint256 hash, int64 time): + TransactionRecord(uint256 hash, qint64 time): hash(hash), time(time), type(Other), address(""), debit(0), credit(0), idx(0) { } - TransactionRecord(uint256 hash, int64 time, + TransactionRecord(uint256 hash, qint64 time, Type type, const std::string &address, - int64 debit, int64 credit): + qint64 debit, qint64 credit): hash(hash), time(time), type(type), address(address), debit(debit), credit(credit), idx(0) { @@ -104,11 +108,11 @@ public: /** @name Immutable transaction attributes @{*/ uint256 hash; - int64 time; + qint64 time; Type type; std::string address; - int64 debit; - int64 credit; + qint64 debit; + qint64 credit; /**@}*/ /** Subtransaction index, for sort key */ diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 07f6a62150..3777e82f31 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -1,23 +1,30 @@ +// Copyright (c) 2011-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 "transactiontablemodel.h" -#include "guiutil.h" -#include "transactionrecord.h" +#include "addresstablemodel.h" +#include "bitcoinunits.h" #include "guiconstants.h" +#include "guiutil.h" +#include "optionsmodel.h" #include "transactiondesc.h" +#include "transactionrecord.h" #include "walletmodel.h" -#include "optionsmodel.h" -#include "addresstablemodel.h" -#include "bitcoinunits.h" +#include "main.h" +#include "sync.h" +#include "uint256.h" +#include "util.h" #include "wallet.h" -#include "ui_interface.h" -#include <QList> #include <QColor> -#include <QTimer> -#include <QIcon> #include <QDateTime> #include <QDebug> +#include <QIcon> +#include <QList> +#include <QTimer> // Amount column is right-aligned it contains numbers static int column_alignments[] = { @@ -250,9 +257,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/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 6b2961c285..c23c606c31 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -1,14 +1,19 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef TRANSACTIONTABLEMODEL_H #define TRANSACTIONTABLEMODEL_H #include <QAbstractTableModel> #include <QStringList> -class CWallet; -class TransactionTablePriv; class TransactionRecord; +class TransactionTablePriv; class WalletModel; +class CWallet; + /** UI model for the transaction table of a wallet. */ class TransactionTableModel : public QAbstractTableModel diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index a43e29c476..d3e2bdf400 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -1,30 +1,35 @@ +// Copyright (c) 2011-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 "transactionview.h" -#include "transactionfilterproxy.h" -#include "transactionrecord.h" -#include "walletmodel.h" #include "addresstablemodel.h" -#include "transactiontablemodel.h" #include "bitcoinunits.h" #include "csvmodelwriter.h" -#include "transactiondescdialog.h" #include "editaddressdialog.h" -#include "optionsmodel.h" #include "guiutil.h" +#include "optionsmodel.h" +#include "transactiondescdialog.h" +#include "transactionfilterproxy.h" +#include "transactionrecord.h" +#include "transactiontablemodel.h" +#include "walletmodel.h" + +#include "ui_interface.h" -#include <QScrollBar> #include <QComboBox> +#include <QDateTimeEdit> #include <QDoubleValidator> #include <QHBoxLayout> -#include <QVBoxLayout> -#include <QLineEdit> -#include <QTableView> #include <QHeaderView> -#include <QMessageBox> -#include <QPoint> -#include <QMenu> #include <QLabel> -#include <QDateTimeEdit> +#include <QLineEdit> +#include <QMenu> +#include <QPoint> +#include <QScrollBar> +#include <QTableView> +#include <QVBoxLayout> TransactionView::TransactionView(QWidget *parent) : QWidget(parent), model(0), transactionProxyModel(0), @@ -266,12 +271,12 @@ void TransactionView::changedAmount(const QString &amount) void TransactionView::exportClicked() { // CSV is currently the only supported format - QString filename = GUIUtil::getSaveFileName( - this, - tr("Export Transaction Data"), QString(), - tr("Comma separated file (*.csv)")); + QString filename = GUIUtil::getSaveFileName(this, + tr("Export Transaction History"), QString(), + tr("Comma separated file (*.csv)")); - if (filename.isNull()) return; + if (filename.isNull()) + return; CSVModelWriter writer(filename); @@ -285,10 +290,13 @@ void TransactionView::exportClicked() writer.addColumn(tr("Amount"), 0, TransactionTableModel::FormattedAmountRole); writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole); - if(!writer.write()) - { - QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename), - QMessageBox::Abort, QMessageBox::Abort); + if(!writer.write()) { + emit message(tr("Exporting Failed"), tr("There was an error trying to save the transaction history to %1.").arg(filename), + CClientUIInterface::MSG_ERROR); + } + else { + emit message(tr("Exporting Successful"), tr("The transaction history was successfully saved to %1.").arg(filename), + CClientUIInterface::MSG_INFORMATION); } } diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index bb41a83e32..18f2b9bfc9 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -1,19 +1,23 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef TRANSACTIONVIEW_H #define TRANSACTIONVIEW_H #include <QWidget> -class WalletModel; class TransactionFilterProxy; +class WalletModel; QT_BEGIN_NAMESPACE -class QTableView; class QComboBox; +class QDateTimeEdit; +class QFrame; class QLineEdit; -class QModelIndex; class QMenu; -class QFrame; -class QDateTimeEdit; +class QModelIndex; +class QTableView; QT_END_NAMESPACE /** Widget showing the transaction list for a wallet, including a filter row. @@ -71,6 +75,9 @@ private slots: signals: void doubleClicked(const QModelIndex&); + /** Fired when a message should be reported to the user */ + void message(const QString &title, const QString &message, unsigned int style); + public slots: void chooseDate(int idx); void chooseType(int idx); diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 99a6647a65..04eea59169 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -1,24 +1,24 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011-2012 - * The Bitcoin Developers 2011-2013 - */ +// Copyright (c) 2011-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 "walletframe.h" + #include "bitcoingui.h" -#include "walletstack.h" +#include "walletview.h" + +#include <cstdio> #include <QHBoxLayout> -#include <QMessageBox> WalletFrame::WalletFrame(BitcoinGUI *_gui) : - QFrame(_gui) + QFrame(_gui), + gui(_gui) { // Leave HBox hook for adding a list view later QHBoxLayout *walletFrameLayout = new QHBoxLayout(this); setContentsMargins(0,0,0,0); - walletStack = new WalletStack(this); - walletStack->setBitcoinGUI(_gui); + walletStack = new QStackedWidget(this); walletFrameLayout->setContentsMargins(0,0,0,0); walletFrameLayout->addWidget(walletStack); } @@ -29,98 +29,157 @@ WalletFrame::~WalletFrame() void WalletFrame::setClientModel(ClientModel *clientModel) { - if (clientModel) - walletStack->setClientModel(clientModel); + this->clientModel = clientModel; } bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel) { - return walletStack->addWallet(name, walletModel); + if (!gui || !clientModel || !walletModel || mapWalletViews.count(name) > 0) + return false; + + WalletView *walletView = new WalletView(this); + walletView->setBitcoinGUI(gui); + walletView->setClientModel(clientModel); + walletView->setWalletModel(walletModel); + walletView->showOutOfSyncWarning(bOutOfSync); + + /* TODO we should goto the currently selected page once dynamically adding wallets is supported */ + walletView->gotoOverviewPage(); + walletStack->addWidget(walletView); + mapWalletViews[name] = walletView; + + // Ensure a walletView is able to show the main window + connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); + + return true; } bool WalletFrame::setCurrentWallet(const QString& name) { - // TODO: Check if valid name - return walletStack->setCurrentWallet(name); + if (mapWalletViews.count(name) == 0) + return false; + + WalletView *walletView = mapWalletViews.value(name); + walletStack->setCurrentWidget(walletView); + walletView->updateEncryptionStatus(); + return true; +} + +bool WalletFrame::removeWallet(const QString &name) +{ + if (mapWalletViews.count(name) == 0) + return false; + + WalletView *walletView = mapWalletViews.take(name); + walletStack->removeWidget(walletView); + return true; } void WalletFrame::removeAllWallets() { - walletStack->removeAllWallets(); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + walletStack->removeWidget(i.value()); + mapWalletViews.clear(); } bool WalletFrame::handlePaymentRequest(const SendCoinsRecipient &recipient) { - return walletStack->handlePaymentRequest(recipient); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (!walletView) + return false; + + return walletView->handlePaymentRequest(recipient); } void WalletFrame::showOutOfSyncWarning(bool fShow) { - if (!walletStack) { - QMessageBox box; - box.setText("walletStack is null"); - box.exec(); - return; - } - walletStack->showOutOfSyncWarning(fShow); + bOutOfSync = fShow; + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->showOutOfSyncWarning(fShow); } void WalletFrame::gotoOverviewPage() { - walletStack->gotoOverviewPage(); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoOverviewPage(); } void WalletFrame::gotoHistoryPage() { - walletStack->gotoHistoryPage(); -} - -void WalletFrame::gotoAddressBookPage() -{ - walletStack->gotoAddressBookPage(); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoHistoryPage(); } void WalletFrame::gotoReceiveCoinsPage() { - walletStack->gotoReceiveCoinsPage(); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoReceiveCoinsPage(); } void WalletFrame::gotoSendCoinsPage(QString addr) { - walletStack->gotoSendCoinsPage(addr); + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoSendCoinsPage(addr); } void WalletFrame::gotoSignMessageTab(QString addr) { - walletStack->gotoSignMessageTab(addr); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->gotoSignMessageTab(addr); } void WalletFrame::gotoVerifyMessageTab(QString addr) { - walletStack->gotoSignMessageTab(addr); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->gotoVerifyMessageTab(addr); } void WalletFrame::encryptWallet(bool status) { - walletStack->encryptWallet(status); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->encryptWallet(status); } void WalletFrame::backupWallet() { - walletStack->backupWallet(); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->backupWallet(); } void WalletFrame::changePassphrase() { - walletStack->changePassphrase(); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->changePassphrase(); } void WalletFrame::unlockWallet() { - walletStack->unlockWallet(); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->unlockWallet(); +} + +void WalletFrame::usedSendingAddresses() +{ + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->usedSendingAddresses(); } -void WalletFrame::setEncryptionStatus() +void WalletFrame::usedReceivingAddresses() { - walletStack->setEncryptionStatus(); + WalletView *walletView = (WalletView*)walletStack->currentWidget(); + if (walletView) + walletView->usedReceivingAddresses(); } diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index eaae053ccd..8cfaa0e1d4 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -1,19 +1,22 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011-2012 - * The Bitcoin Developers 2011-2013 - */ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef WALLETFRAME_H #define WALLETFRAME_H #include <QFrame> +#include <QMap> class BitcoinGUI; class ClientModel; class SendCoinsRecipient; class WalletModel; -class WalletStack; +class WalletView; + +QT_BEGIN_NAMESPACE +class QStackedWidget; +QT_END_NAMESPACE class WalletFrame : public QFrame { @@ -27,7 +30,7 @@ public: bool addWallet(const QString& name, WalletModel *walletModel); bool setCurrentWallet(const QString& name); - + bool removeWallet(const QString &name); void removeAllWallets(); bool handlePaymentRequest(const SendCoinsRecipient& recipient); @@ -35,15 +38,18 @@ public: void showOutOfSyncWarning(bool fShow); private: - WalletStack *walletStack; + QStackedWidget *walletStack; + BitcoinGUI *gui; + ClientModel *clientModel; + QMap<QString, WalletView*> mapWalletViews; + + bool bOutOfSync; public slots: /** Switch to overview (home) page */ void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); - /** Switch to address book page */ - void gotoAddressBookPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ @@ -63,11 +69,10 @@ public slots: /** Ask for passphrase to unlock wallet temporarily */ void unlockWallet(); - /** Set the encryption status as shown in the UI. - @param[in] status current encryption status - @see WalletModel::EncryptionStatus - */ - void setEncryptionStatus(); + /** Show used sending addresses */ + void usedSendingAddresses(); + /** Show used receiving addresses */ + void usedReceivingAddresses(); }; #endif // WALLETFRAME_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 1dcecbe60b..b1d770e1a7 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -1,16 +1,27 @@ +// Copyright (c) 2011-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 "walletmodel.h" -#include "guiconstants.h" -#include "optionsmodel.h" + #include "addresstablemodel.h" +#include "guiconstants.h" #include "transactiontablemodel.h" +#include "base58.h" +#include "db.h" +#include "keystore.h" +#include "main.h" +#include "sync.h" #include "ui_interface.h" +#include "wallet.h" #include "walletdb.h" // for BackupWallet -#include "base58.h" +#include <stdint.h> + +#include <QDebug> #include <QSet> #include <QTimer> -#include <QDebug> WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), @@ -73,10 +84,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(); } } @@ -129,7 +140,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact { qint64 total = 0; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); - std::vector<std::pair<CScript, int64> > vecSend; + std::vector<std::pair<CScript, int64_t> > vecSend; if(recipients.empty()) { @@ -143,8 +154,8 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact foreach(const SendCoinsRecipient &rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) - { // PaymentRequest... - int64 subtotal = 0; + { // PaymentRequest... + int64_t subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { @@ -153,7 +164,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); - vecSend.push_back(std::pair<CScript, int64>(scriptPubKey, out.amount())); + vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, out.amount())); } if (subtotal <= 0) { @@ -176,7 +187,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); - vecSend.push_back(std::pair<CScript, int64>(scriptPubKey, rcp.amount)); + vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, rcp.amount)); total += rcp.amount; } @@ -201,7 +212,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); - int64 nFeeRequired = 0; + int64_t nFeeRequired = 0; std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); @@ -258,22 +269,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 payment request + if (!rcp.paymentRequest.IsInitialized()) { - 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/walletmodel.h b/src/qt/walletmodel.h index 6abcdaf8cb..f39e9dfca0 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -1,19 +1,24 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef WALLETMODEL_H #define WALLETMODEL_H -#include <QObject> +#include "paymentrequestplus.h" +#include "walletmodeltransaction.h" #include "allocators.h" /* for SecureString */ -#include "wallet.h" -#include "walletmodeltransaction.h" -#include "paymentrequestplus.h" -class OptionsModel; +#include <QObject> + class AddressTableModel; +class OptionsModel; class TransactionTableModel; -class CWallet; class WalletModelTransaction; +class CWallet; + QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE @@ -21,15 +26,25 @@ QT_END_NAMESPACE class SendCoinsRecipient { public: - SendCoinsRecipient() : amount(0) { } - + explicit SendCoinsRecipient() : amount(0) { } + explicit SendCoinsRecipient(const QString &addr, const QString &label, quint64 amount, const QString &message): + address(addr), label(label), amount(amount), message(message) {} + + // If from an insecure payment request, this is used for storing + // the addresses, e.g. address-A<br />address-B<br />address-C. + // Info: As we don't need to process addresses in here when using + // payment requests, we can abuse it for displaying an address list. + // Todo: This is a hack, should be replaced with a cleaner solution! QString address; QString label; qint64 amount; + // If from a payment request, this is used for storing the memo + QString message; // If from a payment request, paymentRequest.IsInitialized() will be true PaymentRequestPlus paymentRequest; - QString authenticatedMerchant; // Empty if no authentication or invalid signature/cert/etc. + // Empty if no authentication or invalid signature/cert/etc. + QString authenticatedMerchant; }; /** Interface to Bitcoin wallet from Qt view code. */ @@ -157,7 +172,7 @@ signals: // this means that the unlocking failed or was cancelled. void requireUnlock(); - // Asynchronous message notification + // Fired when a message should be reported to the user void message(const QString &title, const QString &message, unsigned int style); // Coins sent: from wallet, to recipient, in (serialized) transaction: diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 96fc3edbb2..943f13e208 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -1,5 +1,11 @@ +// Copyright (c) 2011-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 "walletmodeltransaction.h" +#include "wallet.h" + WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &recipients) : recipients(recipients), walletTransaction(0), @@ -32,7 +38,7 @@ qint64 WalletModelTransaction::getTransactionFee() void WalletModelTransaction::setTransactionFee(qint64 newFee) { - fee=newFee; + fee = newFee; } qint64 WalletModelTransaction::getTotalTransactionAmount() @@ -40,7 +46,7 @@ qint64 WalletModelTransaction::getTotalTransactionAmount() qint64 totalTransactionAmount = 0; foreach(const SendCoinsRecipient &rcp, recipients) { - totalTransactionAmount+=rcp.amount; + totalTransactionAmount += rcp.amount; } return totalTransactionAmount; } diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index c4848fb12d..a948808a75 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -1,10 +1,20 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef WALLETMODELTRANSACTION_H #define WALLETMODELTRANSACTION_H #include "walletmodel.h" +#include <QObject> + class SendCoinsRecipient; +class CReserveKey; +class CWallet; +class CWalletTx; + /** Data model for a walletmodel transaction. */ class WalletModelTransaction { diff --git a/src/qt/walletstack.cpp b/src/qt/walletstack.cpp deleted file mode 100644 index 4ef87aed52..0000000000 --- a/src/qt/walletstack.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011-2012 - * The Bitcoin Developers 2011-2013 - */ -#include "walletstack.h" -#include "walletview.h" -#include "bitcoingui.h" - -#include <QMap> -#include <QMessageBox> - -WalletStack::WalletStack(QWidget *parent) : - QStackedWidget(parent), - gui(0), - clientModel(0), - bOutOfSync(true) -{ - setContentsMargins(0,0,0,0); -} - -WalletStack::~WalletStack() -{ -} - -bool WalletStack::addWallet(const QString& name, WalletModel *walletModel) -{ - if (!gui || !clientModel || !walletModel || mapWalletViews.count(name) > 0) - return false; - - WalletView *walletView = new WalletView(this, gui); - walletView->setBitcoinGUI(gui); - walletView->setClientModel(clientModel); - walletView->setWalletModel(walletModel); - walletView->showOutOfSyncWarning(bOutOfSync); - addWidget(walletView); - mapWalletViews[name] = walletView; - - // Ensure a walletView is able to show the main window - connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); - - return true; -} - -bool WalletStack::removeWallet(const QString& name) -{ - if (mapWalletViews.count(name) == 0) - return false; - - WalletView *walletView = mapWalletViews.take(name); - removeWidget(walletView); - return true; -} - -void WalletStack::removeAllWallets() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - removeWidget(i.value()); - mapWalletViews.clear(); -} - -bool WalletStack::handlePaymentRequest(const SendCoinsRecipient &recipient) -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (!walletView) - return false; - - return walletView->handlePaymentRequest(recipient); -} - -void WalletStack::showOutOfSyncWarning(bool fShow) -{ - bOutOfSync = fShow; - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->showOutOfSyncWarning(fShow); -} - -void WalletStack::gotoOverviewPage() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoOverviewPage(); -} - -void WalletStack::gotoHistoryPage() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoHistoryPage(); -} - -void WalletStack::gotoAddressBookPage() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoAddressBookPage(); -} - -void WalletStack::gotoReceiveCoinsPage() -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoReceiveCoinsPage(); -} - -void WalletStack::gotoSendCoinsPage(QString addr) -{ - QMap<QString, WalletView*>::const_iterator i; - for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) - i.value()->gotoSendCoinsPage(addr); -} - -void WalletStack::gotoSignMessageTab(QString addr) -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->gotoSignMessageTab(addr); -} - -void WalletStack::gotoVerifyMessageTab(QString addr) -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->gotoVerifyMessageTab(addr); -} - -void WalletStack::encryptWallet(bool status) -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->encryptWallet(status); -} - -void WalletStack::backupWallet() -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->backupWallet(); -} - -void WalletStack::changePassphrase() -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->changePassphrase(); -} - -void WalletStack::unlockWallet() -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->unlockWallet(); -} - -void WalletStack::setEncryptionStatus() -{ - WalletView *walletView = (WalletView*)currentWidget(); - if (walletView) - walletView->setEncryptionStatus(); -} - -bool WalletStack::setCurrentWallet(const QString& name) -{ - if (mapWalletViews.count(name) == 0) - return false; - - WalletView *walletView = mapWalletViews.value(name); - setCurrentWidget(walletView); - walletView->setEncryptionStatus(); - return true; -} diff --git a/src/qt/walletstack.h b/src/qt/walletstack.h deleted file mode 100644 index 74b9f09081..0000000000 --- a/src/qt/walletstack.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011-2012 - * The Bitcoin Developers 2011-2013 - */ -#ifndef WALLETSTACK_H -#define WALLETSTACK_H - -#include <QStackedWidget> -#include <QMap> -#include <boost/shared_ptr.hpp> - -class BitcoinGUI; -class TransactionTableModel; -class ClientModel; -class WalletModel; -class WalletView; -class TransactionView; -class OverviewPage; -class AddressBookPage; -class SendCoinsDialog; -class SendCoinsRecipient; -class SignVerifyMessageDialog; -class Notificator; -class RPCConsole; - -class CWalletManager; - -QT_BEGIN_NAMESPACE -class QLabel; -class QModelIndex; -QT_END_NAMESPACE - -/* - WalletStack class. This class is a container for WalletView instances. It takes the place of centralWidget. - It was added to support multiple wallet functionality. It communicates with both the client and the - wallet models to give the user an up-to-date view of the current core state. It manages all the wallet views - it contains and updates them accordingly. - */ -class WalletStack : public QStackedWidget -{ - Q_OBJECT - -public: - explicit WalletStack(QWidget *parent = 0); - ~WalletStack(); - - void setBitcoinGUI(BitcoinGUI *gui) { this->gui = gui; } - - void setClientModel(ClientModel *clientModel) { this->clientModel = clientModel; } - - bool addWallet(const QString& name, WalletModel *walletModel); - bool removeWallet(const QString& name); - - void removeAllWallets(); - - bool handlePaymentRequest(const SendCoinsRecipient &recipient); - - void showOutOfSyncWarning(bool fShow); - -private: - BitcoinGUI *gui; - ClientModel *clientModel; - QMap<QString, WalletView*> mapWalletViews; - - bool bOutOfSync; - -public slots: - bool setCurrentWallet(const QString& name); - - /** Switch to overview (home) page */ - void gotoOverviewPage(); - /** Switch to history (transactions) page */ - void gotoHistoryPage(); - /** Switch to address book page */ - void gotoAddressBookPage(); - /** Switch to receive coins page */ - void gotoReceiveCoinsPage(); - /** Switch to send coins page */ - void gotoSendCoinsPage(QString addr = ""); - - /** Show Sign/Verify Message dialog and switch to sign message tab */ - void gotoSignMessageTab(QString addr = ""); - /** Show Sign/Verify Message dialog and switch to verify message tab */ - void gotoVerifyMessageTab(QString addr = ""); - - /** Encrypt the wallet */ - void encryptWallet(bool status); - /** Backup the wallet */ - void backupWallet(); - /** Change encrypted wallet passphrase */ - void changePassphrase(); - /** Ask for passphrase to unlock wallet temporarily */ - void unlockWallet(); - - /** Set the encryption status as shown in the UI. - @param[in] status current encryption status - @see WalletModel::EncryptionStatus - */ - void setEncryptionStatus(); -}; - -#endif // WALLETSTACK_H diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index efb74efaa0..2485688531 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -1,37 +1,34 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011-2012 - * The Bitcoin Developers 2011-2013 - */ +// Copyright (c) 2011-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 "walletview.h" -#include "bitcoingui.h" -#include "transactiontablemodel.h" + #include "addressbookpage.h" -#include "sendcoinsdialog.h" -#include "signverifymessagedialog.h" +#include "askpassphrasedialog.h" +#include "bitcoingui.h" #include "clientmodel.h" -#include "walletmodel.h" +#include "guiutil.h" #include "optionsmodel.h" -#include "transactionview.h" #include "overviewpage.h" -#include "askpassphrasedialog.h" +#include "receivecoinsdialog.h" +#include "sendcoinsdialog.h" +#include "signverifymessagedialog.h" +#include "transactiontablemodel.h" +#include "transactionview.h" +#include "walletmodel.h" + #include "ui_interface.h" -#include <QHBoxLayout> -#include <QVBoxLayout> #include <QAction> -#if QT_VERSION < 0x050000 -#include <QDesktopServices> -#else -#include <QStandardPaths> -#endif +#include <QActionGroup> #include <QFileDialog> +#include <QHBoxLayout> #include <QPushButton> +#include <QVBoxLayout> -WalletView::WalletView(QWidget *parent, BitcoinGUI *_gui): +WalletView::WalletView(QWidget *parent): QStackedWidget(parent), - gui(_gui), clientModel(0), walletModel(0) { @@ -53,37 +50,27 @@ WalletView::WalletView(QWidget *parent, BitcoinGUI *_gui): vbox->addLayout(hbox_buttons); transactionsPage->setLayout(vbox); - addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab); - - receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab); - - sendCoinsPage = new SendCoinsDialog(gui); - - signVerifyMessageDialog = new SignVerifyMessageDialog(gui); + receiveCoinsPage = new ReceiveCoinsDialog(); + sendCoinsPage = new SendCoinsDialog(); addWidget(overviewPage); addWidget(transactionsPage); - addWidget(addressBookPage); addWidget(receiveCoinsPage); addWidget(sendCoinsPage); - // Clicking on a transaction on the overview page simply sends you to transaction history page - connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); + // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); // Double-clicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); - // Clicking on "Send Coins" in the address book sends you to the send coins tab - connect(addressBookPage, SIGNAL(sendCoins(QString)), this, SLOT(gotoSendCoinsPage(QString))); - // Clicking on "Verify Message" in the address book opens the verify message tab in the Sign/Verify Message dialog - connect(addressBookPage, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString))); - // Clicking on "Sign Message" in the receive coins page opens the sign message tab in the Sign/Verify Message dialog - connect(receiveCoinsPage, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString))); // Clicking on "Export" allows to export the transaction list connect(exportButton, SIGNAL(clicked()), transactionView, SLOT(exportClicked())); - gotoOverviewPage(); + // Pass through messages from sendCoinsPage + connect(sendCoinsPage, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); + // Pass through messages from transactionView + connect(transactionView, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); } WalletView::~WalletView() @@ -92,49 +79,58 @@ WalletView::~WalletView() void WalletView::setBitcoinGUI(BitcoinGUI *gui) { - this->gui = gui; + if (gui) + { + // Clicking on a transaction on the overview page simply sends you to transaction history page + connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), gui, SLOT(gotoHistoryPage())); + + // Receive and report messages + connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int))); + + // Pass through encryption status changed signals + connect(this, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int))); + + // Pass through transaction notifications + connect(this, SIGNAL(incomingTransaction(QString,int,qint64,QString,QString)), gui, SLOT(incomingTransaction(QString,int,qint64,QString,QString))); + } } void WalletView::setClientModel(ClientModel *clientModel) { this->clientModel = clientModel; - if (clientModel) - { - overviewPage->setClientModel(clientModel); - addressBookPage->setOptionsModel(clientModel->getOptionsModel()); - receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel()); - } + + overviewPage->setClientModel(clientModel); } void WalletView::setWalletModel(WalletModel *walletModel) { this->walletModel = walletModel; + + // Put transaction list in tabs + transactionView->setModel(walletModel); + overviewPage->setWalletModel(walletModel); + receiveCoinsPage->setModel(walletModel); + sendCoinsPage->setModel(walletModel); + if (walletModel) { - // Receive and report messages from wallet thread - connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int))); + // Receive and pass through messages from wallet model + connect(walletModel, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); - // Put transaction list in tabs - transactionView->setModel(walletModel); - overviewPage->setWalletModel(walletModel); - addressBookPage->setModel(walletModel->getAddressTableModel()); - receiveCoinsPage->setModel(walletModel->getAddressTableModel()); - sendCoinsPage->setModel(walletModel); - signVerifyMessageDialog->setModel(walletModel); - - setEncryptionStatus(); - connect(walletModel, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int))); + // Handle changes in encryption status + connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SIGNAL(encryptionStatusChanged(int))); + updateEncryptionStatus(); // Balloon pop-up for new transaction connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(incomingTransaction(QModelIndex,int,int))); + this, SLOT(processNewTransaction(QModelIndex,int,int))); // Ask for passphrase if needed connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet())); } } -void WalletView::incomingTransaction(const QModelIndex& parent, int start, int /*end*/) +void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/) { // Prevent balloon-spam when initial block download is in progress if (!walletModel || !clientModel || clientModel->inInitialBlockDownload()) @@ -147,36 +143,26 @@ void WalletView::incomingTransaction(const QModelIndex& parent, int start, int / QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString(); QString address = ttm->index(start, TransactionTableModel::ToAddress, parent).data().toString(); - gui->incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address); + emit incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address); } void WalletView::gotoOverviewPage() { - gui->getOverviewAction()->setChecked(true); setCurrentWidget(overviewPage); } void WalletView::gotoHistoryPage() { - gui->getHistoryAction()->setChecked(true); setCurrentWidget(transactionsPage); } -void WalletView::gotoAddressBookPage() -{ - gui->getAddressBookAction()->setChecked(true); - setCurrentWidget(addressBookPage); -} - void WalletView::gotoReceiveCoinsPage() { - gui->getReceiveCoinsAction()->setChecked(true); setCurrentWidget(receiveCoinsPage); } void WalletView::gotoSendCoinsPage(QString addr) { - gui->getSendCoinsAction()->setChecked(true); setCurrentWidget(sendCoinsPage); if (!addr.isEmpty()) @@ -185,7 +171,10 @@ void WalletView::gotoSendCoinsPage(QString addr) void WalletView::gotoSignMessageTab(QString addr) { - // call show() in showTab_SM() + // calls show() in showTab_SM() + SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(this); + signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose); + signVerifyMessageDialog->setModel(walletModel); signVerifyMessageDialog->showTab_SM(true); if (!addr.isEmpty()) @@ -194,7 +183,10 @@ void WalletView::gotoSignMessageTab(QString addr) void WalletView::gotoVerifyMessageTab(QString addr) { - // call show() in showTab_VM() + // calls show() in showTab_VM() + SignVerifyMessageDialog *signVerifyMessageDialog = new SignVerifyMessageDialog(this); + signVerifyMessageDialog->setAttribute(Qt::WA_DeleteOnClose); + signVerifyMessageDialog->setModel(walletModel); signVerifyMessageDialog->showTab_VM(true); if (!addr.isEmpty()) @@ -203,15 +195,7 @@ void WalletView::gotoVerifyMessageTab(QString addr) bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient) { - // URI has to be valid - if (sendCoinsPage->handlePaymentRequest(recipient)) - { - gotoSendCoinsPage(); - emit showNormalIfMinimized(); - return true; - } - else - return false; + return sendCoinsPage->handlePaymentRequest(recipient); } void WalletView::showOutOfSyncWarning(bool fShow) @@ -219,9 +203,9 @@ void WalletView::showOutOfSyncWarning(bool fShow) overviewPage->showOutOfSyncWarning(fShow); } -void WalletView::setEncryptionStatus() +void WalletView::updateEncryptionStatus() { - gui->setEncryptionStatus(walletModel->getEncryptionStatus()); + emit encryptionStatusChanged(walletModel->getEncryptionStatus()); } void WalletView::encryptWallet(bool status) @@ -232,25 +216,25 @@ void WalletView::encryptWallet(bool status) dlg.setModel(walletModel); dlg.exec(); - setEncryptionStatus(); + updateEncryptionStatus(); } void WalletView::backupWallet() { -#if QT_VERSION < 0x050000 - QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); -#else - QString saveDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); -#endif - QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)")); - if (!filename.isEmpty()) { - if (!walletModel->backupWallet(filename)) { - gui->message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location."), - CClientUIInterface::MSG_ERROR); + QString filename = GUIUtil::getSaveFileName(this, + tr("Backup Wallet"), QString(), + tr("Wallet Data (*.dat)")); + + if (filename.isEmpty()) + return; + + if (!walletModel->backupWallet(filename)) { + emit message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename), + CClientUIInterface::MSG_ERROR); } - else - gui->message(tr("Backup Successful"), tr("The wallet data was successfully saved to the new location."), - CClientUIInterface::MSG_INFORMATION); + else { + emit message(tr("Backup Successful"), tr("The wallet data was successfully saved to %1.").arg(filename), + CClientUIInterface::MSG_INFORMATION); } } @@ -273,3 +257,23 @@ void WalletView::unlockWallet() dlg.exec(); } } + +void WalletView::usedSendingAddresses() +{ + if(!walletModel) + return; + AddressBookPage *dlg = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab, this); + dlg->setModel(walletModel->getAddressTableModel()); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->show(); +} + +void WalletView::usedReceivingAddresses() +{ + if(!walletModel) + return; + AddressBookPage *dlg = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this); + dlg->setModel(walletModel->getAddressTableModel()); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->show(); +} diff --git a/src/qt/walletview.h b/src/qt/walletview.h index ce4e051098..d951691621 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -1,9 +1,7 @@ -/* - * Qt4 bitcoin GUI. - * - * W.J. van der Laan 2011-2012 - * The Bitcoin Developers 2011-2013 - */ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef WALLETVIEW_H #define WALLETVIEW_H @@ -11,17 +9,14 @@ class BitcoinGUI; class ClientModel; -class WalletModel; -class TransactionView; class OverviewPage; -class AddressBookPage; +class ReceiveCoinsDialog; class SendCoinsDialog; class SendCoinsRecipient; -class SignVerifyMessageDialog; -class RPCConsole; +class TransactionView; +class WalletModel; QT_BEGIN_NAMESPACE -class QLabel; class QModelIndex; QT_END_NAMESPACE @@ -36,7 +31,7 @@ class WalletView : public QStackedWidget Q_OBJECT public: - explicit WalletView(QWidget *parent, BitcoinGUI *_gui); + explicit WalletView(QWidget *parent); ~WalletView(); void setBitcoinGUI(BitcoinGUI *gui); @@ -55,16 +50,13 @@ public: void showOutOfSyncWarning(bool fShow); private: - BitcoinGUI *gui; ClientModel *clientModel; WalletModel *walletModel; OverviewPage *overviewPage; QWidget *transactionsPage; - AddressBookPage *addressBookPage; - AddressBookPage *receiveCoinsPage; + ReceiveCoinsDialog *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; - SignVerifyMessageDialog *signVerifyMessageDialog; TransactionView *transactionView; @@ -73,8 +65,6 @@ public slots: void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); - /** Switch to address book page */ - void gotoAddressBookPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ @@ -89,7 +79,7 @@ public slots: The new items are those between start and end inclusive, under the given parent item. */ - void incomingTransaction(const QModelIndex& parent, int start, int /*end*/); + void processNewTransaction(const QModelIndex& parent, int start, int /*end*/); /** Encrypt the wallet */ void encryptWallet(bool status); /** Backup the wallet */ @@ -99,11 +89,22 @@ public slots: /** Ask for passphrase to unlock wallet temporarily */ void unlockWallet(); - void setEncryptionStatus(); + /** Show used sending addresses */ + void usedSendingAddresses(); + /** Show used receiving addresses */ + void usedReceivingAddresses(); + /** Re-emit encryption status signal */ + void updateEncryptionStatus(); signals: /** Signal that we want to show the main window */ void showNormalIfMinimized(); + /** Fired when a message should be reported to the user */ + void message(const QString &title, const QString &message, unsigned int style); + /** Encryption status of wallet changed */ + void encryptionStatusChanged(int status); + /** Notify that a new transaction appeared */ + void incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address); }; #endif // WALLETVIEW_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index adff708a06..483d8d2e8d 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -1,15 +1,22 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "main.h" + + #include "bitcoinrpc.h" +#include "main.h" +#include "sync.h" + +#include <stdint.h> + +#include "json/json_spirit_value.h" 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 +24,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 +73,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 +87,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 +97,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) @@ -112,7 +119,7 @@ Value settxfee(const Array& params, bool fHelp) "<amount> is a real and is rounded to the nearest 0.00000001 btc per kb"); // Amount - int64 nAmount = 0; + int64_t nAmount = 0; if (params[0].get_real() != 0.0) nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts @@ -145,11 +152,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) @@ -238,14 +245,16 @@ Value gettxout(const Array& params, bool fHelp) if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) return Value::null; - ret.push_back(Pair("bestblock", pcoinsTip->GetBestBlock()->GetBlockHash().GetHex())); + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + CBlockIndex *pindex = it->second; + ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) ret.push_back(Pair("confirmations", 0)); else - ret.push_back(Pair("confirmations", pcoinsTip->GetBestBlock()->nHeight - coins.nHeight + 1)); + ret.push_back(Pair("confirmations", pindex->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..68d412490b 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -1,33 +1,35 @@ -// Copyright (c) 2009-2012 Bitcoin Developers +// 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 <iostream> -#include <fstream> -#include "init.h" // for pwalletMain -#include "bitcoinrpc.h" -#include "ui_interface.h" + #include "base58.h" +#include "bitcoinrpc.h" +#include "init.h" +#include "main.h" +#include "sync.h" +#include "wallet.h" + +#include <fstream> +#include <stdint.h> +#include <boost/algorithm/string.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/lexical_cast.hpp> -#include <boost/variant/get.hpp> -#include <boost/algorithm/string.hpp> - -#define printf OutputDebugStringF +#include "json/json_spirit_value.h" using namespace json_spirit; using namespace std; void EnsureWalletIsUnlocked(); -std::string static EncodeDumpTime(int64 nTime) { - return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); +std::string static EncodeDumpTime(int64_t nTime) { + return DateTimeStrFormat("%Y-%m-%"PRId64"T%H:%M:%SZ", nTime); } -int64 static DecodeDumpTime(const std::string &str) { - static const boost::posix_time::time_input_facet facet("%Y-%m-%dT%H:%M:%SZ"); +int64_t static DecodeDumpTime(const std::string &str) { + static boost::posix_time::time_input_facet facet("%Y-%m-%dT%H:%M:%SZ"); static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0); const std::locale loc(std::locale::classic(), &facet); std::istringstream iss(str); @@ -104,7 +106,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 +128,8 @@ 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_t nTimeBegin = chainActive.Tip()->nTime; + bool fGood = true; @@ -147,10 +150,10 @@ 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]); + int64_t nTime = DecodeDumpTime(vstr[1]); std::string strLabel; bool fLabel = true; for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { @@ -165,7 +168,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 +180,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(); @@ -229,14 +232,14 @@ Value dumpwallet(const Array& params, bool fHelp) if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); - std::map<CKeyID, int64> mapKeyBirth; + std::map<CKeyID, int64_t> mapKeyBirth; std::set<CKeyID> setKeyPool; pwalletMain->GetKeyBirthTimes(mapKeyBirth); pwalletMain->GetAllReserveKeys(setKeyPool); // sort time/key pairs - std::vector<std::pair<int64, CKeyID> > vKeyBirth; - for (std::map<CKeyID, int64>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) { + std::vector<std::pair<int64_t, CKeyID> > vKeyBirth; + for (std::map<CKeyID, int64_t>::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) { vKeyBirth.push_back(std::make_pair(it->second, it->first)); } mapKeyBirth.clear(); @@ -245,10 +248,10 @@ 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++) { + for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; std::string strTime = EncodeDumpTime(it->first); std::string strAddr = CBitcoinAddress(keyid).ToString(); diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index c7f516caa7..5fe464da81 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -1,13 +1,23 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "bitcoinrpc.h" #include "chainparams.h" #include "db.h" #include "init.h" +#include "net.h" +#include "main.h" #include "miner.h" -#include "bitcoinrpc.h" +#include "wallet.h" + +#include <stdint.h> + +#include "json/json_spirit_utils.h" +#include "json/json_spirit_value.h" using namespace json_spirit; using namespace std; @@ -18,15 +28,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_t minTime = pb0->GetBlockTime(); + int64_t maxTime = minTime; + for (int i = 0; i < lookup; i++) { + pb0 = pb0->pprev; + int64_t 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_t 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 +100,9 @@ Value getgenerate(const Array& params, bool fHelp) "getgenerate\n" "Returns true or false."); + if (!pMiningKey) + return false; + return GetBoolArg("-gen", false); } @@ -59,6 +128,7 @@ Value setgenerate(const Array& params, bool fHelp) } mapArgs["-gen"] = (fGenerate ? "1" : "0"); + assert(pwalletMain != NULL); GenerateBitcoins(fGenerate, pwalletMain); return Value::null; } @@ -85,14 +155,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; @@ -126,12 +197,12 @@ Value getwork(const Array& params, bool fHelp) // Update block static unsigned int nTransactionsUpdatedLast; static CBlockIndex* pindexPrev; - static int64 nStart; + static int64_t nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)) + if (pindexPrev != chainActive.Tip() || + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)) { - if (pindexPrev != pindexBest) + if (pindexPrev != chainActive.Tip()) { // Deallocate old blocks since they're obsolete now mapNewBlock.clear(); @@ -144,8 +215,8 @@ Value getwork(const Array& params, bool fHelp) pindexPrev = NULL; // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block @@ -207,6 +278,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); } } @@ -261,17 +333,17 @@ Value getblocktemplate(const Array& params, bool fHelp) // Update block static unsigned int nTransactionsUpdatedLast; static CBlockIndex* pindexPrev; - static int64 nStart; + static int64_t nStart; static CBlockTemplate* pblocktemplate; - if (pindexPrev != pindexBest || - (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) + if (pindexPrev != chainActive.Tip() || + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { // Clear pindexPrev so future calls make a new block, despite any failures from here on pindexPrev = NULL; // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = nTransactionsUpdated; - CBlockIndex* pindexPrevNew = pindexBest; + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); // Create new block diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index bd7bc0ba10..4885758395 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -1,9 +1,20 @@ -// Copyright (c) 2009-2012 Bitcoin Developers +// 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 "net.h" + + #include "bitcoinrpc.h" +#include "net.h" +#include "netbase.h" +#include "protocol.h" +#include "sync.h" +#include "util.h" + +#include <inttypes.h> + +#include <boost/foreach.hpp> +#include "json/json_spirit_value.h" using namespace json_spirit; using namespace std; @@ -19,6 +30,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(); @@ -48,12 +77,17 @@ Value getpeerinfo(const Array& params, bool fHelp) Object obj; obj.push_back(Pair("addr", stats.addrName)); - obj.push_back(Pair("services", strprintf("%08"PRI64x, stats.nServices))); + if (!(stats.addrLocal.empty())) + obj.push_back(Pair("addrlocal", stats.addrLocal)); + obj.push_back(Pair("services", strprintf("%08"PRIx64, stats.nServices))); obj.push_back(Pair("lastsend", (boost::int64_t)stats.nLastSend)); obj.push_back(Pair("lastrecv", (boost::int64_t)stats.nLastRecv)); 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)); @@ -97,13 +131,13 @@ Value addnode(const Array& params, bool fHelp) if (strCommand == "add") { if (it != vAddedNodes.end()) - throw JSONRPCError(-23, "Error: Node already added"); + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); vAddedNodes.push_back(strNode); } else if(strCommand == "remove") { if (it == vAddedNodes.end()) - throw JSONRPCError(-24, "Error: Node has not been added."); + throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); vAddedNodes.erase(it); } @@ -140,7 +174,7 @@ Value getaddednodeinfo(const Array& params, bool fHelp) break; } if (laddedNodes.size() == 0) - throw JSONRPCError(-24, "Error: Node has not been added."); + throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); } if (!fDns) @@ -202,3 +236,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..4912f71f9d 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -1,67 +1,41 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 <boost/assign/list_of.hpp> + #include "base58.h" #include "bitcoinrpc.h" -#include "db.h" #include "init.h" #include "net.h" +#include "uint256.h" #include "wallet.h" +#include <stdint.h> + +#include <boost/assign/list_of.hpp> +#include "json/json_spirit_utils.h" +#include "json/json_spirit_value.h" + using namespace std; 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)) { - out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD))); + out.push_back(Pair("type", GetTxnOutputType(type))); return; } @@ -106,7 +80,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 +93,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 +177,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) { @@ -219,7 +194,7 @@ Value listunspent(const Array& params, bool fHelp) continue; } - int64 nValue = out.tx->vout[out.i].nValue; + int64_t nValue = out.tx->vout[out.i].nValue; const CScript& pk = out.tx->vout[out.i].scriptPubKey; Object entry; entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); @@ -300,7 +275,7 @@ Value createrawtransaction(const Array& params, bool fHelp) CScript scriptPubKey; scriptPubKey.SetDestination(address.Get()); - int64 nAmount = AmountFromValue(s.value_); + int64_t nAmount = AmountFromValue(s.value_); CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); @@ -334,6 +309,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 +465,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 +525,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 +535,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 +556,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) if (!fHave) { // push to local node CValidationState state; - if (!mempool.accept(state, tx, false, NULL)) + if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state } } @@ -564,7 +566,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) // Not in block, but already in the memory pool; will drop // through to re-relay it. } else { - SyncWithWallets(hashTx, tx, NULL, true); + SyncWithWallets(hashTx, tx, NULL); } RelayTransaction(tx, hashTx); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 14b4956a15..cb445ed11d 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1,27 +1,36 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 <boost/assign/list_of.hpp> -#include "wallet.h" -#include "walletdb.h" + +#include "base58.h" #include "bitcoinrpc.h" #include "init.h" -#include "base58.h" +#include "net.h" +#include "netbase.h" +#include "util.h" +#include "wallet.h" +#include "walletdb.h" + +#include <stdint.h> + +#include <boost/assign/list_of.hpp> +#include "json/json_spirit_utils.h" +#include "json/json_spirit_value.h" using namespace std; using namespace boost; using namespace boost::assign; using namespace json_spirit; -int64 nWalletUnlockTime; +int64_t nWalletUnlockTime; 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 +81,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; @@ -283,7 +296,7 @@ Value sendtoaddress(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); // Amount - int64 nAmount = AmountFromValue(params[1]); + int64_t nAmount = AmountFromValue(params[1]); // Wallet comments CWalletTx wtx; @@ -312,7 +325,7 @@ Value listaddressgroupings(const Array& params, bool fHelp) "in past transactions"); Array jsonGroupings; - map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances(); + map<CTxDestination, int64_t> balances = pwalletMain->GetAddressBalances(); BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings()) { Array jsonGrouping; @@ -427,7 +440,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) nMinDepth = params[1].get_int(); // Tally - int64 nAmount = 0; + int64_t nAmount = 0; for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; @@ -461,7 +474,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) set<CTxDestination> setAddress = pwalletMain->GetAccountAddresses(strAccount); // Tally - int64 nAmount = 0; + int64_t nAmount = 0; for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; @@ -481,9 +494,9 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) } -int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) +int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth) { - int64 nBalance = 0; + int64_t nBalance = 0; // Tally wallet transactions for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) @@ -492,7 +505,7 @@ int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinD if (!IsFinalTx(wtx)) continue; - int64 nReceived, nSent, nFee; + int64_t nReceived, nSent, nFee; wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee); if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) @@ -506,7 +519,7 @@ int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinD return nBalance; } -int64 GetAccountBalance(const string& strAccount, int nMinDepth) +int64_t GetAccountBalance(const string& strAccount, int nMinDepth) { CWalletDB walletdb(pwalletMain->strWalletFile); return GetAccountBalance(walletdb, strAccount, nMinDepth); @@ -532,24 +545,24 @@ Value getbalance(const Array& params, bool fHelp) // Calculate total balance a different way from GetBalance() // (GetBalance() sums up all unspent TxOuts) // getbalance and getbalance '*' 0 should return the same number - int64 nBalance = 0; + int64_t nBalance = 0; for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; if (!wtx.IsConfirmed()) continue; - int64 allFee; + int64_t allFee; string strSentAccount; - list<pair<CTxDestination, int64> > listReceived; - list<pair<CTxDestination, int64> > listSent; + list<pair<CTxDestination, int64_t> > listReceived; + list<pair<CTxDestination, int64_t> > listSent; wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount); if (wtx.GetDepthInMainChain() >= nMinDepth) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived) + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived) nBalance += r.second; } - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listSent) + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent) nBalance -= r.second; nBalance -= allFee; } @@ -558,7 +571,7 @@ Value getbalance(const Array& params, bool fHelp) string strAccount = AccountFromValue(params[0]); - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + int64_t nBalance = GetAccountBalance(strAccount, nMinDepth); return ValueFromAmount(nBalance); } @@ -573,7 +586,7 @@ Value movecmd(const Array& params, bool fHelp) string strFrom = AccountFromValue(params[0]); string strTo = AccountFromValue(params[1]); - int64 nAmount = AmountFromValue(params[2]); + int64_t nAmount = AmountFromValue(params[2]); if (params.size() > 3) // unused parameter, used to be nMinDepth, keep type-checking it though (void)params[3].get_int(); @@ -585,7 +598,7 @@ Value movecmd(const Array& params, bool fHelp) if (!walletdb.TxnBegin()) throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); - int64 nNow = GetAdjustedTime(); + int64_t nNow = GetAdjustedTime(); // Debit CAccountingEntry debit; @@ -626,7 +639,7 @@ Value sendfrom(const Array& params, bool fHelp) CBitcoinAddress address(params[1].get_str()); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - int64 nAmount = AmountFromValue(params[2]); + int64_t nAmount = AmountFromValue(params[2]); int nMinDepth = 1; if (params.size() > 3) nMinDepth = params[3].get_int(); @@ -641,7 +654,7 @@ Value sendfrom(const Array& params, bool fHelp) EnsureWalletIsUnlocked(); // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + int64_t nBalance = GetAccountBalance(strAccount, nMinDepth); if (nAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); @@ -674,9 +687,9 @@ Value sendmany(const Array& params, bool fHelp) wtx.mapValue["comment"] = params[3].get_str(); set<CBitcoinAddress> setAddress; - vector<pair<CScript, int64> > vecSend; + vector<pair<CScript, int64_t> > vecSend; - int64 totalAmount = 0; + int64_t totalAmount = 0; BOOST_FOREACH(const Pair& s, sendTo) { CBitcoinAddress address(s.name_); @@ -689,7 +702,7 @@ Value sendmany(const Array& params, bool fHelp) CScript scriptPubKey; scriptPubKey.SetDestination(address.Get()); - int64 nAmount = AmountFromValue(s.value_); + int64_t nAmount = AmountFromValue(s.value_); totalAmount += nAmount; vecSend.push_back(make_pair(scriptPubKey, nAmount)); @@ -698,13 +711,13 @@ Value sendmany(const Array& params, bool fHelp) EnsureWalletIsUnlocked(); // Check funds - int64 nBalance = GetAccountBalance(strAccount, nMinDepth); + int64_t nBalance = GetAccountBalance(strAccount, nMinDepth); if (totalAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); // Send CReserveKey keyChange(pwalletMain); - int64 nFeeRequired = 0; + int64_t nFeeRequired = 0; string strFailReason; bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason); if (!fCreated) @@ -738,7 +751,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)) @@ -822,7 +835,7 @@ Value createmultisig(const Array& params, bool fHelp) struct tallyitem { - int64 nAmount; + int64_t nAmount; int nConf; vector<uint256> txids; tallyitem() @@ -881,7 +894,7 @@ Value ListReceived(const Array& params, bool fByAccounts) if (it == mapTally.end() && !fIncludeEmpty) continue; - int64 nAmount = 0; + int64_t nAmount = 0; int nConf = std::numeric_limits<int>::max(); if (it != mapTally.end()) { @@ -919,7 +932,7 @@ Value ListReceived(const Array& params, bool fByAccounts) { for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) { - int64 nAmount = (*it).second.nAmount; + int64_t nAmount = (*it).second.nAmount; int nConf = (*it).second.nConf; Object obj; obj.push_back(Pair("account", (*it).first)); @@ -964,12 +977,19 @@ Value listreceivedbyaccount(const Array& params, bool fHelp) return ListReceived(params, true); } +static void MaybePushAddress(Object & entry, const CTxDestination &dest) +{ + CBitcoinAddress addr; + if (addr.Set(dest)) + entry.push_back(Pair("address", addr.ToString())); +} + void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret) { - int64 nFee; + int64_t nFee; string strSentAccount; - list<pair<CTxDestination, int64> > listReceived; - list<pair<CTxDestination, int64> > listSent; + list<pair<CTxDestination, int64_t> > listReceived; + list<pair<CTxDestination, int64_t> > listSent; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); @@ -978,11 +998,11 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe // Sent if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent) + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) { Object entry; entry.push_back(Pair("account", strSentAccount)); - entry.push_back(Pair("address", CBitcoinAddress(s.first).ToString())); + MaybePushAddress(entry, s.first); entry.push_back(Pair("category", "send")); entry.push_back(Pair("amount", ValueFromAmount(-s.second))); entry.push_back(Pair("fee", ValueFromAmount(-nFee))); @@ -995,7 +1015,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe // Received if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived) + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) { string account; if (pwalletMain->mapAddressBook.count(r.first)) @@ -1004,7 +1024,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { Object entry; entry.push_back(Pair("account", account)); - entry.push_back(Pair("address", CBitcoinAddress(r.first).ToString())); + MaybePushAddress(entry, r.first); if (wtx.IsCoinBase()) { if (wtx.GetDepthInMainChain() < 1) @@ -1111,7 +1131,7 @@ Value listaccounts(const Array& params, bool fHelp) if (params.size() > 0) nMinDepth = params[0].get_int(); - map<string, int64> mapAccountBalances; + map<string, int64_t> mapAccountBalances; BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { if (IsMine(*pwalletMain, entry.first)) // This address belongs to me mapAccountBalances[entry.second.name] = 0; @@ -1120,17 +1140,17 @@ Value listaccounts(const Array& params, bool fHelp) for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - int64 nFee; + int64_t nFee; string strSentAccount; - list<pair<CTxDestination, int64> > listReceived; - list<pair<CTxDestination, int64> > listSent; + list<pair<CTxDestination, int64_t> > listReceived; + list<pair<CTxDestination, int64_t> > listSent; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); mapAccountBalances[strSentAccount] -= nFee; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& s, listSent) + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) mapAccountBalances[strSentAccount] -= s.second; if (wtx.GetDepthInMainChain() >= nMinDepth) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64)& r, listReceived) + BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) if (pwalletMain->mapAddressBook.count(r.first)) mapAccountBalances[pwalletMain->mapAddressBook[r.first].name] += r.second; else @@ -1144,7 +1164,7 @@ Value listaccounts(const Array& params, bool fHelp) mapAccountBalances[entry.strAccount] += entry.nCreditDebit; Object ret; - BOOST_FOREACH(const PAIRTYPE(string, int64)& accountBalance, mapAccountBalances) { + BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) { ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); } return ret; @@ -1165,7 +1185,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 +1198,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 +1210,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)); @@ -1228,10 +1235,10 @@ Value gettransaction(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - int64 nCredit = wtx.GetCredit(); - int64 nDebit = wtx.GetDebit(); - int64 nNet = nCredit - nDebit; - int64 nFee = (wtx.IsFromMe() ? GetValueOut(wtx) - nDebit : 0); + int64_t nCredit = wtx.GetCredit(); + int64_t nDebit = wtx.GetDebit(); + int64_t nNet = nCredit - nDebit; + int64_t nFee = (wtx.IsFromMe() ? GetValueOut(wtx) - nDebit : 0); entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); if (wtx.IsFromMe()) @@ -1270,10 +1277,10 @@ Value keypoolrefill(const Array& params, bool fHelp) "Fills the keypool." + HelpRequiringPassphrase()); - unsigned int kpSize = max(GetArg("-keypool", 100), 0LL); + unsigned int kpSize = max(GetArg("-keypool", 100), (int64_t) 0); if (params.size() > 0) { if (params[0].get_int() < 0) - throw JSONRPCError(-8, "Invalid parameter, expected valid size"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size"); kpSize = (unsigned int) params[0].get_int(); } @@ -1303,6 +1310,8 @@ Value walletpassphrase(const Array& params, bool fHelp) "Stores the wallet decryption key in memory for <timeout> seconds."); if (fHelp) return true; + if (!fServer) + throw JSONRPCError(RPC_SERVER_NOT_STARTED, "Error: RPC server was not started, use server=1 to change this."); if (!pwalletMain->IsCrypted()) throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); @@ -1325,7 +1334,7 @@ Value walletpassphrase(const Array& params, bool fHelp) pwalletMain->TopUpKeyPool(); - int64 nSleepTime = params[1].get_int64(); + int64_t nSleepTime = params[1].get_int64(); LOCK(cs_nWalletUnlockTime); nWalletUnlockTime = GetTime() + nSleepTime; RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); @@ -1447,6 +1456,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 +1484,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..dbf391b2ab 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1,25 +1,28 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "script.h" -#include "core.h" -#include "keystore.h" + #include "bignum.h" +#include "core.h" +#include "hash.h" #include "key.h" +#include "keystore.h" #include "sync.h" +#include "uint256.h" #include "util.h" +#include <stdint.h> + #include <boost/foreach.hpp> #include <boost/tuple/tuple.hpp> +#include <boost/tuple/tuple_comparison.hpp> using namespace std; using namespace boost; -bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); - - - typedef vector<unsigned char> valtype; static const valtype vchFalse(0); static const valtype vchZero(0); @@ -30,6 +33,7 @@ static const CBigNum bnFalse(0); static const CBigNum bnTrue(1); static const size_t nMaxNumSize = 4; +bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); CBigNum CastToBigNum(const valtype& vch) { @@ -79,6 +83,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_PUBKEYHASH: return "pubkeyhash"; case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; + case TX_NULL_DATA: return "nulldata"; } return NULL; } @@ -220,6 +225,7 @@ const char* GetOpName(opcodetype opcode) // template matching params case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; case OP_PUBKEY : return "OP_PUBKEY"; + case OP_SMALLDATA : return "OP_SMALLDATA"; case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; default: @@ -971,62 +977,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); @@ -1066,12 +1128,12 @@ public: // (~200 bytes per cache entry times 50,000 entries) // Since there are a maximum of 20,000 signature operations per block // 50,000 is a reasonable default. - int64 nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); if (nMaxCacheSize <= 0) return; boost::unique_lock<boost::shared_mutex> lock(cs_sigcache); - while (static_cast<int64>(setValid.size()) > nMaxCacheSize) + while (static_cast<int64_t>(setValid.size()) > nMaxCacheSize) { // Evict a random entry. Random because that helps // foil would-be DoS attackers who might try to pre-generate @@ -1137,7 +1199,7 @@ bool CheckSig(vector<unsigned char> vchSig, const vector<unsigned char> &vchPubK bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet) { // Templates - static map<txnouttype, CScript> mTemplates; + static multimap<txnouttype, CScript> mTemplates; if (mTemplates.empty()) { // Standard tx, sender provides pubkey, receiver adds signature @@ -1148,6 +1210,10 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi // Sender provides N pubkeys, receivers provides M signatures mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); + + // Empty, provably prunable, data-carrying output + mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); + mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN)); } // Shortcut for pay-to-script-hash, which are more constrained than the other types: @@ -1232,6 +1298,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi else break; } + else if (opcode2 == OP_SMALLDATA) + { + // small pushdata, <= 80 bytes + if (vch1.size() > 80) + break; + } else if (opcode1 != opcode2 || vch1 != vch2) { // Others must match exactly @@ -1294,6 +1366,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash switch (whichTypeRet) { case TX_NONSTANDARD: + case TX_NULL_DATA: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -1324,6 +1397,7 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c switch (t) { case TX_NONSTANDARD: + case TX_NULL_DATA: return -1; case TX_PUBKEY: return 1; @@ -1339,10 +1413,9 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c return -1; } -bool IsStandard(const CScript& scriptPubKey) +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) { vector<valtype> vSolutions; - txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; @@ -1401,6 +1474,7 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) switch (whichType) { case TX_NONSTANDARD: + case TX_NULL_DATA: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -1462,6 +1536,10 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto vector<valtype> vSolutions; if (!Solver(scriptPubKey, typeRet, vSolutions)) return false; + if (typeRet == TX_NULL_DATA){ + // This is data, not addresses + return false; + } if (typeRet == TX_MULTISIG) { @@ -1677,6 +1755,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, switch (txType) { case TX_NONSTANDARD: + case TX_NULL_DATA: // Don't know anything about this, assume bigger one is correct: if (sigs1.size() >= sigs2.size()) return PushAll(sigs1); diff --git a/src/script.h b/src/script.h index 0ab47678e8..bd120cc07d 100644 --- a/src/script.h +++ b/src/script.h @@ -1,20 +1,25 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef H_BITCOIN_SCRIPT #define H_BITCOIN_SCRIPT +#include "bignum.h" +#include "key.h" +#include "util.h" + +#include <stdexcept> +#include <stdint.h> #include <string> #include <vector> #include <boost/foreach.hpp> #include <boost/variant.hpp> -#include "keystore.h" -#include "bignum.h" - class CCoins; +class CKeyStore; class CTransaction; static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes @@ -46,6 +51,7 @@ enum txnouttype TX_PUBKEYHASH, TX_SCRIPTHASH, TX_MULTISIG, + TX_NULL_DATA, }; class CNoDestination { @@ -202,6 +208,7 @@ enum opcodetype // template matching params + OP_SMALLDATA = 0xf9, OP_SMALLINTEGER = 0xfa, OP_PUBKEYS = 0xfb, OP_PUBKEYHASH = 0xfd, @@ -245,7 +252,7 @@ inline std::string StackString(const std::vector<std::vector<unsigned char> >& v class CScript : public std::vector<unsigned char> { protected: - CScript& push_int64(int64 n) + CScript& push_int64(int64_t n) { if (n == -1 || (n >= 1 && n <= 16)) { @@ -259,7 +266,7 @@ protected: return *this; } - CScript& push_uint64(uint64 n) + CScript& push_uint64(uint64_t n) { if (n >= 1 && n <= 16) { @@ -296,16 +303,16 @@ public: //explicit CScript(char b) is not portable. Use 'signed char' or 'unsigned char'. - explicit CScript(signed char b) { operator<<(b); } - explicit CScript(short b) { operator<<(b); } - explicit CScript(int b) { operator<<(b); } - explicit CScript(long b) { operator<<(b); } - explicit CScript(int64 b) { operator<<(b); } - explicit CScript(unsigned char b) { operator<<(b); } - explicit CScript(unsigned int b) { operator<<(b); } - explicit CScript(unsigned short b) { operator<<(b); } - explicit CScript(unsigned long b) { operator<<(b); } - explicit CScript(uint64 b) { operator<<(b); } + explicit CScript(signed char b) { operator<<(b); } + explicit CScript(short b) { operator<<(b); } + explicit CScript(int b) { operator<<(b); } + explicit CScript(long b) { operator<<(b); } + explicit CScript(long long b) { operator<<(b); } + explicit CScript(unsigned char b) { operator<<(b); } + explicit CScript(unsigned int b) { operator<<(b); } + explicit CScript(unsigned short b) { operator<<(b); } + explicit CScript(unsigned long b) { operator<<(b); } + explicit CScript(unsigned long long b) { operator<<(b); } explicit CScript(opcodetype b) { operator<<(b); } explicit CScript(const uint256& b) { operator<<(b); } @@ -314,16 +321,16 @@ public: //CScript& operator<<(char b) is not portable. Use 'signed char' or 'unsigned char'. - CScript& operator<<(signed char b) { return push_int64(b); } - CScript& operator<<(short b) { return push_int64(b); } - CScript& operator<<(int b) { return push_int64(b); } - CScript& operator<<(long b) { return push_int64(b); } - CScript& operator<<(int64 b) { return push_int64(b); } - CScript& operator<<(unsigned char b) { return push_uint64(b); } - CScript& operator<<(unsigned int b) { return push_uint64(b); } - CScript& operator<<(unsigned short b) { return push_uint64(b); } - CScript& operator<<(unsigned long b) { return push_uint64(b); } - CScript& operator<<(uint64 b) { return push_uint64(b); } + CScript& operator<<(signed char b) { return push_int64(b); } + CScript& operator<<(short b) { return push_int64(b); } + CScript& operator<<(int b) { return push_int64(b); } + CScript& operator<<(long b) { return push_int64(b); } + CScript& operator<<(long long b) { return push_int64(b); } + CScript& operator<<(unsigned char b) { return push_uint64(b); } + CScript& operator<<(unsigned int b) { return push_uint64(b); } + CScript& operator<<(unsigned short b) { return push_uint64(b); } + CScript& operator<<(unsigned long b) { return push_uint64(b); } + CScript& operator<<(unsigned long long b) { return push_uint64(b); } CScript& operator<<(opcodetype opcode) { @@ -553,6 +560,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 +574,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 +602,7 @@ public: void print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } CScriptID GetID() const @@ -676,7 +690,7 @@ bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig, unsigned int bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); -bool IsStandard(const CScript& scriptPubKey); +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CTxDestination &dest); void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys); diff --git a/src/serialize.h b/src/serialize.h index e3d9939bcc..4a9ff579f4 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -1,33 +1,32 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_SERIALIZE_H #define BITCOIN_SERIALIZE_H -#include <string> -#include <vector> +#include "allocators.h" + +#include <algorithm> +#include <assert.h> +#include <limits> +#include <ios> #include <map> #include <set> -#include <cassert> -#include <limits> -#include <cstring> -#include <cstdio> +#include <stdint.h> +#include <string> +#include <string.h> +#include <utility> +#include <vector> -#include <boost/type_traits/is_fundamental.hpp> #include <boost/tuple/tuple.hpp> -#include <boost/tuple/tuple_comparison.hpp> -#include <boost/tuple/tuple_io.hpp> - -#include "allocators.h" -#include "version.h" - -typedef long long int64; -typedef unsigned long long uint64; +#include <boost/type_traits/is_fundamental.hpp> -class CScript; -class CDataStream; class CAutoFile; +class CDataStream; +class CScript; + static const unsigned int MAX_SIZE = 0x02000000; // Used to bypass the rule against non-const reference to temporary @@ -103,47 +102,47 @@ enum #define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) #define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) -inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(int64 a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(uint64 a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } -inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } - -template<typename Stream> inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, int64 a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, uint64 a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } -template<typename Stream> inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } - -template<typename Stream> inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, int64& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, uint64& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } -template<typename Stream> inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } +inline unsigned int GetSerializeSize(char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned char a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned short a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned int a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(signed long long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(unsigned long long a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(float a, int, int=0) { return sizeof(a); } +inline unsigned int GetSerializeSize(double a, int, int=0) { return sizeof(a); } + +template<typename Stream> inline void Serialize(Stream& s, char a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, signed char a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, unsigned char a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, signed short a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, unsigned short a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, signed int a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, unsigned int a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, signed long a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, signed long long a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, unsigned long long a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, float a, int, int=0) { WRITEDATA(s, a); } +template<typename Stream> inline void Serialize(Stream& s, double a, int, int=0) { WRITEDATA(s, a); } + +template<typename Stream> inline void Unserialize(Stream& s, char& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, signed char& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, unsigned char& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, signed short& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, unsigned short& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, signed int& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, unsigned int& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, signed long& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, unsigned long& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, signed long long& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, unsigned long long& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, float& a, int, int=0) { READDATA(s, a); } +template<typename Stream> inline void Unserialize(Stream& s, double& a, int, int=0) { READDATA(s, a); } inline unsigned int GetSerializeSize(bool a, int, int=0) { return sizeof(char); } template<typename Stream> inline void Serialize(Stream& s, bool a, int, int=0) { char f=a; WRITEDATA(s, f); } @@ -161,16 +160,16 @@ template<typename Stream> inline void Unserialize(Stream& s, bool& a, int, int=0 // size <= UINT_MAX -- 5 bytes (254 + 4 bytes) // size > UINT_MAX -- 9 bytes (255 + 8 bytes) // -inline unsigned int GetSizeOfCompactSize(uint64 nSize) +inline unsigned int GetSizeOfCompactSize(uint64_t nSize) { if (nSize < 253) return sizeof(unsigned char); else if (nSize <= std::numeric_limits<unsigned short>::max()) return sizeof(unsigned char) + sizeof(unsigned short); else if (nSize <= std::numeric_limits<unsigned int>::max()) return sizeof(unsigned char) + sizeof(unsigned int); - else return sizeof(unsigned char) + sizeof(uint64); + else return sizeof(unsigned char) + sizeof(uint64_t); } template<typename Stream> -void WriteCompactSize(Stream& os, uint64 nSize) +void WriteCompactSize(Stream& os, uint64_t nSize) { if (nSize < 253) { @@ -194,7 +193,7 @@ void WriteCompactSize(Stream& os, uint64 nSize) else { unsigned char chSize = 255; - uint64 xSize = nSize; + uint64_t xSize = nSize; WRITEDATA(os, chSize); WRITEDATA(os, xSize); } @@ -202,11 +201,11 @@ void WriteCompactSize(Stream& os, uint64 nSize) } template<typename Stream> -uint64 ReadCompactSize(Stream& is) +uint64_t ReadCompactSize(Stream& is) { unsigned char chSize; READDATA(is, chSize); - uint64 nSizeRet = 0; + uint64_t nSizeRet = 0; if (chSize < 253) { nSizeRet = chSize; @@ -216,20 +215,26 @@ uint64 ReadCompactSize(Stream& is) unsigned short xSize; READDATA(is, xSize); nSizeRet = xSize; + if (nSizeRet < 253) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); } else if (chSize == 254) { unsigned int xSize; READDATA(is, xSize); nSizeRet = xSize; + if (nSizeRet < 0x10000u) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); } else { - uint64 xSize; + uint64_t xSize; READDATA(is, xSize); nSizeRet = xSize; + if (nSizeRet < 0x100000000LLu) + throw std::ios_base::failure("non-canonical ReadCompactSize()"); } - if (nSizeRet > (uint64)MAX_SIZE) + if (nSizeRet > (uint64_t)MAX_SIZE) throw std::ios_base::failure("ReadCompactSize() : size too large"); return nSizeRet; } @@ -895,19 +900,6 @@ public: iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } - void insert(iterator it, const_iterator first, const_iterator last) - { - assert(last - first >= 0); - if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) - { - // special case for inserting at the front when there's room - nReadPos -= (last - first); - memcpy(&vch[nReadPos], &first[0], last - first); - } - else - vch.insert(it, first, last); - } - void insert(iterator it, std::vector<char>::const_iterator first, std::vector<char>::const_iterator last) { assert(last - first >= 0); @@ -1098,8 +1090,8 @@ public: } void GetAndClear(CSerializeData &data) { - vch.swap(data); - CSerializeData().swap(vch); + data.insert(data.end(), begin(), end()); + clear(); } }; @@ -1234,10 +1226,10 @@ class CBufferedFile { private: FILE *src; // source file - uint64 nSrcPos; // how many bytes have been read from source - uint64 nReadPos; // how many bytes have been read from this - uint64 nReadLimit; // up to which position we're allowed to read - uint64 nRewind; // how many bytes we guarantee to rewind + uint64_t nSrcPos; // how many bytes have been read from source + uint64_t nReadPos; // how many bytes have been read from this + uint64_t nReadLimit; // up to which position we're allowed to read + uint64_t nRewind; // how many bytes we guarantee to rewind std::vector<char> vchBuf; // the buffer short state; @@ -1273,8 +1265,8 @@ public: int nType; int nVersion; - CBufferedFile(FILE *fileIn, uint64 nBufSize, uint64 nRewindIn, int nTypeIn, int nVersionIn) : - src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0), + CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) : + src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64_t)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0), state(0), exceptmask(std::ios_base::badbit | std::ios_base::failbit), nType(nTypeIn), nVersion(nVersionIn) { } @@ -1312,12 +1304,12 @@ public: } // return the current reading position - uint64 GetPos() { + uint64_t GetPos() { return nReadPos; } // rewind to a given reading position - bool SetPos(uint64 nPos) { + bool SetPos(uint64_t nPos) { nReadPos = nPos; if (nReadPos + nRewind < nSrcPos) { nReadPos = nSrcPos - nRewind; @@ -1330,9 +1322,9 @@ public: } } - bool Seek(uint64 nPos) { + bool Seek(uint64_t nPos) { long nLongPos = nPos; - if (nPos != (uint64)nLongPos) + if (nPos != (uint64_t)nLongPos) return false; if (fseek(src, nLongPos, SEEK_SET)) return false; @@ -1345,7 +1337,7 @@ public: // prevent reading beyond a certain position // no argument removes the limit - bool SetLimit(uint64 nPos = (uint64)(-1)) { + bool SetLimit(uint64_t nPos = (uint64_t)(-1)) { if (nPos < nReadPos) return false; nReadLimit = nPos; diff --git a/src/sync.cpp b/src/sync.cpp index 1ac4403beb..33e1219541 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "sync.h" + #include "util.h" #include <boost/foreach.hpp> @@ -10,8 +11,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 +57,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 +79,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()); + LogPrint("lock", "Locking: %s\n", locklocation.ToString().c_str()); dd_mutex.lock(); (*lockstack).push_back(std::make_pair(c, locklocation)); @@ -108,7 +109,7 @@ static void pop_lock() if (fDebug) { const CLockLocation& locklocation = (*lockstack).rbegin()->second; - printf("Unlocked: %s\n", locklocation.ToString().c_str()); + LogPrint("lock", "Unlocked: %s\n", locklocation.ToString().c_str()); } dd_mutex.lock(); (*lockstack).pop_back(); diff --git a/src/sync.h b/src/sync.h index 64de7cc57c..39f2cb5155 100644 --- a/src/sync.h +++ b/src/sync.h @@ -1,15 +1,17 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_SYNC_H #define BITCOIN_SYNC_H +#include "threadsafety.h" + +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/locks.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/recursive_mutex.hpp> -#include <boost/thread/locks.hpp> -#include <boost/thread/condition_variable.hpp> -#include "threadsafety.h" //////////////////////////////////////////////// diff --git a/src/test/Checkpoints_tests.cpp b/src/test/Checkpoints_tests.cpp index b14e9f7057..78b0236b74 100644 --- a/src/test/Checkpoints_tests.cpp +++ b/src/test/Checkpoints_tests.cpp @@ -1,12 +1,12 @@ // // Unit tests for block-chain checkpoints // -#include <boost/assign/list_of.hpp> // for 'map_list_of()' -#include <boost/test/unit_test.hpp> -#include <boost/foreach.hpp> -#include "../checkpoints.h" -#include "../util.h" +#include "checkpoints.h" + +#include "uint256.h" + +#include <boost/test/unit_test.hpp> using namespace std; diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index c7f968da7c..f0fb84bc54 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -1,21 +1,23 @@ // // Unit tests for denial-of-service detection/prevention code // -#include <algorithm> -#include <boost/assign/list_of.hpp> // for 'map_list_of()' -#include <boost/date_time/posix_time/posix_time_types.hpp> -#include <boost/test/unit_test.hpp> -#include <boost/foreach.hpp> -#include "chainparams.h" + +#include "bignum.h" +#include "keystore.h" #include "main.h" -#include "wallet.h" #include "net.h" -#include "util.h" +#include "script.h" +#include "serialize.h" #include <stdint.h> +#include <boost/assign/list_of.hpp> // for 'map_list_of()' +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/foreach.hpp> +#include <boost/test/unit_test.hpp> + // Tests this internal-to-main.cpp method: extern bool AddOrphanTx(const CTransaction& tx); extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans); @@ -67,7 +69,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) BOOST_AUTO_TEST_CASE(DoS_bantime) { CNode::ClearBanned(); - int64 nStartTime = GetTime(); + int64_t nStartTime = GetTime(); SetMockTime(nStartTime); // Overrides future calls to GetTime() CAddress addr(ip(0xa0b0c001)); @@ -83,11 +85,11 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) BOOST_CHECK(!CNode::IsBanned(addr)); } -static bool CheckNBits(unsigned int nbits1, int64 time1, unsigned int nbits2, int64 time2)\ +static bool CheckNBits(unsigned int nbits1, int64_t time1, unsigned int nbits2, int64_t time2)\ { if (time1 > time2) return CheckNBits(nbits2, time2, nbits1, time1); - int64 deltaTime = time2-time1; + int64_t deltaTime = time2-time1; CBigNum required; required.SetCompact(ComputeMinWork(nbits1, deltaTime)); @@ -102,7 +104,7 @@ BOOST_AUTO_TEST_CASE(DoS_checknbits) // Timestamps,nBits from the bitcoin block chain. // These are the block-chain checkpoint blocks - typedef std::map<int64, unsigned int> BlockData; + typedef std::map<int64_t, unsigned int> BlockData; BlockData chainData = map_list_of(1239852051,486604799)(1262749024,486594666) (1279305360,469854461)(1280200847,469830746)(1281678674,469809688) diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 80571aa295..180fd7effa 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -1,30 +1,27 @@ 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) - -AM_LDFLAGS = $(PTHREAD_CFLAGS) +AM_CPPFLAGS += -I$(top_srcdir)/src 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 + +RAW_TEST_FILES = data/alertTests.raw -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 +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 +30,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/accounting_tests.cpp b/src/test/accounting_tests.cpp index 8ac657288b..5f79436e48 100644 --- a/src/test/accounting_tests.cpp +++ b/src/test/accounting_tests.cpp @@ -1,15 +1,19 @@ -#include <boost/test/unit_test.hpp> -#include <boost/foreach.hpp> -#include "init.h" #include "wallet.h" #include "walletdb.h" +#include <stdint.h> + +#include <boost/foreach.hpp> +#include <boost/test/unit_test.hpp> + +extern CWallet* pwalletMain; + BOOST_AUTO_TEST_SUITE(accounting_tests) static void -GetResults(CWalletDB& walletdb, std::map<int64, CAccountingEntry>& results) +GetResults(CWalletDB& walletdb, std::map<int64_t, CAccountingEntry>& results) { std::list<CAccountingEntry> aes; @@ -28,7 +32,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) std::vector<CWalletTx*> vpwtx; CWalletTx wtx; CAccountingEntry ae; - std::map<int64, CAccountingEntry> results; + std::map<int64_t, CAccountingEntry> results; ae.strAccount = ""; ae.nCreditDebit = 1; diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index f7a11376d3..a7e668222d 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -2,13 +2,18 @@ // Unit tests for alert system // -#include <boost/foreach.hpp> -#include <boost/test/unit_test.hpp> -#include <fstream> - #include "alert.h" +#include "data/alertTests.raw.h" + #include "serialize.h" #include "util.h" +#include "version.h" + +#include <fstream> + +#include <boost/filesystem/operations.hpp> +#include <boost/foreach.hpp> +#include <boost/test/unit_test.hpp> #if 0 // @@ -22,7 +27,7 @@ alert.nID = 1; alert.nCancel = 0; // cancels previous messages up to this ID number alert.nMinVer = 0; // These versions are protocol versions - alert.nMaxVer = 70001; + alert.nMaxVer = 999001; alert.nPriority = 1; alert.strComment = "Alert comment"; alert.strStatusBar = "Alert 1"; @@ -71,27 +76,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,27 +116,30 @@ 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, "")); + BOOST_CHECK(alerts[0].AppliesTo(999001, "")); BOOST_CHECK(alerts[0].AppliesTo(1, "/Satoshi:11.11.11/")); BOOST_CHECK(alerts[1].AppliesTo(1, "/Satoshi:0.1.0/")); - BOOST_CHECK(alerts[1].AppliesTo(70001, "/Satoshi:0.1.0/")); + BOOST_CHECK(alerts[1].AppliesTo(999001, "/Satoshi:0.1.0/")); BOOST_CHECK(alerts[2].AppliesTo(1, "/Satoshi:0.1.0/")); BOOST_CHECK(alerts[2].AppliesTo(1, "/Satoshi:0.2.0/")); // Don't match: BOOST_CHECK(!alerts[0].AppliesTo(-1, "")); - BOOST_CHECK(!alerts[0].AppliesTo(70002, "")); + BOOST_CHECK(!alerts[0].AppliesTo(999002, "")); BOOST_CHECK(!alerts[1].AppliesTo(1, "")); BOOST_CHECK(!alerts[1].AppliesTo(1, "Satoshi:0.1.0")); BOOST_CHECK(!alerts[1].AppliesTo(1, "/Satoshi:0.1.0")); BOOST_CHECK(!alerts[1].AppliesTo(1, "Satoshi:0.1.0/")); BOOST_CHECK(!alerts[1].AppliesTo(-1, "/Satoshi:0.1.0/")); - BOOST_CHECK(!alerts[1].AppliesTo(70002, "/Satoshi:0.1.0/")); + BOOST_CHECK(!alerts[1].AppliesTo(999002, "/Satoshi:0.1.0/")); BOOST_CHECK(!alerts[1].AppliesTo(1, "/Satoshi:0.2.0/")); BOOST_CHECK(!alerts[2].AppliesTo(1, "/Satoshi:0.3.0/")); diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp index d5cb8e8101..490dff87e5 100644 --- a/src/test/allocator_tests.cpp +++ b/src/test/allocator_tests.cpp @@ -1,9 +1,9 @@ -#include <boost/test/unit_test.hpp> -#include "init.h" -#include "main.h" + #include "util.h" +#include <boost/test/unit_test.hpp> + BOOST_AUTO_TEST_SUITE(allocator_tests) // Dummy memory page locker for platform independent tests diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp index fdf3285913..580e87f65f 100644 --- a/src/test/base32_tests.cpp +++ b/src/test/base32_tests.cpp @@ -1,7 +1,9 @@ -#include <boost/test/unit_test.hpp> + #include "util.h" +#include <boost/test/unit_test.hpp> + BOOST_AUTO_TEST_SUITE(base32_tests) BOOST_AUTO_TEST_CASE(base32_testvectors) diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index af65416485..84db99d816 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -1,21 +1,29 @@ +#include "base58.h" + +#include "data/base58_encode_decode.json.h" +#include "data/base58_keys_invalid.json.h" +#include "data/base58_keys_valid.json.h" + +#include "key.h" +#include "script.h" +#include "uint256.h" +#include "util.h" + +#include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> #include "json/json_spirit_reader_template.h" -#include "json/json_spirit_writer_template.h" #include "json/json_spirit_utils.h" - -#include "base58.h" -#include "util.h" +#include "json/json_spirit_writer_template.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 +44,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 +112,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 +171,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 +239,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/base64_tests.cpp b/src/test/base64_tests.cpp index c5a053e2eb..c59c290669 100644 --- a/src/test/base64_tests.cpp +++ b/src/test/base64_tests.cpp @@ -1,9 +1,9 @@ -#include <boost/test/unit_test.hpp> -#include "main.h" -#include "wallet.h" + #include "util.h" +#include <boost/test/unit_test.hpp> + BOOST_AUTO_TEST_SUITE(base64_tests) BOOST_AUTO_TEST_CASE(base64_testvectors) diff --git a/src/test/bignum_tests.cpp b/src/test/bignum_tests.cpp index 196b7274f4..9d67324c76 100644 --- a/src/test/bignum_tests.cpp +++ b/src/test/bignum_tests.cpp @@ -1,8 +1,9 @@ -#include <boost/test/unit_test.hpp> +#include "bignum.h" + #include <limits> +#include <stdint.h> -#include "bignum.h" -#include "util.h" +#include <boost/test/unit_test.hpp> BOOST_AUTO_TEST_SUITE(bignum_tests) @@ -46,7 +47,7 @@ BOOST_AUTO_TEST_SUITE(bignum_tests) // Let's force this code not to be inlined, in order to actually // test a generic version of the function. This increases the chance // that -ftrapv will detect overflows. -NOINLINE void mysetint64(CBigNum& num, int64 n) +NOINLINE void mysetint64(CBigNum& num, int64_t n) { num.setint64(n); } @@ -55,7 +56,7 @@ NOINLINE void mysetint64(CBigNum& num, int64 n) // value to 0, then the second one with a non-inlined function. BOOST_AUTO_TEST_CASE(bignum_setint64) { - int64 n; + int64_t n; { n = 0; @@ -103,7 +104,7 @@ BOOST_AUTO_TEST_CASE(bignum_setint64) BOOST_CHECK(num.ToString() == "-5"); } { - n = std::numeric_limits<int64>::min(); + n = std::numeric_limits<int64_t>::min(); CBigNum num(n); BOOST_CHECK(num.ToString() == "-9223372036854775808"); num.setulong(0); @@ -112,7 +113,7 @@ BOOST_AUTO_TEST_CASE(bignum_setint64) BOOST_CHECK(num.ToString() == "-9223372036854775808"); } { - n = std::numeric_limits<int64>::max(); + n = std::numeric_limits<int64_t>::max(); CBigNum num(n); BOOST_CHECK(num.ToString() == "9223372036854775807"); num.setulong(0); @@ -175,4 +176,12 @@ BOOST_AUTO_TEST_CASE(bignum_SetCompact) BOOST_CHECK_EQUAL(num.GetCompact(), 0xff123456U); } +BOOST_AUTO_TEST_CASE(bignum_SetHex) +{ + std::string hexStr = "deecf97fd890808b9cc0f1b6a3e7a60b400f52710e6ad075b1340755bfa58cc9"; + CBigNum num; + num.SetHex(hexStr); + BOOST_CHECK_EQUAL(num.GetHex(), hexStr); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index 66d2330fbf..2bcca24336 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -1,13 +1,13 @@ #include <boost/test/unit_test.hpp> -#include <string> -#include <vector> - -#include "key.h" #include "base58.h" +#include "key.h" #include "uint256.h" #include "util.h" +#include <string> +#include <vector> + struct TestDerivation { std::string pub; std::string prv; diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 0d349a990c..c4e4403b70 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -1,11 +1,15 @@ -#include <boost/test/unit_test.hpp> -#include <vector> - #include "bloom.h" -#include "util.h" -#include "key.h" + #include "base58.h" +#include "key.h" #include "main.h" +#include "serialize.h" +#include "uint256.h" +#include "util.h" + +#include <vector> + +#include <boost/test/unit_test.hpp> using namespace std; using namespace boost::tuples; diff --git a/src/test/canonical_tests.cpp b/src/test/canonical_tests.cpp index 09988da259..c521f2cf9c 100644 --- a/src/test/canonical_tests.cpp +++ b/src/test/canonical_tests.cpp @@ -1,20 +1,25 @@ // // Unit tests for canonical signatures +// + -#include "json/json_spirit_writer_template.h" -#include <boost/test/unit_test.hpp> -#include <openssl/ecdsa.h> -#include "key.h" #include "script.h" #include "util.h" +#include "data/sig_noncanonical.json.h" +#include "data/sig_canonical.json.h" + +#include <boost/foreach.hpp> +#include <boost/test/unit_test.hpp> +#include "json/json_spirit_writer_template.h" +#include <openssl/ecdsa.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 +63,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 +77,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..d47a33fd46 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -1,17 +1,17 @@ // // Unit tests for block.CheckBlock() // -#include <algorithm> -#include <boost/assign/list_of.hpp> // for 'map_list_of()' -#include <boost/date_time/posix_time/posix_time_types.hpp> -#include <boost/test/unit_test.hpp> -#include <boost/foreach.hpp> + #include "main.h" -#include "wallet.h" -#include "net.h" -#include "util.h" + +#include <cstdio> + +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/test/unit_test.hpp> + BOOST_AUTO_TEST_SUITE(CheckBlock_tests) @@ -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/compress_tests.cpp b/src/test/compress_tests.cpp index 71b86bcb41..4d16914774 100644 --- a/src/test/compress_tests.cpp +++ b/src/test/compress_tests.cpp @@ -1,9 +1,11 @@ -#include <boost/test/unit_test.hpp> -#include <string> -#include <vector> #include "main.h" +#include "util.h" + +#include <stdint.h> + +#include <boost/test/unit_test.hpp> // amounts 0.00000001 .. 0.00100000 #define NUM_MULTIPLES_UNIT 100000 @@ -17,19 +19,17 @@ // amounts 50 .. 21000000 #define NUM_MULTIPLES_50BTC 420000 -using namespace std; - BOOST_AUTO_TEST_SUITE(compress_tests) -bool static TestEncode(uint64 in) { +bool static TestEncode(uint64_t in) { return in == CTxOutCompressor::DecompressAmount(CTxOutCompressor::CompressAmount(in)); } -bool static TestDecode(uint64 in) { +bool static TestDecode(uint64_t in) { return in == CTxOutCompressor::CompressAmount(CTxOutCompressor::DecompressAmount(in)); } -bool static TestPair(uint64 dec, uint64 enc) { +bool static TestPair(uint64_t dec, uint64_t enc) { return CTxOutCompressor::CompressAmount(dec) == enc && CTxOutCompressor::DecompressAmount(enc) == dec; } @@ -43,19 +43,19 @@ BOOST_AUTO_TEST_CASE(compress_amounts) BOOST_CHECK(TestPair( 50*COIN, 0x32)); BOOST_CHECK(TestPair(21000000*COIN, 0x1406f40)); - for (uint64 i = 1; i <= NUM_MULTIPLES_UNIT; i++) + for (uint64_t i = 1; i <= NUM_MULTIPLES_UNIT; i++) BOOST_CHECK(TestEncode(i)); - for (uint64 i = 1; i <= NUM_MULTIPLES_CENT; i++) + for (uint64_t i = 1; i <= NUM_MULTIPLES_CENT; i++) BOOST_CHECK(TestEncode(i * CENT)); - for (uint64 i = 1; i <= NUM_MULTIPLES_1BTC; i++) + for (uint64_t i = 1; i <= NUM_MULTIPLES_1BTC; i++) BOOST_CHECK(TestEncode(i * COIN)); - for (uint64 i = 1; i <= NUM_MULTIPLES_50BTC; i++) + for (uint64_t i = 1; i <= NUM_MULTIPLES_50BTC; i++) BOOST_CHECK(TestEncode(i * 50 * COIN)); - for (uint64 i = 0; i < 100000; i++) + for (uint64_t i = 0; i < 100000; i++) BOOST_CHECK(TestDecode(i)); } diff --git a/src/test/data/alertTests b/src/test/data/alertTests Binary files differdeleted file mode 100644 index 7fc4528961..0000000000 --- a/src/test/data/alertTests +++ /dev/null diff --git a/src/test/data/alertTests.raw b/src/test/data/alertTests.raw Binary files differnew file mode 100644 index 0000000000..01f50680b9 --- /dev/null +++ b/src/test/data/alertTests.raw diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json index 13b0af6c45..e4c181cae8 100644 --- a/src/test/data/script_valid.json +++ b/src/test/data/script_valid.json @@ -403,5 +403,13 @@ ["NOP", "NOP10 1"], ["0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "Very basic P2SH"], -["0x4c 0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"] +["0x4c 0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"], + +["0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"Basic PUSH signedness check"], + +["0x4c 0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"Basic PUSHDATA1 signedness check"] ] diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index faf911a97f..63e7074a32 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -83,5 +83,9 @@ [[["63cfa5a09dc540bf63e53713b82d9ea3692ca97cd608c384f2aa88e51a0aac70", 0, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"], ["04e8d0fcf3846c6734477b98f0f3d4badfb78f020ee097a0be5fe347645b817d", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"], ["ee1377aff5d0579909e11782e1d2f5f7b84d26537be7f5516dd4e43373091f3f", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"]], - "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000", true] + "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000", true], + + ["ddc454a1c0c35c188c98976b17670f69e586d9c0f3593ea879928332f0a069e7, which spends an input that pushes using a PUSHDATA1 that is negative when read as signed"], + [[["c5510a5dd97a25f43175af1fe649b707b1df8e1a41489bac33a23087027a2f48", 0, "0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG"]], + "0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c5000000008b483045022100bf0bbae9bde51ad2b222e87fbf67530fbafc25c903519a1e5dcc52a32ff5844e022028c4d9ad49b006dd59974372a54291d5764be541574bb0c4dc208ec51f80b7190141049dd4aad62741dc27d5f267f7b70682eee22e7e9c1923b9c0957bdae0b96374569b460eb8d5b40d972e8c7c0ad441de3d94c4a29864b212d56050acb980b72b2bffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac00000000", true] ] diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index c89d218f80..d76124885b 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -1,9 +1,14 @@ + + +#include "util.h" + +#include <string> +#include <vector> + #include <boost/algorithm/string.hpp> #include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> -#include "util.h" - BOOST_AUTO_TEST_SUITE(getarg_tests) static void ResetArgs(const std::string& strArg) diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp index d69cfd706d..28410f0fed 100644 --- a/src/test/hash_tests.cpp +++ b/src/test/hash_tests.cpp @@ -1,8 +1,11 @@ -#include <boost/test/unit_test.hpp> -#include <vector> -#include "util.h" + #include "hash.h" +#include "util.h" + +#include <vector> + +#include <boost/test/unit_test.hpp> using namespace std; diff --git a/src/test/hmac_tests.cpp b/src/test/hmac_tests.cpp index 92ca5e6aff..68868866aa 100644 --- a/src/test/hmac_tests.cpp +++ b/src/test/hmac_tests.cpp @@ -1,8 +1,10 @@ -#include <boost/test/unit_test.hpp> + #include "hash.h" #include "util.h" +#include <boost/test/unit_test.hpp> + using namespace std; BOOST_AUTO_TEST_SUITE(hmac_tests) diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index c004521d1a..421ffeac45 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -1,13 +1,15 @@ -#include <boost/test/unit_test.hpp> - -#include <string> -#include <vector> - #include "key.h" + #include "base58.h" +#include "script.h" #include "uint256.h" #include "util.h" +#include <string> +#include <vector> + +#include <boost/test/unit_test.hpp> + using namespace std; static const string strSecret1 ("5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 8183504147..dcb7f9abd4 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -1,12 +1,14 @@ -#include <boost/test/unit_test.hpp> -#include "init.h" + #include "main.h" +#include "miner.h" #include "uint256.h" #include "util.h" -#include "miner.h" #include "wallet.h" +#include <boost/test/unit_test.hpp> + +extern CWallet* pwalletMain; extern void SHA256Transform(void* pstate, void* pinput, const void* pinit); BOOST_AUTO_TEST_SUITE(miner_tests) @@ -65,10 +67,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 +85,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 +195,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/mruset_tests.cpp b/src/test/mruset_tests.cpp index 64a6678a73..638c88fabe 100644 --- a/src/test/mruset_tests.cpp +++ b/src/test/mruset_tests.cpp @@ -1,13 +1,16 @@ -#include <boost/test/unit_test.hpp> - -using namespace std; - #include "mruset.h" + #include "util.h" +#include <set> + +#include <boost/test/unit_test.hpp> + #define NUM_TESTS 16 #define MAX_SIZE 100 +using namespace std; + class mrutester { private: diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 9ef932b5b4..772217b888 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -1,25 +1,21 @@ -#include <boost/assert.hpp> -#include <boost/assign/list_of.hpp> -#include <boost/assign/list_inserter.hpp> -#include <boost/assign/std/vector.hpp> -#include <boost/test/unit_test.hpp> -#include <boost/foreach.hpp> -#include <boost/tuple/tuple.hpp> -#include <openssl/ec.h> -#include <openssl/err.h> +#include "key.h" #include "keystore.h" #include "main.h" #include "script.h" -#include "wallet.h" +#include "uint256.h" + +#include <boost/assign/std/vector.hpp> +#include <boost/foreach.hpp> +#include <boost/test/unit_test.hpp> using namespace std; 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) @@ -133,21 +129,23 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) for (int i = 0; i < 4; i++) key[i].MakeNewKey(true); + txnouttype whichType; + CScript a_and_b; a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(a_and_b)); + BOOST_CHECK(::IsStandard(a_and_b, whichType)); CScript a_or_b; a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(a_or_b)); + BOOST_CHECK(::IsStandard(a_or_b, whichType)); CScript escrow; escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(escrow)); + BOOST_CHECK(::IsStandard(escrow, whichType)); CScript one_of_four; one_of_four << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << key[3].GetPubKey() << OP_4 << OP_CHECKMULTISIG; - BOOST_CHECK(!::IsStandard(one_of_four)); + BOOST_CHECK(!::IsStandard(one_of_four, whichType)); CScript malformed[6]; malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; @@ -158,7 +156,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey(); for (int i = 0; i < 6; i++) - BOOST_CHECK(!::IsStandard(malformed[i])); + BOOST_CHECK(!::IsStandard(malformed[i], whichType)); } BOOST_AUTO_TEST_CASE(multisig_Solver1) diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index e5a7562d97..412ba04910 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -1,9 +1,8 @@ -#include <boost/test/unit_test.hpp> +#include "netbase.h" #include <string> -#include <vector> -#include "netbase.h" +#include <boost/test/unit_test.hpp> using namespace std; diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index cf09421617..dea5e1b824 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -1,7 +1,12 @@ -#include <boost/test/unit_test.hpp> -#include "uint256.h" + #include "main.h" +#include "serialize.h" +#include "uint256.h" + +#include <vector> + +#include <boost/test/unit_test.hpp> using namespace std; diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 35eabed0e2..4fb2aeb6ae 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -1,10 +1,9 @@ -#include <boost/algorithm/string.hpp> -#include <boost/foreach.hpp> -#include <boost/test/unit_test.hpp> +#include "bitcoinrpc.h" #include "base58.h" -#include "util.h" -#include "bitcoinrpc.h" + +#include <boost/algorithm/string.hpp> +#include <boost/test/unit_test.hpp> using namespace std; using namespace json_spirit; @@ -168,4 +167,35 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign) BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); } +BOOST_AUTO_TEST_CASE(rpc_format_monetary_values) +{ + BOOST_CHECK(write_string(ValueFromAmount(0LL), false) == "0.00000000"); + BOOST_CHECK(write_string(ValueFromAmount(1LL), false) == "0.00000001"); + BOOST_CHECK(write_string(ValueFromAmount(17622195LL), false) == "0.17622195"); + BOOST_CHECK(write_string(ValueFromAmount(50000000LL), false) == "0.50000000"); + BOOST_CHECK(write_string(ValueFromAmount(89898989LL), false) == "0.89898989"); + BOOST_CHECK(write_string(ValueFromAmount(100000000LL), false) == "1.00000000"); + BOOST_CHECK(write_string(ValueFromAmount(2099999999999990LL), false) == "20999999.99999990"); + BOOST_CHECK(write_string(ValueFromAmount(2099999999999999LL), false) == "20999999.99999999"); +} + +static Value ValueFromString(const std::string &str) +{ + Value value; + BOOST_CHECK(read_string(str, value)); + return value; +} + +BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values) +{ + BOOST_CHECK(AmountFromValue(ValueFromString("0.00000001")) == 1LL); + BOOST_CHECK(AmountFromValue(ValueFromString("0.17622195")) == 17622195LL); + BOOST_CHECK(AmountFromValue(ValueFromString("0.5")) == 50000000LL); + BOOST_CHECK(AmountFromValue(ValueFromString("0.50000000")) == 50000000LL); + BOOST_CHECK(AmountFromValue(ValueFromString("0.89898989")) == 89898989LL); + BOOST_CHECK(AmountFromValue(ValueFromString("1.00000000")) == 100000000LL); + BOOST_CHECK(AmountFromValue(ValueFromString("20999999.9999999")) == 2099999999999990LL); + BOOST_CHECK(AmountFromValue(ValueFromString("20999999.99999999")) == 2099999999999999LL); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 3c666d2842..e57dc9bbba 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -1,13 +1,13 @@ -#include <boost/assert.hpp> -#include <boost/assign/list_of.hpp> -#include <boost/assign/list_inserter.hpp> -#include <boost/assign/std/vector.hpp> -#include <boost/test/unit_test.hpp> -#include <boost/foreach.hpp> +#include "script.h" + +#include "key.h" +#include "keystore.h" +#include "main.h" +#include "script.h" -#include "../main.h" -#include "../script.h" -#include "../wallet.h" +#include <vector> + +#include <boost/test/unit_test.hpp> using namespace std; diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index c1f6f178db..dee0f110ad 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1,25 +1,34 @@ -#include <iostream> +#include "script.h" + +#include "data/script_invalid.json.h" +#include "data/script_valid.json.h" + +#include "key.h" +#include "keystore.h" +#include "main.h" + #include <fstream> +#include <stdint.h> +#include <string> #include <vector> + #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/split.hpp> +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem/path.hpp> #include <boost/foreach.hpp> -#include <boost/preprocessor/stringize.hpp> #include <boost/test/unit_test.hpp> #include "json/json_spirit_reader_template.h" -#include "json/json_spirit_writer_template.h" #include "json/json_spirit_utils.h" - -#include "main.h" -#include "wallet.h" +#include "json/json_spirit_writer_template.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; @@ -58,7 +67,7 @@ ParseScript(string s) (starts_with(w, "-") && all(string(w.begin()+1, w.end()), is_digit()))) { // Number - int64 n = atoi64(w); + int64_t n = atoi64(w); result << n; } else if (starts_with(w, "0x") && IsHex(string(w.begin()+2, w.end()))) @@ -90,34 +99,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 +120,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 +144,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/serialize_tests.cpp b/src/test/serialize_tests.cpp index 19ffdcab66..afcdd118bc 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -1,9 +1,8 @@ -#include <boost/test/unit_test.hpp> +#include "serialize.h" -#include <string> -#include <vector> +#include <stdint.h> -#include "serialize.h" +#include <boost/test/unit_test.hpp> using namespace std; @@ -21,7 +20,7 @@ BOOST_AUTO_TEST_CASE(varints) BOOST_CHECK(size == ss.size()); } - for (uint64 i = 0; i < 100000000000ULL; i += 999999937) { + for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) { ss << VARINT(i); size += ::GetSerializeSize(VARINT(i), 0, 0); BOOST_CHECK(size == ss.size()); @@ -34,12 +33,120 @@ BOOST_AUTO_TEST_CASE(varints) BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } - for (uint64 i = 0; i < 100000000000ULL; i += 999999937) { - uint64 j = -1; + for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) { + uint64_t j = -1; ss >> VARINT(j); BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } +} + +BOOST_AUTO_TEST_CASE(compactsize) +{ + CDataStream ss(SER_DISK, 0); + vector<char>::size_type i, j; + + for (i = 1; i <= MAX_SIZE; i *= 2) + { + WriteCompactSize(ss, i-1); + WriteCompactSize(ss, i); + } + for (i = 1; i <= MAX_SIZE; i *= 2) + { + j = ReadCompactSize(ss); + BOOST_CHECK_MESSAGE((i-1) == j, "decoded:" << j << " expected:" << (i-1)); + j = ReadCompactSize(ss); + BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); + } +} + +static bool isCanonicalException(const std::ios_base::failure& ex) +{ + return std::string("non-canonical ReadCompactSize()") == ex.what(); +} + +BOOST_AUTO_TEST_CASE(noncanonical) +{ + // Write some non-canonical CompactSize encodings, and + // make sure an exception is thrown when read back. + CDataStream ss(SER_DISK, 0); + vector<char>::size_type n; + + // zero encoded with three bytes: + ss.write("\xfd\x00\x00", 3); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // 0xfc encoded with three bytes: + ss.write("\xfd\xfc\x00", 3); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // 0xfd encoded with three bytes is OK: + ss.write("\xfd\xfd\x00", 3); + n = ReadCompactSize(ss); + BOOST_CHECK(n == 0xfd); + + // zero encoded with five bytes: + ss.write("\xfe\x00\x00\x00\x00", 5); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // 0xffff encoded with five bytes: + ss.write("\xfe\xff\xff\x00\x00", 5); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // zero encoded with nine bytes: + ss.write("\xff\x00\x00\x00\x00\x00\x00\x00\x00", 9); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); + + // 0x01ffffff encoded with nine bytes: + ss.write("\xff\xff\xff\xff\x01\x00\x00\x00\x00", 9); + BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); +} + +BOOST_AUTO_TEST_CASE(insert_delete) +{ + // Test inserting/deleting bytes. + CDataStream ss(SER_DISK, 0); + BOOST_CHECK_EQUAL(ss.size(), 0); + + ss.write("\x00\x01\x02\xff", 4); + BOOST_CHECK_EQUAL(ss.size(), 4); + + char c = (char)11; + + // Inserting at beginning/end/middle: + ss.insert(ss.begin(), c); + BOOST_CHECK_EQUAL(ss.size(), 5); + BOOST_CHECK_EQUAL(ss[0], c); + BOOST_CHECK_EQUAL(ss[1], 0); + + ss.insert(ss.end(), c); + BOOST_CHECK_EQUAL(ss.size(), 6); + BOOST_CHECK_EQUAL(ss[4], (char)0xff); + BOOST_CHECK_EQUAL(ss[5], c); + + ss.insert(ss.begin()+2, c); + BOOST_CHECK_EQUAL(ss.size(), 7); + BOOST_CHECK_EQUAL(ss[2], c); + + // Delete at beginning/end/middle + ss.erase(ss.begin()); + BOOST_CHECK_EQUAL(ss.size(), 6); + BOOST_CHECK_EQUAL(ss[0], 0); + + ss.erase(ss.begin()+ss.size()-1); + BOOST_CHECK_EQUAL(ss.size(), 5); + BOOST_CHECK_EQUAL(ss[4], (char)0xff); + + ss.erase(ss.begin()+1); + BOOST_CHECK_EQUAL(ss.size(), 4); + BOOST_CHECK_EQUAL(ss[0], 0); + BOOST_CHECK_EQUAL(ss[1], 1); + BOOST_CHECK_EQUAL(ss[2], 2); + BOOST_CHECK_EQUAL(ss[3], (char)0xff); + // Make sure GetAndClear does the right thing: + CSerializeData d; + ss.GetAndClear(d); + BOOST_CHECK_EQUAL(ss.size(), 0); } BOOST_AUTO_TEST_SUITE_END() 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/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index 5a87f17600..96f663ccef 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -1,9 +1,13 @@ -#include <vector> -#include <boost/test/unit_test.hpp> -#include <boost/foreach.hpp> -#include "script.h" + #include "key.h" +#include "script.h" +#include "uint256.h" + +#include <vector> + +#include <boost/foreach.hpp> +#include <boost/test/unit_test.hpp> using namespace std; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 1bf9a28acc..f947167597 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -1,12 +1,17 @@ #define BOOST_TEST_MODULE Bitcoin Test Suite -#include <boost/test/unit_test.hpp> -#include <boost/filesystem.hpp> + + #include "db.h" -#include "txdb.h" #include "main.h" -#include "wallet.h" +#include "txdb.h" +#include "ui_interface.h" #include "util.h" +#include "wallet.h" + +#include <boost/filesystem.hpp> +#include <boost/test/unit_test.hpp> + CWallet* pwalletMain; CClientUIInterface uiInterface; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 0c7475b4f2..ad33184bc5 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -1,16 +1,24 @@ + + +#include "data/tx_invalid.json.h" +#include "data/tx_valid.json.h" + +#include "key.h" +#include "keystore.h" +#include "main.h" +#include "script.h" + #include <map> #include <string> + #include <boost/test/unit_test.hpp> #include "json/json_spirit_writer_template.h" -#include "main.h" -#include "wallet.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 +30,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 +99,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) { @@ -271,6 +279,33 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, reason)); + + // 80-byte TX_NULL_DATA (standard) + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + BOOST_CHECK(IsStandardTx(t, reason)); + + // 81-byte TX_NULL_DATA (non-standard) + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"); + BOOST_CHECK(!IsStandardTx(t, reason)); + + // TX_NULL_DATA w/o PUSHDATA + t.vout.resize(1); + t.vout[0].scriptPubKey = CScript() << OP_RETURN; + BOOST_CHECK(IsStandardTx(t, reason)); + + // Only one TX_NULL_DATA permitted in all cases + t.vout.resize(2); + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + BOOST_CHECK(!IsStandardTx(t, reason)); + + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + t.vout[1].scriptPubKey = CScript() << OP_RETURN; + BOOST_CHECK(!IsStandardTx(t, reason)); + + t.vout[0].scriptPubKey = CScript() << OP_RETURN; + t.vout[1].scriptPubKey = CScript() << OP_RETURN; + BOOST_CHECK(!IsStandardTx(t, reason)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/uint160_tests.cpp b/src/test/uint160_tests.cpp index 35cb35b25a..87418fabec 100644 --- a/src/test/uint160_tests.cpp +++ b/src/test/uint160_tests.cpp @@ -1,7 +1,9 @@ -#include <boost/test/unit_test.hpp> - #include "uint256.h" +#include <stdint.h> + +#include <boost/test/unit_test.hpp> + BOOST_AUTO_TEST_SUITE(uint160_tests) BOOST_AUTO_TEST_CASE(uint160_equality) @@ -10,7 +12,7 @@ BOOST_AUTO_TEST_CASE(uint160_equality) uint160 num2 = 11; BOOST_CHECK(num1+1 == num2); - uint64 num3 = 10; + uint64_t num3 = 10; BOOST_CHECK(num1 == num3); BOOST_CHECK(num1+num2 == num3+num2); } diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp index efdc8a6aeb..188635dcbd 100644 --- a/src/test/uint256_tests.cpp +++ b/src/test/uint256_tests.cpp @@ -1,6 +1,9 @@ -#include <boost/test/unit_test.hpp> - #include "uint256.h" +#include <string> + +#include <stdint.h> + +#include <boost/test/unit_test.hpp> BOOST_AUTO_TEST_SUITE(uint256_tests) @@ -10,9 +13,17 @@ BOOST_AUTO_TEST_CASE(uint256_equality) uint256 num2 = 11; BOOST_CHECK(num1+1 == num2); - uint64 num3 = 10; + uint64_t num3 = 10; BOOST_CHECK(num1 == num3); BOOST_CHECK(num1+num2 == num3+num2); } +BOOST_AUTO_TEST_CASE(uint256_hex) +{ + std::string hexStr = "d35583ed493a5eee756931353144f558e6a9ab3ad6024a63ced7f10daf7faad9"; + uint256 num1; + num1.SetHex(hexStr); + BOOST_CHECK(num1.GetHex() == hexStr); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index abfd882655..6a9a787fa6 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1,10 +1,11 @@ +#include "util.h" + +#include "sync.h" + +#include <stdint.h> #include <vector> -#include <boost/test/unit_test.hpp> -#include <boost/foreach.hpp> -#include "main.h" -#include "wallet.h" -#include "util.h" +#include <boost/test/unit_test.hpp> using namespace std; @@ -200,7 +201,7 @@ BOOST_AUTO_TEST_CASE(util_FormatMoney) BOOST_AUTO_TEST_CASE(util_ParseMoney) { - int64 ret = 0; + int64_t ret = 0; BOOST_CHECK(ParseMoney("0.0", ret)); BOOST_CHECK_EQUAL(ret, 0); diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp index a14f6b2b70..0acd94ef36 100644 --- a/src/test/wallet_tests.cpp +++ b/src/test/wallet_tests.cpp @@ -1,8 +1,13 @@ -#include <boost/test/unit_test.hpp> - -#include "main.h" #include "wallet.h" +#include <set> +#include <stdint.h> +#include <utility> +#include <vector> + +#include <boost/foreach.hpp> +#include <boost/test/unit_test.hpp> + // how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles #define RUN_TESTS 100 @@ -19,7 +24,7 @@ BOOST_AUTO_TEST_SUITE(wallet_tests) static CWallet wallet; static vector<COutput> vCoins; -static void add_coin(int64 nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) +static void add_coin(int64_t nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) { static int nextLockTime = 0; CTransaction tx; @@ -55,7 +60,7 @@ static bool equal_sets(CoinSet a, CoinSet b) BOOST_AUTO_TEST_CASE(coin_selection_tests) { CoinSet setCoinsRet, setCoinsRet2; - int64 nValueRet; + int64_t nValueRet; // test multiple times to allow for differences in the shuffle order for (int i = 0; i < RUN_TESTS; i++) @@ -289,6 +294,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/threadsafety.h b/src/threadsafety.h index 3d3d526fd6..176060ac65 100644 --- a/src/threadsafety.h +++ b/src/threadsafety.h @@ -2,6 +2,7 @@ // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_THREADSAFETY_H #define BITCOIN_THREADSAFETY_H diff --git a/src/txdb.cpp b/src/txdb.cpp index 34836eaa97..99deb31404 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -1,12 +1,14 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "txdb.h" -#include "main.h" -#include "hash.h" -#include "chainparams.h" + +#include "core.h" +#include "uint256.h" + +#include <stdint.h> using namespace std; @@ -38,35 +40,32 @@ bool CCoinsViewDB::HaveCoins(const uint256 &txid) { return db.Exists(make_pair('c', txid)); } -CBlockIndex *CCoinsViewDB::GetBestBlock() { +uint256 CCoinsViewDB::GetBestBlock() { uint256 hashBestChain; if (!db.Read('B', hashBestChain)) - return NULL; - std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hashBestChain); - if (it == mapBlockIndex.end()) - return NULL; - return it->second; + return uint256(0); + return hashBestChain; } -bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { +bool CCoinsViewDB::SetBestBlock(const uint256 &hashBlock) { CLevelDBBatch batch; - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + BatchWriteHashBestChain(batch, hashBlock); return db.WriteBatch(batch); } -bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { - printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); +bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock) { + 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++) BatchWriteCoins(batch, it->first, it->second); - if (pindex) - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + if (hashBlock != uint256(0)) + BatchWriteHashBestChain(batch, hashBlock); return db.WriteBatch(batch); } -CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDB(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { +CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { } bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) @@ -74,13 +73,9 @@ bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) return Write(make_pair('b', blockindex.GetBlockHash()), blockindex); } -bool CBlockTreeDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) -{ - return Read('I', bnBestInvalidWork); -} - bool CBlockTreeDB::WriteBestInvalidWork(const CBigNum& bnBestInvalidWork) { + // Obsolete; only written for backward compatibility. return Write('I', bnBestInvalidWork); } @@ -117,9 +112,9 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) { pcursor->SeekToFirst(); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - stats.hashBlock = GetBestBlock()->GetBlockHash(); + stats.hashBlock = GetBestBlock(); ss << stats.hashBlock; - int64 nTotalAmount = 0; + int64_t nTotalAmount = 0; while (pcursor->Valid()) { boost::this_thread::interruption_point(); try { @@ -157,7 +152,7 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) { } } delete pcursor; - stats.nHeight = GetBestBlock()->nHeight; + stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight; stats.hashSerialized = ss.GetHash(); stats.nTotalAmount = nTotalAmount; return true; @@ -223,10 +218,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/txdb.h b/src/txdb.h index f59fc5da86..7ce6585d37 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -1,32 +1,42 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_TXDB_LEVELDB_H #define BITCOIN_TXDB_LEVELDB_H +#include "leveldbwrapper.h" #include "main.h" -#include "leveldb.h" + +#include <map> +#include <string> +#include <utility> +#include <vector> + +class CBigNum; +class CCoins; +class uint256; /** CCoinsView backed by the LevelDB coin database (chainstate/) */ class CCoinsViewDB : public CCoinsView { protected: - CLevelDB db; + CLevelDBWrapper db; public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); bool GetCoins(const uint256 &txid, CCoins &coins); bool SetCoins(const uint256 &txid, const CCoins &coins); bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex); + uint256 GetBestBlock(); + bool SetBestBlock(const uint256 &hashBlock); + bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats); }; /** Access to the block database (blocks/index/) */ -class CBlockTreeDB : public CLevelDB +class CBlockTreeDB : public CLevelDBWrapper { public: CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); @@ -35,7 +45,6 @@ private: void operator=(const CBlockTreeDB&); public: bool WriteBlockIndex(const CDiskBlockIndex& blockindex); - bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); bool WriteBestInvalidWork(const CBigNum& bnBestInvalidWork); bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo); diff --git a/src/txmempool.cpp b/src/txmempool.cpp new file mode 100644 index 0000000000..d501b89ecf --- /dev/null +++ b/src/txmempool.cpp @@ -0,0 +1,180 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "core.h" +#include "txmempool.h" + +using namespace std; + +CTxMemPool::CTxMemPool() +{ + // Sanity checks off by default for performance, because otherwise + // accepting transactions becomes O(N^2) where N is the number + // of transactions in the pool + fSanityCheck = false; +} + +void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) +{ + LOCK(cs); + + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); + + // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx + while (it != mapNextTx.end() && it->first.hash == hashTx) { + coins.Spend(it->first.n); // and remove those outputs from coins + it++; + } +} + +unsigned int CTxMemPool::GetTransactionsUpdated() const +{ + LOCK(cs); + return nTransactionsUpdated; +} + +void CTxMemPool::AddTransactionsUpdated(unsigned int n) +{ + LOCK(cs); + nTransactionsUpdated += n; +} + + +bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx) +{ + // Add to memory pool without checking anything. + // Used by main.cpp AcceptToMemoryPool(), which DOES do + // all the appropriate checks. + LOCK(cs); + { + mapTx[hash] = tx; + for (unsigned int i = 0; i < tx.vin.size(); i++) + mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); + nTransactionsUpdated++; + } + return true; +} + + +bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) +{ + // Remove transaction from memory pool + { + LOCK(cs); + uint256 hash = tx.GetHash(); + if (fRecursive) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i)); + if (it != mapNextTx.end()) + remove(*it->second.ptx, true); + } + } + if (mapTx.count(hash)) + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapNextTx.erase(txin.prevout); + mapTx.erase(hash); + nTransactionsUpdated++; + } + } + return true; +} + +bool CTxMemPool::removeConflicts(const CTransaction &tx) +{ + // Remove transactions which depend on inputs of tx, recursively + LOCK(cs); + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); + if (it != mapNextTx.end()) { + const CTransaction &txConflict = *it->second.ptx; + if (txConflict != tx) + remove(txConflict, true); + } + } + return true; +} + +void CTxMemPool::clear() +{ + LOCK(cs); + mapTx.clear(); + mapNextTx.clear(); + ++nTransactionsUpdated; +} + +void CTxMemPool::check(CCoinsViewCache *pcoins) const +{ + if (!fSanityCheck) + return; + + LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); + + LOCK(cs); + for (std::map<uint256, CTransaction>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + unsigned int i = 0; + BOOST_FOREACH(const CTxIn &txin, it->second.vin) { + // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. + std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(txin.prevout.hash); + if (it2 != mapTx.end()) { + assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull()); + } else { + CCoins &coins = pcoins->GetCoins(txin.prevout.hash); + assert(coins.IsAvailable(txin.prevout.n)); + } + // Check whether its inputs are marked in mapNextTx. + std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout); + assert(it3 != mapNextTx.end()); + assert(it3->second.ptx == &it->second); + assert(it3->second.n == i); + i++; + } + } + for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { + uint256 hash = it->second.ptx->GetHash(); + std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(hash); + assert(it2 != mapTx.end()); + assert(&it2->second == it->second.ptx); + assert(it2->second.vin.size() > it->second.n); + assert(it->first == it->second.ptx->vin[it->second.n].prevout); + } +} + +void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) +{ + vtxid.clear(); + + LOCK(cs); + vtxid.reserve(mapTx.size()); + for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) + vtxid.push_back((*mi).first); +} + +bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const +{ + LOCK(cs); + std::map<uint256, CTransaction>::const_iterator i = mapTx.find(hash); + if (i == mapTx.end()) return false; + result = i->second; + return true; +} + +CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } + +bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { + if (base->GetCoins(txid, coins)) + return true; + CTransaction tx; + if (mempool.lookup(txid, tx)) { + coins = CCoins(tx, MEMPOOL_HEIGHT); + return true; + } + return false; +} + +bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { + return mempool.exists(txid) || base->HaveCoins(txid); +} + diff --git a/src/txmempool.h b/src/txmempool.h new file mode 100644 index 0000000000..57b92789fb --- /dev/null +++ b/src/txmempool.h @@ -0,0 +1,84 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_TXMEMPOOL_H +#define BITCOIN_TXMEMPOOL_H + +#include "coins.h" +#include "core.h" +#include "sync.h" + +/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ +static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; + +/* + * CTxMemPool stores valid-according-to-the-current-best-chain + * transactions that may be included in the next block. + * + * Transactions are added when they are seen on the network + * (or created by the local node), but not all transactions seen + * are added to the pool: if a new transaction double-spends + * an input of a transaction in the pool, it is dropped, + * as are non-standard transactions. + */ +class CTxMemPool +{ +private: + bool fSanityCheck; // Normally false, true if -checkmempool or -regtest + unsigned int nTransactionsUpdated; + +public: + mutable CCriticalSection cs; + std::map<uint256, CTransaction> mapTx; + std::map<COutPoint, CInPoint> mapNextTx; + + CTxMemPool(); + + /* + * If sanity-checking is turned on, check makes sure the pool is + * consistent (does not contain two transactions that spend the same inputs, + * all inputs are in the mapNextTx array). If sanity-checking is turned off, + * check does nothing. + */ + void check(CCoinsViewCache *pcoins) const; + void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } + + bool addUnchecked(const uint256& hash, const CTransaction &tx); + bool remove(const CTransaction &tx, bool fRecursive = false); + bool removeConflicts(const CTransaction &tx); + void clear(); + void queryHashes(std::vector<uint256>& vtxid); + void pruneSpent(const uint256& hash, CCoins &coins); + unsigned int GetTransactionsUpdated() const; + void AddTransactionsUpdated(unsigned int n); + + unsigned long size() + { + LOCK(cs); + return mapTx.size(); + } + + bool exists(uint256 hash) + { + LOCK(cs); + return (mapTx.count(hash) != 0); + } + + bool lookup(uint256 hash, CTransaction& result) const; +}; + +/** CCoinsView that brings transactions from a memorypool into view. + It does not check for spendings by memory pool transactions. */ +class CCoinsViewMemPool : public CCoinsViewBacked +{ +protected: + CTxMemPool &mempool; + +public: + CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); + bool GetCoins(const uint256 &txid, CCoins &coins); + bool HaveCoins(const uint256 &txid); +}; + +#endif /* BITCOIN_TXMEMPOOL_H */ diff --git a/src/ui_interface.h b/src/ui_interface.h index 5b0555c10d..2378d5dfbb 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -2,13 +2,15 @@ // Copyright (c) 2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_UI_INTERFACE_H #define BITCOIN_UI_INTERFACE_H +#include <stdint.h> #include <string> -#include "util.h" // for int64 -#include <boost/signals2/signal.hpp> + #include <boost/signals2/last_value.hpp> +#include <boost/signals2/signal.hpp> class CBasicKeyStore; class CWallet; @@ -71,7 +73,7 @@ public: boost::signals2::signal<bool (const std::string& message, const std::string& caption, unsigned int style), boost::signals2::last_value<bool> > ThreadSafeMessageBox; /** Ask the user whether they want to pay a fee or not. */ - boost::signals2::signal<bool (int64 nFeeRequired), boost::signals2::last_value<bool> > ThreadSafeAskFee; + boost::signals2::signal<bool (int64_t nFeeRequired), boost::signals2::last_value<bool> > ThreadSafeAskFee; /** Handle a URL passed at the command line. */ boost::signals2::signal<void (const std::string& strURI)> ThreadSafeHandleURI; diff --git a/src/uint256.h b/src/uint256.h index 2a252c94f3..7dbb3f83bb 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -1,25 +1,27 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_UINT256_H #define BITCOIN_UINT256_H -#include <limits.h> +#include <stdint.h> #include <stdio.h> -#include <string.h> -#include <inttypes.h> #include <string> +#include <string.h> #include <vector> -typedef long long int64; -typedef unsigned long long uint64; +extern const signed char p_util_hexdigit[256]; // defined in util.cpp +inline signed char HexDigit(char c) +{ + return p_util_hexdigit[(unsigned char)c]; +} inline int Testuint256AdHoc(std::vector<std::string> vArg); - /** Base class without constructors for uint256 and uint160. * This makes the compiler let you use it in a union. */ @@ -67,7 +69,7 @@ public: return ret; } - base_uint& operator=(uint64 b) + base_uint& operator=(uint64_t b) { pn[0] = (unsigned int)b; pn[1] = (unsigned int)(b >> 32); @@ -97,14 +99,14 @@ public: return *this; } - base_uint& operator^=(uint64 b) + base_uint& operator^=(uint64_t b) { pn[0] ^= (unsigned int)b; pn[1] ^= (unsigned int)(b >> 32); return *this; } - base_uint& operator|=(uint64 b) + base_uint& operator|=(uint64_t b) { pn[0] |= (unsigned int)b; pn[1] |= (unsigned int)(b >> 32); @@ -147,10 +149,10 @@ public: base_uint& operator+=(const base_uint& b) { - uint64 carry = 0; + uint64_t carry = 0; for (int i = 0; i < WIDTH; i++) { - uint64 n = carry + pn[i] + b.pn[i]; + uint64_t n = carry + pn[i] + b.pn[i]; pn[i] = n & 0xffffffff; carry = n >> 32; } @@ -163,7 +165,7 @@ public: return *this; } - base_uint& operator+=(uint64 b64) + base_uint& operator+=(uint64_t b64) { base_uint b; b = b64; @@ -171,7 +173,7 @@ public: return *this; } - base_uint& operator-=(uint64 b64) + base_uint& operator-=(uint64_t b64) { base_uint b; b = b64; @@ -271,7 +273,7 @@ public: return true; } - friend inline bool operator==(const base_uint& a, uint64 b) + friend inline bool operator==(const base_uint& a, uint64_t b) { if (a.pn[0] != (unsigned int)b) return false; @@ -288,7 +290,7 @@ public: return (!(a == b)); } - friend inline bool operator!=(const base_uint& a, uint64 b) + friend inline bool operator!=(const base_uint& a, uint64_t b) { return (!(a == b)); } @@ -305,8 +307,7 @@ public: void SetHex(const char* psz) { - for (int i = 0; i < WIDTH; i++) - pn[i] = 0; + memset(pn,0,sizeof(pn)); // skip leading spaces while (isspace(*psz)) @@ -317,19 +318,18 @@ public: psz += 2; // hex string to uint - static const unsigned char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 }; const char* pbegin = psz; - while (phexdigit[(unsigned char)*psz] || *psz == '0') + while (::HexDigit(*psz) != -1) psz++; psz--; unsigned char* p1 = (unsigned char*)pn; unsigned char* pend = p1 + WIDTH * 4; while (psz >= pbegin && p1 < pend) { - *p1 = phexdigit[(unsigned char)*psz--]; + *p1 = ::HexDigit(*psz--); if (psz >= pbegin) { - *p1 |= (phexdigit[(unsigned char)*psz--] << 4); + *p1 |= ((unsigned char)::HexDigit(*psz--) << 4); p1++; } } @@ -370,9 +370,9 @@ public: return sizeof(pn); } - uint64 Get64(int n=0) const + uint64_t Get64(int n=0) const { - return pn[2*n] | (uint64)pn[2*n+1] << 32; + return pn[2*n] | (uint64_t)pn[2*n+1] << 32; } // unsigned int GetSerializeSize(int nType=0, int nVersion=PROTOCOL_VERSION) const @@ -443,7 +443,7 @@ public: return *this; } - uint160(uint64 b) + uint160(uint64_t b) { pn[0] = (unsigned int)b; pn[1] = (unsigned int)(b >> 32); @@ -451,7 +451,7 @@ public: pn[i] = 0; } - uint160& operator=(uint64 b) + uint160& operator=(uint64_t b) { pn[0] = (unsigned int)b; pn[1] = (unsigned int)(b >> 32); @@ -474,8 +474,8 @@ public: } }; -inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; } -inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; } +inline bool operator==(const uint160& a, uint64_t b) { return (base_uint160)a == b; } +inline bool operator!=(const uint160& a, uint64_t b) { return (base_uint160)a != b; } inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; } inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; } inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; } @@ -487,41 +487,41 @@ inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { r inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; } inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; } -inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } - -inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } - -inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } -inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } -inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } -inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } -inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } -inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } -inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } -inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } -inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } -inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } -inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } +inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; } + +inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; } +inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; } +inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; } +inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; } +inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; } +inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; } +inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; } +inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } +inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; } +inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; } @@ -558,7 +558,7 @@ public: return *this; } - uint256(uint64 b) + uint256(uint64_t b) { pn[0] = (unsigned int)b; pn[1] = (unsigned int)(b >> 32); @@ -566,7 +566,7 @@ public: pn[i] = 0; } - uint256& operator=(uint64 b) + uint256& operator=(uint64_t b) { pn[0] = (unsigned int)b; pn[1] = (unsigned int)(b >> 32); @@ -589,8 +589,8 @@ public: } }; -inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; } -inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; } +inline bool operator==(const uint256& a, uint64_t b) { return (base_uint256)a == b; } +inline bool operator!=(const uint256& a, uint64_t b) { return (base_uint256)a != b; } inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; } inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; } inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; } @@ -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..5411bb2fe3 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,88 +1,101 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "util.h" + +#include "chainparams.h" +#include "netbase.h" +#include "sync.h" +#include "ui_interface.h" +#include "uint256.h" +#include "version.h" + +#include <inttypes.h> +#include <stdarg.h> +#include <stdint.h> + #ifndef WIN32 // for posix_fallocate -#ifdef __linux__ +#ifdef __linux_ + +#ifdef _POSIX_C_SOURCE +#undef _POSIX_C_SOURCE +#endif + #define _POSIX_C_SOURCE 200112L +#include <sys/prctl.h> + #endif + #include <algorithm> #include <fcntl.h> -#include <sys/stat.h> #include <sys/resource.h> -#endif - -#include "chainparams.h" -#include "util.h" -#include "sync.h" -#include "version.h" -#include "ui_interface.h" -#include <boost/algorithm/string/join.hpp> -#include <boost/algorithm/string/case_conv.hpp> // for to_lower() -#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith() - -// Work around clang compilation problem in Boost 1.46: -// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup -// See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options -// http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION -namespace boost { - namespace program_options { - std::string to_internal(const std::string&); - } -} +#include <sys/stat.h> -#include <boost/program_options/detail/config_file.hpp> -#include <boost/program_options/parsers.hpp> -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> -#include <boost/foreach.hpp> -#include <boost/thread.hpp> -#include <openssl/crypto.h> -#include <openssl/rand.h> -#include <stdarg.h> +#else -#ifdef WIN32 #ifdef _MSC_VER #pragma warning(disable:4786) #pragma warning(disable:4804) #pragma warning(disable:4805) #pragma warning(disable:4717) #endif + #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif #define _WIN32_WINNT 0x0501 + #ifdef _WIN32_IE #undef _WIN32_IE #endif #define _WIN32_IE 0x0501 + #define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif + #include <io.h> /* for _commit */ -#include "shlobj.h" -#elif defined(__linux__) -# include <sys/prctl.h> +#include <shlobj.h> #endif +#include <boost/algorithm/string/case_conv.hpp> // for to_lower() +#include <boost/algorithm/string/join.hpp> +#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith() +#include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> +#include <boost/foreach.hpp> +#include <boost/program_options/detail/config_file.hpp> +#include <boost/program_options/parsers.hpp> +#include <openssl/crypto.h> +#include <openssl/rand.h> + +// Work around clang compilation problem in Boost 1.46: +// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup +// See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options +// http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION +namespace boost { + namespace program_options { + std::string to_internal(const std::string&); + } +} + + using namespace std; map<string, string> mapArgs; map<string, vector<string> > mapMultiArgs; bool fDebug = false; -bool fDebugNet = false; bool fPrintToConsole = false; bool fPrintToDebugger = false; bool fDaemon = false; bool fServer = false; -bool fCommandLine = false; string strMiscWarning; bool fNoListen = false; bool fLogTimestamps = false; -CMedianFilter<int64> vTimeOffsets(200,0); volatile bool fReopenDebugLog = false; // Init OpenSSL library multithreading support @@ -96,8 +109,6 @@ void locking_callback(int mode, int i, const char* file, int line) } } -LockedPageManager LockedPageManager::instance; - // Init class CInit { @@ -139,7 +150,7 @@ instance_of_cinit; void RandAddSeed() { // Seed with CPU performance counter - int64 nCounter = GetPerformanceCounter(); + int64_t nCounter = GetPerformanceCounter(); RAND_add(&nCounter, sizeof(nCounter), 1.5); memset(&nCounter, 0, sizeof(nCounter)); } @@ -149,7 +160,7 @@ void RandAddSeedPerfmon() RandAddSeed(); // This can take up to 2 seconds, so only do it every 10 minutes - static int64 nLastPerfmon; + static int64_t nLastPerfmon; if (GetTime() < nLastPerfmon + 10 * 60) return; nLastPerfmon = GetTime(); @@ -166,20 +177,20 @@ 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 } -uint64 GetRand(uint64 nMax) +uint64_t GetRand(uint64_t nMax) { if (nMax == 0) return 0; // The range of the random source must be a multiple of the modulus // to give every possible output value an equal possibility - uint64 nRange = (std::numeric_limits<uint64>::max() / nMax) * nMax; - uint64 nRand = 0; + uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax; + uint64_t nRand = 0; do RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); while (nRand >= nRange); @@ -198,15 +209,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 +236,26 @@ 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"]; + bool allCategories = count(categories.begin(), categories.end(), string("")); + + // Only look for categories, if not -debug/-debug=1 was passed, + // as that implies every category should be logged. + if (!allCategories) + { + // Category was not found (not supplied via -debug=<category>) + if (find(categories.begin(), categories.end(), string(category)) == categories.end()) + return 0; + } + } + int ret = 0; // Returns total number of characters written if (fPrintToConsole) { @@ -360,7 +381,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; } @@ -385,14 +406,14 @@ void ParseString(const string& str, char c, vector<string>& v) } -string FormatMoney(int64 n, bool fPlus) +string FormatMoney(int64_t n, bool fPlus) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. - int64 n_abs = (n > 0 ? n : -n); - int64 quotient = n_abs/COIN; - int64 remainder = n_abs%COIN; - string str = strprintf("%"PRI64d".%08"PRI64d, quotient, remainder); + int64_t n_abs = (n > 0 ? n : -n); + int64_t quotient = n_abs/COIN; + int64_t remainder = n_abs%COIN; + string str = strprintf("%"PRId64".%08"PRId64, quotient, remainder); // Right-trim excess zeros before the decimal point: int nTrim = 0; @@ -409,15 +430,15 @@ string FormatMoney(int64 n, bool fPlus) } -bool ParseMoney(const string& str, int64& nRet) +bool ParseMoney(const string& str, int64_t& nRet) { return ParseMoney(str.c_str(), nRet); } -bool ParseMoney(const char* pszIn, int64& nRet) +bool ParseMoney(const char* pszIn, int64_t& nRet) { string strWhole; - int64 nUnits = 0; + int64_t nUnits = 0; const char* p = pszIn; while (isspace(*p)) p++; @@ -426,7 +447,7 @@ bool ParseMoney(const char* pszIn, int64& nRet) if (*p == '.') { p++; - int64 nMult = CENT*10; + int64_t nMult = CENT*10; while (isdigit(*p) && (nMult > 0)) { nUnits += nMult * (*p++ - '0'); @@ -447,15 +468,28 @@ bool ParseMoney(const char* pszIn, int64& nRet) return false; if (nUnits < 0 || nUnits > COIN) return false; - int64 nWhole = atoi64(strWhole); - int64 nValue = nWhole*COIN + nUnits; + int64_t nWhole = atoi64(strWhole); + int64_t nValue = nWhole*COIN + nUnits; nRet = nValue; return true; } +// safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything +// even possibly remotely dangerous like & or > +static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); +string SanitizeString(const string& str) +{ + string strResult; + for (std::string::size_type i = 0; i < str.size(); i++) + { + if (safeChars.find(str[i]) != std::string::npos) + strResult.push_back(str[i]); + } + return strResult; +} -static const signed char phexdigit[256] = +const signed char p_util_hexdigit[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, @@ -475,9 +509,9 @@ static const signed char phexdigit[256] = bool IsHex(const string& str) { - BOOST_FOREACH(unsigned char c, str) + BOOST_FOREACH(char c, str) { - if (phexdigit[c] < 0) + if (HexDigit(c) < 0) return false; } return (str.size() > 0) && (str.size()%2 == 0); @@ -491,11 +525,11 @@ vector<unsigned char> ParseHex(const char* psz) { while (isspace(*psz)) psz++; - signed char c = phexdigit[(unsigned char)*psz++]; + signed char c = HexDigit(*psz++); if (c == (signed char)-1) break; unsigned char n = (c << 4); - c = phexdigit[(unsigned char)*psz++]; + c = HexDigit(*psz++); if (c == (signed char)-1) break; n |= c; @@ -576,7 +610,7 @@ std::string GetArg(const std::string& strArg, const std::string& strDefault) return strDefault; } -int64 GetArg(const std::string& strArg, int64 nDefault) +int64_t GetArg(const std::string& strArg, int64_t nDefault) { if (mapArgs.count(strArg)) return atoi64(mapArgs[strArg]); @@ -994,13 +1028,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 +1043,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 +1091,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 +1190,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 @@ -1208,7 +1244,7 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { // Windows-specific version HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); LARGE_INTEGER nFileSize; - int64 nEndPos = (int64)offset + length; + int64_t nEndPos = (int64_t)offset + length; nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF; nFileSize.u.HighPart = nEndPos >> 32; SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN); @@ -1283,48 +1319,52 @@ void ShrinkDebugFile() // - Median of other nodes clocks // - The user (asking the user to fix the system clock if the first two disagree) // -static int64 nMockTime = 0; // For unit testing +static int64_t nMockTime = 0; // For unit testing -int64 GetTime() +int64_t GetTime() { if (nMockTime) return nMockTime; return time(NULL); } -void SetMockTime(int64 nMockTimeIn) +void SetMockTime(int64_t nMockTimeIn) { nMockTime = nMockTimeIn; } -static int64 nTimeOffset = 0; +static CCriticalSection cs_nTimeOffset; +static int64_t nTimeOffset = 0; -int64 GetTimeOffset() +int64_t GetTimeOffset() { + LOCK(cs_nTimeOffset); return nTimeOffset; } -int64 GetAdjustedTime() +int64_t GetAdjustedTime() { return GetTime() + GetTimeOffset(); } -void AddTimeData(const CNetAddr& ip, int64 nTime) +void AddTimeData(const CNetAddr& ip, int64_t nTime) { - int64 nOffsetSample = nTime - GetTime(); + int64_t nOffsetSample = nTime - GetTime(); + LOCK(cs_nTimeOffset); // Ignore duplicates static set<CNetAddr> setKnown; if (!setKnown.insert(ip).second) return; // Add data + static CMedianFilter<int64_t> vTimeOffsets(200,0); 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 %+"PRId64" (%+"PRId64" minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) { - int64 nMedian = vTimeOffsets.median(); - std::vector<int64> vSorted = vTimeOffsets.sorted(); + int64_t nMedian = vTimeOffsets.median(); + std::vector<int64_t> vSorted = vTimeOffsets.sorted(); // Only let other nodes change our time by so much if (abs64(nMedian) < 70 * 60) { @@ -1339,7 +1379,7 @@ void AddTimeData(const CNetAddr& ip, int64 nTime) { // If nobody has a time different than ours but within 5 minutes of ours, give a warning bool fMatch = false; - BOOST_FOREACH(int64 nOffset, vSorted) + BOOST_FOREACH(int64_t nOffset, vSorted) if (nOffset != 0 && abs64(nOffset) < 5 * 60) fMatch = true; @@ -1348,17 +1388,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("| "); + BOOST_FOREACH(int64_t n, vSorted) + LogPrintf("%+"PRId64" ", n); + LogPrintf("| "); } - printf("nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60); + LogPrintf("nTimeOffset = %+"PRId64" (%+"PRId64" minutes)\n", nTimeOffset, nTimeOffset/60); } } @@ -1420,7 +1460,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 +1480,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 +1491,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..7fae5cc7ec 100644 --- a/src/util.h +++ b/src/util.h @@ -1,39 +1,44 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H -#include "uint256.h" +#if defined(HAVE_CONFIG_H) +#include "bitcoin-config.h" +#endif + +#include "compat.h" +#include "serialize.h" +#include <cstdio> +#include <exception> +#include <inttypes.h> +#include <map> #include <stdarg.h> +#include <stdint.h> +#include <string> +#include <utility> +#include <vector> #ifndef WIN32 -#include <sys/types.h> -#include <sys/time.h> #include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#else +typedef int pid_t; /* define for Windows compatibility */ #endif -#include <map> -#include <list> -#include <utility> -#include <vector> -#include <string> -#include <boost/version.hpp> -#include <boost/thread.hpp> -#include <boost/filesystem.hpp> #include <boost/filesystem/path.hpp> -#include <boost/date_time/gregorian/gregorian_types.hpp> -#include <boost/date_time/posix_time/posix_time_types.hpp> - -#include "netbase.h" // for AddTimeData +#include <boost/thread.hpp> -typedef long long int64; -typedef unsigned long long uint64; +class CNetAddr; +class uint256; -static const int64 COIN = 100000000; -static const int64 CENT = 1000000; +static const int64_t COIN = 100000000; +static const int64_t CENT = 1000000; #define BEGIN(a) ((char*)&(a)) #define END(a) ((char*)&((&(a))[1])) @@ -41,18 +46,6 @@ static const int64 CENT = 1000000; #define UEND(a) ((unsigned char*)&((&(a))[1])) #define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) -#ifndef PRI64d -#if defined(_MSC_VER) || defined(__MSVCRT__) -#define PRI64d "I64d" -#define PRI64u "I64u" -#define PRI64x "I64x" -#else -#define PRI64d "lld" -#define PRI64u "llu" -#define PRI64x "llx" -#endif -#endif - /* Format characters for (s)size_t and ptrdiff_t */ #if defined(_MSC_VER) || defined(__MSVCRT__) /* (s)size_t and ptrdiff_t have the same size specifier in MSVC: @@ -73,6 +66,7 @@ static const int64 CENT = 1000000; #define PRIpdd "td" #endif + // This is needed because the foreach macro can't get over the comma in pair<t1, t2> #define PAIRTYPE(t1, t2) std::pair<t1, t2> @@ -101,11 +95,11 @@ T* alignup(T* p) #define MAX_PATH 1024 #endif // As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall, it is defined as 0 -#ifndef MSG_NOSIGNAL +#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif -inline void MilliSleep(int64 n) +inline void MilliSleep(int64_t n) { // Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 // until fixed in 1.52. Use the deprecated sleep method for the broken case. @@ -140,12 +134,10 @@ inline void MilliSleep(int64 n) extern std::map<std::string, std::string> mapArgs; extern std::map<std::string, std::vector<std::string> > mapMultiArgs; extern bool fDebug; -extern bool fDebugNet; extern bool fPrintToConsole; extern bool fPrintToDebugger; extern bool fDaemon; extern bool fServer; -extern bool fCommandLine; extern std::string strMiscWarning; extern bool fNoListen; extern bool fLogTimestamps; @@ -153,7 +145,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,21 +168,14 @@ 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); void ParseString(const std::string& str, char c, std::vector<std::string>& v); -std::string FormatMoney(int64 n, bool fPlus=false); -bool ParseMoney(const std::string& str, int64& nRet); -bool ParseMoney(const char* pszIn, int64& nRet); +std::string FormatMoney(int64_t n, bool fPlus=false); +bool ParseMoney(const std::string& str, int64_t& nRet); +bool ParseMoney(const char* pszIn, int64_t& nRet); +std::string SanitizeString(const std::string& str); std::vector<unsigned char> ParseHex(const char* psz); std::vector<unsigned char> ParseHex(const std::string& str); bool IsHex(const std::string& str); @@ -222,15 +210,15 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); boost::filesystem::path GetTempPath(); void ShrinkDebugFile(); int GetRandInt(int nMax); -uint64 GetRand(uint64 nMax); +uint64_t GetRand(uint64_t nMax); uint256 GetRandHash(); -int64 GetTime(); -void SetMockTime(int64 nMockTimeIn); -int64 GetAdjustedTime(); -int64 GetTimeOffset(); +int64_t GetTime(); +void SetMockTime(int64_t nMockTimeIn); +int64_t GetAdjustedTime(); +int64_t GetTimeOffset(); std::string FormatFullVersion(); std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments); -void AddTimeData(const CNetAddr& ip, int64 nTime); +void AddTimeData(const CNetAddr& ip, int64_t nTime); void runCommand(std::string strCommand); @@ -241,9 +229,9 @@ void runCommand(std::string strCommand); -inline std::string i64tostr(int64 n) +inline std::string i64tostr(int64_t n) { - return strprintf("%"PRI64d, n); + return strprintf("%"PRId64, n); } inline std::string itostr(int n) @@ -251,7 +239,7 @@ inline std::string itostr(int n) return strprintf("%d", n); } -inline int64 atoi64(const char* psz) +inline int64_t atoi64(const char* psz) { #ifdef _MSC_VER return _atoi64(psz); @@ -260,7 +248,7 @@ inline int64 atoi64(const char* psz) #endif } -inline int64 atoi64(const std::string& str) +inline int64_t atoi64(const std::string& str) { #ifdef _MSC_VER return _atoi64(str.c_str()); @@ -279,12 +267,12 @@ inline int roundint(double d) return (int)(d > 0 ? d + 0.5 : d - 0.5); } -inline int64 roundint64(double d) +inline int64_t roundint64(double d) { - return (int64)(d > 0 ? d + 0.5 : d - 0.5); + return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); } -inline int64 abs64(int64 n) +inline int64_t abs64(int64_t n) { return (n >= 0 ? n : -n); } @@ -317,40 +305,40 @@ 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() +inline int64_t GetPerformanceCounter() { - int64 nCounter = 0; + int64_t nCounter = 0; #ifdef WIN32 QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); #else timeval t; gettimeofday(&t, NULL); - nCounter = (int64) t.tv_sec * 1000000 + t.tv_usec; + nCounter = (int64_t) t.tv_sec * 1000000 + t.tv_usec; #endif return nCounter; } -inline int64 GetTimeMillis() +inline int64_t GetTimeMillis() { return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); } -inline int64 GetTimeMicros() +inline int64_t GetTimeMicros() { return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); } -inline std::string DateTimeStrFormat(const char* pszFormat, int64 nTime) +inline std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) { time_t n = nTime; struct tm* ptmTime = gmtime(&n); @@ -391,7 +379,7 @@ std::string GetArg(const std::string& strArg, const std::string& strDefault); * @param default (e.g. 1) * @return command-line argument (0 if invalid number) or default value */ -int64 GetArg(const std::string& strArg, int64 nDefault); +int64_t GetArg(const std::string& strArg, int64_t nDefault); /** * Return boolean argument or default value @@ -522,7 +510,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 @@ -556,11 +544,11 @@ inline uint32_t ByteReverse(uint32_t value) // or maybe: // boost::function<void()> f = boost::bind(&FunctionWithArg, argument); // threadGroup.create_thread(boost::bind(&LoopForever<boost::function<void()> >, "nothing", f, milliseconds)); -template <typename Callable> void LoopForever(const char* name, Callable func, int64 msecs) +template <typename Callable> void LoopForever(const char* name, Callable func, int64_t msecs) { 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 +559,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 +576,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/version.cpp b/src/version.cpp index d9d6724a02..904451ac59 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -1,10 +1,11 @@ // Copyright (c) 2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <string> #include "version.h" +#include <string> + // Name of client reported in the 'version' message. Report the same name // for both bitcoind and bitcoin-qt, to make it harder for attackers to // target servers or GUI users specifically. diff --git a/src/version.h b/src/version.h index f1e7c4cd7b..3d1abacb94 100644 --- a/src/version.h +++ b/src/version.h @@ -5,6 +5,7 @@ #define BITCOIN_VERSION_H #include "clientversion.h" + #include <string> // @@ -25,10 +26,13 @@ extern const std::string CLIENT_DATE; // network protocol versioning // -static const int PROTOCOL_VERSION = 70001; +static const int PROTOCOL_VERSION = 70002; + +// intial proto version, to be increased after version/verack negotiation +static const int INIT_PROTO_VERSION = 209; -// earlier versions not supported as of Feb 2012, and are disconnected -static const int MIN_PROTO_VERSION = 209; +// disconnect from peers older than this proto version +static const int MIN_PEER_PROTO_VERSION = 209; // nTime field added to CAddress, starting with this version; // if possible, avoid requesting addresses nodes older than this diff --git a/src/wallet.cpp b/src/wallet.cpp index 54ede12a50..6e49ef7b8e 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1,14 +1,18 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "wallet.h" -#include "walletdb.h" -#include "crypter.h" -#include "ui_interface.h" + #include "base58.h" +#include "net.h" + +#include <inttypes.h> +#include <stdint.h> + #include <boost/algorithm/string/replace.hpp> +#include <openssl/rand.h> using namespace std; @@ -20,8 +24,8 @@ using namespace std; struct CompareValueOnly { - bool operator()(const pair<int64, pair<const CWalletTx*, unsigned int> >& t1, - const pair<int64, pair<const CWalletTx*, unsigned int> >& t2) const + bool operator()(const pair<int64_t, pair<const CWalletTx*, unsigned int> >& t1, + const pair<int64_t, pair<const CWalletTx*, unsigned int> >& t2) const { return t1.first < t2.first; } @@ -42,7 +46,7 @@ CPubKey CWallet::GenerateNewKey() CPubKey pubkey = secret.GetPubKey(); // Create new metadata - int64 nCreationTime = GetTime(); + int64_t nCreationTime = GetTime(); mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime); if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) nTimeFirstKey = nCreationTime; @@ -148,7 +152,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; if (CCryptoKeyStore::Unlock(vMasterKey)) { - int64 nStartTime = GetTimeMillis(); + int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); @@ -159,7 +163,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; @@ -256,7 +260,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); CCrypter crypter; - int64 nStartTime = GetTimeMillis(); + int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); @@ -267,7 +271,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; @@ -319,9 +323,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) return true; } -int64 CWallet::IncOrderPosNext(CWalletDB *pwalletdb) +int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { - int64 nRet = nOrderPosNext++; + int64_t nRet = nOrderPosNext++; if (pwalletdb) { pwalletdb->WriteOrderPosNext(nOrderPosNext); } else { @@ -368,10 +372,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); @@ -414,7 +418,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) unsigned int latestEntry = 0; { // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future - int64 latestTolerated = latestNow + 300; + int64_t latestTolerated = latestNow + 300; std::list<CAccountingEntry> acentries; TxItems txOrdered = OrderedTxItems(acentries); for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) @@ -423,7 +427,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) if (pwtx == &wtx) continue; CAccountingEntry *const pacentry = (*it).second.second; - int64 nSmartTime; + int64_t nSmartTime; if (pwtx) { nSmartTime = pwtx->nTimeSmart; @@ -446,9 +450,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,32 +480,13 @@ 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) if (!wtx.WriteToDisk()) return false; - if (!fHaveGUI) { - // If default receiving address gets used, replace it with a new one - if (vchDefaultKey.IsValid()) { - CScript scriptDefaultKey; - scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); - BOOST_FOREACH(const CTxOut& txout, wtx.vout) - { - if (txout.scriptPubKey == scriptDefaultKey) - { - CPubKey newDefaultKey; - if (GetKeyFromPool(newDefaultKey)) - { - SetDefaultKey(newDefaultKey); - SetAddressBook(vchDefaultKey.GetID(), "", "receive"); - } - } - } - } - } // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins WalletUpdateSpent(wtx); @@ -524,7 +509,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) // Add a transaction to the wallet, or update it. // pblock is optional, but should be provided if the transaction is known to be in a block. // If fUpdate is true, existing transactions will be updated. -bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) +bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) { { LOCK(cs_wallet); @@ -544,16 +529,20 @@ bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& return false; } -bool CWallet::EraseFromWallet(uint256 hash) +void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock) { + AddToWalletIfInvolvingMe(hash, tx, pblock, true); +} + +void CWallet::EraseFromWallet(const uint256 &hash) { if (!fFileBacked) - return false; + return; { LOCK(cs_wallet); if (mapWallet.erase(hash)) CWalletDB(strWalletFile).EraseTx(hash); } - return true; + return; } @@ -573,7 +562,7 @@ bool CWallet::IsMine(const CTxIn &txin) const return false; } -int64 CWallet::GetDebit(const CTxIn &txin) const +int64_t CWallet::GetDebit(const CTxIn &txin) const { { LOCK(cs_wallet); @@ -609,9 +598,9 @@ bool CWallet::IsChange(const CTxOut& txout) const return false; } -int64 CWalletTx::GetTxTime() const +int64_t CWalletTx::GetTxTime() const { - int64 n = nTimeSmart; + int64_t n = nTimeSmart; return n ? n : nTimeReceived; } @@ -654,8 +643,8 @@ int CWalletTx::GetRequestCount() const return nRequests; } -void CWalletTx::GetAmounts(list<pair<CTxDestination, int64> >& listReceived, - list<pair<CTxDestination, int64> >& listSent, int64& nFee, string& strSentAccount) const +void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, + list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount) const { nFee = 0; listReceived.clear(); @@ -663,57 +652,70 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64> >& listReceived, strSentAccount = strFromAccount; // Compute fee: - int64 nDebit = GetDebit(); + int64_t nDebit = GetDebit(); if (nDebit > 0) // debit>0 means we signed/sent this transaction { - int64 nValueOut = GetValueOut(*this); + int64_t nValueOut = GetValueOut(*this); nFee = nDebit - nValueOut; } // Sent/received. BOOST_FOREACH(const CTxOut& txout, vout) { + bool fIsMine; + // Only need to handle txouts if AT LEAST one of these is true: + // 1) they debit from us (sent) + // 2) the output is to us (received) + if (nDebit > 0) + { + // Don't report 'change' txouts + if (pwallet->IsChange(txout)) + continue; + fIsMine = pwallet->IsMine(txout); + } + else if (!(fIsMine = pwallet->IsMine(txout))) + continue; + + // In either case, we need to get the destination address CTxDestination address; - 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()); + address = CNoDestination(); } - // Don't report 'change' txouts - if (nDebit > 0 && pwallet->IsChange(txout)) - continue; - + // If we are debited by the transaction, add the output as a "sent" entry if (nDebit > 0) listSent.push_back(make_pair(address, txout.nValue)); - if (pwallet->IsMine(txout)) + // If we are receiving the output, add it as a "received" entry + if (fIsMine) listReceived.push_back(make_pair(address, txout.nValue)); } } -void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nReceived, - int64& nSent, int64& nFee) const +void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived, + int64_t& nSent, int64_t& nFee) const { nReceived = nSent = nFee = 0; - int64 allFee; + int64_t allFee; string strSentAccount; - list<pair<CTxDestination, int64> > listReceived; - list<pair<CTxDestination, int64> > listSent; + list<pair<CTxDestination, int64_t> > listReceived; + list<pair<CTxDestination, int64_t> > listSent; GetAmounts(listReceived, listSent, allFee, strSentAccount); if (strAccount == strSentAccount) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& s, listSent) + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& s, listSent) nSent += s.second; nFee = allFee; } { LOCK(pwallet->cs_wallet); - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived) + BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived) { if (pwallet->mapAddressBook.count(r.first)) { @@ -779,6 +781,25 @@ void CWalletTx::AddSupportingTransactions() reverse(vtxPrev.begin(), vtxPrev.end()); } +bool CWalletTx::AcceptWalletTransaction() +{ + { + LOCK(mempool.cs); + // Add previous supporting transactions first + BOOST_FOREACH(CMerkleTx& tx, vtxPrev) + { + if (!tx.IsCoinBase()) + { + uint256 hash = tx.GetHash(); + if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) + tx.AcceptToMemoryPool(false); + } + } + return AcceptToMemoryPool(false); + } + return false; +} + bool CWalletTx::WriteToDisk() { return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); @@ -799,7 +820,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 +831,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 +870,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 +885,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 +903,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 +926,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 @@ -915,7 +936,7 @@ void CWallet::ResendWalletTransactions() CWalletTx& wtx = item.second; // Don't rebroadcast until it's had plenty of time that // it should have gotten in already by now. - if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) + if (nTimeBestReceived - (int64_t)wtx.nTimeReceived > 5 * 60) mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); } BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) @@ -937,9 +958,9 @@ void CWallet::ResendWalletTransactions() // -int64 CWallet::GetBalance() const +int64_t CWallet::GetBalance() const { - int64 nTotal = 0; + int64_t nTotal = 0; { LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) @@ -953,9 +974,9 @@ int64 CWallet::GetBalance() const return nTotal; } -int64 CWallet::GetUnconfirmedBalance() const +int64_t CWallet::GetUnconfirmedBalance() const { - int64 nTotal = 0; + int64_t nTotal = 0; { LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) @@ -968,9 +989,9 @@ int64 CWallet::GetUnconfirmedBalance() const return nTotal; } -int64 CWallet::GetImmatureBalance() const +int64_t CWallet::GetImmatureBalance() const { - int64 nTotal = 0; + int64_t nTotal = 0; { LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) @@ -1011,8 +1032,8 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed) const } } -static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsigned int> > >vValue, int64 nTotalLower, int64 nTargetValue, - vector<char>& vfBest, int64& nBest, int iterations = 1000) +static void ApproximateBestSubset(vector<pair<int64_t, pair<const CWalletTx*,unsigned int> > >vValue, int64_t nTotalLower, int64_t nTargetValue, + vector<char>& vfBest, int64_t& nBest, int iterations = 1000) { vector<char> vfIncluded; @@ -1024,7 +1045,7 @@ static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsig for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { vfIncluded.assign(vValue.size(), false); - int64 nTotal = 0; + int64_t nTotal = 0; bool fReachedTarget = false; for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) { @@ -1057,18 +1078,18 @@ static void ApproximateBestSubset(vector<pair<int64, pair<const CWalletTx*,unsig } } -bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins, - set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const +bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins, + set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const { setCoinsRet.clear(); nValueRet = 0; // List of values less than target - pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger; - coinLowestLarger.first = std::numeric_limits<int64>::max(); + pair<int64_t, pair<const CWalletTx*,unsigned int> > coinLowestLarger; + coinLowestLarger.first = std::numeric_limits<int64_t>::max(); coinLowestLarger.second.first = NULL; - vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue; - int64 nTotalLower = 0; + vector<pair<int64_t, pair<const CWalletTx*,unsigned int> > > vValue; + int64_t nTotalLower = 0; random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); @@ -1080,9 +1101,9 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe continue; int i = output.i; - int64 n = pcoin->vout[i].nValue; + int64_t n = pcoin->vout[i].nValue; - pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i)); + pair<int64_t,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i)); if (n == nTargetValue) { @@ -1123,7 +1144,7 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe // Solve subset sum by stochastic approximation sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); vector<char> vfBest; - int64 nBest; + int64_t nBest; ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000); if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT) @@ -1145,18 +1166,17 @@ 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; } -bool CWallet::SelectCoins(int64 nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const +bool CWallet::SelectCoins(int64_t nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const { vector<COutput> vCoins; AvailableCoins(vCoins); @@ -1169,11 +1189,11 @@ bool CWallet::SelectCoins(int64 nTargetValue, set<pair<const CWalletTx*,unsigned -bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason) +bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, + CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason) { - int64 nValue = 0; - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + int64_t nValue = 0; + BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) { if (nValue < 0) { @@ -1200,10 +1220,10 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, wtxNew.vout.clear(); wtxNew.fFromMe = true; - int64 nTotalValue = nValue + nFeeRet; + int64_t nTotalValue = nValue + nFeeRet; double dPriority = 0; // vouts to the payees - BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) { CTxOut txout(s.second, s.first); if (txout.IsDust(CTransaction::nMinRelayTxFee)) @@ -1216,7 +1236,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, // Choose coins to use set<pair<const CWalletTx*,unsigned int> > setCoins; - int64 nValueIn = 0; + int64_t nValueIn = 0; if (!SelectCoins(nTotalValue, setCoins, nValueIn)) { strFailReason = _("Insufficient funds"); @@ -1224,20 +1244,21 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, } BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { - int64 nCredit = pcoin.first->vout[pcoin.second].nValue; + int64_t nCredit = pcoin.first->vout[pcoin.second].nValue; //The priority after the next block (depth+1) is used instead of the current, //reflecting an assumption the user would accept a bit more delay for //a chance at a free transaction. dPriority += (double)nCredit * (pcoin.first->GetDepthInMainChain()+1); } - int64 nChange = nValueIn - nValue - nFeeRet; - // if sub-cent change is required, the fee must be raised to at least nMinTxFee - // or until nChange becomes zero - // NOTE: this depends on the exact behaviour of GetMinFee + int64_t nChange = nValueIn - nValue - nFeeRet; + // The following if statement should be removed once enough miners + // have upgraded to the 0.9 GetMinFee() rules. Until then, this avoids + // creating free transactions that have change outputs less than + // CENT bitcoins. if (nFeeRet < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT) { - int64 nMoveToFee = min(nChange, CTransaction::nMinTxFee - nFeeRet); + int64_t nMoveToFee = min(nChange, CTransaction::nMinTxFee - nFeeRet); nChange -= nMoveToFee; nFeeRet += nMoveToFee; } @@ -1300,12 +1321,20 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, strFailReason = _("Transaction too large"); return false; } - dPriority /= nBytes; + unsigned int nTxSizeMod = nBytes; + // See miner.c's dPriority logic for the matching network-node side code. + BOOST_FOREACH(const CTxIn& txin, (*(CTransaction*)&wtxNew).vin) + { + unsigned int offset = 41U + min(110U, (unsigned int)txin.scriptSig.size()); + if (nTxSizeMod > offset) + nTxSizeMod -= offset; + } + dPriority /= nTxSizeMod; // Check that enough fee is included - int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); + int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); bool fAllowFree = AllowFree(dPriority); - int64 nMinFee = GetMinFee(wtxNew, fAllowFree, GMF_SEND); + int64_t nMinFee = GetMinFee(wtxNew, fAllowFree, GMF_SEND); if (nFeeRet < max(nPayFee, nMinFee)) { nFeeRet = max(nPayFee, nMinFee); @@ -1323,10 +1352,10 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, return true; } -bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, - CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason) +bool CWallet::CreateTransaction(CScript scriptPubKey, int64_t nValue, + CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason) { - vector< pair<CScript, int64> > vecSend; + vector< pair<CScript, int64_t> > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason); } @@ -1336,7 +1365,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 +1401,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(); @@ -1383,15 +1412,15 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee) { CReserveKey reservekey(this); - int64 nFeeRequired; + int64_t nFeeRequired; 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 +1428,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; } @@ -1414,7 +1443,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, -string CWallet::SendMoneyToDestination(const CTxDestination& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee) +string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nValue, CWalletTx& wtxNew, bool fAskFee) { // Check amount if (nValue <= 0) @@ -1483,33 +1512,6 @@ bool CWallet::DelAddressBook(const CTxDestination& address) return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); } -void CWallet::PrintWallet(const CBlock& block) -{ - { - LOCK(cs_wallet); - 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()); - } - } - printf("\n"); -} - -bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) -{ - { - LOCK(cs_wallet); - map<uint256, CWalletTx>::iterator mi = mapWallet.find(hashTx); - if (mi != mapWallet.end()) - { - wtx = (*mi).second; - return true; - } - } - return false; -} - bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) { if (fFileBacked) @@ -1521,14 +1523,6 @@ bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) return true; } -bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) -{ - if (!pwallet->fFileBacked) - return false; - strWalletFileOut = pwallet->strWalletFile; - return true; -} - // // Mark old keypool keys as used, // and generate all new keys @@ -1538,21 +1532,21 @@ bool CWallet::NewKeyPool() { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile); - BOOST_FOREACH(int64 nIndex, setKeyPool) + BOOST_FOREACH(int64_t nIndex, setKeyPool) walletdb.ErasePool(nIndex); setKeyPool.clear(); if (IsLocked()) return false; - int64 nKeys = max(GetArg("-keypool", 100), (int64)0); + int64_t nKeys = max(GetArg("-keypool", 100), (int64_t)0); for (int i = 0; i < nKeys; i++) { - int64 nIndex = i+1; + int64_t nIndex = i+1; walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); setKeyPool.insert(nIndex); } - printf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys); + LogPrintf("CWallet::NewKeyPool wrote %"PRId64" new keys\n", nKeys); } return true; } @@ -1572,23 +1566,23 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) if (kpSize > 0) nTargetSize = kpSize; else - nTargetSize = max(GetArg("-keypool", 100), 0LL); + nTargetSize = max(GetArg("-keypool", 100), (int64_t) 0); while (setKeyPool.size() < (nTargetSize + 1)) { - int64 nEnd = 1; + int64_t nEnd = 1; if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; 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 %"PRId64", size=%"PRIszu"\n", nEnd, setKeyPool.size()); } } return true; } -void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) +void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) { nIndex = -1; keypool.vchPubKey = CPubKey(); @@ -1611,17 +1605,17 @@ 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 %"PRId64"\n", nIndex); } } -int64 CWallet::AddReserveKey(const CKeyPool& keypool) +int64_t CWallet::AddReserveKey(const CKeyPool& keypool) { { LOCK2(cs_main, cs_wallet); CWalletDB walletdb(strWalletFile); - int64 nIndex = 1 + *(--setKeyPool.end()); + int64_t nIndex = 1 + *(--setKeyPool.end()); if (!walletdb.WritePool(nIndex, keypool)) throw runtime_error("AddReserveKey() : writing added key failed"); setKeyPool.insert(nIndex); @@ -1630,7 +1624,7 @@ int64 CWallet::AddReserveKey(const CKeyPool& keypool) return -1; } -void CWallet::KeepKey(int64 nIndex) +void CWallet::KeepKey(int64_t nIndex) { // Remove from key pool if (fFileBacked) @@ -1638,22 +1632,22 @@ void CWallet::KeepKey(int64 nIndex) CWalletDB walletdb(strWalletFile); walletdb.ErasePool(nIndex); } - printf("keypool keep %"PRI64d"\n", nIndex); + LogPrintf("keypool keep %"PRId64"\n", nIndex); } -void CWallet::ReturnKey(int64 nIndex) +void CWallet::ReturnKey(int64_t nIndex) { // Return to key pool { LOCK(cs_wallet); setKeyPool.insert(nIndex); } - printf("keypool return %"PRI64d"\n", nIndex); + LogPrintf("keypool return %"PRId64"\n", nIndex); } bool CWallet::GetKeyFromPool(CPubKey& result) { - int64 nIndex = 0; + int64_t nIndex = 0; CKeyPool keypool; { LOCK(cs_wallet); @@ -1670,9 +1664,9 @@ bool CWallet::GetKeyFromPool(CPubKey& result) return true; } -int64 CWallet::GetOldestKeyPoolTime() +int64_t CWallet::GetOldestKeyPoolTime() { - int64 nIndex = 0; + int64_t nIndex = 0; CKeyPool keypool; ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) @@ -1681,9 +1675,9 @@ int64 CWallet::GetOldestKeyPoolTime() return keypool.nTime; } -std::map<CTxDestination, int64> CWallet::GetAddressBalances() +std::map<CTxDestination, int64_t> CWallet::GetAddressBalances() { - map<CTxDestination, int64> balances; + map<CTxDestination, int64_t> balances; { LOCK(cs_wallet); @@ -1709,7 +1703,7 @@ std::map<CTxDestination, int64> CWallet::GetAddressBalances() if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr)) continue; - int64 n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue; + int64_t n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue; if (!balances.count(addr)) balances[addr] = 0; @@ -1836,7 +1830,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; @@ -1870,7 +1864,7 @@ void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress) const CWalletDB walletdb(strWalletFile); LOCK2(cs_main, cs_wallet); - BOOST_FOREACH(const int64& id, setKeyPool) + BOOST_FOREACH(const int64_t& id, setKeyPool) { CKeyPool keypool; if (!walletdb.ReadPool(id, keypool)) @@ -1925,7 +1919,7 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) } } -void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const { +void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const { mapKeyBirth.clear(); // get birth times for keys with metadata @@ -1934,7 +1928,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 +1948,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..5c38d7a1a0 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -1,29 +1,33 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_WALLET_H #define BITCOIN_WALLET_H -#include "walletdb.h" - -#include <string> -#include <vector> - -#include <stdlib.h> - -#include "main.h" +#include "core.h" +#include "crypter.h" #include "key.h" #include "keystore.h" -#include "script.h" +#include "main.h" #include "ui_interface.h" #include "util.h" +#include "walletdb.h" + +#include <algorithm> +#include <map> +#include <set> +#include <stdexcept> +#include <stdint.h> +#include <string> +#include <utility> +#include <vector> class CAccountingEntry; -class CWalletTx; -class CReserveKey; class COutput; -class CWalletDB; +class CReserveKey; +class CScript; +class CWalletTx; /** (client) version numbers for particular wallet features */ enum WalletFeature @@ -41,7 +45,7 @@ enum WalletFeature class CKeyPool { public: - int64 nTime; + int64_t nTime; CPubKey vchPubKey; CKeyPool() @@ -80,10 +84,10 @@ public: /** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. */ -class CWallet : public CCryptoKeyStore +class CWallet : public CCryptoKeyStore, public CWalletInterface { private: - bool SelectCoins(int64 nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const; + bool SelectCoins(int64_t nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const; CWalletDB *pwalletdbEncryption; @@ -93,8 +97,8 @@ private: // the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded int nWalletMaxVersion; - int64 nNextResend; - int64 nLastResend; + int64_t nNextResend; + int64_t nLastResend; public: mutable CCriticalSection cs_wallet; @@ -102,7 +106,7 @@ public: bool fFileBacked; std::string strWalletFile; - std::set<int64> setKeyPool; + std::set<int64_t> setKeyPool; std::map<CKeyID, CKeyMetadata> mapKeyMetadata; typedef std::map<unsigned int, CMasterKey> MasterKeyMap; @@ -134,7 +138,7 @@ public: } std::map<uint256, CWalletTx> mapWallet; - int64 nOrderPosNext; + int64_t nOrderPosNext; std::map<uint256, int> mapRequestCount; std::map<CTxDestination, CAddressBookData> mapAddressBook; @@ -143,13 +147,13 @@ public: std::set<COutPoint> setLockedCoins; - int64 nTimeFirstKey; + int64_t nTimeFirstKey; // check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; } void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true) const; - bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const; + bool SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const; bool IsLockedCoin(uint256 hash, unsigned int n) const; void LockCoin(COutPoint& output); void UnlockCoin(COutPoint& output); @@ -179,15 +183,15 @@ public: bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); - void GetKeyBirthTimes(std::map<CKeyID, int64> &mapKeyBirth) const; + void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const; /** Increment the next transaction order id @return next transaction order id */ - int64 IncOrderPosNext(CWalletDB *pwalletdb = NULL); + int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL); typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair; - typedef std::multimap<int64, TxPair > TxItems; + typedef std::multimap<int64_t, TxPair > TxItems; /** Get the wallet's activity log @return multimap of ordered transactions and accounting entries @@ -197,52 +201,53 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn); - bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); - bool EraseFromWallet(uint256 hash); + void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock); + bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate); + void EraseFromWallet(const uint256 &hash); void WalletUpdateSpent(const CTransaction& prevout); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(); - int64 GetBalance() const; - int64 GetUnconfirmedBalance() const; - int64 GetImmatureBalance() const; - bool CreateTransaction(const std::vector<std::pair<CScript, int64> >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason); - bool CreateTransaction(CScript scriptPubKey, int64 nValue, - CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, std::string& strFailReason); + int64_t GetBalance() const; + int64_t GetUnconfirmedBalance() const; + int64_t GetImmatureBalance() const; + bool CreateTransaction(const std::vector<std::pair<CScript, int64_t> >& vecSend, + CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason); + bool CreateTransaction(CScript scriptPubKey, int64_t nValue, + CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); - std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); - std::string SendMoneyToDestination(const CTxDestination &address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); + std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); + std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); bool NewKeyPool(); bool TopUpKeyPool(unsigned int kpSize = 0); - int64 AddReserveKey(const CKeyPool& keypool); - void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); - void KeepKey(int64 nIndex); - void ReturnKey(int64 nIndex); + int64_t AddReserveKey(const CKeyPool& keypool); + void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool); + void KeepKey(int64_t nIndex); + void ReturnKey(int64_t nIndex); bool GetKeyFromPool(CPubKey &key); - int64 GetOldestKeyPoolTime(); + int64_t GetOldestKeyPoolTime(); void GetAllReserveKeys(std::set<CKeyID>& setAddress) const; std::set< std::set<CTxDestination> > GetAddressGroupings(); - std::map<CTxDestination, int64> GetAddressBalances(); + std::map<CTxDestination, int64_t> GetAddressBalances(); std::set<CTxDestination> GetAccountAddresses(std::string strAccount) const; bool IsMine(const CTxIn& txin) const; - int64 GetDebit(const CTxIn& txin) const; + int64_t GetDebit(const CTxIn& txin) const; bool IsMine(const CTxOut& txout) const { return ::IsMine(*this, txout.scriptPubKey); } - int64 GetCredit(const CTxOut& txout) const + int64_t GetCredit(const CTxOut& txout) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error("CWallet::GetCredit() : value out of range"); return (IsMine(txout) ? txout.nValue : 0); } bool IsChange(const CTxOut& txout) const; - int64 GetChange(const CTxOut& txout) const + int64_t GetChange(const CTxOut& txout) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error("CWallet::GetChange() : value out of range"); @@ -259,9 +264,9 @@ public: { return (GetDebit(tx) > 0); } - int64 GetDebit(const CTransaction& tx) const + int64_t GetDebit(const CTransaction& tx) const { - int64 nDebit = 0; + int64_t nDebit = 0; BOOST_FOREACH(const CTxIn& txin, tx.vin) { nDebit += GetDebit(txin); @@ -270,9 +275,9 @@ public: } return nDebit; } - int64 GetCredit(const CTransaction& tx) const + int64_t GetCredit(const CTransaction& tx) const { - int64 nCredit = 0; + int64_t nCredit = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { nCredit += GetCredit(txout); @@ -281,9 +286,9 @@ public: } return nCredit; } - int64 GetChange(const CTransaction& tx) const + int64_t GetChange(const CTransaction& tx) const { - int64 nChange = 0; + int64_t nChange = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { nChange += GetChange(txout); @@ -302,8 +307,6 @@ public: void UpdatedTransaction(const uint256 &hashTx); - void PrintWallet(const CBlock& block); - void Inventory(const uint256 &hash) { { @@ -319,8 +322,6 @@ public: return setKeyPool.size(); } - bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx); - bool SetDefaultKey(const CPubKey &vchPubKey); // signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower @@ -352,7 +353,7 @@ class CReserveKey { protected: CWallet* pwallet; - int64 nIndex; + int64_t nIndex; CPubKey vchPubKey; public: CReserveKey(CWallet* pwalletIn) @@ -375,7 +376,7 @@ public: typedef std::map<std::string, std::string> mapValue_t; -static void ReadOrderPos(int64& nOrderPos, mapValue_t& mapValue) +static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue) { if (!mapValue.count("n")) { @@ -386,7 +387,7 @@ static void ReadOrderPos(int64& nOrderPos, mapValue_t& mapValue) } -static void WriteOrderPos(const int64& nOrderPos, mapValue_t& mapValue) +static void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) { if (nOrderPos == -1) return; @@ -412,7 +413,7 @@ public: char fFromMe; std::string strFromAccount; std::vector<char> vfSpent; // which outputs are already spent - int64 nOrderPos; // position in ordered transaction list + int64_t nOrderPos; // position in ordered transaction list // memory only mutable bool fDebitCached; @@ -420,11 +421,11 @@ public: mutable bool fImmatureCreditCached; mutable bool fAvailableCreditCached; mutable bool fChangeCached; - mutable int64 nDebitCached; - mutable int64 nCreditCached; - mutable int64 nImmatureCreditCached; - mutable int64 nAvailableCreditCached; - mutable int64 nChangeCached; + mutable int64_t nDebitCached; + mutable int64_t nCreditCached; + mutable int64_t nImmatureCreditCached; + mutable int64_t nAvailableCreditCached; + mutable int64_t nChangeCached; CWalletTx() { @@ -584,7 +585,7 @@ public: return (!!vfSpent[nOut]); } - int64 GetDebit() const + int64_t GetDebit() const { if (vin.empty()) return 0; @@ -595,7 +596,7 @@ public: return nDebitCached; } - int64 GetCredit(bool fUseCache=true) const + int64_t GetCredit(bool fUseCache=true) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) @@ -609,7 +610,7 @@ public: return nCreditCached; } - int64 GetImmatureCredit(bool fUseCache=true) const + int64_t GetImmatureCredit(bool fUseCache=true) const { if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) { @@ -623,7 +624,7 @@ public: return 0; } - int64 GetAvailableCredit(bool fUseCache=true) const + int64_t GetAvailableCredit(bool fUseCache=true) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) @@ -632,7 +633,7 @@ public: if (fUseCache && fAvailableCreditCached) return nAvailableCreditCached; - int64 nCredit = 0; + int64_t nCredit = 0; for (unsigned int i = 0; i < vout.size(); i++) { if (!IsSpent(i)) @@ -650,7 +651,7 @@ public: } - int64 GetChange() const + int64_t GetChange() const { if (fChangeCached) return nChangeCached; @@ -659,11 +660,11 @@ public: return nChangeCached; } - void GetAmounts(std::list<std::pair<CTxDestination, int64> >& listReceived, - std::list<std::pair<CTxDestination, int64> >& listSent, int64& nFee, std::string& strSentAccount) const; + void GetAmounts(std::list<std::pair<CTxDestination, int64_t> >& listReceived, + std::list<std::pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, std::string& strSentAccount) const; - void GetAccountAmounts(const std::string& strAccount, int64& nReceived, - int64& nSent, int64& nFee) const; + void GetAccountAmounts(const std::string& strAccount, int64_t& nReceived, + int64_t& nSent, int64_t& nFee) const; bool IsFromMe() const { @@ -715,7 +716,7 @@ public: bool WriteToDisk(); - int64 GetTxTime() const; + int64_t GetTxTime() const; int GetRequestCount() const; void AddSupportingTransactions(); @@ -745,7 +746,7 @@ public: void print() const { - printf("%s\n", ToString().c_str()); + LogPrintf("%s\n", ToString().c_str()); } }; @@ -757,13 +758,13 @@ class CWalletKey { public: CPrivKey vchPrivKey; - int64 nTimeCreated; - int64 nTimeExpires; + int64_t nTimeCreated; + int64_t nTimeExpires; std::string strComment; //// todo: add something to note what created it (user, getnewaddress, change) //// maybe should have a map<string, string> property map - CWalletKey(int64 nExpires=0) + CWalletKey(int64_t nExpires=0) { nTimeCreated = (nExpires ? GetTime() : 0); nTimeExpires = nExpires; @@ -820,13 +821,13 @@ class CAccountingEntry { public: std::string strAccount; - int64 nCreditDebit; - int64 nTime; + int64_t nCreditDebit; + int64_t nTime; std::string strOtherAccount; std::string strComment; mapValue_t mapValue; - int64 nOrderPos; // position in ordered transaction list - uint64 nEntryNo; + int64_t nOrderPos; // position in ordered transaction list + uint64_t nEntryNo; CAccountingEntry() { @@ -891,6 +892,4 @@ private: std::vector<char> _ssExtra; }; -bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); - #endif diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 554d140024..8317862f7c 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -1,18 +1,27 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// 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 "walletdb.h" + +#include "base58.h" +#include "protocol.h" +#include "serialize.h" +#include "sync.h" #include "wallet.h" -#include <boost/version.hpp> + +#include <inttypes.h> +#include <stdint.h> + #include <boost/filesystem.hpp> +#include <boost/foreach.hpp> using namespace std; using namespace boost; -static uint64 nAccountingEntryNumber = 0; +static uint64_t nAccountingEntryNumber = 0; // // CWalletDB @@ -44,6 +53,119 @@ bool CWalletDB::ErasePurpose(const string& strPurpose) return Erase(make_pair(string("purpose"), strPurpose)); } +bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("tx"), hash), wtx); +} + +bool CWalletDB::EraseTx(uint256 hash) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("tx"), hash)); +} + +bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) +{ + nWalletDBUpdated++; + + if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), + keyMeta, false)) + return 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 CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, + const std::vector<unsigned char>& vchCryptedSecret, + const CKeyMetadata &keyMeta) +{ + const bool fEraseUnencryptedKey = true; + nWalletDBUpdated++; + + if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), + keyMeta)) + return false; + + if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) + return false; + if (fEraseUnencryptedKey) + { + Erase(std::make_pair(std::string("key"), vchPubKey)); + Erase(std::make_pair(std::string("wkey"), vchPubKey)); + } + return true; +} + +bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); +} + +bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); +} + +bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) +{ + nWalletDBUpdated++; + return Write(std::string("bestblock"), locator); +} + +bool CWalletDB::ReadBestBlock(CBlockLocator& locator) +{ + return Read(std::string("bestblock"), locator); +} + +bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) +{ + nWalletDBUpdated++; + return Write(std::string("orderposnext"), nOrderPosNext); +} + +bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) +{ + nWalletDBUpdated++; + return Write(std::string("defaultkey"), vchPubKey); +} + +bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) +{ + return Read(std::make_pair(std::string("pool"), nPool), keypool); +} + +bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("pool"), nPool), keypool); +} + +bool CWalletDB::ErasePool(int64_t nPool) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("pool"), nPool)); +} + +bool CWalletDB::EraseSetting(const std::string& strKey) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("setting"), strKey)); +} + +bool CWalletDB::WriteMinVersion(int nVersion) +{ + return Write(std::string("minversion"), nVersion); +} + bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) { account.SetNull(); @@ -55,7 +177,7 @@ bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) return Write(make_pair(string("acc"), strAccount), account); } -bool CWalletDB::WriteAccountingEntry(const uint64 nAccEntryNum, const CAccountingEntry& acentry) +bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) { return Write(boost::make_tuple(string("acentry"), acentry.strAccount, nAccEntryNum), acentry); } @@ -65,12 +187,12 @@ bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) return WriteAccountingEntry(++nAccountingEntryNumber, acentry); } -int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) +int64_t CWalletDB::GetAccountCreditDebit(const string& strAccount) { list<CAccountingEntry> entries; ListAccountCreditDebit(strAccount, entries); - int64 nCreditDebit = 0; + int64_t nCreditDebit = 0; BOOST_FOREACH (const CAccountingEntry& entry, entries) nCreditDebit += entry.nCreditDebit; @@ -90,7 +212,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountin // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) - ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0)); + ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64_t(0)); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; @@ -130,7 +252,7 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. typedef pair<CWalletTx*, CAccountingEntry*> TxPair; - typedef multimap<int64, TxPair > TxItems; + typedef multimap<int64_t, TxPair > TxItems; TxItems txByTime; for (map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) @@ -145,14 +267,14 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); } - int64& nOrderPosNext = pwallet->nOrderPosNext; + int64_t& nOrderPosNext = pwallet->nOrderPosNext; nOrderPosNext = 0; - std::vector<int64> nOrderPosOffsets; + std::vector<int64_t> nOrderPosOffsets; for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) { CWalletTx *const pwtx = (*it).second.first; CAccountingEntry *const pacentry = (*it).second.second; - int64& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; + int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; if (nOrderPos == -1) { @@ -166,8 +288,8 @@ CWalletDB::ReorderTransactions(CWallet* pwallet) } else { - int64 nOrderPosOff = 0; - BOOST_FOREACH(const int64& nOffsetStart, nOrderPosOffsets) + int64_t nOrderPosOff = 0; + BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets) { if (nOrderPos >= nOffsetStart) ++nOrderPosOff; @@ -271,8 +393,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"PRId64" %s %s %s\n", // wtx.vout[0].nValue, // DateTimeStrFormat("%Y-%m-%d %H:%M:%S", wtx.GetBlockTime()).c_str(), // wtx.hashBlock.ToString().c_str(), @@ -282,7 +404,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { string strAccount; ssKey >> strAccount; - uint64 nNumber; + uint64_t nNumber; ssKey >> nNumber; if (nNumber > nAccountingEntryNumber) nAccountingEntryNumber = nNumber; @@ -306,6 +428,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } CKey key; CPrivKey pkey; + uint256 hash = 0; + if (strType == "key") { wss.nKeys++; @@ -315,14 +439,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; } - if (key.GetPubKey() != vchPubKey) + catch(...){} + + bool fSkipCheck = false; + + if (hash != 0) { - strErr = "Error reading wallet database: CPrivKey pubkey inconsistency"; + // 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.Load(pkey, vchPubKey, fSkipCheck)) + { + strErr = "Error reading wallet database: CPrivKey corrupt"; return false; } if (!pwallet->LoadKey(key, vchPubKey)) @@ -382,7 +532,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "pool") { - int64 nIndex; + int64_t nIndex; ssKey >> nIndex; CKeyPool keypool; ssValue >> keypool; @@ -451,7 +601,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 +615,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 +637,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 +656,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 @@ -545,7 +695,7 @@ void ThreadFlushWalletDB(const string& strFile) unsigned int nLastSeen = nWalletDBUpdated; unsigned int nLastFlushed = nWalletDBUpdated; - int64 nLastWalletUpdate = GetTime(); + int64_t nLastWalletUpdate = GetTime(); while (true) { MilliSleep(500); @@ -576,16 +726,16 @@ 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(); + int64_t nStart = GetTimeMillis(); // Flush wallet.dat so it's self contained bitdb.CloseDb(strFile); bitdb.CheckpointLSN(strFile); bitdb.mapFileUseCount.erase(mi++); - printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); + LogPrint("db", "Flushed wallet.dat %"PRId64"ms\n", GetTimeMillis() - nStart); } } } @@ -620,10 +770,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; } } @@ -645,16 +795,16 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) // Rewrite salvaged data to wallet.dat // Set -rescan so any missing transactions will be // found. - int64 now = GetTime(); - std::string newFilename = strprintf("wallet.%"PRI64d".bak", now); + int64_t now = GetTime(); + std::string newFilename = strprintf("wallet.%"PRId64".bak", now); 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 +812,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 +827,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 +847,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..88ba89f9d5 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -1,18 +1,29 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2009-2013 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_WALLETDB_H #define BITCOIN_WALLETDB_H #include "db.h" -#include "base58.h" +#include "key.h" + +#include <list> +#include <stdint.h> +#include <string> +#include <utility> +#include <vector> -class CKeyPool; class CAccount; class CAccountingEntry; +struct CBlockLocator; +class CKeyPool; +class CMasterKey; +class CScript; class CWallet; class CWalletTx; +class uint160; +class uint256; /** Error statuses for the wallet database */ enum DBErrors @@ -30,13 +41,13 @@ class CKeyMetadata public: static const int CURRENT_VERSION=1; int nVersion; - int64 nCreateTime; // 0 means unknown + int64_t nCreateTime; // 0 means unknown CKeyMetadata() { SetNull(); } - CKeyMetadata(int64 nCreateTime_) + CKeyMetadata(int64_t nCreateTime_) { nVersion = CKeyMetadata::CURRENT_VERSION; nCreateTime = nCreateTime_; @@ -73,102 +84,25 @@ public: bool WritePurpose(const std::string& strAddress, const std::string& purpose); bool ErasePurpose(const std::string& strAddress); - bool WriteTx(uint256 hash, const CWalletTx& wtx) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("tx"), hash), wtx); - } - - bool EraseTx(uint256 hash) - { - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("tx"), hash)); - } - - bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, - const CKeyMetadata &keyMeta) - { - nWalletDBUpdated++; - - if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), - keyMeta)) - return false; - - return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); - } - - bool WriteCryptedKey(const CPubKey& vchPubKey, - const std::vector<unsigned char>& vchCryptedSecret, - const CKeyMetadata &keyMeta) - { - const bool fEraseUnencryptedKey = true; - nWalletDBUpdated++; - - if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), - keyMeta)) - return false; - - if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) - return false; - if (fEraseUnencryptedKey) - { - Erase(std::make_pair(std::string("key"), vchPubKey)); - Erase(std::make_pair(std::string("wkey"), vchPubKey)); - } - return true; - } - - bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); - } - - bool WriteCScript(const uint160& hash, const CScript& redeemScript) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); - } - - bool WriteBestBlock(const CBlockLocator& locator) - { - nWalletDBUpdated++; - return Write(std::string("bestblock"), locator); - } + bool WriteTx(uint256 hash, const CWalletTx& wtx); + bool EraseTx(uint256 hash); - bool ReadBestBlock(CBlockLocator& locator) - { - return Read(std::string("bestblock"), locator); - } + bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata &keyMeta); + bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector<unsigned char>& vchCryptedSecret, const CKeyMetadata &keyMeta); + bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey); - bool WriteOrderPosNext(int64 nOrderPosNext) - { - nWalletDBUpdated++; - return Write(std::string("orderposnext"), nOrderPosNext); - } + bool WriteCScript(const uint160& hash, const CScript& redeemScript); - bool WriteDefaultKey(const CPubKey& vchPubKey) - { - nWalletDBUpdated++; - return Write(std::string("defaultkey"), vchPubKey); - } + bool WriteBestBlock(const CBlockLocator& locator); + bool ReadBestBlock(CBlockLocator& locator); - bool ReadPool(int64 nPool, CKeyPool& keypool) - { - return Read(std::make_pair(std::string("pool"), nPool), keypool); - } + bool WriteOrderPosNext(int64_t nOrderPosNext); - bool WritePool(int64 nPool, const CKeyPool& keypool) - { - nWalletDBUpdated++; - return Write(std::make_pair(std::string("pool"), nPool), keypool); - } + bool WriteDefaultKey(const CPubKey& vchPubKey); - bool ErasePool(int64 nPool) - { - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("pool"), nPool)); - } + bool ReadPool(int64_t nPool, CKeyPool& keypool); + bool WritePool(int64_t nPool, const CKeyPool& keypool); + bool ErasePool(int64_t nPool); // Settings are no longer stored in wallet.dat; these are // used only for backwards compatibility: @@ -183,24 +117,18 @@ public: nWalletDBUpdated++; return Write(std::make_pair(std::string("setting"), strKey), value); } - bool EraseSetting(const std::string& strKey) - { - nWalletDBUpdated++; - return Erase(std::make_pair(std::string("setting"), strKey)); - } + + bool EraseSetting(const std::string& strKey); - bool WriteMinVersion(int nVersion) - { - return Write(std::string("minversion"), nVersion); - } + bool WriteMinVersion(int nVersion); bool ReadAccount(const std::string& strAccount, CAccount& account); bool WriteAccount(const std::string& strAccount, const CAccount& account); private: - bool WriteAccountingEntry(const uint64 nAccEntryNum, const CAccountingEntry& acentry); + bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry); public: bool WriteAccountingEntry(const CAccountingEntry& acentry); - int64 GetAccountCreditDebit(const std::string& strAccount); + int64_t GetAccountCreditDebit(const std::string& strAccount); void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries); DBErrors ReorderTransactions(CWallet*); |