diff options
Diffstat (limited to 'src')
82 files changed, 1439 insertions, 1018 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 9b0b97b7ac..ff23747592 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -91,6 +91,7 @@ BITCOIN_CORE_H = \ noui.h \ pow.h \ protocol.h \ + random.h \ rpcclient.h \ rpcprotocol.h \ rpcserver.h \ @@ -197,14 +198,15 @@ libbitcoin_common_a_SOURCES = \ # backward-compatibility objects and their sanity checks are linked. libbitcoin_util_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_util_a_SOURCES = \ + compat/glibc_sanity.cpp \ + compat/glibcxx_sanity.cpp \ chainparamsbase.cpp \ + random.cpp \ rpcprotocol.cpp \ sync.cpp \ uint256.cpp \ util.cpp \ version.cpp \ - compat/glibc_sanity.cpp \ - compat/glibcxx_sanity.cpp \ $(BITCOIN_CORE_H) if GLIBC_BACK_COMPAT @@ -274,7 +276,7 @@ EXTRA_DIST = leveldb secp256k1 clean-local: -$(MAKE) -C leveldb clean - -$(MAKE) -C secp256k1 clean + -$(MAKE) -C secp256k1 clean 2>/dev/null rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno -rm -f config.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 75b7b683dd..2772bc753a 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -145,6 +145,7 @@ BITCOIN_MM = \ QT_MOC = \ qt/bitcoin.moc \ + qt/bitcoinamountfield.moc \ qt/intro.moc \ qt/overviewpage.moc \ qt/rpcconsole.moc diff --git a/src/addrman.h b/src/addrman.h index c4c296560e..052d364655 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -7,6 +7,7 @@ #include "netbase.h" #include "protocol.h" +#include "random.h" #include "sync.h" #include "timedata.h" #include "util.h" @@ -16,8 +17,6 @@ #include <stdint.h> #include <vector> -#include <openssl/rand.h> - /** Extended statistics about a CAddress */ class CAddrInfo : public CAddress { @@ -246,145 +245,147 @@ protected: void Connected_(const CService &addr, int64_t nTime); public: + // serialized format: + // * version byte (currently 0) + // * nKey + // * nNew + // * nTried + // * number of "new" buckets + // * all nNew addrinfos in vvNew + // * all nTried addrinfos in vvTried + // * for each bucket: + // * number of elements + // * for each element: index + // + // Notice that vvTried, mapAddr and vVector are never encoded explicitly; + // they are instead reconstructed from the other information. + // + // vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change, + // otherwise it is reconstructed as well. + // + // This format is more complex, but significantly smaller (at most 1.5 MiB), and supports + // changes to the ADDRMAN_ parameters without breaking the on-disk structure. + // + // We don't use IMPLEMENT_SERIALIZE since the serialization and deserialization code has + // very little in common. + template<typename Stream> + void Serialize(Stream &s, int nType, int nVersionDummy) const + { + LOCK(cs); + + unsigned char nVersion = 0; + s << nVersion; + s << nKey; + s << nNew; + s << nTried; + + int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT; + s << nUBuckets; + std::map<int, int> mapUnkIds; + int nIds = 0; + for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { + if (nIds == nNew) break; // this means nNew was wrong, oh ow + mapUnkIds[(*it).first] = nIds; + const CAddrInfo &info = (*it).second; + if (info.nRefCount) { + s << info; + nIds++; + } + } + nIds = 0; + for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { + if (nIds == nTried) break; // this means nTried was wrong, oh ow + const CAddrInfo &info = (*it).second; + if (info.fInTried) { + s << info; + nIds++; + } + } + for (std::vector<std::set<int> >::const_iterator it = vvNew.begin(); it != vvNew.end(); it++) { + const std::set<int> &vNew = (*it); + int nSize = vNew.size(); + s << nSize; + for (std::set<int>::const_iterator it2 = vNew.begin(); it2 != vNew.end(); it2++) { + int nIndex = mapUnkIds[*it2]; + s << nIndex; + } + } + } - IMPLEMENT_SERIALIZE - (({ - // serialized format: - // * version byte (currently 0) - // * nKey - // * nNew - // * nTried - // * number of "new" buckets - // * all nNew addrinfos in vvNew - // * all nTried addrinfos in vvTried - // * for each bucket: - // * number of elements - // * for each element: index - // - // Notice that vvTried, mapAddr and vVector are never encoded explicitly; - // they are instead reconstructed from the other information. - // - // vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change, - // otherwise it is reconstructed as well. - // - // This format is more complex, but significantly smaller (at most 1.5 MiB), and supports - // changes to the ADDRMAN_ parameters without breaking the on-disk structure. - { - LOCK(cs); - unsigned char nVersion = 0; - READWRITE(nVersion); - READWRITE(nKey); - READWRITE(nNew); - READWRITE(nTried); - - CAddrMan *am = const_cast<CAddrMan*>(this); - if (fWrite) - { - int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT; - READWRITE(nUBuckets); - std::map<int, int> mapUnkIds; - int nIds = 0; - for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) - { - if (nIds == nNew) break; // this means nNew was wrong, oh ow - mapUnkIds[(*it).first] = nIds; - CAddrInfo &info = (*it).second; - if (info.nRefCount) - { - READWRITE(info); - nIds++; - } - } - nIds = 0; - for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++) - { - if (nIds == nTried) break; // this means nTried was wrong, oh ow - CAddrInfo &info = (*it).second; - if (info.fInTried) - { - READWRITE(info); - nIds++; - } - } - for (std::vector<std::set<int> >::iterator it = am->vvNew.begin(); it != am->vvNew.end(); it++) - { - const std::set<int> &vNew = (*it); - int nSize = vNew.size(); - READWRITE(nSize); - for (std::set<int>::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++) - { - int nIndex = mapUnkIds[*it2]; - READWRITE(nIndex); - } - } + template<typename Stream> + void Unserialize(Stream& s, int nType, int nVersionDummy) + { + LOCK(cs); + + unsigned char nVersion; + s >> nVersion; + s >> nKey; + s >> nNew; + s >> nTried; + + int nUBuckets = 0; + s >> nUBuckets; + nIdCount = 0; + mapInfo.clear(); + mapAddr.clear(); + vRandom.clear(); + vvTried = std::vector<std::vector<int> >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)); + vvNew = std::vector<std::set<int> >(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>()); + for (int n = 0; n < nNew; n++) { + CAddrInfo &info = mapInfo[n]; + s >> info; + mapAddr[info] = n; + info.nRandomPos = vRandom.size(); + vRandom.push_back(n); + if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) { + vvNew[info.GetNewBucket(nKey)].insert(n); + info.nRefCount++; + } + } + nIdCount = nNew; + int nLost = 0; + for (int n = 0; n < nTried; n++) { + CAddrInfo info; + s >> info; + std::vector<int> &vTried = vvTried[info.GetTriedBucket(nKey)]; + if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) { + info.nRandomPos = vRandom.size(); + info.fInTried = true; + vRandom.push_back(nIdCount); + mapInfo[nIdCount] = info; + mapAddr[info] = nIdCount; + vTried.push_back(nIdCount); + nIdCount++; } else { - int nUBuckets = 0; - READWRITE(nUBuckets); - am->nIdCount = 0; - am->mapInfo.clear(); - am->mapAddr.clear(); - am->vRandom.clear(); - am->vvTried = std::vector<std::vector<int> >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)); - am->vvNew = std::vector<std::set<int> >(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>()); - for (int n = 0; n < am->nNew; n++) - { - CAddrInfo &info = am->mapInfo[n]; - READWRITE(info); - am->mapAddr[info] = n; - info.nRandomPos = vRandom.size(); - am->vRandom.push_back(n); - if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) - { - am->vvNew[info.GetNewBucket(am->nKey)].insert(n); - info.nRefCount++; - } - } - am->nIdCount = am->nNew; - int nLost = 0; - for (int n = 0; n < am->nTried; n++) - { - CAddrInfo info; - READWRITE(info); - std::vector<int> &vTried = am->vvTried[info.GetTriedBucket(am->nKey)]; - if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) - { - info.nRandomPos = vRandom.size(); - info.fInTried = true; - am->vRandom.push_back(am->nIdCount); - am->mapInfo[am->nIdCount] = info; - am->mapAddr[info] = am->nIdCount; - vTried.push_back(am->nIdCount); - am->nIdCount++; - } else { - nLost++; - } - } - am->nTried -= nLost; - for (int b = 0; b < nUBuckets; b++) - { - std::set<int> &vNew = am->vvNew[b]; - int nSize = 0; - READWRITE(nSize); - for (int n = 0; n < nSize; n++) - { - int nIndex = 0; - READWRITE(nIndex); - CAddrInfo &info = am->mapInfo[nIndex]; - if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) - { - info.nRefCount++; - vNew.insert(nIndex); - } - } + nLost++; + } + } + nTried -= nLost; + for (int b = 0; b < nUBuckets; b++) { + std::set<int> &vNew = vvNew[b]; + int nSize = 0; + s >> nSize; + for (int n = 0; n < nSize; n++) { + int nIndex = 0; + s >> nIndex; + CAddrInfo &info = mapInfo[nIndex]; + if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) { + info.nRefCount++; + vNew.insert(nIndex); } } } - });) + } + + unsigned int GetSerializeSize(int nType, int nVersion) const + { + return (CSizeComputer(nType, nVersion) << *this).size(); + } CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>()) { nKey.resize(32); - RAND_bytes(&nKey[0], 32); + GetRandBytes(&nKey[0], 32); nIdCount = 0; nTried = 0; diff --git a/src/allocators.h b/src/allocators.h index 7012ef7e2a..be0be7ab96 100644 --- a/src/allocators.h +++ b/src/allocators.h @@ -131,7 +131,7 @@ public: * 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. + * static-initialized, it is created on demand. */ class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker> { diff --git a/src/bloom.cpp b/src/bloom.cpp index 85a2ddc189..26e366179c 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -94,13 +94,6 @@ bool CBloomFilter::contains(const uint256& hash) const return contains(data); } -void CBloomFilter::clear() -{ - vData.assign(vData.size(),0); - isFull = false; - isEmpty = true; -} - bool CBloomFilter::IsWithinSizeConstraints() const { return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; diff --git a/src/bloom.h b/src/bloom.h index d0caf9e9fa..956bead87f 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -78,8 +78,6 @@ public: bool contains(const COutPoint& outpoint) const; bool contains(const uint256& hash) const; - void clear(); - // True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS // (catch a filter which was just deserialized which was too big) bool IsWithinSizeConstraints() const; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index b9097ea961..fb1d05f833 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -6,6 +6,7 @@ #include "chainparams.h" #include "assert.h" +#include "random.h" #include "util.h" #include <boost/assign/list_of.hpp> diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 19a9e72cc9..720e24c4a8 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -91,3 +91,7 @@ bool SelectBaseParamsFromCommandLine() { } return true; } + +bool AreBaseParamsConfigured() { + return pCurrentBaseParams != NULL; +} diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 4a3b268909..4398f69548 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -49,4 +49,10 @@ void SelectBaseParams(CBaseChainParams::Network network); */ bool SelectBaseParamsFromCommandLine(); +/** + * Return true if SelectBaseParamsFromCommandLine() has been called to select + * a network. + */ +bool AreBaseParamsConfigured(); + #endif diff --git a/src/coins.cpp b/src/coins.cpp index 13a4ea95cd..fe40911db7 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -4,6 +4,8 @@ #include "coins.h" +#include "random.h" + #include <assert.h> // calculate number of bytes for the bitmask, and its number of non-zero bytes @@ -69,6 +71,8 @@ void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } +CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} + CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { } bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { @@ -84,8 +88,8 @@ bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { } CCoinsMap::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { - CCoinsMap::iterator it = cacheCoins.lower_bound(txid); - if (it != cacheCoins.end() && it->first == txid) + CCoinsMap::iterator it = cacheCoins.find(txid); + if (it != cacheCoins.end()) return it; CCoins tmp; if (!base->GetCoins(txid,tmp)) @@ -107,7 +111,12 @@ bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { } bool CCoinsViewCache::HaveCoins(const uint256 &txid) { - return FetchCoins(txid) != cacheCoins.end(); + CCoinsMap::iterator it = FetchCoins(txid); + // We're using vtx.empty() instead of IsPruned here for performance reasons, + // as we only care about the case where an transaction was replaced entirely + // in a reorganization (which wipes vout entirely, as opposed to spending + // which just cleans individual outputs). + return (it != cacheCoins.end() && !it->second.vout.empty()); } uint256 CCoinsViewCache::GetBestBlock() { diff --git a/src/coins.h b/src/coins.h index c57a5ec722..9f90fe6bd0 100644 --- a/src/coins.h +++ b/src/coins.h @@ -13,6 +13,7 @@ #include <stdint.h> #include <boost/foreach.hpp> +#include <boost/unordered_map.hpp> /** pruned version of CTransaction: only retains metadata and unspent transaction outputs * @@ -239,7 +240,19 @@ public: } }; -typedef std::map<uint256,CCoins> CCoinsMap; +class CCoinsKeyHasher +{ +private: + uint256 salt; + +public: + CCoinsKeyHasher(); + uint64_t operator()(const uint256& key) const { + return key.GetHash(salt); + } +}; + +typedef boost::unordered_map<uint256, CCoins, CCoinsKeyHasher> CCoinsMap; struct CCoinsStats { diff --git a/src/compat.h b/src/compat.h index 8fbafb6cce..1b3a60d11b 100644 --- a/src/compat.h +++ b/src/compat.h @@ -59,19 +59,4 @@ typedef u_int SOCKET; #define SOCKET_ERROR -1 #endif -inline int myclosesocket(SOCKET& hSocket) -{ - if (hSocket == INVALID_SOCKET) - return WSAENOTSOCK; -#ifdef WIN32 - int ret = closesocket(hSocket); -#else - int ret = close(hSocket); -#endif - hSocket = INVALID_SOCKET; - return ret; -} -#define closesocket(s) myclosesocket(s) - - #endif diff --git a/src/core.cpp b/src/core.cpp index b56994ecf3..149b3532a1 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -124,22 +124,6 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) { return *this; } -bool CTransaction::IsEquivalentTo(const CTransaction& tx) const -{ - if (nVersion != tx.nVersion || - nLockTime != tx.nLockTime || - vin.size() != tx.vin.size() || - vout != tx.vout) - return false; - for (unsigned int i = 0; i < vin.size(); i++) - { - if (vin[i].nSequence != tx.vin[i].nSequence || - vin[i].prevout != tx.vin[i].prevout) - return false; - } - return true; -} - int64_t CTransaction::GetValueOut() const { int64_t nValueOut = 0; diff --git a/src/core.h b/src/core.h index 0387336c98..fb64e6c08e 100644 --- a/src/core.h +++ b/src/core.h @@ -255,9 +255,6 @@ public: return hash; } - // True if only scriptSigs are different - bool IsEquivalentTo(const CTransaction& tx) const; - // Return sum of txouts. int64_t GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because diff --git a/src/init.cpp b/src/init.cpp index f9e568a207..3488a8bedf 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -38,8 +38,8 @@ #include <boost/interprocess/sync/file_lock.hpp> #include <openssl/crypto.h> -using namespace std; using namespace boost; +using namespace std; #ifdef ENABLE_WALLET CWallet* pwalletMain; @@ -58,7 +58,8 @@ CWallet* pwalletMain; enum BindFlags { BF_NONE = 0, BF_EXPLICIT = (1U << 0), - BF_REPORT_ERROR = (1U << 1) + BF_REPORT_ERROR = (1U << 1), + BF_WHITELIST = (1U << 2), }; static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; @@ -109,10 +110,11 @@ static CCoinsViewDB *pcoinsdbview; void Shutdown() { - LogPrintf("Shutdown : In progress...\n"); + LogPrintf("%s: In progress...\n", __func__); static CCriticalSection cs_Shutdown; TRY_LOCK(cs_Shutdown, lockShutdown); - if (!lockShutdown) return; + if (!lockShutdown) + return; RenameThread("bitcoin-shutoff"); mempool.AddTransactionsUpdated(1); @@ -125,12 +127,14 @@ void Shutdown() StopNode(); UnregisterNodeSignals(GetNodeSignals()); - boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; - CAutoFile est_fileout = CAutoFile(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION); - if (est_fileout) - mempool.WriteFeeEstimates(est_fileout); - else - LogPrintf("failed to write fee estimates"); + { + boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; + CAutoFile est_fileout(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION); + if (est_fileout) + mempool.WriteFeeEstimates(est_fileout); + else + LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string()); + } { LOCK(cs_main); @@ -142,9 +146,12 @@ void Shutdown() pblocktree->Flush(); if (pcoinsTip) pcoinsTip->Flush(); - delete pcoinsTip; pcoinsTip = NULL; - delete pcoinsdbview; pcoinsdbview = NULL; - delete pblocktree; pblocktree = NULL; + delete pcoinsTip; + pcoinsTip = NULL; + delete pcoinsdbview; + pcoinsdbview = NULL; + delete pblocktree; + pblocktree = NULL; } #ifdef ENABLE_WALLET if (pwalletMain) @@ -156,7 +163,7 @@ void Shutdown() if (pwalletMain) delete pwalletMain; #endif - LogPrintf("Shutdown : done\n"); + LogPrintf("%s: done\n", __func__); } // @@ -174,13 +181,13 @@ void HandleSIGHUP(int) bool static InitError(const std::string &str) { - uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::NOSHOWGUI); + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); return false; } bool static InitWarning(const std::string &str) { - uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING | CClientUIInterface::NOSHOWGUI); + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); return true; } @@ -188,7 +195,7 @@ bool static Bind(const CService &addr, unsigned int flags) { if (!(flags & BF_EXPLICIT) && IsLimited(addr)) return false; std::string strError; - if (!BindListenPort(addr, strError)) { + if (!BindListenPort(addr, strError, flags & BF_WHITELIST)) { if (flags & BF_REPORT_ERROR) return InitError(strError); return false; @@ -214,14 +221,17 @@ std::string HelpMessage(HelpMessageMode mode) } strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; strUsage += " -dbcache=<n> " + strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache) + "\n"; - strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + " " + _("on startup") + "\n"; strUsage += " -maxorphanblocks=<n> " + strprintf(_("Keep at most <n> unconnectable blocks in memory (default: %u)"), DEFAULT_MAX_ORPHAN_BLOCKS) + "\n"; strUsage += " -par=<n> " + strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS) + "\n"; strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n"; strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + " " + _("on startup") + "\n"; +#if !defined(WIN32) + strUsage += " -sysperms " + _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)") + "\n"; +#endif strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n"; + strUsage += "\n" + _("Connection options:") + "\n"; strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n"; strUsage += " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n"; @@ -238,6 +248,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n"; strUsage += " -onion=<ip:port> " + _("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)") + "\n"; strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n"; + strUsage += " -permitbaremultisig " + _("Relay non-P2SH multisig (default: 1)") + "\n"; strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n"; strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS5 proxy") + "\n"; strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n"; @@ -249,10 +260,13 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n"; #endif #endif + strUsage += " -whitebind=<addr> " + _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6") + "\n"; + strUsage += " -whitelist=<netmask> " + _("Whitelist peers connecting from the given netmask or ip. Can be specified multiple times.") + "\n"; #ifdef ENABLE_WALLET strUsage += "\n" + _("Wallet options:") + "\n"; strUsage += " -disablewallet " + _("Do not load the wallet and disable wallet RPC calls") + "\n"; + strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; if (GetBoolArg("-help-debug", false)) strUsage += " -mintxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), FormatMoney(CWallet::minTxFee.GetFeePerK())) + "\n"; strUsage += " -paytxfee=<amt> " + strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK())) + "\n"; @@ -288,8 +302,10 @@ std::string HelpMessage(HelpMessageMode mode) if (mode == HMM_BITCOIN_QT) strUsage += ", qt"; strUsage += ".\n"; +#ifdef ENABLE_WALLET strUsage += " -gen " + _("Generate coins (default: 0)") + "\n"; strUsage += " -genproclimit=<n> " + _("Set the processor limit for when generation is on (-1 = unlimited, default: -1)") + "\n"; +#endif strUsage += " -help-debug " + _("Show all debugging options (usage: --help -help-debug)") + "\n"; strUsage += " -logips " + _("Include IP addresses in debug output (default: 0)") + "\n"; strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n"; @@ -315,6 +331,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += "\n" + _("Node relay options:") + "\n"; strUsage += " -datacarrier " + _("Relay and mine data carrier transactions (default: 1)") + "\n"; + strUsage += "\n" + _("Block creation options:") + "\n"; strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; strUsage += " -blockmaxsize=<n> " + strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE) + "\n"; @@ -474,7 +491,15 @@ bool AppInit2(boost::thread_group& threadGroup) } #endif #ifndef WIN32 - umask(077); + + if (GetBoolArg("-sysperms", false)) { +#ifdef ENABLE_WALLET + if (!GetBoolArg("-disablewallet", false)) + return InitError("Error: -sysperms is not allowed in combination with enabled wallet functionality"); +#endif + } else { + umask(077); + } // Clean shutdown on SIGTERM struct sigaction sa; @@ -499,11 +524,11 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 2: parameter interactions - if (mapArgs.count("-bind")) { + if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified if (SoftSetBoolArg("-listen", true)) - LogPrintf("AppInit2 : parameter interaction: -bind set -> setting -listen=1\n"); + LogPrintf("AppInit2 : parameter interaction: -bind or -whitebind set -> setting -listen=1\n"); } if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { @@ -547,7 +572,7 @@ bool AppInit2(boost::thread_group& threadGroup) } // Make sure enough file descriptors are available - int nBind = std::max((int)mapArgs.count("-bind"), 1); + int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1); nMaxConnections = GetArg("-maxconnections", 125); nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); @@ -564,9 +589,9 @@ bool AppInit2(boost::thread_group& threadGroup) if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), string("0")) != categories.end()) fDebug = false; - // Check for -debugnet (deprecated) + // Check for -debugnet if (GetBoolArg("-debugnet", false)) - InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net")); + InitWarning(_("Warning: Unsupported argument -debugnet ignored, use -debug=net.")); // Check for -socks - as this is a privacy risk to continue, exit here if (mapArgs.count("-socks")) return InitError(_("Error: Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.")); @@ -652,7 +677,10 @@ bool AppInit2(boost::thread_group& threadGroup) bSpendZeroConfChange = GetArg("-spendzeroconfchange", true); std::string strWalletFile = GetArg("-wallet", "wallet.dat"); -#endif +#endif // ENABLE_WALLET + + fIsBareMultisigStd = GetArg("-permitbaremultisig", true); + // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log // Sanity check if (!InitSanityCheck()) @@ -764,6 +792,15 @@ bool AppInit2(boost::thread_group& threadGroup) } } + if (mapArgs.count("-whitelist")) { + BOOST_FOREACH(const std::string& net, mapMultiArgs["-whitelist"]) { + CSubNet subnet(net); + if (!subnet.IsValid()) + return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net)); + CNode::AddWhitelistedRange(subnet); + } + } + CService addrProxy; bool fProxy = false; if (mapArgs.count("-proxy")) { @@ -800,13 +837,21 @@ bool AppInit2(boost::thread_group& threadGroup) bool fBound = false; if (fListen) { - if (mapArgs.count("-bind")) { + if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) { BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind)); fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } + BOOST_FOREACH(std::string strBind, mapMultiArgs["-whitebind"]) { + CService addrBind; + if (!Lookup(strBind.c_str(), addrBind, 0, false)) + return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind)); + if (addrBind.GetPort() == 0) + return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind)); + fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST)); + } } else { struct in_addr inaddr_any; @@ -994,6 +1039,7 @@ bool AppInit2(boost::thread_group& threadGroup) boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_filein = CAutoFile(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION); + // Allowed to fail as this file IS missing on first startup. if (est_filein) mempool.ReadFeeEstimates(est_filein); @@ -1183,7 +1229,6 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); #endif - InitRespendFilter(); StartNode(threadGroup); if (fServer) StartRPCThreads(); diff --git a/src/key.cpp b/src/key.cpp index 3c4fa77e72..a253f8666a 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,11 +1,11 @@ -// Copyright (c) 2009-2013 The Bitcoin developers +// Copyright (c) 2009-2014 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 "crypto/sha2.h" -#include <openssl/rand.h> +#include "random.h" #ifdef USE_SECP256K1 #include <secp256k1.h> @@ -194,7 +194,7 @@ public: 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)) @@ -412,7 +412,7 @@ bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) { void CKey::MakeNewKey(bool fCompressedIn) { do { - RAND_bytes(vch, sizeof(vch)); + GetRandBytes(vch, sizeof(vch)); } while (!Check(vch)); fValid = true; fCompressed = fCompressedIn; @@ -745,5 +745,3 @@ bool ECC_InitSanityCheck() { return true; #endif } - - diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp index 5b4a9c147b..9e849696a8 100644 --- a/src/leveldbwrapper.cpp +++ b/src/leveldbwrapper.cpp @@ -32,6 +32,11 @@ static leveldb::Options GetOptions(size_t nCacheSize) { options.filter_policy = leveldb::NewBloomFilterPolicy(10); options.compression = leveldb::kNoCompression; options.max_open_files = 64; + if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { + // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error + // on corruption in later versions. + options.paranoid_checks = true; + } return options; } diff --git a/src/m4/bitcoin_qt.m4 b/src/m4/bitcoin_qt.m4 index 244b03a5c2..9356aac37f 100644 --- a/src/m4/bitcoin_qt.m4 +++ b/src/m4/bitcoin_qt.m4 @@ -86,14 +86,68 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ fi if test x$use_pkgconfig = xyes; then - if test x$PKG_CONFIG == x; then - AC_MSG_ERROR(pkg-config not found.) - fi BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS_WITH_PKGCONFIG([$2])]) else BITCOIN_QT_CHECK([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG]) fi + dnl This is ugly and complicated. Yuck. Works as follows: + dnl We can't discern whether Qt4 builds are static or not. For Qt5, we can + dnl check a header to find out. When Qt is built statically, some plugins must + dnl be linked into the final binary as well. These plugins have changed between + dnl Qt4 and Qt5. With Qt5, languages moved into core and the WindowsIntegration + dnl plugin was added. Since we can't tell if Qt4 is static or not, it is + dnl assumed for windows builds. + dnl _BITCOIN_QT_CHECK_STATIC_PLUGINS does a quick link-check and appends the + dnl results to QT_LIBS. + BITCOIN_QT_CHECK([ + TEMP_CPPFLAGS=$CPPFLAGS + CPPFLAGS=$QT_INCLUDES + if test x$bitcoin_qt_got_major_vers == x5; then + _BITCOIN_QT_IS_STATIC + if test x$bitcoin_cv_static_qt == xyes; then + AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) + if test x$qt_plugin_path != x; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" + if test x$bitcoin_qt_got_major_vers == x5; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" + else + QT_LIBS="$QT_LIBS -L$qt_plugin_path/codecs" + fi + fi + if test x$use_pkgconfig = xyes; then + PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"]) + fi + _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets]) + if test x$TARGET_OS == xwindows; then + _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows]) + AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows]) + elif test x$TARGET_OS == xlinux; then + _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)],[-lqxcb -lxcb-static -lxcb]) + AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb]) + elif test x$TARGET_OS == xdarwin; then + if test x$use_pkgconfig = xyes; then + PKG_CHECK_MODULES([QTPRINT], [Qt5PrintSupport], [QT_LIBS="$QTPRINT_LIBS $QT_LIBS"]) + fi + AX_CHECK_LINK_FLAG([[-framework IOKit]],[QT_LIBS="$QT_LIBS -framework IOKit"],[AC_MSG_ERROR(could not iokit framework)]) + _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)],[-lqcocoa]) + AC_DEFINE(QT_QPA_PLATFORM_COCOA, 1, [Define this symbol if the qt platform is cocoa]) + fi + fi + else + if test x$TARGET_OS == xwindows; then + AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) + _BITCOIN_QT_CHECK_STATIC_PLUGINS([ + Q_IMPORT_PLUGIN(qcncodecs) + Q_IMPORT_PLUGIN(qjpcodecs) + Q_IMPORT_PLUGIN(qtwcodecs) + Q_IMPORT_PLUGIN(qkrcodecs) + Q_IMPORT_PLUGIN(AccessibleFactory)], + [-lqcncodecs -lqjpcodecs -lqtwcodecs -lqkrcodecs -lqtaccessiblewidgets]) + fi + fi + CPPFLAGS=$TEMP_CPPFLAGS + ]) BITCOIN_QT_PATH_PROGS([MOC], [moc-qt${bitcoin_qt_got_major_vers} moc${bitcoin_qt_got_major_vers} moc], $qt_bin_path) BITCOIN_QT_PATH_PROGS([UIC], [uic-qt${bitcoin_qt_got_major_vers} uic${bitcoin_qt_got_major_vers} uic], $qt_bin_path) BITCOIN_QT_PATH_PROGS([RCC], [rcc-qt${bitcoin_qt_got_major_vers} rcc${bitcoin_qt_got_major_vers} rcc], $qt_bin_path) @@ -303,26 +357,15 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[ ]) BITCOIN_QT_CHECK([ - LIBS= - if test x$qt_lib_path != x; then - LIBS="$LIBS -L$qt_lib_path" - fi - if test x$qt_plugin_path != x; then - LIBS="$LIBS -L$qt_plugin_path/accessible" - if test x$bitcoin_qt_got_major_vers == x5; then - LIBS="$LIBS -L$qt_plugin_path/platforms" - else - LIBS="$LIBS -L$qt_plugin_path/codecs" - fi - fi - if test x$TARGET_OS == xwindows; then AC_CHECK_LIB([imm32], [main],, BITCOIN_QT_FAIL(libimm32 not found)) fi ]) - BITCOIN_QT_CHECK(AC_CHECK_LIB([z] ,[main],,BITCOIN_QT_FAIL(zlib not found))) - BITCOIN_QT_CHECK(AC_CHECK_LIB([png] ,[main],,BITCOIN_QT_FAIL(png not found))) + BITCOIN_QT_CHECK(AC_CHECK_LIB([z] ,[main],,AC_MSG_WARN([zlib not found. Assuming qt has it built-in]))) + BITCOIN_QT_CHECK(AC_CHECK_LIB([png] ,[main],,AC_MSG_WARN([libpng not found. Assuming qt has it built-in]))) + BITCOIN_QT_CHECK(AC_CHECK_LIB([jpeg] ,[main],,AC_MSG_WARN([libjpeg not found. Assuming qt has it built-in]))) + BITCOIN_QT_CHECK(AC_CHECK_LIB([pcre] ,[main],,AC_MSG_WARN([libpcre not found. Assuming qt has it built-in]))) BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Core] ,[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXCore not found))) BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Gui] ,[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXGui not found))) BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Network],[main],,BITCOIN_QT_FAIL(lib$QT_LIB_PREFIXNetwork not found))) @@ -332,37 +375,6 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[ QT_LIBS="$LIBS" LIBS="$TEMP_LIBS" - dnl This is ugly and complicated. Yuck. Works as follows: - dnl We can't discern whether Qt4 builds are static or not. For Qt5, we can - dnl check a header to find out. When Qt is built statically, some plugins must - dnl be linked into the final binary as well. These plugins have changed between - dnl Qt4 and Qt5. With Qt5, languages moved into core and the WindowsIntegration - dnl plugin was added. Since we can't tell if Qt4 is static or not, it is - dnl assumed for all non-pkg-config builds. - dnl _BITCOIN_QT_CHECK_STATIC_PLUGINS does a quick link-check and appends the - dnl results to QT_LIBS. - BITCOIN_QT_CHECK([ - if test x$bitcoin_qt_got_major_vers == x5; then - _BITCOIN_QT_IS_STATIC - if test x$bitcoin_cv_static_qt == xyes; then - AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets]) - if test x$TARGET_OS == xwindows; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows]) - fi - fi - else - AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) - _BITCOIN_QT_CHECK_STATIC_PLUGINS([ - Q_IMPORT_PLUGIN(qcncodecs) - Q_IMPORT_PLUGIN(qjpcodecs) - Q_IMPORT_PLUGIN(qtwcodecs) - Q_IMPORT_PLUGIN(qkrcodecs) - Q_IMPORT_PLUGIN(AccessibleFactory)], - [-lqcncodecs -lqjpcodecs -lqtwcodecs -lqkrcodecs -lqtaccessiblewidgets]) - fi - ]) - BITCOIN_QT_CHECK([ LIBS= if test x$qt_lib_path != x; then diff --git a/src/main.cpp b/src/main.cpp index 913a1702eb..06ce15b5b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,6 @@ #include "addrman.h" #include "alert.h" -#include "bloom.h" #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" @@ -41,11 +40,14 @@ CCriticalSection cs_main; map<uint256, CBlockIndex*> mapBlockIndex; CChain chainActive; int64_t nTimeBestReceived = 0; +CWaitableCriticalSection csBestBlock; +CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; bool fImporting = false; bool fReindex = false; bool fBenchmark = false; bool fTxIndex = false; +bool fIsBareMultisigStd = true; unsigned int nCoinCacheSize = 5000; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ @@ -123,15 +125,6 @@ namespace { } // anon namespace -// Bloom filter to limit respend relays to one -static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000; -static CBloomFilter doubleSpendFilter; -void InitRespendFilter() { - seed_insecure_rand(); - doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE); -} - - ////////////////////////////////////////////////////////////////////////////// // // dispatching functions @@ -158,7 +151,6 @@ struct CMainSignals { } // anon namespace - void RegisterWallet(CWalletInterface* pwalletIn) { g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2)); g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); @@ -210,7 +202,7 @@ struct CBlockReject { struct CNodeState { // Accumulated misbehaviour score for this peer. int nMisbehavior; - // Whether this peer should be disconnected and banned. + // Whether this peer should be disconnected and banned (unless whitelisted). bool fShouldBan; // String name of this peer (debugging/logging purposes). std::string name; @@ -602,9 +594,13 @@ bool IsStandardTx(const CTransaction& tx, string& reason) reason = "scriptpubkey"; return false; } + if (whichType == TX_NULL_DATA) nDataOut++; - else if (txout.IsDust(::minRelayTxFee)) { + else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { + reason = "bare-multisig"; + return false; + } else if (txout.IsDust(::minRelayTxFee)) { reason = "dust"; return false; } @@ -874,60 +870,6 @@ int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF return nMinFee; } -// Exponentially limit the rate of nSize flow to nLimit. nLimit unit is thousands-per-minute. -bool RateLimitExceeded(double& dCount, int64_t& nLastTime, int64_t nLimit, unsigned int nSize) -{ - static CCriticalSection csLimiter; - int64_t nNow = GetTime(); - - LOCK(csLimiter); - - dCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - if (dCount >= nLimit*10*1000) - return true; - dCount += nSize; - return false; -} - -static bool RelayableRespend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter) -{ - // Relaying double-spend attempts to our peers lets them detect when - // somebody might be trying to cheat them. However, blindly relaying - // every double-spend across the entire network gives attackers - // a denial-of-service attack: just generate a stream of double-spends - // re-spending the same (limited) set of outpoints owned by the attacker. - // So, we use a bloom filter and only relay (at most) the first double - // spend for each outpoint. False-positives ("we have already relayed") - // are OK, because if the peer doesn't hear about the double-spend - // from us they are very likely to hear about it from another peer, since - // each peer uses a different, randomized bloom filter. - - if (fInBlock || filter.contains(outPoint)) return false; - - // Apply an independent rate limit to double-spend relays - static double dRespendCount; - static int64_t nLastRespendTime; - static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100); - unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION); - - if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize)) - { - LogPrint("mempool", "Double-spend relay rejected by rate limiter\n"); - return false; - } - - LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize); - - // Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM - // insertions - if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0) - filter.clear(); - - filter.insert(outPoint); - - return true; -} bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee) @@ -957,18 +899,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return false; // Check for conflicts with in-memory transactions - bool relayableRespend = false; { LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; - // Does tx conflict with a member of the pool, and is it not equivalent to that member? - if (pool.mapNextTx.count(outpoint) && !tx.IsEquivalentTo(*pool.mapNextTx[outpoint].ptx)) + if (pool.mapNextTx.count(outpoint)) { - relayableRespend = RelayableRespend(outpoint, tx, false, doubleSpendFilter); - if (!relayableRespend) - return false; + // Disable replacement feature for now + return false; } } } @@ -1039,15 +978,23 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // be annoying or make others' transactions take longer to confirm. if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize)) { + static CCriticalSection csFreeLimiter; static double dFreeCount; - static int64_t nLastFreeTime; - static int64_t nFreeLimit = GetArg("-limitfreerelay", 15); + static int64_t nLastTime; + int64_t nNow = GetTime(); + + LOCK(csFreeLimiter); - if (RateLimitExceeded(dFreeCount, nLastFreeTime, nFreeLimit, nSize)) + // Use an exponentially decaying ~10-minute window: + dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; + // -limitfreerelay unit is thousand-bytes-per-minute + // At default rate it would take over a month to fill 1GB + if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) return 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 > ::minRelayTxFee.GetFee(nSize) * 10000) @@ -1061,21 +1008,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); } - - if (relayableRespend) - { - RelayTransaction(tx); - } - else - { - // Store transaction in memory - pool.addUnchecked(hash, entry); - } + // Store transaction in memory + pool.addUnchecked(hash, entry); } g_signals.SyncTransaction(tx, NULL); - return !relayableRespend; + return true; } @@ -1425,7 +1364,8 @@ void Misbehaving(NodeId pnode, int howmuch) return; state->nMisbehavior += howmuch; - if (state->nMisbehavior >= GetArg("-banscore", 100)) + int banscore = GetArg("-banscore", 100); + if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); state->fShouldBan = true; @@ -1915,7 +1855,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C // Update the on-disk chain state. bool static WriteChainState(CValidationState &state) { static int64_t nLastWrite = 0; - if (!IsInitialBlockDownload() || pcoinsTip->GetCacheSize() > nCoinCacheSize || GetTimeMicros() > nLastWrite + 600*1000000) { + if (pcoinsTip->GetCacheSize() > nCoinCacheSize || (!IsInitialBlockDownload() && GetTimeMicros() > nLastWrite + 600*1000000)) { // Typical CCoins structures on disk are around 100 bytes in size. // Pushing a new one to the database can cause it to be written // twice (once in the log, and once in the tables). This is already @@ -1944,11 +1884,14 @@ void static UpdateTip(CBlockIndex *pindexNew) { // New best block nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); + LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainActive.Tip())); + cvBlockChange.notify_all(); + // Check the version of the last 100 blocks to see if we need to upgrade: if (!fIsInitialDownload) { @@ -2512,7 +2455,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return false; if (!CheckBlock(block, state)) { - if (state.Invalid() && !state.CorruptionPossible()) { + if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; } return false; @@ -3948,6 +3891,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); if (nEvicted > 0) LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); + } else if (pfrom->fWhitelisted) { + // Always relay transactions received from whitelisted peers, even + // if they are already in the mempool (allowing the node to function + // as a gateway for nodes hidden behind it). + RelayTransaction(tx); } int nDoS = 0; if (state.IsInvalid(nDoS)) @@ -4371,7 +4319,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (pingSend) { uint64_t nonce = 0; while (nonce == 0) { - RAND_bytes((unsigned char*)&nonce, sizeof(nonce)); + GetRandBytes((unsigned char*)&nonce, sizeof(nonce)); } pto->fPingQueued = false; pto->nPingUsecStart = GetTimeMicros(); @@ -4441,11 +4389,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) CNodeState &state = *State(pto->GetId()); if (state.fShouldBan) { - if (pto->addr.IsLocal()) - LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString()); + if (pto->fWhitelisted) + LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString()); else { pto->fDisconnect = true; - CNode::Ban(pto->addr); + if (pto->addr.IsLocal()) + LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); + else + CNode::Ban(pto->addr); } state.fShouldBan = false; } diff --git a/src/main.h b/src/main.h index f6bac889be..5f231fa45b 100644 --- a/src/main.h +++ b/src/main.h @@ -87,11 +87,14 @@ extern uint64_t nLastBlockTx; extern uint64_t nLastBlockSize; extern const std::string strMessageMagic; extern int64_t nTimeBestReceived; +extern CWaitableCriticalSection csBestBlock; +extern CConditionVariable cvBlockChange; extern bool fImporting; extern bool fReindex; extern bool fBenchmark; extern int nScriptCheckThreads; extern bool fTxIndex; +extern bool fIsBareMultisigStd; extern unsigned int nCoinCacheSize; extern CFeeRate minRelayTxFee; @@ -109,9 +112,6 @@ struct CNodeStateStats; struct CBlockTemplate; -/** Initialize respend bloom filter **/ -void InitRespendFilter(); - /** Register a wallet to receive updates from core */ void RegisterWallet(CWalletInterface* pwalletIn); /** Unregister a wallet from core */ diff --git a/src/miner.cpp b/src/miner.cpp index 17918a1280..ec56c71192 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -467,7 +467,10 @@ void static BitcoinMiner(CWallet *pwallet) auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); if (!pblocktemplate.get()) + { + LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n"); return; + } CBlock *pblock = &pblocktemplate->block; IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); diff --git a/src/net.cpp b/src/net.cpp index 6a660dc9bd..b55cd72e86 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -50,7 +50,16 @@ using namespace std; using namespace boost; -static const int MAX_OUTBOUND_CONNECTIONS = 8; +namespace { + const int MAX_OUTBOUND_CONNECTIONS = 8; + + struct ListenSocket { + SOCKET socket; + bool whitelisted; + + ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {} + }; +} // // Global state variables @@ -65,7 +74,7 @@ static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; static CNode* pnodeSync = NULL; uint64_t nLocalHostNonce = 0; -static std::vector<SOCKET> vhListenSocket; +static std::vector<ListenSocket> vhListenSocket; CAddrMan addrman; int nMaxConnections = 125; @@ -323,7 +332,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha { if (!RecvLine(hSocket, strLine)) { - closesocket(hSocket); + CloseSocket(hSocket); return false; } if (pszKeyword == NULL) @@ -334,7 +343,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha break; } } - closesocket(hSocket); + CloseSocket(hSocket); if (strLine.find("<") != string::npos) strLine = strLine.substr(0, strLine.find("<")); strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r")); @@ -348,7 +357,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha return true; } } - closesocket(hSocket); + CloseSocket(hSocket); return error("GetMyExternalIP() : connection closed"); } @@ -524,8 +533,7 @@ void CNode::CloseSocketDisconnect() if (hSocket != INVALID_SOCKET) { LogPrint("net", "disconnecting peer=%d\n", id); - closesocket(hSocket); - hSocket = INVALID_SOCKET; + CloseSocket(hSocket); } // in case this fails, we'll empty the recv buffer when the CNode is deleted @@ -546,7 +554,7 @@ void CNode::PushVersion() 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)); + GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); if (fLogIPs) LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); else @@ -593,6 +601,24 @@ bool CNode::Ban(const CNetAddr &addr) { return true; } + +std::vector<CSubNet> CNode::vWhitelistedRange; +CCriticalSection CNode::cs_vWhitelistedRange; + +bool CNode::IsWhitelistedRange(const CNetAddr &addr) { + LOCK(cs_vWhitelistedRange); + BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) { + if (subnet.Match(addr)) + return true; + } + return false; +} + +void CNode::AddWhitelistedRange(const CSubNet &subnet) { + LOCK(cs_vWhitelistedRange); + vWhitelistedRange.push_back(subnet); +} + #undef X #define X(name) stats.name = name void CNode::copyStats(CNodeStats &stats) @@ -609,6 +635,7 @@ void CNode::copyStats(CNodeStats &stats) X(nStartingHeight); X(nSendBytes); X(nRecvBytes); + X(fWhitelisted); stats.fSyncNode = (this == pnodeSync); // It is common for nodes with good ping times to suddenly become lagged, @@ -848,9 +875,9 @@ void ThreadSocketHandler() SOCKET hSocketMax = 0; bool have_fds = false; - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) { - FD_SET(hListenSocket, &fdsetRecv); - hSocketMax = max(hSocketMax, hListenSocket); + BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) { + FD_SET(hListenSocket.socket, &fdsetRecv); + hSocketMax = max(hSocketMax, hListenSocket.socket); have_fds = true; } @@ -917,13 +944,13 @@ void ThreadSocketHandler() // // Accept new connections // - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) + BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) { - if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) + if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); - SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); + SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len); CAddress addr; int nInbound = 0; @@ -931,6 +958,7 @@ void ThreadSocketHandler() if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) LogPrintf("Warning: Unknown socket family\n"); + bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -946,17 +974,18 @@ void ThreadSocketHandler() } else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) { - closesocket(hSocket); + CloseSocket(hSocket); } - else if (CNode::IsBanned(addr)) + else if (CNode::IsBanned(addr) && !whitelisted) { LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); - closesocket(hSocket); + CloseSocket(hSocket); } else { CNode* pnode = new CNode(hSocket, addr, "", true); pnode->AddRef(); + pnode->fWhitelisted = whitelisted; { LOCK(cs_vNodes); @@ -1580,7 +1609,7 @@ void ThreadMessageHandler() -bool BindListenPort(const CService &addrBind, string& strError) +bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted) { strError = ""; int nOne = 1; @@ -1649,6 +1678,7 @@ bool BindListenPort(const CService &addrBind, string& strError) else strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); LogPrintf("%s\n", strError); + CloseSocket(hListenSocket); return false; } LogPrintf("Bound to %s\n", addrBind.ToString()); @@ -1658,12 +1688,13 @@ bool BindListenPort(const CService &addrBind, string& strError) { strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); + CloseSocket(hListenSocket); return false; } - vhListenSocket.push_back(hListenSocket); + vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted)); - if (addrBind.IsRoutable() && fDiscover) + if (addrBind.IsRoutable() && fDiscover && !fWhitelisted) AddLocal(addrBind, LOCAL_BIND); return true; @@ -1787,11 +1818,11 @@ public: // Close sockets BOOST_FOREACH(CNode* pnode, vNodes) if (pnode->hSocket != INVALID_SOCKET) - closesocket(pnode->hSocket); - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) - if (hListenSocket != INVALID_SOCKET) - if (closesocket(hListenSocket) == SOCKET_ERROR) - LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); + CloseSocket(pnode->hSocket); + BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) + if (hListenSocket.socket != INVALID_SOCKET) + if (!CloseSocket(hListenSocket.socket)) + LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); // clean up some globals (to help leak detection) BOOST_FOREACH(CNode *pnode, vNodes) @@ -1931,7 +1962,7 @@ bool CAddrDB::Write(const CAddrMan& addr) { // Generate random temporary filename unsigned short randv = 0; - RAND_bytes((unsigned char *)&randv, sizeof(randv)); + GetRandBytes((unsigned char*)&randv, sizeof(randv)); std::string tmpfn = strprintf("peers.dat.%04x", randv); // serialize addresses, checksum data up to that point, then append csum @@ -13,6 +13,7 @@ #include "mruset.h" #include "netbase.h" #include "protocol.h" +#include "random.h" #include "sync.h" #include "uint256.h" #include "util.h" @@ -26,7 +27,6 @@ #include <boost/foreach.hpp> #include <boost/signals2/signal.hpp> -#include <openssl/rand.h> class CAddrMan; class CBlockIndex; @@ -64,7 +64,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL); bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); -bool BindListenPort(const CService &bindAddr, std::string& strError); +bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); void StartNode(boost::thread_group& threadGroup); bool StopNode(); void SocketSendData(CNode *pnode); @@ -154,6 +154,7 @@ public: uint64_t nSendBytes; uint64_t nRecvBytes; bool fSyncNode; + bool fWhitelisted; double dPingTime; double dPingWait; std::string addrLocal; @@ -236,6 +237,7 @@ public: // store the sanitized version in cleanSubVer. The original should be used when dealing with // the network or wire types and the cleaned string used when displayed or logged. std::string strSubVer, cleanSubVer; + bool fWhitelisted; // This peer can bypass DoS banning. bool fOneShot; bool fClient; bool fInbound; @@ -259,6 +261,11 @@ protected: static std::map<CNetAddr, int64_t> setBanned; static CCriticalSection cs_setBanned; + // Whitelisted ranges. Any node connecting from these is automatically + // whitelisted (as well as those connecting to whitelisted binds). + static std::vector<CSubNet> vWhitelistedRange; + static CCriticalSection cs_vWhitelistedRange; + // Basic fuzz-testing void Fuzz(int nChance); // modifies ssSend @@ -305,6 +312,7 @@ public: addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; nVersion = 0; strSubVer = ""; + fWhitelisted = false; fOneShot = false; fClient = false; // set by version message fInbound = fInboundIn; @@ -349,8 +357,7 @@ public: { if (hSocket != INVALID_SOCKET) { - closesocket(hSocket); - hSocket = INVALID_SOCKET; + CloseSocket(hSocket); } if (pfilter) delete pfilter; @@ -720,6 +727,9 @@ public: static bool Ban(const CNetAddr &ip); void copyStats(CNodeStats &stats); + static bool IsWhitelistedRange(const CNetAddr &ip); + static void AddWhitelistedRange(const CSubNet &subnet); + // Network stats static void RecordBytesRecv(uint64_t bytes); static void RecordBytesSent(uint64_t bytes); diff --git a/src/netbase.cpp b/src/netbase.cpp index 067cfa024b..e9f3515456 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -218,7 +218,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket) LogPrintf("SOCKS5 connecting %s\n", strDest); if (strDest.size() > 255) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Hostname too long"); } char pszSocks5Init[] = "\5\1\0"; @@ -227,18 +227,18 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket) ssize_t ret = send(hSocket, pszSocks5Init, nSize, MSG_NOSIGNAL); if (ret != nSize) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Error sending to proxy"); } char pchRet1[2]; if (recv(hSocket, pchRet1, 2, 0) != 2) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Error reading proxy response"); } if (pchRet1[0] != 0x05 || pchRet1[1] != 0x00) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Proxy failed to initialize"); } string strSocks5("\5\1"); @@ -250,23 +250,23 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket) ret = send(hSocket, strSocks5.c_str(), strSocks5.size(), MSG_NOSIGNAL); if (ret != (ssize_t)strSocks5.size()) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Error sending to proxy"); } char pchRet2[4]; if (recv(hSocket, pchRet2, 4, 0) != 4) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Error reading proxy response"); } if (pchRet2[0] != 0x05) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Proxy failed to accept request"); } if (pchRet2[1] != 0x00) { - closesocket(hSocket); + CloseSocket(hSocket); switch (pchRet2[1]) { case 0x01: return error("Proxy error: general failure"); @@ -282,7 +282,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket) } if (pchRet2[2] != 0x00) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Error: malformed proxy response"); } char pchRet3[256]; @@ -294,23 +294,23 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket) { ret = recv(hSocket, pchRet3, 1, 0) != 1; if (ret) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Error reading from proxy"); } int nRecv = pchRet3[0]; ret = recv(hSocket, pchRet3, nRecv, 0) != nRecv; break; } - default: closesocket(hSocket); return error("Error: malformed proxy response"); + default: CloseSocket(hSocket); return error("Error: malformed proxy response"); } if (ret) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Error reading from proxy"); } if (recv(hSocket, pchRet3, 2, 0) != 2) { - closesocket(hSocket); + CloseSocket(hSocket); return error("Error reading from proxy"); } LogPrintf("SOCKS5 connected %s\n", strDest); @@ -344,7 +344,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1) #endif { - closesocket(hSocket); + CloseSocket(hSocket); return false; } @@ -365,13 +365,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe if (nRet == 0) { LogPrint("net", "connection to %s timeout\n", addrConnect.ToString()); - closesocket(hSocket); + CloseSocket(hSocket); return false; } if (nRet == SOCKET_ERROR) { LogPrintf("select() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); - closesocket(hSocket); + CloseSocket(hSocket); return false; } socklen_t nRetSize = sizeof(nRet); @@ -382,13 +382,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe #endif { LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); - closesocket(hSocket); + CloseSocket(hSocket); return false; } if (nRet != 0) { LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet)); - closesocket(hSocket); + CloseSocket(hSocket); return false; } } @@ -399,7 +399,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe #endif { LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); - closesocket(hSocket); + CloseSocket(hSocket); return false; } } @@ -415,7 +415,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR) #endif { - closesocket(hSocket); + CloseSocket(hSocket); return false; } @@ -1258,3 +1258,16 @@ std::string NetworkErrorString(int err) return strprintf("%s (%d)", s, err); } #endif + +bool CloseSocket(SOCKET& hSocket) +{ + if (hSocket == INVALID_SOCKET) + return false; +#ifdef WIN32 + int ret = closesocket(hSocket); +#else + int ret = close(hSocket); +#endif + hSocket = INVALID_SOCKET; + return ret != SOCKET_ERROR; +} diff --git a/src/netbase.h b/src/netbase.h index ad1e230834..05221a5fde 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -178,5 +178,7 @@ bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout = nCon bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault = 0, int nTimeout = nConnectTimeout); /** Return readable error string for a network error code */ std::string NetworkErrorString(int err); +/** Close socket and set hSocket to INVALID_SOCKET */ +bool CloseSocket(SOCKET& hSocket); #endif diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 5df8f19729..f336d47e83 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -282,7 +282,7 @@ void AddressBookPage::on_exportButton_clicked() if(!writer.write()) { QMessageBox::critical(this, tr("Exporting Failed"), - tr("There was an error trying to save the address list to %1.").arg(filename)); + tr("There was an error trying to save the address list to %1. Please try again.").arg(filename)); } } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 7c4af25edf..7bf531f538 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -34,6 +34,7 @@ #include <boost/filesystem/operations.hpp> #include <QApplication> +#include <QDebug> #include <QLibraryInfo> #include <QLocale> #include <QMessageBox> @@ -52,7 +53,13 @@ Q_IMPORT_PLUGIN(qkrcodecs) Q_IMPORT_PLUGIN(qtaccessiblewidgets) #else Q_IMPORT_PLUGIN(AccessibleFactory) +#if defined(QT_QPA_PLATFORM_XCB) +Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); +#elif defined(QT_QPA_PLATFORM_WINDOWS) Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); +#elif defined(QT_QPA_PLATFORM_COCOA) +Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); +#endif #endif #endif @@ -237,7 +244,7 @@ void BitcoinCore::initialize() { try { - LogPrintf("Running AppInit2 in thread\n"); + qDebug() << __func__ << ": Running AppInit2 in thread"; int rv = AppInit2(threadGroup); if(rv) { @@ -258,11 +265,11 @@ void BitcoinCore::shutdown() { try { - LogPrintf("Running Shutdown in thread\n"); + qDebug() << __func__ << ": Running Shutdown in thread"; threadGroup.interrupt_all(); threadGroup.join_all(); Shutdown(); - LogPrintf("Shutdown finished\n"); + qDebug() << __func__ << ": Shutdown finished"; emit shutdownResult(1); } catch (std::exception& e) { handleRunawayException(&e); @@ -285,15 +292,17 @@ BitcoinApplication::BitcoinApplication(int &argc, char **argv): returnValue(0) { setQuitOnLastWindowClosed(false); - startThread(); } BitcoinApplication::~BitcoinApplication() { - LogPrintf("Stopping thread\n"); - emit stopThread(); - coreThread->wait(); - LogPrintf("Stopped thread\n"); + if(coreThread) + { + qDebug() << __func__ << ": Stopping thread"; + emit stopThread(); + coreThread->wait(); + qDebug() << __func__ << ": Stopped thread"; + } delete window; window = 0; @@ -336,6 +345,8 @@ void BitcoinApplication::createSplashScreen(bool isaTestNet) void BitcoinApplication::startThread() { + if(coreThread) + return; coreThread = new QThread(this); BitcoinCore *executor = new BitcoinCore(); executor->moveToThread(coreThread); @@ -355,13 +366,15 @@ void BitcoinApplication::startThread() void BitcoinApplication::requestInitialize() { - LogPrintf("Requesting initialize\n"); + qDebug() << __func__ << ": Requesting initialize"; + startThread(); emit requestedInitialize(); } void BitcoinApplication::requestShutdown() { - LogPrintf("Requesting shutdown\n"); + qDebug() << __func__ << ": Requesting shutdown"; + startThread(); window->hide(); window->setClientModel(0); pollShutdownTimer->stop(); @@ -383,7 +396,7 @@ void BitcoinApplication::requestShutdown() void BitcoinApplication::initializeResult(int retval) { - LogPrintf("Initialization result: %i\n", retval); + qDebug() << __func__ << ": Initialization result: " << retval; // Set exit result: 0 if successful, 1 if failure returnValue = retval ? 0 : 1; if(retval) @@ -393,8 +406,6 @@ void BitcoinApplication::initializeResult(int retval) paymentServer->setOptionsModel(optionsModel); #endif - emit splashFinished(window); - clientModel = new ClientModel(optionsModel); window->setClientModel(clientModel); @@ -411,6 +422,8 @@ void BitcoinApplication::initializeResult(int retval) } #endif + emit splashFinished(window); + // If -min option passed, start window minimized. if(GetBoolArg("-min", false)) { @@ -438,7 +451,7 @@ void BitcoinApplication::initializeResult(int retval) void BitcoinApplication::shutdownResult(int retval) { - LogPrintf("Shutdown result: %i\n", retval); + qDebug() << __func__ << ": Shutdown result: " << retval; quit(); // Exit main loop after shutdown finished } diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 25ad0c66af..6466039013 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -9,19 +9,186 @@ #include "qvaluecombobox.h" #include <QApplication> -#include <QDoubleSpinBox> +#include <QAbstractSpinBox> #include <QHBoxLayout> #include <QKeyEvent> -#include <qmath.h> // for qPow() +#include <QLineEdit> + +/** QSpinBox that uses fixed-point numbers internally and uses our own + * formatting/parsing functions. + */ +class AmountSpinBox: public QAbstractSpinBox +{ + Q_OBJECT +public: + explicit AmountSpinBox(QWidget *parent): + QAbstractSpinBox(parent), + currentUnit(BitcoinUnits::BTC), + singleStep(100000) // satoshis + { + setAlignment(Qt::AlignRight); + + connect(lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged())); + } + + QValidator::State validate(QString &text, int &pos) const + { + if(text.isEmpty()) + return QValidator::Intermediate; + bool valid = false; + parse(text, &valid); + /* Make sure we return Intermediate so that fixup() is called on defocus */ + return valid ? QValidator::Intermediate : QValidator::Invalid; + } + + void fixup(QString &input) const + { + bool valid = false; + qint64 val = parse(input, &valid); + if(valid) + { + input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways); + lineEdit()->setText(input); + } + } + + qint64 value(bool *valid_out=0) const + { + return parse(text(), valid_out); + } + + void setValue(qint64 value) + { + lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways)); + emit valueChanged(); + } + + void stepBy(int steps) + { + bool valid = false; + qint64 val = value(&valid); + val = val + steps * singleStep; + val = qMin(qMax(val, Q_INT64_C(0)), BitcoinUnits::maxMoney()); + setValue(val); + } + + StepEnabled stepEnabled() const + { + StepEnabled rv = 0; + if(text().isEmpty()) // Allow step-up with empty field + return StepUpEnabled; + bool valid = false; + qint64 val = value(&valid); + if(valid) + { + if(val > 0) + rv |= StepDownEnabled; + if(val < BitcoinUnits::maxMoney()) + rv |= StepUpEnabled; + } + return rv; + } + + void setDisplayUnit(int unit) + { + bool valid = false; + qint64 val = value(&valid); + + currentUnit = unit; + + if(valid) + setValue(val); + else + clear(); + } + + void setSingleStep(qint64 step) + { + singleStep = step; + } + + QSize minimumSizeHint() const + { + if(cachedMinimumSizeHint.isEmpty()) + { + ensurePolished(); + + const QFontMetrics fm(fontMetrics()); + int h = lineEdit()->minimumSizeHint().height(); + int w = fm.width(BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways)); + w += 2; // cursor blinking space + + QStyleOptionSpinBox opt; + initStyleOption(&opt); + QSize hint(w, h); + QSize extra(35, 6); + opt.rect.setSize(hint + extra); + extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt, + QStyle::SC_SpinBoxEditField, this).size(); + // get closer to final result by repeating the calculation + opt.rect.setSize(hint + extra); + extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt, + QStyle::SC_SpinBoxEditField, this).size(); + hint += extra; + + opt.rect = rect(); + + cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this) + .expandedTo(QApplication::globalStrut()); + } + return cachedMinimumSizeHint; + } +private: + int currentUnit; + qint64 singleStep; + mutable QSize cachedMinimumSizeHint; + + /** + * Parse a string into a number of base monetary units and + * return validity. + * @note Must return 0 if !valid. + */ + qint64 parse(const QString &text, bool *valid_out=0) const + { + qint64 val = 0; + bool valid = BitcoinUnits::parse(currentUnit, text, &val); + if(valid) + { + if(val < 0 || val > BitcoinUnits::maxMoney()) + valid = false; + } + if(valid_out) + *valid_out = valid; + return valid ? val : 0; + } + +protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) + { + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + if (keyEvent->key() == Qt::Key_Comma) + { + // Translate a comma into a period + QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count()); + return QAbstractSpinBox::event(&periodKeyEvent); + } + } + return QAbstractSpinBox::event(event); + } + +signals: + void valueChanged(); +}; + +#include "bitcoinamountfield.moc" BitcoinAmountField::BitcoinAmountField(QWidget *parent) : QWidget(parent), - amount(0), - currentUnit(-1) + amount(0) { - nSingleStep = 100000; // satoshis - - amount = new QDoubleSpinBox(this); + amount = new AmountSpinBox(this); amount->setLocale(QLocale::c()); amount->installEventFilter(this); amount->setMaximumWidth(170); @@ -40,21 +207,13 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) : setFocusProxy(amount); // If one if the widgets changes, the combined content changes as well - connect(amount, SIGNAL(valueChanged(QString)), this, SIGNAL(textChanged())); + connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged())); connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int))); // Set default based on configuration unitChanged(unit->currentIndex()); } -void BitcoinAmountField::setText(const QString &text) -{ - if (text.isEmpty()) - amount->clear(); - else - amount->setValue(text.toDouble()); -} - void BitcoinAmountField::clear() { amount->clear(); @@ -63,16 +222,9 @@ void BitcoinAmountField::clear() bool BitcoinAmountField::validate() { - bool valid = true; - if (amount->value() == 0.0) - valid = false; - else if (!BitcoinUnits::parse(currentUnit, text(), 0)) - valid = false; - else if (amount->value() > BitcoinUnits::maxAmount(currentUnit)) - valid = false; - + bool valid = false; + value(&valid); setValid(valid); - return valid; } @@ -84,14 +236,6 @@ void BitcoinAmountField::setValid(bool valid) amount->setStyleSheet(STYLE_INVALID); } -QString BitcoinAmountField::text() const -{ - if (amount->text().isEmpty()) - return QString(); - else - return amount->text(); -} - bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::FocusIn) @@ -99,17 +243,6 @@ bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) // Clear invalid flag on focus setValid(true); } - else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) - { - QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); - if (keyEvent->key() == Qt::Key_Comma) - { - // Translate a comma into a period - QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count()); - QApplication::sendEvent(object, &periodKeyEvent); - return true; - } - } return QWidget::eventFilter(object, event); } @@ -122,18 +255,12 @@ QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) qint64 BitcoinAmountField::value(bool *valid_out) const { - qint64 val_out = 0; - bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out); - if (valid_out) - { - *valid_out = valid; - } - return val_out; + return amount->value(valid_out); } void BitcoinAmountField::setValue(qint64 value) { - setText(BitcoinUnits::format(currentUnit, value)); + amount->setValue(value); } void BitcoinAmountField::setReadOnly(bool fReadOnly) @@ -150,28 +277,7 @@ void BitcoinAmountField::unitChanged(int idx) // Determine new unit ID int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt(); - // Parse current value and convert to new unit - bool valid = false; - qint64 currentValue = value(&valid); - - currentUnit = newUnit; - - // Set max length after retrieving the value, to prevent truncation - amount->setDecimals(BitcoinUnits::decimals(currentUnit)); - amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals())); - amount->setSingleStep((double)nSingleStep / (double)BitcoinUnits::factor(currentUnit)); - - if (valid) - { - // If value was valid, re-place it in the widget with the new unit - setValue(currentValue); - } - else - { - // If current value is invalid, just clear field - setText(""); - } - setValid(true); + amount->setDisplayUnit(newUnit); } void BitcoinAmountField::setDisplayUnit(int newUnit) @@ -181,6 +287,5 @@ void BitcoinAmountField::setDisplayUnit(int newUnit) void BitcoinAmountField::setSingleStep(qint64 step) { - nSingleStep = step; - unitChanged(unit->currentIndex()); + amount->setSingleStep(step); } diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 521a9ed561..c713f5d687 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -8,17 +8,18 @@ #include <QWidget> QT_BEGIN_NAMESPACE -class QDoubleSpinBox; class QValueComboBox; QT_END_NAMESPACE +class AmountSpinBox; + /** Widget for entering bitcoin amounts. */ class BitcoinAmountField: public QWidget { Q_OBJECT - Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true) + Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY valueChanged USER true) public: explicit BitcoinAmountField(QWidget *parent = 0); @@ -49,20 +50,15 @@ public: QWidget *setupTabChain(QWidget *prev); signals: - void textChanged(); + void valueChanged(); protected: /** Intercept focus-in event and ',' key presses */ bool eventFilter(QObject *object, QEvent *event); private: - QDoubleSpinBox *amount; + AmountSpinBox *amount; QValueComboBox *unit; - int currentUnit; - qint64 nSingleStep; - - void setText(const QString &text); - QString text() const; private slots: void unitChanged(int idx); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6b3aa2a2df..5fc2f500b5 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -159,10 +159,13 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : labelEncryptionIcon = new QLabel(); labelConnectionsIcon = new QLabel(); labelBlocksIcon = new QLabel(); - frameBlocksLayout->addStretch(); - frameBlocksLayout->addWidget(unitDisplayControl); - frameBlocksLayout->addStretch(); - frameBlocksLayout->addWidget(labelEncryptionIcon); + if(enableWallet) + { + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(unitDisplayControl); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelEncryptionIcon); + } frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelConnectionsIcon); frameBlocksLayout->addStretch(); @@ -780,11 +783,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK))) buttons = QMessageBox::Ok; - // Ensure we get users attention, but only if main window is visible - // as we don't want to pop up the main window for messages that happen before - // initialization is finished. - if(!(style & CClientUIInterface::NOSHOWGUI)) - showNormalIfMinimized(); + showNormalIfMinimized(); QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this); int r = mBox.exec(); if (ret != NULL) @@ -921,6 +920,8 @@ void BitcoinGUI::setEncryptionStatus(int status) void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) { + if(!clientModel) + return; // activateWindow() (sometimes) helps with keyboard focus on Windows if (isHidden()) { @@ -1012,7 +1013,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel() setToolTip(tr("Unit to show amounts in. Click to select another unit.")); } -/** So that it responds to left-button clicks */ +/** So that it responds to button clicks */ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) { onDisplayUnitsClicked(event->pos()); @@ -1029,10 +1030,6 @@ void UnitDisplayStatusBarControl::createContextMenu() menu->addAction(menuAction); } connect(menu,SIGNAL(triggered(QAction*)),this,SLOT(onMenuSelection(QAction*))); - - // what happens on right click. - setContextMenuPolicy(Qt::CustomContextMenu); - connect(this,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(onDisplayUnitsClicked(const QPoint&))); } /** Lets the control know about the Options Model (and its signals) */ diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index bbc9b2e5af..6f506d3f25 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -4,6 +4,8 @@ #include "bitcoinunits.h" +#include "core.h" + #include <QStringList> BitcoinUnits::BitcoinUnits(QObject *parent): @@ -61,8 +63,8 @@ QString BitcoinUnits::description(int unit) switch(unit) { case BTC: return QString("Bitcoins"); - case mBTC: return QString("Milli-Bitcoins (1 / 1,000)"); - case uBTC: return QString("Micro-Bitcoins (1 / 1,000,000)"); + case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)"); + case uBTC: return QString("Micro-Bitcoins (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); default: return QString("???"); } } @@ -78,28 +80,6 @@ qint64 BitcoinUnits::factor(int unit) } } -qint64 BitcoinUnits::maxAmount(int unit) -{ - switch(unit) - { - case BTC: return Q_INT64_C(21000000); - case mBTC: return Q_INT64_C(21000000000); - case uBTC: return Q_INT64_C(21000000000000); - default: return 0; - } -} - -int BitcoinUnits::amountDigits(int unit) -{ - switch(unit) - { - case BTC: return 8; // 21,000,000 (# digits, without commas) - case mBTC: return 11; // 21,000,000,000 - case uBTC: return 14; // 21,000,000,000,000 - default: return 0; - } -} - int BitcoinUnits::decimals(int unit) { switch(unit) @@ -111,7 +91,7 @@ int BitcoinUnits::decimals(int unit) } } -QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) +QString BitcoinUnits::format(int unit, qint64 n, bool fPlus, SeparatorStyle separators) { // Note: not using straight sprintf here because we do NOT want // localized number formatting. @@ -125,11 +105,20 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) QString quotient_str = QString::number(quotient); QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); - // Right-trim excess zeros after the decimal point - int nTrim = 0; - for (int i = remainder_str.size()-1; i>=2 && (remainder_str.at(i) == '0'); --i) - ++nTrim; - remainder_str.chop(nTrim); + // Use SI-stule separators as these are locale indendent and can't be + // confused with the decimal marker. Rule is to use a thin space every + // three digits on *both* sides of the decimal point - but only if there + // are five or more digits + QChar thin_sp(THIN_SP_CP); + int q_size = quotient_str.size(); + if (separators == separatorAlways || (separators == separatorStandard && q_size > 4)) + for (int i = 3; i < q_size; i += 3) + quotient_str.insert(q_size - i, thin_sp); + + int r_size = remainder_str.size(); + if (separators == separatorAlways || (separators == separatorStandard && r_size > 4)) + for (int i = 3, adj = 0; i < r_size ; i += 3, adj++) + remainder_str.insert(i + adj, thin_sp); if (n < 0) quotient_str.insert(0, '-'); @@ -138,17 +127,43 @@ QString BitcoinUnits::format(int unit, qint64 n, bool fPlus) return quotient_str + QString(".") + remainder_str; } -QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign) + +// TODO: Review all remaining calls to BitcoinUnits::formatWithUnit to +// TODO: determine whether the output is used in a plain text context +// TODO: or an HTML context (and replace with +// TODO: BtcoinUnits::formatHtmlWithUnit in the latter case). Hopefully +// TODO: there aren't instances where the result could be used in +// TODO: either context. + +// NOTE: Using formatWithUnit in an HTML context risks wrapping +// quantities at the thousands separator. More subtly, it also results +// in a standard space rather than a thin space, due to a bug in Qt's +// XML whitespace canonicalisation +// +// Please take care to use formatHtmlWithUnit instead, when +// appropriate. + +QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators) +{ + return format(unit, amount, plussign, separators) + QString(" ") + name(unit); +} + +QString BitcoinUnits::formatHtmlWithUnit(int unit, qint64 amount, bool plussign, SeparatorStyle separators) { - return format(unit, amount, plussign) + QString(" ") + name(unit); + QString str(formatWithUnit(unit, amount, plussign, separators)); + str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML)); + return QString("<span style='white-space: nowrap;'>%1</span>").arg(str); } + bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out) { if(!valid(unit) || value.isEmpty()) return false; // Refuse to parse invalid unit or empty string int num_decimals = decimals(unit); - QStringList parts = value.split("."); + + // Ignore spaces and thin spaces when parsing + QStringList parts = removeSpaces(value).split("."); if(parts.size() > 2) { @@ -215,3 +230,8 @@ QVariant BitcoinUnits::data(const QModelIndex &index, int role) const } return QVariant(); } + +qint64 BitcoinUnits::maxMoney() +{ + return MAX_MONEY; +} diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index da34ed8976..be9dca6012 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -8,6 +8,37 @@ #include <QAbstractListModel> #include <QString> +// U+2009 THIN SPACE = UTF-8 E2 80 89 +#define REAL_THIN_SP_CP 0x2009 +#define REAL_THIN_SP_UTF8 "\xE2\x80\x89" +#define REAL_THIN_SP_HTML " " + +// U+200A HAIR SPACE = UTF-8 E2 80 8A +#define HAIR_SP_CP 0x200A +#define HAIR_SP_UTF8 "\xE2\x80\x8A" +#define HAIR_SP_HTML " " + +// U+2006 SIX-PER-EM SPACE = UTF-8 E2 80 86 +#define SIXPEREM_SP_CP 0x2006 +#define SIXPEREM_SP_UTF8 "\xE2\x80\x86" +#define SIXPEREM_SP_HTML " " + +// U+2007 FIGURE SPACE = UTF-8 E2 80 87 +#define FIGURE_SP_CP 0x2007 +#define FIGURE_SP_UTF8 "\xE2\x80\x87" +#define FIGURE_SP_HTML " " + +// QMessageBox seems to have a bug whereby it doesn't display thin/hair spaces +// correctly. Workaround is to display a space in a small font. If you +// change this, please test that it doesn't cause the parent span to start +// wrapping. +#define HTML_HACK_SP "<span style='white-space: nowrap; font-size: 6pt'> </span>" + +// Define THIN_SP_* variables to be our preferred type of thin space +#define THIN_SP_CP REAL_THIN_SP_CP +#define THIN_SP_UTF8 REAL_THIN_SP_UTF8 +#define THIN_SP_HTML HTML_HACK_SP + /** Bitcoin unit definitions. Encapsulates parsing and formatting and serves as list model for drop-down selection boxes. */ @@ -28,6 +59,13 @@ public: uBTC }; + enum SeparatorStyle + { + separatorNever, + separatorStandard, + separatorAlways + }; + //! @name Static API //! Unit conversion and formatting ///@{ @@ -44,16 +82,13 @@ public: static QString description(int unit); //! Number of Satoshis (1e-8) per unit static qint64 factor(int unit); - //! Max amount per unit - static qint64 maxAmount(int unit); - //! Number of amount digits (to represent max number of coins) - static int amountDigits(int unit); //! Number of decimals left static int decimals(int unit); //! Format as string - static QString format(int unit, qint64 amount, bool plussign=false); + static QString format(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard); //! Format as string (with unit) - static QString formatWithUnit(int unit, qint64 amount, bool plussign=false); + static QString formatWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard); + static QString formatHtmlWithUnit(int unit, qint64 amount, bool plussign=false, SeparatorStyle separators=separatorStandard); //! Parse string to coin amount static bool parse(int unit, const QString &value, qint64 *val_out); //! Gets title for amount column including current display unit if optionsModel reference available */ @@ -71,6 +106,19 @@ public: QVariant data(const QModelIndex &index, int role) const; ///@} + static QString removeSpaces(QString text) + { + text.remove(' '); + text.remove(QChar(THIN_SP_CP)); +#if (THIN_SP_CP != REAL_THIN_SP_CP) + text.remove(QChar(REAL_THIN_SP_CP)); +#endif + return text; + } + + //! Return maximum number of base units (Satoshis) + static qint64 maxMoney(); + private: QList<BitcoinUnits::Unit> unitlist; }; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index b4ddda3eaa..7b30f8de09 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -225,7 +225,7 @@ void CoinControlDialog::showMenu(const QPoint &point) // context menu action: copy amount void CoinControlDialog::copyAmount() { - GUIUtil::setClipboard(contextMenuItem->text(COLUMN_AMOUNT)); + GUIUtil::setClipboard(BitcoinUnits::removeSpaces(contextMenuItem->text(COLUMN_AMOUNT))); } // context menu action: copy label diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 696761e234..5ae4bc833d 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -23,10 +23,6 @@ static const int STATUSBAR_ICONSIZE = 16; #define COLOR_NEGATIVE QColor(255, 0, 0) /* Transaction list -- bare address (without label) */ #define COLOR_BAREADDRESS QColor(140, 140, 140) -/* Transaction list -- has conflicting transactions */ -#define COLOR_HASCONFLICTING QColor(255, 255, 255) -/* Transaction list -- has conflicting transactions - background */ -#define COLOR_HASCONFLICTING_BG QColor(192, 0, 0) /* Tooltips longer than this (in characters) are converted into rich text, so that they can be word-wrapped. diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 60a131df7e..33a50a078d 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -187,7 +187,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) if (info.amount) { - ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount)); + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::separatorNever)); paramCount++; } diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 1278f368cf..1c700b37ff 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -72,7 +72,7 @@ public: foreground = option.palette.color(QPalette::Text); } painter->setPen(foreground); - QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true); + QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways); if(!confirmed) { amountText = QString("[") + amountText + QString("]"); @@ -147,14 +147,14 @@ void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 currentWatchOnlyBalance = watchOnlyBalance; currentWatchUnconfBalance = watchUnconfBalance; currentWatchImmatureBalance = watchImmatureBalance; - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); - ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); - ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance)); - ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance)); - ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance)); - ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance)); - ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance)); - ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance)); + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance, false, BitcoinUnits::separatorAlways)); + ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance, false, BitcoinUnits::separatorAlways)); + ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance, false, BitcoinUnits::separatorAlways)); + ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance, false, BitcoinUnits::separatorAlways)); // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index b5a998f9f5..9e3976644e 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -79,10 +79,17 @@ QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) cons case Amount: if (rec->recipient.amount == 0 && role == Qt::DisplayRole) return tr("(no amount)"); + else if (role == Qt::EditRole) + return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount, false, BitcoinUnits::separatorNever); else return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount); } } + else if (role == Qt::TextAlignmentRole) + { + if (index.column() == Amount) + return (int)(Qt::AlignRight|Qt::AlignVCenter); + } return QVariant(); } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index e1f40ddd09..9b67f8125f 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -473,6 +473,10 @@ void RPCConsole::on_tabWidget_currentChanged(int index) { ui->lineEdit->setFocus(); } + else if(ui->tabWidget->widget(index) == ui->tab_peers) + { + initPeerTable(); + } } void RPCConsole::on_openDebugLogfileButton_clicked() @@ -648,11 +652,27 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *combinedStats) ui->peerBanScore->setText(tr("Fetching...")); } +void RPCConsole::initPeerTable() +{ + if (!clientModel) + return; + + // peerWidget needs a resize in case the dialog has non-default geometry + columnResizingFixer->stretchColumnWidth(PeerTableModel::Address); + + // start PeerTableModel auto refresh + clientModel->getPeerTableModel()->startAutoRefresh(1000); +} + // We override the virtual resizeEvent of the QWidget to adjust tables column // sizes as the tables width is proportional to the dialogs width. void RPCConsole::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); + + if (!clientModel) + return; + columnResizingFixer->stretchColumnWidth(PeerTableModel::Address); } @@ -660,17 +680,16 @@ void RPCConsole::showEvent(QShowEvent *event) { QWidget::showEvent(event); - // peerWidget needs a resize in case the dialog has non-default geometry - columnResizingFixer->stretchColumnWidth(PeerTableModel::Address); - - // start PeerTableModel auto refresh - clientModel->getPeerTableModel()->startAutoRefresh(1000); + initPeerTable(); } void RPCConsole::hideEvent(QHideEvent *event) { QWidget::hideEvent(event); + if (!clientModel) + return; + // stop PeerTableModel auto refresh clientModel->getPeerTableModel()->stopAutoRefresh(); } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 3aeff3eace..94672b30cc 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -47,6 +47,8 @@ protected: private: /** show detailed information on ui about selected node */ void updateNodeDetail(const CNodeCombinedStats *combinedStats); + /** initialize peer table */ + void initPeerTable(); enum ColumnWidths { diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 6f10ed5b0b..25e3d2a0dc 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -143,7 +143,7 @@ void SendCoinsDialog::on_sendButton_clicked() foreach(const SendCoinsRecipient &rcp, recipients) { // generate bold amount string - QString amount = "<b>" + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); + QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); amount.append("</b>"); // generate monospace address string QString address = "<span style='font-family: monospace;'>" + rcp.address; @@ -211,7 +211,7 @@ void SendCoinsDialog::on_sendButton_clicked() { // append fee string if a fee is required questionString.append("<hr /><span style='color:#aa0000;'>"); - questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); + questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); questionString.append("</span> "); questionString.append(tr("added as transaction fee")); } @@ -223,10 +223,10 @@ void SendCoinsDialog::on_sendButton_clicked() foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits()) { if(u != model->getOptionsModel()->getDisplayUnit()) - alternativeUnits.append(BitcoinUnits::formatWithUnit(u, totalAmount)); + alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount)); } questionString.append(tr("Total Amount %1 (= %2)") - .arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)) + .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)) .arg(alternativeUnits.join(" " + tr("or") + " "))); QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index e0f56f8cd2..52545c3857 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -34,6 +34,12 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) : GUIUtil::setupAddressWidget(ui->payTo, this); // just a label for displaying bitcoin address(es) ui->payTo_is->setFont(GUIUtil::bitcoinAddressFont()); + + // Connect signals + connect(ui->payAmount, SIGNAL(valueChanged()), this, SIGNAL(payAmountChanged())); + connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked())); + connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked())); + connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked())); } SendCoinsEntry::~SendCoinsEntry() @@ -72,11 +78,6 @@ void SendCoinsEntry::setModel(WalletModel *model) if (model && model->getOptionsModel()) connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - connect(ui->payAmount, SIGNAL(textChanged()), this, SIGNAL(payAmountChanged())); - connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked())); - connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked())); - connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked())); - clear(); } @@ -130,6 +131,13 @@ bool SendCoinsEntry::validate() retval = false; } + // Sending a zero amount is invalid + if (ui->payAmount->value(0) <= 0) + { + ui->payAmount->setValid(false); + retval = false; + } + // Reject dust outputs: if (retval && GUIUtil::isDust(ui->payTo->text(), ui->payAmount->value())) { ui->payAmount->setValid(false); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 36e4d50e30..ac1614efd0 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -136,7 +136,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco nUnmatured += wallet->GetCredit(txout, ISMINE_ALL); strHTML += "<b>" + tr("Credit") + ":</b> "; if (wtx.IsInMainChain()) - strHTML += BitcoinUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; + strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; else strHTML += "(" + tr("not accepted") + ")"; strHTML += "<br>"; @@ -146,7 +146,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // Credit // - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet) + "<br>"; } else { @@ -197,9 +197,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco } } - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "<br>"; + strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue) + "<br>"; if(toSelf) - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, txout.nValue) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "<br>"; } if (fAllToMe) @@ -207,13 +207,13 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // Payment to self int64_t nChange = wtx.GetChange(); int64_t nValue = nCredit - nChange; - strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>"; - strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>"; + strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "<br>"; + strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>"; } int64_t nTxFee = nDebit - wtx.GetValueOut(); if (nTxFee > 0) - strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "<br>"; + strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "<br>"; } else { @@ -222,14 +222,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // BOOST_FOREACH(const CTxIn& txin, wtx.vin) if (wallet->IsMine(txin)) - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; + strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if (wallet->IsMine(txout)) - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; } } - strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet, true) + "<br>"; + strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nNet, true) + "<br>"; // // Message @@ -275,10 +275,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "<hr><br>" + tr("Debug information") + "<br><br>"; BOOST_FOREACH(const CTxIn& txin, wtx.vin) if(wallet->IsMine(txin)) - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; + strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if(wallet->IsMine(txout)) - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>"; strHTML += "<br><b>" + tr("Transaction") + ":</b><br>"; strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); @@ -304,7 +304,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); } - strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(unit, vout.nValue); + strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>"; strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>"; } diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 7293029787..f9546fddb5 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -24,7 +24,7 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : typeFilter(ALL_TYPES), minAmount(0), limitRows(-1), - showInactive(false) + showInactive(true) { } @@ -39,7 +39,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); int status = index.data(TransactionTableModel::StatusRole).toInt(); - if(!showInactive && status == TransactionStatus::Conflicted && type == TransactionRecord::Other) + if(!showInactive && status == TransactionStatus::Conflicted) return false; if(!(TYPE(type) & typeFilter)) return false; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 7d29c212b3..d7bd25e08b 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -184,8 +184,6 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = chainActive.Height(); - status.hasConflicting = false; - if (!IsFinalTx(wtx, chainActive.Height() + 1)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) @@ -229,7 +227,6 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) if (status.depth < 0) { status.status = TransactionStatus::Conflicted; - status.hasConflicting = !(wtx.GetConflicts(false).empty()); } else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) { @@ -238,7 +235,6 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) else if (status.depth == 0) { status.status = TransactionStatus::Unconfirmed; - status.hasConflicting = !(wtx.GetConflicts(false).empty()); } else if (status.depth < RecommendedNumConfirmations) { @@ -249,13 +245,13 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.status = TransactionStatus::Confirmed; } } + } -bool TransactionRecord::statusUpdateNeeded(int64_t nConflictsReceived) +bool TransactionRecord::statusUpdateNeeded() { AssertLockHeld(cs_main); - return (status.cur_num_blocks != chainActive.Height() || - status.cur_num_conflicts != nConflictsReceived); + return status.cur_num_blocks != chainActive.Height(); } QString TransactionRecord::getTxID() const diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index d3cfa77d97..626b7654c6 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -19,17 +19,9 @@ class TransactionStatus { public: TransactionStatus(): - countsForBalance(false), - sortKey(""), - matures_in(0), - status(Offline), - hasConflicting(false), - depth(0), - open_for(0), - cur_num_blocks(-1), - cur_num_conflicts(-1) - { - } + countsForBalance(false), sortKey(""), + matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) + { } enum Status { Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/ @@ -59,10 +51,6 @@ public: /** @name Reported status @{*/ Status status; - - // Has conflicting transactions spending same prevout - bool hasConflicting; - qint64 depth; qint64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number of additional blocks that need to be mined before @@ -71,10 +59,6 @@ public: /** Current number of blocks (to know whether cached status is still valid) */ int cur_num_blocks; - - /** Number of conflicts received into wallet as of last status update */ - int64_t cur_num_conflicts; - }; /** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has @@ -152,7 +136,7 @@ public: /** Return whether a status update is needed. */ - bool statusUpdateNeeded(int64_t nConflictsReceived); + bool statusUpdateNeeded(); }; #endif // TRANSACTIONRECORD_H diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 3605cc1bad..4825713b69 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -5,7 +5,6 @@ #include "transactiontablemodel.h" #include "addresstablemodel.h" -#include "bitcoinunits.h" #include "guiconstants.h" #include "guiutil.h" #include "optionsmodel.h" @@ -168,7 +167,8 @@ public: parent->endRemoveRows(); break; case CT_UPDATED: - emit parent->dataChanged(parent->index(lowerIndex, parent->Status), parent->index(upperIndex-1, parent->Amount)); + // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for + // visible transactions. break; } } @@ -189,21 +189,20 @@ public: // stuck if the core is holding the locks for a longer time - for // example, during a wallet rescan. // - // If a status update is needed (blocks or conflicts came in since last check), - // update the status of this transaction from the wallet. Otherwise, + // If a status update is needed (blocks came in since last check), + // update the status of this transaction from the wallet. Otherwise, // simply re-use the cached status. TRY_LOCK(cs_main, lockMain); if(lockMain) { TRY_LOCK(wallet->cs_wallet, lockWallet); - if(lockWallet && rec->statusUpdateNeeded(wallet->nConflictsReceived)) + if(lockWallet && rec->statusUpdateNeeded()) { std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash); if(mi != wallet->mapWallet.end()) { rec->updateStatus(mi->second); - rec->status.cur_num_conflicts = wallet->nConflictsReceived; } } } @@ -369,8 +368,6 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const return tr("Payment to yourself"); case TransactionRecord::Generated: return tr("Mined"); - case TransactionRecord::Other: - return tr("Other"); default: return QString(); } @@ -436,9 +433,9 @@ QVariant TransactionTableModel::addressColor(const TransactionRecord *wtx) const return QVariant(); } -QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed) const +QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators) const { - QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit); + QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit, false, separators); if(showUnconfirmed) { if(!wtx->status.countsForBalance) @@ -523,7 +520,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case ToAddress: return formatTxToAddress(rec, false); case Amount: - return formatTxAmount(rec); + return formatTxAmount(rec, true, BitcoinUnits::separatorAlways); } break; case Qt::EditRole: @@ -546,13 +543,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return formatTooltip(rec); case Qt::TextAlignmentRole: return column_alignments[index.column()]; - case Qt::BackgroundColorRole: - if (rec->status.hasConflicting) - return COLOR_HASCONFLICTING_BG; - break; case Qt::ForegroundRole: - if (rec->status.hasConflicting) - return COLOR_HASCONFLICTING; // Non-confirmed (but not immature) as transactions are grey if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature) { @@ -586,7 +577,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case ConfirmedRole: return rec->status.countsForBalance; case FormattedAmountRole: - return formatTxAmount(rec, false); + // Used for copy/export, so don't include separators + return formatTxAmount(rec, false, BitcoinUnits::separatorNever); case StatusRole: return rec->status.status; } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index e8b6ed065d..ad88d14a90 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -8,6 +8,8 @@ #include <QAbstractTableModel> #include <QStringList> +#include "bitcoinunits.h" + class TransactionRecord; class TransactionTablePriv; class WalletModel; @@ -78,7 +80,7 @@ private: QString formatTxDate(const TransactionRecord *wtx) const; QString formatTxType(const TransactionRecord *wtx) const; QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const; - QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true) const; + QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard) const; QString formatTooltip(const TransactionRecord *rec) const; QVariant txStatusDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const; diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index d6d210a561..7e8b71d8ea 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -123,6 +123,8 @@ TransactionView::TransactionView(QWidget *parent) : view->setTabKeyNavigation(false); view->setContextMenuPolicy(Qt::CustomContextMenu); + view->installEventFilter(this); + transactionView = view; // Actions @@ -480,3 +482,22 @@ void TransactionView::resizeEvent(QResizeEvent* event) QWidget::resizeEvent(event); columnResizingFixer->stretchColumnWidth(TransactionTableModel::ToAddress); } + +// Need to override default Ctrl+C action for amount as default behaviour is just to copy DisplayRole text +bool TransactionView::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) + { + QKeyEvent *ke = static_cast<QKeyEvent *>(event); + if (ke->key() == Qt::Key_C && ke->modifiers().testFlag(Qt::ControlModifier)) + { + QModelIndex i = this->transactionView->currentIndex(); + if (i.isValid() && i.column() == TransactionTableModel::Amount) + { + GUIUtil::setClipboard(i.data(TransactionTableModel::FormattedAmountRole).toString()); + return true; + } + } + } + return QWidget::eventFilter(obj, event); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 7a89fa11ca..618efbc565 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -8,6 +8,7 @@ #include "guiutil.h" #include <QWidget> +#include <QKeyEvent> class TransactionFilterProxy; class WalletModel; @@ -78,6 +79,8 @@ private: virtual void resizeEvent(QResizeEvent* event); + bool eventFilter(QObject *obj, QEvent *event); + private slots: void contextualMenu(const QPoint &); void dateRangeChanged(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 7317c32766..0ad123f39d 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -35,6 +35,8 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) { + fProcessingQueuedTransactions = false; + addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); @@ -162,14 +164,6 @@ void WalletModel::checkBalanceChanged() void WalletModel::updateTransaction(const QString &hash, int status) { - if (status == CT_GOT_CONFLICT) - { - emit message(tr("Conflict Received"), - tr("WARNING: Transaction may never be confirmed. Its input was seen being spent by another transaction on the network. Wait for confirmation!"), - CClientUIInterface::MSG_WARNING); - return; - } - if(transactionTableModel) transactionTableModel->updateTransaction(hash, status); @@ -492,8 +486,15 @@ static void ShowProgress(WalletModel *walletmodel, const std::string &title, int if (nProgress == 100) { fQueueNotifications = false; - BOOST_FOREACH(const PAIRTYPE(uint256, ChangeType)& notification, vQueueNotifications) - NotifyTransactionChanged(walletmodel, NULL, notification.first, notification.second); + if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons + QMetaObject::invokeMethod(walletmodel, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true)); + for (unsigned int i = 0; i < vQueueNotifications.size(); ++i) + { + if (vQueueNotifications.size() - i <= 10) + QMetaObject::invokeMethod(walletmodel, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false)); + + NotifyTransactionChanged(walletmodel, NULL, vQueueNotifications[i].first, vQueueNotifications[i].second); + } std::vector<std::pair<uint256, ChangeType> >().swap(vQueueNotifications); // clear } } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 7ad54ff8e6..2bb91d85a9 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -133,6 +133,7 @@ public: qint64 getWatchImmatureBalance() const; int getNumTransactions() const; EncryptionStatus getEncryptionStatus() const; + bool processingQueuedTransactions() { return fProcessingQueuedTransactions; } // Check address for validity bool validateAddress(const QString &address); @@ -196,6 +197,7 @@ public: private: CWallet *wallet; + bool fProcessingQueuedTransactions; // Wallet has an options model for wallet-specific options // (transaction fee, for example) @@ -256,6 +258,8 @@ public slots: void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ void pollBalanceChanged(); + /* Needed to update fProcessingQueuedTransactions through a QueuedConnection */ + void setProcessingQueuedTransactions(bool value) { fProcessingQueuedTransactions = value; } }; #endif // WALLETMODEL_H diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 1cef48344f..b40ddc0a2f 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -137,7 +137,7 @@ void WalletView::setWalletModel(WalletModel *walletModel) 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()) + if (!walletModel || walletModel->processingQueuedTransactions() || !clientModel || clientModel->inInitialBlockDownload()) return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); diff --git a/src/random.cpp b/src/random.cpp new file mode 100644 index 0000000000..0d20d205ac --- /dev/null +++ b/src/random.cpp @@ -0,0 +1,139 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 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 "random.h" + +#ifdef WIN32 +#include "compat.h" // for Windows API +#endif +#include "util.h" // for LogPrint() + +#ifndef WIN32 +#include <sys/time.h> +#endif + +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/rand.h> + +static inline int64_t GetPerformanceCounter() +{ + int64_t nCounter = 0; +#ifdef WIN32 + QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); +#else + timeval t; + gettimeofday(&t, NULL); + nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec); +#endif + return nCounter; +} + +void RandAddSeed() +{ + // Seed with CPU performance counter + int64_t nCounter = GetPerformanceCounter(); + RAND_add(&nCounter, sizeof(nCounter), 1.5); + OPENSSL_cleanse((void*)&nCounter, sizeof(nCounter)); +} + +void RandAddSeedPerfmon() +{ + RandAddSeed(); + + // This can take up to 2 seconds, so only do it every 10 minutes + static int64_t nLastPerfmon; + if (GetTime() < nLastPerfmon + 10 * 60) + return; + nLastPerfmon = GetTime(); + +#ifdef WIN32 + // Don't need this on Linux, OpenSSL automatically uses /dev/urandom + // Seed with the entire set of perfmon data + std::vector <unsigned char> vData(250000,0); + long ret = 0; + unsigned long nSize = 0; + const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data + while (true) + { + nSize = vData.size(); + ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, begin_ptr(vData), &nSize); + if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) + break; + vData.resize(std::max((vData.size()*3)/2, nMaxSize)); // Grow size of buffer exponentially + } + RegCloseKey(HKEY_PERFORMANCE_DATA); + if (ret == ERROR_SUCCESS) + { + RAND_add(begin_ptr(vData), nSize, nSize/100.0); + OPENSSL_cleanse(begin_ptr(vData), nSize); + LogPrint("rand", "%s: %lu bytes\n", __func__, nSize); + } else { + static bool warned = false; // Warn only once + if (!warned) + { + LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret); + warned = true; + } + } +#endif +} + +bool GetRandBytes(unsigned char *buf, int num) +{ + if (RAND_bytes(buf, num) != 1) { + LogPrintf("%s: OpenSSL RAND_bytes() failed with error: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL)); + return false; + } + return true; +} + +uint64_t GetRand(uint64_t nMax) +{ + if (nMax == 0) + return 0; + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax; + uint64_t nRand = 0; + do { + GetRandBytes((unsigned char*)&nRand, sizeof(nRand)); + } while (nRand >= nRange); + return (nRand % nMax); +} + +int GetRandInt(int nMax) +{ + return GetRand(nMax); +} + +uint256 GetRandHash() +{ + uint256 hash; + GetRandBytes((unsigned char*)&hash, sizeof(hash)); + return hash; +} + +uint32_t insecure_rand_Rz = 11; +uint32_t insecure_rand_Rw = 11; +void seed_insecure_rand(bool fDeterministic) +{ + // The seed values have some unlikely fixed points which we avoid. + if(fDeterministic) + { + insecure_rand_Rz = insecure_rand_Rw = 11; + } else { + uint32_t tmp; + do { + GetRandBytes((unsigned char*)&tmp, 4); + } while(tmp == 0 || tmp == 0x9068ffffU); + insecure_rand_Rz = tmp; + do { + GetRandBytes((unsigned char*)&tmp, 4); + } while(tmp == 0 || tmp == 0x464fffffU); + insecure_rand_Rw = tmp; + } +} diff --git a/src/random.h b/src/random.h new file mode 100644 index 0000000000..a599b08478 --- /dev/null +++ b/src/random.h @@ -0,0 +1,49 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 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_RANDOM_H +#define BITCOIN_RANDOM_H + +#include "uint256.h" + +#include <stdint.h> + +/** + * Seed OpenSSL PRNG with additional entropy data + */ +void RandAddSeed(); +void RandAddSeedPerfmon(); + +/** + * Functions to gather random data via the OpenSSL PRNG + */ +bool GetRandBytes(unsigned char *buf, int num); +uint64_t GetRand(uint64_t nMax); +int GetRandInt(int nMax); +uint256 GetRandHash(); + +/** + * Seed insecure_rand using the random pool. + * @param Deterministic Use a determinstic seed + */ +void seed_insecure_rand(bool fDeterministic = false); + +/** + * MWC RNG of George Marsaglia + * This is intended to be fast. It has a period of 2^59.3, though the + * least significant 16 bits only have a period of about 2^30.1. + * + * @return random value + */ +extern uint32_t insecure_rand_Rz; +extern uint32_t insecure_rand_Rw; +static inline uint32_t insecure_rand(void) +{ + insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16); + insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16); + return (insecure_rand_Rw << 16) + insecure_rand_Rz; +} + +#endif // BITCOIN_RANDOM_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index a67f266a13..253693e624 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -276,7 +276,9 @@ Value getblock(const Array& params, bool fHelp) CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; - ReadBlockFromDisk(block, pblockindex); + + if(!ReadBlockFromDisk(block, pblockindex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); if (!fVerbose) { diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 4193f41b49..ff2361482b 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -72,16 +72,17 @@ Value importprivkey(const Array& params, bool fHelp) "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" "\nArguments:\n" "1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n" - "2. \"label\" (string, optional) an optional label\n" + "2. \"label\" (string, optional, default=\"\") An optional label\n" "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" + "\nNote: This call can take minutes to complete if rescan is true.\n" "\nExamples:\n" "\nDump a private key\n" + HelpExampleCli("dumpprivkey", "\"myaddress\"") + - "\nImport the private key\n" + "\nImport the private key with rescan\n" + HelpExampleCli("importprivkey", "\"mykey\"") + - "\nImport using a label\n" + "\nImport using a label and without rescan\n" + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") + - "\nAs a json rpc call\n" + "\nAs a JSON-RPC call\n" + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false") ); @@ -137,8 +138,21 @@ Value importaddress(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 3) throw runtime_error( - "importaddress <address> [label] [rescan=true]\n" - "Adds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend."); + "importaddress \"address\" ( \"label\" rescan )\n" + "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The address\n" + "2. \"label\" (string, optional, default=\"\") An optional label\n" + "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" + "\nNote: This call can take minutes to complete if rescan is true.\n" + "\nExamples:\n" + "\nImport an address with rescan\n" + + HelpExampleCli("importaddress", "\"myaddress\"") + + "\nImport using a label without rescan\n" + + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") + + "\nAs a JSON-RPC call\n" + + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false") + ); CScript script; diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index c7621dc137..6f72ea7404 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -324,6 +324,7 @@ Value getblocktemplate(const Array& params, bool fHelp) ); std::string strMode = "template"; + Value lpval = Value::null; if (params.size() > 0) { const Object& oparam = params[0].get_obj(); @@ -336,6 +337,7 @@ Value getblocktemplate(const Array& params, bool fHelp) } else throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + lpval = find_value(oparam, "longpollid"); } if (strMode != "template") @@ -347,8 +349,63 @@ Value getblocktemplate(const Array& params, bool fHelp) if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks..."); - // Update block static unsigned int nTransactionsUpdatedLast; + + if (lpval.type() != null_type) + { + // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions + uint256 hashWatchedChain; + boost::system_time checktxtime; + unsigned int nTransactionsUpdatedLastLP; + + if (lpval.type() == str_type) + { + // Format: <hashBestChain><nTransactionsUpdatedLast> + std::string lpstr = lpval.get_str(); + + hashWatchedChain.SetHex(lpstr.substr(0, 64)); + nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); + } + else + { + // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier + hashWatchedChain = chainActive.Tip()->GetBlockHash(); + nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; + } + + // Release the wallet and main lock while waiting +#ifdef ENABLE_WALLET + if(pwalletMain) + LEAVE_CRITICAL_SECTION(pwalletMain->cs_wallet); +#endif + LEAVE_CRITICAL_SECTION(cs_main); + { + checktxtime = boost::get_system_time() + boost::posix_time::minutes(1); + + boost::unique_lock<boost::mutex> lock(csBestBlock); + while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) + { + if (!cvBlockChange.timed_wait(lock, checktxtime)) + { + // Timeout: Check transactions for update + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) + break; + checktxtime += boost::posix_time::seconds(10); + } + } + } + ENTER_CRITICAL_SECTION(cs_main); +#ifdef ENABLE_WALLET + if(pwalletMain) + ENTER_CRITICAL_SECTION(pwalletMain->cs_wallet); +#endif + + if (!IsRPCRunning()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); + // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? + } + + // Update block static CBlockIndex* pindexPrev; static int64_t nStart; static CBlockTemplate* pblocktemplate; @@ -436,6 +493,7 @@ Value getblocktemplate(const Array& params, bool fHelp) result.push_back(Pair("transactions", transactions)); result.push_back(Pair("coinbaseaux", aux)); result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); + result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); result.push_back(Pair("target", hashTarget.GetHex())); result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); result.push_back(Pair("mutable", aMutable)); diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index cf2c293caf..88e7c4ab07 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -79,6 +79,7 @@ Value getpeerinfo(const Array& params, bool fHelp) "\nbResult:\n" "[\n" " {\n" + " \"id\": n, (numeric) Peer index\n" " \"addr\":\"host:port\", (string) The ip address and port of the peer\n" " \"addrlocal\":\"ip:port\", (string) local address\n" " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n" @@ -97,8 +98,7 @@ Value getpeerinfo(const Array& params, bool fHelp) " \"syncnode\" : true|false (booleamn) if sync node\n" " }\n" " ,...\n" - "}\n" - + "]\n" "\nExamples:\n" + HelpExampleCli("getpeerinfo", "") + HelpExampleRpc("getpeerinfo", "") @@ -113,6 +113,7 @@ Value getpeerinfo(const Array& params, bool fHelp) Object obj; CNodeStateStats statestats; bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); + obj.push_back(Pair("id", stats.nodeid)); obj.push_back(Pair("addr", stats.addrName)); if (!(stats.addrLocal.empty())) obj.push_back(Pair("addrlocal", stats.addrLocal)); @@ -137,6 +138,7 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("syncheight", statestats.nSyncHeight)); } obj.push_back(Pair("syncnode", stats.fSyncNode)); + obj.push_back(Pair("whitelisted", stats.fWhitelisted)); ret.push_back(obj); } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 2306b1b883..1efe38e830 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -523,7 +523,7 @@ Value signrawtransaction(const Array& params, bool fHelp) " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n, (numeric, required) The output number\n" " \"scriptPubKey\": \"hex\", (string, required) script key\n" - " \"redeemScript\": \"hex\" (string, required) redeem script\n" + " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n" " }\n" " ,...\n" " ]\n" diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 18fa075101..5deb6a4e08 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -32,6 +32,7 @@ using namespace std; static std::string strRPCUserColonPass; +static bool fRPCRunning = false; // These are created by StartRPCThreads, destroyed in StopRPCThreads static asio::io_service* rpc_io_service = NULL; static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers; @@ -232,8 +233,8 @@ static const CRPCCommand vRPCCommands[] = { "getblockchaininfo", &getblockchaininfo, true, false, false }, { "getbestblockhash", &getbestblockhash, true, false, false }, { "getblockcount", &getblockcount, true, false, false }, - { "getblock", &getblock, false, false, false }, - { "getblockhash", &getblockhash, false, false, false }, + { "getblock", &getblock, true, false, false }, + { "getblockhash", &getblockhash, true, false, false }, { "getdifficulty", &getdifficulty, true, false, false }, { "getrawmempool", &getrawmempool, true, false, false }, { "gettxout", &gettxout, true, false, false }, @@ -245,32 +246,32 @@ static const CRPCCommand vRPCCommands[] = { "getmininginfo", &getmininginfo, true, false, false }, { "getnetworkhashps", &getnetworkhashps, true, false, false }, { "prioritisetransaction", &prioritisetransaction, true, false, false }, - { "submitblock", &submitblock, false, true, false }, + { "submitblock", &submitblock, true, true, false }, /* Raw transactions */ - { "createrawtransaction", &createrawtransaction, false, false, false }, - { "decoderawtransaction", &decoderawtransaction, false, false, false }, - { "decodescript", &decodescript, false, false, false }, - { "getrawtransaction", &getrawtransaction, false, false, false }, + { "createrawtransaction", &createrawtransaction, true, false, false }, + { "decoderawtransaction", &decoderawtransaction, true, false, false }, + { "decodescript", &decodescript, true, false, false }, + { "getrawtransaction", &getrawtransaction, true, false, false }, { "sendrawtransaction", &sendrawtransaction, false, false, false }, { "signrawtransaction", &signrawtransaction, false, false, false }, /* uses wallet if enabled */ /* Utility functions */ { "createmultisig", &createmultisig, true, true , false }, { "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */ - { "verifymessage", &verifymessage, false, false, false }, + { "verifymessage", &verifymessage, true, false, false }, { "estimatefee", &estimatefee, true, true, false }, { "estimatepriority", &estimatepriority, true, true, false }, #ifdef ENABLE_WALLET /* Wallet */ - { "addmultisigaddress", &addmultisigaddress, false, false, true }, + { "addmultisigaddress", &addmultisigaddress, true, false, true }, { "backupwallet", &backupwallet, true, false, true }, { "dumpprivkey", &dumpprivkey, true, false, true }, { "dumpwallet", &dumpwallet, true, false, true }, - { "encryptwallet", &encryptwallet, false, false, true }, + { "encryptwallet", &encryptwallet, true, false, true }, { "getaccountaddress", &getaccountaddress, true, false, true }, - { "getaccount", &getaccount, false, false, true }, + { "getaccount", &getaccount, true, false, true }, { "getaddressesbyaccount", &getaddressesbyaccount, true, false, true }, { "getbalance", &getbalance, false, false, true }, { "getnewaddress", &getnewaddress, true, false, true }, @@ -279,10 +280,10 @@ static const CRPCCommand vRPCCommands[] = { "getreceivedbyaddress", &getreceivedbyaddress, false, false, true }, { "gettransaction", &gettransaction, false, false, true }, { "getunconfirmedbalance", &getunconfirmedbalance, false, false, true }, - { "getwalletinfo", &getwalletinfo, true, false, true }, - { "importprivkey", &importprivkey, false, false, true }, - { "importwallet", &importwallet, false, false, true }, - { "importaddress", &importaddress, false, false, true }, + { "getwalletinfo", &getwalletinfo, false, false, true }, + { "importprivkey", &importprivkey, true, false, true }, + { "importwallet", &importwallet, true, false, true }, + { "importaddress", &importaddress, true, false, true }, { "keypoolrefill", &keypoolrefill, true, false, true }, { "listaccounts", &listaccounts, false, false, true }, { "listaddressgroupings", &listaddressgroupings, false, false, true }, @@ -292,16 +293,16 @@ static const CRPCCommand vRPCCommands[] = { "listsinceblock", &listsinceblock, false, false, true }, { "listtransactions", &listtransactions, false, false, true }, { "listunspent", &listunspent, false, false, true }, - { "lockunspent", &lockunspent, false, false, true }, + { "lockunspent", &lockunspent, true, false, true }, { "move", &movecmd, false, false, true }, { "sendfrom", &sendfrom, false, false, true }, { "sendmany", &sendmany, false, false, true }, { "sendtoaddress", &sendtoaddress, false, false, true }, { "setaccount", &setaccount, true, false, true }, - { "settxfee", &settxfee, false, false, true }, - { "signmessage", &signmessage, false, false, true }, + { "settxfee", &settxfee, true, false, true }, + { "signmessage", &signmessage, true, false, true }, { "walletlock", &walletlock, true, false, true }, - { "walletpassphrasechange", &walletpassphrasechange, false, false, true }, + { "walletpassphrasechange", &walletpassphrasechange, true, false, true }, { "walletpassphrase", &walletpassphrase, true, false, true }, /* Wallet-enabled mining */ @@ -531,7 +532,7 @@ void StartRPCThreads() (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword()) { unsigned char rand_pwd[32]; - RAND_bytes(rand_pwd, 32); + GetRandBytes(rand_pwd, 32); string strWhatAmI = "To use bitcoind"; if (mapArgs.count("-server")) strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); @@ -659,6 +660,7 @@ void StartRPCThreads() rpc_worker_group = new boost::thread_group(); for (int i = 0; i < GetArg("-rpcthreads", 4); i++) rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); + fRPCRunning = true; } void StartDummyRPCThread() @@ -671,12 +673,15 @@ void StartDummyRPCThread() rpc_dummy_work = new asio::io_service::work(*rpc_io_service); rpc_worker_group = new boost::thread_group(); rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); + fRPCRunning = true; } } void StopRPCThreads() { if (rpc_io_service == NULL) return; + // Set this to false first, so that longpolling loops will exit when woken up + fRPCRunning = false; // First, cancel all timers and acceptors // This is not done automatically by ->stop(), and in some cases the destructor of @@ -698,6 +703,7 @@ void StopRPCThreads() deadlineTimers.clear(); rpc_io_service->stop(); + cvBlockChange.notify_all(); if (rpc_worker_group != NULL) rpc_worker_group->join_all(); delete rpc_dummy_work; rpc_dummy_work = NULL; @@ -706,6 +712,11 @@ void StopRPCThreads() delete rpc_io_service; rpc_io_service = NULL; } +bool IsRPCRunning() +{ + return fRPCRunning; +} + void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func) { if (!err) diff --git a/src/rpcserver.h b/src/rpcserver.h index e32eb975a1..31badadd6d 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -40,6 +40,8 @@ void StartRPCThreads(); void StartDummyRPCThread(); /* Stop RPC threads */ void StopRPCThreads(); +/* Query whether RPC is running */ +bool IsRPCRunning(); /* Type-check arguments; throws JSONRPCError if wrong type given. Does not check that diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 1e46129065..5b83fe900e 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -58,10 +58,6 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) conflicts.push_back(conflict.GetHex()); entry.push_back(Pair("walletconflicts", conflicts)); - Array respends; - BOOST_FOREACH(const uint256& respend, wtx.GetConflicts(false)) - respends.push_back(respend.GetHex()); - entry.push_back(Pair("respendsobserved", respends)); entry.push_back(Pair("time", wtx.GetTxTime())); entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) @@ -201,7 +197,7 @@ Value getrawchangeaddress(const Array& params, bool fHelp) CReserveKey reservekey(pwalletMain); CPubKey vchPubKey; if (!reservekey.GetReservedKey(vchPubKey)) - throw JSONRPCError(RPC_WALLET_ERROR, "Error: Unable to obtain key for change"); + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); reservekey.KeepKey(); @@ -641,16 +637,16 @@ Value getbalance(const Array& params, bool fHelp) int64_t allFee; string strSentAccount; - list<pair<CTxDestination, int64_t> > listReceived; - list<pair<CTxDestination, int64_t> > listSent; + list<COutputEntry> listReceived; + list<COutputEntry> listSent; wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); if (wtx.GetDepthInMainChain() >= nMinDepth) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived) - nBalance += r.second; + BOOST_FOREACH(const COutputEntry& r, listReceived) + nBalance += r.amount; } - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent) - nBalance -= r.second; + BOOST_FOREACH(const COutputEntry& s, listSent) + nBalance -= s.amount; nBalance -= allFee; } return ValueFromAmount(nBalance); @@ -1133,8 +1129,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { int64_t nFee; string strSentAccount; - list<pair<CTxDestination, int64_t> > listReceived; - list<pair<CTxDestination, int64_t> > listSent; + list<COutputEntry> listReceived; + list<COutputEntry> listSent; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); @@ -1144,15 +1140,16 @@ 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_t)& s, listSent) + BOOST_FOREACH(const COutputEntry& s, listSent) { Object entry; - if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & ISMINE_WATCH_ONLY)) + if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) entry.push_back(Pair("involvesWatchonly", true)); entry.push_back(Pair("account", strSentAccount)); - MaybePushAddress(entry, s.first); + MaybePushAddress(entry, s.destination); entry.push_back(Pair("category", "send")); - entry.push_back(Pair("amount", ValueFromAmount(-s.second))); + entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); + entry.push_back(Pair("vout", s.vout)); entry.push_back(Pair("fee", ValueFromAmount(-nFee))); if (fLong) WalletTxToJSON(wtx, entry); @@ -1163,18 +1160,18 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe // Received if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) + BOOST_FOREACH(const COutputEntry& r, listReceived) { string account; - if (pwalletMain->mapAddressBook.count(r.first)) - account = pwalletMain->mapAddressBook[r.first].name; + if (pwalletMain->mapAddressBook.count(r.destination)) + account = pwalletMain->mapAddressBook[r.destination].name; if (fAllAccounts || (account == strAccount)) { Object entry; - if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & ISMINE_WATCH_ONLY)) + if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) entry.push_back(Pair("involvesWatchonly", true)); entry.push_back(Pair("account", account)); - MaybePushAddress(entry, r.first); + MaybePushAddress(entry, r.destination); if (wtx.IsCoinBase()) { if (wtx.GetDepthInMainChain() < 1) @@ -1188,7 +1185,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { entry.push_back(Pair("category", "receive")); } - entry.push_back(Pair("amount", ValueFromAmount(r.second))); + entry.push_back(Pair("amount", ValueFromAmount(r.amount))); + entry.push_back(Pair("vout", r.vout)); if (fLong) WalletTxToJSON(wtx, entry); ret.push_back(entry); @@ -1240,6 +1238,7 @@ Value listtransactions(const Array& params, bool fHelp) " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the\n" " 'move' category for moves outbound. It is positive for the 'receive' category,\n" " and for the 'move' category for inbound funds.\n" + " \"vout\" : n, (numeric) the vout value\n" " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the \n" " 'send' category of transactions.\n" " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" @@ -1249,12 +1248,6 @@ Value listtransactions(const Array& params, bool fHelp) " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n" " category of transactions.\n" " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" - " \"walletconflicts\" : [\n" - " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" - " ],\n" - " \"respendsobserved\" : [\n" - " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" - " ],\n" " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" " for 'send' and 'receive' category of transactions.\n" @@ -1375,22 +1368,22 @@ Value listaccounts(const Array& params, bool fHelp) const CWalletTx& wtx = (*it).second; int64_t nFee; string strSentAccount; - list<pair<CTxDestination, int64_t> > listReceived; - list<pair<CTxDestination, int64_t> > listSent; + list<COutputEntry> listReceived; + list<COutputEntry> listSent; int nDepth = wtx.GetDepthInMainChain(); if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) continue; wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); mapAccountBalances[strSentAccount] -= nFee; - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) - mapAccountBalances[strSentAccount] -= s.second; + BOOST_FOREACH(const COutputEntry& s, listSent) + mapAccountBalances[strSentAccount] -= s.amount; if (nDepth >= nMinDepth) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) - if (pwalletMain->mapAddressBook.count(r.first)) - mapAccountBalances[pwalletMain->mapAddressBook[r.first].name] += r.second; + BOOST_FOREACH(const COutputEntry& r, listReceived) + if (pwalletMain->mapAddressBook.count(r.destination)) + mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount; else - mapAccountBalances[""] += r.second; + mapAccountBalances[""] += r.amount; } } @@ -1424,18 +1417,13 @@ Value listsinceblock(const Array& params, bool fHelp) " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the 'move' category for moves \n" " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" + " \"vout\" : n, (numeric) the vout value\n" " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the 'send' category of transactions.\n" " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n" " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" - " \"walletconflicts\" : [\n" - " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" - " ],\n" - " \"respendsobserved\" : [\n" - " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" - " ],\n" " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" @@ -1514,12 +1502,6 @@ Value gettransaction(const Array& params, bool fHelp) " \"blockindex\" : xx, (numeric) The block index\n" " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" " \"txid\" : \"transactionid\", (string) The transaction id.\n" - " \"walletconflicts\" : [\n" - " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" - " ],\n" - " \"respendsobserved\" : [\n" - " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" - " ],\n" " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" " \"details\" : [\n" @@ -1528,6 +1510,7 @@ Value gettransaction(const Array& params, bool fHelp) " \"address\" : \"bitcoinaddress\", (string) The bitcoin address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" " \"amount\" : x.xxx (numeric) The amount in btc\n" + " \"vout\" : n, (numeric) the vout value\n" " }\n" " ,...\n" " ],\n" diff --git a/src/script.cpp b/src/script.cpp index 238a25e72d..39ae001db8 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -5,13 +5,14 @@ #include "script.h" +#include "crypto/ripemd160.h" +#include "crypto/sha1.h" +#include "crypto/sha2.h" #include "core.h" #include "hash.h" #include "key.h" #include "keystore.h" -#include "crypto/sha1.h" -#include "crypto/sha2.h" -#include "crypto/ripemd160.h" +#include "random.h" #include "sync.h" #include "uint256.h" #include "util.h" @@ -1097,7 +1098,6 @@ uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsig // Valid signature cache, to avoid doing expensive ECDSA signature checking // twice for every transaction (once when accepted into memory pool, and // again when accepted into the block chain) - class CSignatureCache { private: diff --git a/src/serialize.h b/src/serialize.h index 5ac85554c6..f876efd9b5 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -830,6 +830,35 @@ struct ser_streamplaceholder typedef std::vector<char, zero_after_free_allocator<char> > CSerializeData; +class CSizeComputer +{ +protected: + size_t nSize; + +public: + int nType; + int nVersion; + + CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {} + + CSizeComputer& write(const char *psz, int nSize) + { + this->nSize += nSize; + return *this; + } + + template<typename T> + CSizeComputer& operator<<(const T& obj) + { + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + size_t size() const { + return nSize; + } +}; + /** Double ended buffer combining vector and stream-like interfaces. * * >> and << read and write unformatted data using the above serialization templates. diff --git a/src/sync.h b/src/sync.h index 077ed59b89..cd319e0171 100644 --- a/src/sync.h +++ b/src/sync.h @@ -84,6 +84,9 @@ typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection; /** Wrapped boost mutex: supports waiting but not recursive locking */ typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection; +/** Just a typedef for boost::condition_variable, can be wrapped later if desired */ +typedef boost::condition_variable CConditionVariable; + #ifdef DEBUG_LOCKORDER void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); void LeaveCritical(); diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 2cdafa4bdd..69de3b5bb1 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -45,10 +45,6 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize) expected[i] = (char)vch[i]; BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end()); - - BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!"); - filter.clear(); - BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter should be empty!"); } BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak) diff --git a/src/test/canonical_tests.cpp b/src/test/canonical_tests.cpp index 23dd74296c..a9798623ea 100644 --- a/src/test/canonical_tests.cpp +++ b/src/test/canonical_tests.cpp @@ -6,12 +6,11 @@ // Unit tests for canonical signatures // - - -#include "script.h" -#include "util.h" #include "data/sig_noncanonical.json.h" #include "data/sig_canonical.json.h" +#include "random.h" +#include "script.h" +#include "util.h" #include <boost/foreach.hpp> #include <boost/test/unit_test.hpp> @@ -21,7 +20,6 @@ using namespace std; using namespace json_spirit; - // In script_tests.cpp extern Array read_json(const std::string& jsondata); diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 7bd98fa381..a17278b803 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -5,6 +5,7 @@ #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha2.h" +#include "random.h" #include "util.h" #include <vector> diff --git a/src/test/mruset_tests.cpp b/src/test/mruset_tests.cpp index 60f11c147a..547cd1090c 100644 --- a/src/test/mruset_tests.cpp +++ b/src/test/mruset_tests.cpp @@ -4,6 +4,7 @@ #include "mruset.h" +#include "random.h" #include "util.h" #include <set> diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 423ae4a789..b99797fccb 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -2,15 +2,16 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <boost/test/unit_test.hpp> -#include <iostream> - +#include "data/sighash.json.h" #include "main.h" -#include "util.h" +#include "random.h" #include "serialize.h" +#include "util.h" #include "version.h" -#include "data/sighash.json.h" +#include <iostream> + +#include <boost/test/unit_test.hpp> #include "json/json_spirit_reader_template.h" #include "json/json_spirit_utils.h" #include "json/json_spirit_writer_template.h" @@ -118,7 +119,7 @@ BOOST_AUTO_TEST_SUITE(sighash_tests) BOOST_AUTO_TEST_CASE(sighash_test) { seed_insecure_rand(false); - + #if defined(PRINT_SIGHASH_JSON) std::cout << "[\n"; std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n"; @@ -205,10 +206,9 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest); continue; } - + sh = SignatureHash(scriptCode, tx, nIn, nHashType); BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest); } } BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index 11762c6ea0..a123f1d197 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -2,11 +2,13 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <boost/test/unit_test.hpp> -#include <vector> #include "main.h" +#include "random.h" #include "util.h" +#include <vector> + +#include <boost/test/unit_test.hpp> #define SKIPLIST_LENGTH 300000 @@ -98,4 +100,3 @@ BOOST_AUTO_TEST_CASE(getlocator_test) } BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index bcd2f75f55..443b5853b2 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -4,9 +4,8 @@ #define BOOST_TEST_MODULE Bitcoin Test Suite - - #include "main.h" +#include "random.h" #include "txdb.h" #include "ui_interface.h" #include "util.h" @@ -89,4 +88,3 @@ bool ShutdownRequested() { return false; } - diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 0b071361d8..068b9f29c8 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -4,6 +4,7 @@ #include "util.h" +#include "random.h" #include "sync.h" #include <stdint.h> diff --git a/src/txmempool.cpp b/src/txmempool.cpp index a852de5da8..164e2741a2 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -420,6 +420,7 @@ void CTxMemPool::remove(const CTransaction &tx, std::list<CTransaction>& removed void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed) { // Remove transactions which depend on inputs of tx, recursively + list<CTransaction> result; LOCK(cs); BOOST_FOREACH(const CTxIn &txin, tx.vin) { std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); @@ -601,14 +602,15 @@ void CTxMemPool::ClearPrioritisation(const uint256 hash) 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 an entry in the mempool exists, always return that one, as it's guaranteed to never + // conflict with the underlying cache, and it cannot have pruned entries (as it contains full) + // transactions. First checking the underlying cache risks returning a pruned entry instead. CTransaction tx; if (mempool.lookup(txid, tx)) { coins = CCoins(tx, MEMPOOL_HEIGHT); return true; } - return false; + return (base->GetCoins(txid, coins) && !coins.IsPruned()); } bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { diff --git a/src/ui_interface.h b/src/ui_interface.h index e9fcd91d41..b3df2b5a85 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -21,8 +21,7 @@ enum ChangeType { CT_NEW, CT_UPDATED, - CT_DELETED, - CT_GOT_CONFLICT + CT_DELETED }; /** Signals for UI communication. */ @@ -63,8 +62,6 @@ public: /** Force blocking, modal message box dialog (not just OS notification) */ MODAL = 0x10000000U, - /** Don't bring GUI to foreground. Use for messages during initialization */ - NOSHOWGUI = 0x20000000U, /** Predefined combinations for certain default usage cases */ MSG_INFORMATION = ICON_INFORMATION, diff --git a/src/uint256.cpp b/src/uint256.cpp index 3392f1e9bc..08c05594fd 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -290,3 +290,46 @@ uint32_t uint256::GetCompact(bool fNegative) const nCompact |= (fNegative && (nCompact & 0x007fffff) ? 0x00800000 : 0); return nCompact; } + +static void inline HashMix(uint32_t& a, uint32_t& b, uint32_t& c) +{ + // Taken from lookup3, by Bob Jenkins. + a -= c; a ^= ((c << 4) | (c >> 28)); c += b; + b -= a; b ^= ((a << 6) | (a >> 26)); a += c; + c -= b; c ^= ((b << 8) | (b >> 24)); b += a; + a -= c; a ^= ((c << 16) | (c >> 16)); c += b; + b -= a; b ^= ((a << 19) | (a >> 13)); a += c; + c -= b; c ^= ((b << 4) | (b >> 28)); b += a; +} + +static void inline HashFinal(uint32_t& a, uint32_t& b, uint32_t& c) +{ + // Taken from lookup3, by Bob Jenkins. + c ^= b; c -= ((b << 14) | (b >> 18)); + a ^= c; a -= ((c << 11) | (c >> 21)); + b ^= a; b -= ((a << 25) | (a >> 7)); + c ^= b; c -= ((b << 16) | (b >> 16)); + a ^= c; a -= ((c << 4) | (c >> 28)); + b ^= a; b -= ((a << 14) | (a >> 18)); + c ^= b; c -= ((b << 24) | (b >> 8)); +} + +uint64_t uint256::GetHash(const uint256 &salt) const +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (WIDTH << 2); + + a += pn[0] ^ salt.pn[0]; + b += pn[1] ^ salt.pn[1]; + c += pn[2] ^ salt.pn[2]; + HashMix(a, b, c); + a += pn[3] ^ salt.pn[3]; + b += pn[4] ^ salt.pn[4]; + c += pn[5] ^ salt.pn[5]; + HashMix(a, b, c); + a += pn[6] ^ salt.pn[6]; + b += pn[7] ^ salt.pn[7]; + HashFinal(a, b, c); + + return ((((uint64_t)b) << 32) | c); +} diff --git a/src/uint256.h b/src/uint256.h index 82db7758c9..ad0a56f447 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -21,7 +21,7 @@ public: template<unsigned int BITS> class base_uint { -private: +protected: enum { WIDTH=BITS/32 }; uint32_t pn[WIDTH]; public: @@ -322,6 +322,8 @@ public: // implementation accident. uint256& SetCompact(uint32_t nCompact, bool *pfNegative = NULL, bool *pfOverflow = NULL); uint32_t GetCompact(bool fNegative = false) const; + + uint64_t GetHash(const uint256& salt) const; }; #endif diff --git a/src/util.cpp b/src/util.cpp index 91ac8833d5..d3fa5182f3 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -6,6 +6,7 @@ #include "util.h" #include "chainparamsbase.h" +#include "random.h" #include "sync.h" #include "uint256.h" #include "version.h" @@ -141,90 +142,6 @@ public: } instance_of_cinit; - - - - - - - -void RandAddSeed() -{ - // Seed with CPU performance counter - int64_t nCounter = GetPerformanceCounter(); - RAND_add(&nCounter, sizeof(nCounter), 1.5); - memset(&nCounter, 0, sizeof(nCounter)); -} - -void RandAddSeedPerfmon() -{ - RandAddSeed(); - - // This can take up to 2 seconds, so only do it every 10 minutes - static int64_t nLastPerfmon; - if (GetTime() < nLastPerfmon + 10 * 60) - return; - nLastPerfmon = GetTime(); - -#ifdef WIN32 - // Don't need this on Linux, OpenSSL automatically uses /dev/urandom - // Seed with the entire set of perfmon data - std::vector <unsigned char> vData(250000,0); - long ret = 0; - unsigned long nSize = 0; - const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data - while (true) - { - nSize = vData.size(); - ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, begin_ptr(vData), &nSize); - if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) - break; - vData.resize(std::max((vData.size()*3)/2, nMaxSize)); // Grow size of buffer exponentially - } - RegCloseKey(HKEY_PERFORMANCE_DATA); - if (ret == ERROR_SUCCESS) - { - RAND_add(begin_ptr(vData), nSize, nSize/100.0); - OPENSSL_cleanse(begin_ptr(vData), nSize); - LogPrint("rand", "%s: %lu bytes\n", __func__, nSize); - } else { - static bool warned = false; // Warn only once - if (!warned) - { - LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret); - warned = true; - } - } -#endif -} - -uint64_t GetRand(uint64_t nMax) -{ - if (nMax == 0) - return 0; - - // The range of the random source must be a multiple of the modulus - // to give every possible output value an equal possibility - uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax; - uint64_t nRand = 0; - do - RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); - while (nRand >= nRange); - return (nRand % nMax); -} - -int GetRandInt(int nMax) -{ - return GetRand(nMax); -} - -uint256 GetRandHash() -{ - uint256 hash; - RAND_bytes((unsigned char*)&hash, sizeof(hash)); - return hash; -} - // LogPrintf() has been broken a couple of times now // by well-meaning people adding mutexes in the most straightforward way. // It breaks because it may be called by global destructors during shutdown. @@ -288,7 +205,7 @@ int LogPrintStr(const std::string &str) // print to console ret = fwrite(str.data(), 1, str.size(), stdout); } - else if (fPrintToDebugLog) + else if (fPrintToDebugLog && AreBaseParamsConfigured()) { static bool fStartedNewLine = true; boost::call_once(&DebugPrintInit, debugPrintInitFlag); @@ -1192,27 +1109,6 @@ void SetMockTime(int64_t nMockTimeIn) nMockTime = nMockTimeIn; } -uint32_t insecure_rand_Rz = 11; -uint32_t insecure_rand_Rw = 11; -void seed_insecure_rand(bool fDeterministic) -{ - //The seed values have some unlikely fixed points which we avoid. - if(fDeterministic) - { - insecure_rand_Rz = insecure_rand_Rw = 11; - } else { - uint32_t tmp; - do { - RAND_bytes((unsigned char*)&tmp, 4); - } while(tmp == 0 || tmp == 0x9068ffffU); - insecure_rand_Rz = tmp; - do { - RAND_bytes((unsigned char*)&tmp, 4); - } while(tmp == 0 || tmp == 0x464fffffU); - insecure_rand_Rw = tmp; - } -} - string FormatVersion(int nVersion) { if (nVersion%100 == 0) diff --git a/src/util.h b/src/util.h index 60db71bfd0..db2005337b 100644 --- a/src/util.h +++ b/src/util.h @@ -90,8 +90,6 @@ inline void MilliSleep(int64_t n) #endif } - - extern std::map<std::string, std::string> mapArgs; extern std::map<std::string, std::vector<std::string> > mapMultiArgs; extern bool fDebug; @@ -103,8 +101,6 @@ extern bool fLogTimestamps; extern bool fLogIPs; extern volatile bool fReopenDebugLog; -void RandAddSeed(); -void RandAddSeedPerfmon(); void SetupEnvironment(); /* Return true if log accepts specified category */ @@ -187,23 +183,12 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif boost::filesystem::path GetTempPath(); void ShrinkDebugFile(); -int GetRandInt(int nMax); -uint64_t GetRand(uint64_t nMax); -uint256 GetRandHash(); int64_t GetTime(); void SetMockTime(int64_t nMockTimeIn); std::string FormatFullVersion(); std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments); void runCommand(std::string strCommand); - - - - - - - - inline std::string i64tostr(int64_t n) { return strprintf("%d", n); @@ -289,19 +274,6 @@ inline std::string HexStr(const T& vch, bool fSpaces=false) */ std::string FormatParagraph(const std::string in, size_t width=79, size_t indent=0); -inline int64_t GetPerformanceCounter() -{ - int64_t nCounter = 0; -#ifdef WIN32 - QueryPerformanceCounter((LARGE_INTEGER*)&nCounter); -#else - timeval t; - gettimeofday(&t, NULL); - nCounter = (int64_t) t.tv_sec * 1000000 + t.tv_usec; -#endif - return nCounter; -} - inline int64_t GetTimeMillis() { return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - @@ -371,28 +343,6 @@ bool SoftSetArg(const std::string& strArg, const std::string& strValue); bool SoftSetBoolArg(const std::string& strArg, bool fValue); /** - * MWC RNG of George Marsaglia - * This is intended to be fast. It has a period of 2^59.3, though the - * least significant 16 bits only have a period of about 2^30.1. - * - * @return random value - */ -extern uint32_t insecure_rand_Rz; -extern uint32_t insecure_rand_Rw; -static inline uint32_t insecure_rand(void) -{ - insecure_rand_Rz = 36969 * (insecure_rand_Rz & 65535) + (insecure_rand_Rz >> 16); - insecure_rand_Rw = 18000 * (insecure_rand_Rw & 65535) + (insecure_rand_Rw >> 16); - return (insecure_rand_Rw << 16) + insecure_rand_Rz; -} - -/** - * Seed insecure_rand using the random pool. - * @param Deterministic Use a determinstic seed - */ -void seed_insecure_rand(bool fDeterministic=false); - -/** * Timing-attack-resistant comparison. * Takes time proportional to length * of first argument. diff --git a/src/version.h b/src/version.h index 3d1abacb94..85c5dbf8d7 100644 --- a/src/version.h +++ b/src/version.h @@ -28,7 +28,7 @@ extern const std::string CLIENT_DATE; static const int PROTOCOL_VERSION = 70002; -// intial proto version, to be increased after version/verack negotiation +// initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; // disconnect from peers older than this proto version @@ -45,7 +45,7 @@ static const int NOBLKS_VERSION_END = 32400; // BIP 0031, pong message, is enabled for all versions AFTER this one static const int BIP0031_VERSION = 60000; -// "mempool" command, enhanced "getdata" behavior starts with this version: +// "mempool" command, enhanced "getdata" behavior starts with this version static const int MEMPOOL_GD_VERSION = 60002; #endif diff --git a/src/wallet.cpp b/src/wallet.cpp index a54494f935..7c04743c0f 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -12,7 +12,6 @@ #include "timedata.h" #include <boost/algorithm/string/replace.hpp> -#include <openssl/rand.h> using namespace std; @@ -275,7 +274,7 @@ bool CWallet::SetMaxVersion(int nVersion) return true; } -set<uint256> CWallet::GetConflicts(const uint256& txid, bool includeEquivalent) const +set<uint256> CWallet::GetConflicts(const uint256& txid) const { set<uint256> result; AssertLockHeld(cs_wallet); @@ -293,8 +292,7 @@ set<uint256> CWallet::GetConflicts(const uint256& txid, bool includeEquivalent) continue; // No conflict if zero or one spends range = mapTxSpends.equal_range(txin.prevout); for (TxSpends::const_iterator it = range.first; it != range.second; ++it) - if (includeEquivalent || !wtx.IsEquivalentTo(mapWallet.at(it->second))) - result.insert(it->second); + result.insert(it->second); } return result; } @@ -323,7 +321,6 @@ void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range) const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; if (copyFrom == copyTo) continue; - if (!copyFrom->IsEquivalentTo(*copyTo)) continue; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; // fTimeReceivedIsTxTime not copied on purpose @@ -384,13 +381,15 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) RandAddSeedPerfmon(); vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); - RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); + if (!GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE)) + return false; CMasterKey kMasterKey; - RandAddSeedPerfmon(); + kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); - RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); + if (!GetRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE)) + return false; CCrypter crypter; int64_t nStartTime = GetTimeMillis(); @@ -609,28 +608,6 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); - // Notifications for existing transactions that now have conflicts with this one - if (fInsertedNew) - { - BOOST_FOREACH(const uint256& conflictHash, wtxIn.GetConflicts(false)) - { - CWalletTx& txConflict = mapWallet[conflictHash]; - NotifyTransactionChanged(this, conflictHash, CT_UPDATED); //Updates UI table - if (IsFromMe(txConflict) || IsMine(txConflict)) - { - NotifyTransactionChanged(this, conflictHash, CT_GOT_CONFLICT); //Throws dialog - // external respend notify - std::string strCmd = GetArg("-respendnotify", ""); - if (!strCmd.empty()) - { - boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); - boost::replace_all(strCmd, "%t", conflictHash.GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free - } - } - } - } - // notify an external script when a wallet transaction comes in or is updated std::string strCmd = GetArg("-walletnotify", ""); @@ -653,12 +630,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl AssertLockHeld(cs_wallet); bool fExisted = mapWallet.count(tx.GetHash()); if (fExisted && !fUpdate) return false; - - bool fIsConflicting = IsConflicting(tx); - if (fIsConflicting) - nConflictsReceived++; - - if (fExisted || IsMine(tx) || IsFromMe(tx) || fIsConflicting) + if (fExisted || IsMine(tx) || IsFromMe(tx)) { CWalletTx wtx(this,tx); // Get merkle branch if transaction was found in a block @@ -797,8 +769,8 @@ int CWalletTx::GetRequestCount() const return nRequests; } -void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, - list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const +void CWalletTx::GetAmounts(list<COutputEntry>& listReceived, + list<COutputEntry>& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const { nFee = 0; listReceived.clear(); @@ -814,10 +786,10 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, } // Sent/received. - BOOST_FOREACH(const CTxOut& txout, vout) + for (unsigned int i = 0; i < vout.size(); ++i) { + const CTxOut& txout = vout[i]; isminetype fIsMine = pwallet->IsMine(txout); - // 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) @@ -839,13 +811,15 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, address = CNoDestination(); } + COutputEntry output = {address, txout.nValue, (int)i}; + // 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)); + listSent.push_back(output); // If we are receiving the output, add it as a "received" entry - if (fIsMine) - listReceived.push_back(make_pair(address, txout.nValue)); + if (fIsMine & filter) + listReceived.push_back(output); } } @@ -857,29 +831,29 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived, int64_t allFee; string strSentAccount; - list<pair<CTxDestination, int64_t> > listReceived; - list<pair<CTxDestination, int64_t> > listSent; + list<COutputEntry> listReceived; + list<COutputEntry> listSent; GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); if (strAccount == strSentAccount) { - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& s, listSent) - nSent += s.second; + BOOST_FOREACH(const COutputEntry& s, listSent) + nSent += s.amount; nFee = allFee; } { LOCK(pwallet->cs_wallet); - BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived) + BOOST_FOREACH(const COutputEntry& r, listReceived) { - if (pwallet->mapAddressBook.count(r.first)) + if (pwallet->mapAddressBook.count(r.destination)) { - map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(r.first); + map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(r.destination); if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount) - nReceived += r.second; + nReceived += r.amount; } else if (strAccount.empty()) { - nReceived += r.second; + nReceived += r.amount; } } } @@ -945,7 +919,7 @@ void CWallet::ReacceptWalletTransactions() int nDepth = wtx.GetDepthInMainChain(); - if (!wtx.IsCoinBase() && nDepth < 0 && (IsMine(wtx) || IsFromMe(wtx))) + if (!wtx.IsCoinBase() && nDepth < 0) { // Try to add to memory pool LOCK(mempool.cs); @@ -965,13 +939,13 @@ void CWalletTx::RelayWalletTransaction() } } -set<uint256> CWalletTx::GetConflicts(bool includeEquivalent) const +set<uint256> CWalletTx::GetConflicts() const { set<uint256> result; if (pwallet != NULL) { uint256 myHash = GetHash(); - result = pwallet->GetConflicts(myHash, includeEquivalent); + result = pwallet->GetConflicts(myHash); result.erase(myHash); } return result; @@ -1075,7 +1049,7 @@ int64_t CWallet::GetWatchOnlyBalance() const { int64_t nTotal = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; @@ -1091,7 +1065,7 @@ int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const { int64_t nTotal = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; @@ -1106,7 +1080,7 @@ int64_t CWallet::GetImmatureWatchOnlyBalance() const { int64_t nTotal = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; @@ -2010,11 +1984,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey) if (nIndex != -1) vchPubKey = keypool.vchPubKey; else { - if (pwallet->vchDefaultKey.IsValid()) { - LogPrintf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); - vchPubKey = pwallet->vchDefaultKey; - } else - return false; + return false; } } assert(vchPubKey.IsValid()); diff --git a/src/wallet.h b/src/wallet.h index ab9550a984..73fcfa24e0 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -144,9 +144,6 @@ public: MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID; - // Increment to cause UI refresh, similar to new block - int64_t nConflictsReceived; - CWallet() { SetNull(); @@ -169,7 +166,6 @@ public: nNextResend = 0; nLastResend = 0; nTimeFirstKey = 0; - nConflictsReceived = 0; } std::map<uint256, CWalletTx> mapWallet; @@ -322,13 +318,6 @@ public: { return (GetDebit(tx, ISMINE_ALL) > 0); } - bool IsConflicting(const CTransaction& tx) const - { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (mapTxSpends.count(txin.prevout)) - return true; - return false; - } int64_t GetDebit(const CTransaction& tx, const isminefilter& filter) const { int64_t nDebit = 0; @@ -401,7 +390,7 @@ public: int GetVersion() { LOCK(cs_wallet); return nWalletVersion; } // Get wallet transactions that conflict with given transaction (spend same outputs) - std::set<uint256> GetConflicts(const uint256& txid, bool includeEquivalent) const; + std::set<uint256> GetConflicts(const uint256& txid) const; /** Address book entry changed. * @note called with lock cs_wallet held. @@ -467,6 +456,12 @@ static void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) mapValue["n"] = i64tostr(nOrderPos); } +struct COutputEntry +{ + CTxDestination destination; + int64_t amount; + int vout; +}; /** A transaction with a bunch of additional info that only the owner cares about. * It includes any unrecorded transactions needed to link it back to the block chain. @@ -761,8 +756,8 @@ public: return nChangeCached; } - 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 isminefilter& filter) const; + void GetAmounts(std::list<COutputEntry>& listReceived, + std::list<COutputEntry>& listSent, int64_t& nFee, std::string& strSentAccount, const isminefilter& filter) const; void GetAccountAmounts(const std::string& strAccount, int64_t& nReceived, int64_t& nSent, int64_t& nFee, const isminefilter& filter) const; @@ -806,7 +801,7 @@ public: void RelayWalletTransaction(); - std::set<uint256> GetConflicts(bool includeEquivalent=true) const; + std::set<uint256> GetConflicts() const; }; |