diff options
Diffstat (limited to 'src')
90 files changed, 1601 insertions, 1311 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 6c67dee7d5..655bfc88c3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -109,6 +109,9 @@ BITCOIN_CORE_H = \ ui_interface.h \ uint256.h \ util.h \ + utilstrencodings.h \ + utilmoneystr.h \ + utiltime.h \ version.h \ walletdb.h \ wallet.h \ @@ -219,6 +222,9 @@ libbitcoin_util_a_SOURCES = \ sync.cpp \ uint256.cpp \ util.cpp \ + utilstrencodings.cpp \ + utilmoneystr.cpp \ + utiltime.cpp \ version.cpp \ $(BITCOIN_CORE_H) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 28d7053fc4..1ea039adb3 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -233,6 +233,9 @@ RES_ICONS = \ qt/res/icons/editcopy.png \ qt/res/icons/editpaste.png \ qt/res/icons/export.png \ + qt/res/icons/eye.png \ + qt/res/icons/eye_minus.png \ + qt/res/icons/eye_plus.png \ qt/res/icons/filesave.png \ qt/res/icons/history.png \ qt/res/icons/key.png \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index cc407f679f..b4360831bb 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -61,6 +61,7 @@ BITCOIN_TESTS =\ test/sigopcount_tests.cpp \ test/skiplist_tests.cpp \ test/test_bitcoin.cpp \ + test/timedata_tests.cpp \ test/transaction_tests.cpp \ test/uint256_tests.cpp \ test/univalue_tests.cpp \ diff --git a/src/alert.cpp b/src/alert.cpp index 2cd684cc4c..3271ecfbfd 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -19,6 +19,7 @@ #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/foreach.hpp> +#include <boost/thread.hpp> using namespace std; diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 0609adcab3..871aaf93df 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -8,6 +8,8 @@ #include "rpcclient.h" #include "rpcprotocol.h" #include "chainparamsbase.h" +#include "utilstrencodings.h" +#include "version.h" #include <boost/filesystem/operations.hpp> diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 6cd2768d70..75f0fb69f2 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -4,6 +4,7 @@ #include "base58.h" #include "util.h" +#include "utilmoneystr.h" #include "core.h" #include "main.h" // for MAX_BLOCK_SIZE #include "keystore.h" diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 880955481b..5be8708979 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -12,6 +12,7 @@ #include <boost/algorithm/string/predicate.hpp> #include <boost/filesystem.hpp> +#include <boost/thread.hpp> /* Introduction text for doxygen: */ diff --git a/src/coins.cpp b/src/coins.cpp index 6137b51c55..7bfb84ef3e 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -52,30 +52,30 @@ bool CCoins::Spend(int nPos) { } -bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } +bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; } bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } -bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } -uint256 CCoinsView::GetBestBlock() { return uint256(0); } +bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; } +uint256 CCoinsView::GetBestBlock() const { return uint256(0); } bool CCoinsView::SetBestBlock(const uint256 &hashBlock) { return false; } bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; } -bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } +bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; } CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } -bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } +bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); } bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } -bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } -uint256 CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } +bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); } +uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); } bool CCoinsViewBacked::SetBestBlock(const uint256 &hashBlock) { return base->SetBestBlock(hashBlock); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } -bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } +bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { 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) { +bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const { if (cacheCoins.count(txid)) { coins = cacheCoins[txid]; return true; @@ -99,19 +99,29 @@ CCoinsMap::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) { return ret; } +CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const { + /* Avoid redundant implementation with the const-cast. */ + return const_cast<CCoinsViewCache*>(this)->FetchCoins(txid); +} + CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) { CCoinsMap::iterator it = FetchCoins(txid); assert(it != cacheCoins.end()); return it->second; } +const CCoins &CCoinsViewCache::GetCoins(const uint256 &txid) const { + /* Avoid redundant implementation with the const-cast. */ + return const_cast<CCoinsViewCache*>(this)->GetCoins(txid); +} + bool CCoinsViewCache::SetCoins(const uint256 &txid, const CCoins &coins) { cacheCoins[txid] = coins; return true; } -bool CCoinsViewCache::HaveCoins(const uint256 &txid) { - CCoinsMap::iterator it = FetchCoins(txid); +bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { + CCoinsMap::const_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 @@ -119,7 +129,7 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) { return (it != cacheCoins.end() && !it->second.vout.empty()); } -uint256 CCoinsViewCache::GetBestBlock() { +uint256 CCoinsViewCache::GetBestBlock() const { if (hashBlock == uint256(0)) hashBlock = base->GetBestBlock(); return hashBlock; @@ -146,18 +156,18 @@ bool CCoinsViewCache::Flush() { return fOk; } -unsigned int CCoinsViewCache::GetCacheSize() { +unsigned int CCoinsViewCache::GetCacheSize() const { return cacheCoins.size(); } -const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) +const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const { const CCoins &coins = GetCoins(input.prevout.hash); assert(coins.IsAvailable(input.prevout.n)); return coins.vout[input.prevout.n]; } -int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) +int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) const { if (tx.IsCoinBase()) return 0; @@ -169,7 +179,7 @@ int64_t CCoinsViewCache::GetValueIn(const CTransaction& tx) return nResult; } -bool CCoinsViewCache::HaveInputs(const CTransaction& tx) +bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const { if (!tx.IsCoinBase()) { // first check whether information about the prevout hash is available @@ -190,7 +200,7 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) return true; } -double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) +double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const { if (tx.IsCoinBase()) return 0.0; diff --git a/src/coins.h b/src/coins.h index 2b657299e3..08913531d8 100644 --- a/src/coins.h +++ b/src/coins.h @@ -276,17 +276,17 @@ class CCoinsView { public: // Retrieve the CCoins (unspent transaction outputs) for a given txid - virtual bool GetCoins(const uint256 &txid, CCoins &coins); + virtual bool GetCoins(const uint256 &txid, CCoins &coins) const; // Modify the CCoins for a given txid virtual bool SetCoins(const uint256 &txid, const CCoins &coins); // Just check whether we have data for a given txid. // This may (but cannot always) return true for fully spent transactions - virtual bool HaveCoins(const uint256 &txid); + virtual bool HaveCoins(const uint256 &txid) const; // Retrieve the block hash whose state this CCoinsView currently represents - virtual uint256 GetBestBlock(); + virtual uint256 GetBestBlock() const; // Modify the currently active block hash virtual bool SetBestBlock(const uint256 &hashBlock); @@ -296,7 +296,7 @@ public: virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); // Calculate statistics about the unspent transaction output set - virtual bool GetStats(CCoinsStats &stats); + virtual bool GetStats(CCoinsStats &stats) const; // As we use CCoinsViews polymorphically, have a virtual destructor virtual ~CCoinsView() {} @@ -311,14 +311,14 @@ protected: public: CCoinsViewBacked(CCoinsView &viewIn); - bool GetCoins(const uint256 &txid, CCoins &coins); + bool GetCoins(const uint256 &txid, CCoins &coins) const; bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - uint256 GetBestBlock(); + bool HaveCoins(const uint256 &txid) const; + uint256 GetBestBlock() const; bool SetBestBlock(const uint256 &hashBlock); void SetBackend(CCoinsView &viewIn); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); - bool GetStats(CCoinsStats &stats); + bool GetStats(CCoinsStats &stats) const; }; @@ -326,17 +326,20 @@ public: class CCoinsViewCache : public CCoinsViewBacked { protected: - uint256 hashBlock; - CCoinsMap cacheCoins; + + /* Make mutable so that we can "fill the cache" even from Get-methods + declared as "const". */ + mutable uint256 hashBlock; + mutable CCoinsMap cacheCoins; public: CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false); // Standard CCoinsView methods - bool GetCoins(const uint256 &txid, CCoins &coins); + bool GetCoins(const uint256 &txid, CCoins &coins) const; bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - uint256 GetBestBlock(); + bool HaveCoins(const uint256 &txid) const; + uint256 GetBestBlock() const; bool SetBestBlock(const uint256 &hashBlock); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); @@ -344,6 +347,7 @@ public: // Many methods explicitly require a CCoinsViewCache because of this method, to reduce // copying. CCoins &GetCoins(const uint256 &txid); + const CCoins &GetCoins(const uint256 &txid) const; // Push the modifications applied to this cache to its base. // Failure to call this method before destruction will cause the changes to be forgotten. @@ -351,7 +355,7 @@ public: bool Flush(); // Calculate the size of the cache (in number of transactions) - unsigned int GetCacheSize(); + unsigned int GetCacheSize() const; /** Amount of bitcoins coming in to a transaction Note that lightweight clients may not know anything besides the hash of previous transactions, @@ -360,18 +364,19 @@ public: @param[in] tx transaction for which we are checking input total @return Sum of value of all inputs (scriptSigs) */ - int64_t GetValueIn(const CTransaction& tx); + int64_t GetValueIn(const CTransaction& tx) const; // Check whether all prevouts of the transaction are present in the UTXO set represented by this view - bool HaveInputs(const CTransaction& tx); + bool HaveInputs(const CTransaction& tx) const; // Return priority of tx at height nHeight - double GetPriority(const CTransaction &tx, int nHeight); + double GetPriority(const CTransaction &tx, int nHeight) const; - const CTxOut &GetOutputFor(const CTxIn& input); + const CTxOut &GetOutputFor(const CTxIn& input) const; private: CCoinsMap::iterator FetchCoins(const uint256 &txid); + CCoinsMap::const_iterator FetchCoins(const uint256 &txid) const; }; #endif diff --git a/src/compat.h b/src/compat.h index 52c7817130..4fc28a36e0 100644 --- a/src/compat.h +++ b/src/compat.h @@ -59,4 +59,29 @@ typedef u_int SOCKET; #define SOCKET_ERROR -1 #endif +#ifdef WIN32 +#ifndef S_IRUSR +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#endif +#else +#define MAX_PATH 1024 +#endif + +// As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall, it is defined as 0 +#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) +#define MSG_NOSIGNAL 0 +#endif + +#ifndef WIN32 +// PRIO_MAX is not defined on Solaris +#ifndef PRIO_MAX +#define PRIO_MAX 20 +#endif +#define THREAD_PRIORITY_LOWEST PRIO_MAX +#define THREAD_PRIORITY_BELOW_NORMAL 2 +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_ABOVE_NORMAL (-2) +#endif + #endif // _BITCOIN_COMPAT_H diff --git a/src/core.h b/src/core.h index 9552f70254..e3ceac97aa 100644 --- a/src/core.h +++ b/src/core.h @@ -14,6 +14,9 @@ class CTransaction; +static const int64_t COIN = 100000000; +static const int64_t CENT = 1000000; + /** No amount larger than this (in satoshi) is valid */ static const int64_t MAX_MONEY = 21000000 * COIN; inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } diff --git a/src/core_write.cpp b/src/core_write.cpp index 37dd69f7b8..b395b5c090 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -8,6 +8,7 @@ #include "core.h" #include "serialize.h" #include "util.h" +#include "utilmoneystr.h" #include "base58.h" using namespace std; diff --git a/src/crypter.cpp b/src/crypter.cpp index 122e06d97e..8aa2bb0517 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -5,6 +5,7 @@ #include "crypter.h" #include "script.h" +#include "util.h" #include <string> #include <vector> diff --git a/src/db.cpp b/src/db.cpp index eb40f3cc40..8c139843a1 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -9,6 +9,7 @@ #include "hash.h" #include "protocol.h" #include "util.h" +#include "utilstrencodings.h" #include <stdint.h> @@ -17,6 +18,7 @@ #endif #include <boost/filesystem.hpp> +#include <boost/thread.hpp> #include <boost/version.hpp> #include <openssl/rand.h> diff --git a/src/init.cpp b/src/init.cpp index 708e6386aa..e972413c4b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -19,6 +19,7 @@ #include "txdb.h" #include "ui_interface.h" #include "util.h" +#include "utilmoneystr.h" #ifdef ENABLE_WALLET #include "db.h" #include "wallet.h" @@ -36,6 +37,7 @@ #include <boost/algorithm/string/predicate.hpp> #include <boost/filesystem.hpp> #include <boost/interprocess/sync/file_lock.hpp> +#include <boost/thread.hpp> #include <openssl/crypto.h> using namespace boost; diff --git a/src/keystore.cpp b/src/keystore.cpp index 2a4c88d565..72ae9b0a30 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -8,6 +8,7 @@ #include "crypter.h" #include "key.h" #include "script.h" +#include "util.h" #include <boost/foreach.hpp> @@ -71,3 +72,9 @@ bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const LOCK(cs_KeyStore); return setWatchOnly.count(dest) > 0; } + +bool CBasicKeyStore::HaveWatchOnly() const +{ + LOCK(cs_KeyStore); + return (!setWatchOnly.empty()); +} diff --git a/src/keystore.h b/src/keystore.h index 6a3a0dc13e..7aaf197cd7 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -41,6 +41,7 @@ public: // Support for Watch-only addresses virtual bool AddWatchOnly(const CScript &dest) =0; virtual bool HaveWatchOnly(const CScript &dest) const =0; + virtual bool HaveWatchOnly() const =0; }; typedef std::map<CKeyID, CKey> KeyMap; @@ -98,6 +99,7 @@ public: virtual bool AddWatchOnly(const CScript &dest); virtual bool HaveWatchOnly(const CScript &dest) const; + virtual bool HaveWatchOnly() const; }; typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial; diff --git a/src/leveldbwrapper.h b/src/leveldbwrapper.h index 043a56bf38..452df92839 100644 --- a/src/leveldbwrapper.h +++ b/src/leveldbwrapper.h @@ -82,7 +82,7 @@ public: CLevelDBWrapper(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); ~CLevelDBWrapper(); - template<typename K, typename V> bool Read(const K& key, V& value) throw(leveldb_error) { + template<typename K, typename V> bool Read(const K& key, V& value) const throw(leveldb_error) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); ssKey << key; @@ -111,7 +111,7 @@ public: return WriteBatch(batch, fSync); } - template<typename K> bool Exists(const K& key) throw(leveldb_error) { + template<typename K> bool Exists(const K& key) const throw(leveldb_error) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); ssKey << key; diff --git a/src/main.cpp b/src/main.cpp index a5c1103ef4..c7ba15ef13 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,10 +20,10 @@ #include <sstream> -#include <boost/dynamic_bitset.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> +#include <boost/thread.hpp> using namespace std; using namespace boost; @@ -644,7 +644,7 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) // 2. P2SH scripts with a crazy number of expensive // CHECKSIG/CHECKMULTISIG operations // -bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs) +bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) { if (tx.IsCoinBase()) return true; // Coinbases don't use vin normally @@ -717,7 +717,7 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx) return nSigOps; } -unsigned int GetP2SHSigOpCount(const CTransaction& tx, CCoinsViewCache& inputs) +unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs) { if (tx.IsCoinBase()) return 0; @@ -1470,7 +1470,7 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); } -bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) { if (!tx.IsCoinBase()) { @@ -3527,75 +3527,6 @@ void static ProcessGetData(CNode* pfrom) } } -struct CCoin { - uint32_t nTxVer; // Don't call this nVersion, that name has a special meaning inside IMPLEMENT_SERIALIZE - uint32_t nHeight; - CTxOut out; - - IMPLEMENT_SERIALIZE( - READWRITE(nTxVer); - READWRITE(nHeight); - READWRITE(out); - ) -}; - -bool ProcessGetUTXOs(const vector<COutPoint> &vOutPoints, bool fCheckMemPool, vector<unsigned char> *result, vector<CCoin> *resultCoins) -{ - // Defined by BIP 64. - // - // Allows a peer to retrieve the CTxOut structures corresponding to the given COutPoints. - // Note that this data is not authenticated by anything: this code could just invent any - // old rubbish and hand it back, with the peer being unable to tell unless they are checking - // the outpoints against some out of band data. - // - // Also the answer could change the moment after we give it. However some apps can tolerate - // this, because they're only using the result as a hint or are willing to trust the results - // based on something else. For example we may be a "trusted node" for the peer, or it may - // be checking the results given by several nodes for consistency, it may - // run the UTXOs returned against scriptSigs of transactions obtained elsewhere (after checking - // for a standard script form), and because the height in which the UTXO was defined is provided - // a client that has a map of heights to block headers (as SPV clients do, for recent blocks) - // can request the creating block via hash. - // - // IMPORTANT: Clients expect ordering to be preserved! - if (vOutPoints.size() > MAX_INV_SZ) - return error("message getutxos size() = %u", vOutPoints.size()); - - LogPrint("net", "getutxos for %d queries %s mempool\n", vOutPoints.size(), fCheckMemPool ? "with" : "without"); - - boost::dynamic_bitset<unsigned char> hits(vOutPoints.size()); - { - LOCK2(cs_main, mempool.cs); - CCoinsViewMemPool cvMemPool(*pcoinsTip, mempool); - CCoinsViewCache view(fCheckMemPool ? cvMemPool : *pcoinsTip); - for (size_t i = 0; i < vOutPoints.size(); i++) - { - CCoins coins; - uint256 hash = vOutPoints[i].hash; - if (view.GetCoins(hash, coins)) - { - mempool.pruneSpent(hash, coins); - if (coins.IsAvailable(vOutPoints[i].n)) - { - hits[i] = true; - // Safe to index into vout here because IsAvailable checked if it's off the end of the array, or if - // n is valid but points to an already spent output (IsNull). - CCoin coin; - coin.nTxVer = coins.nVersion; - coin.nHeight = coins.nHeight; - coin.out = coins.vout.at(vOutPoints[i].n); - assert(!coin.out.IsNull()); - resultCoins->push_back(coin); - } - } - } - } - - boost::to_block_range(hits, std::back_inserter(*result)); - return true; -} - - bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { RandAddSeedPerfmon(); @@ -3944,22 +3875,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "getutxos") - { - bool fCheckMemPool; - vector<COutPoint> vOutPoints; - vRecv >> fCheckMemPool; - vRecv >> vOutPoints; - - vector<unsigned char> bitmap; - vector<CCoin> outs; - if (ProcessGetUTXOs(vOutPoints, fCheckMemPool, &bitmap, &outs)) - pfrom->PushMessage("utxos", chainActive.Height(), chainActive.Tip()->GetBlockHash(), bitmap, outs); - else - Misbehaving(pfrom->GetId(), 20); - } - - else if (strCommand == "tx") { vector<uint256> vWorkQueue; @@ -4664,7 +4579,68 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } +bool CBlockUndo::WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock) +{ + // Open history file to append + CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CBlockUndo::WriteToDisk : OpenUndoFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(*this); + fileout << FLATDATA(Params().MessageStart()) << nSize; + + // Write undo data + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) + return error("CBlockUndo::WriteToDisk : ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << *this; + + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + fileout << hasher.GetHash(); + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload()) + FileCommit(fileout); + + return true; +} + +bool CBlockUndo::ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock) +{ + // Open history file to read + CAutoFile filein = CAutoFile(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CBlockUndo::ReadFromDisk : OpenBlockFile failed"); + + // Read block + uint256 hashChecksum; + try { + filein >> *this; + filein >> hashChecksum; + } + catch (std::exception &e) { + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); + } + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + if (hashChecksum != hasher.GetHash()) + return error("CBlockUndo::ReadFromDisk : Checksum mismatch"); + + return true; +} + std::string CBlockFileInfo::ToString() const { + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str()); + } diff --git a/src/main.h b/src/main.h index 444c887204..142c41444e 100644 --- a/src/main.h +++ b/src/main.h @@ -265,7 +265,7 @@ int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF @param[in] mapInputs Map of previous transactions that have outputs we're spending @return True if all inputs (scriptSigs) use only standard transaction forms */ -bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs); +bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs); /** Count ECDSA signature operations the old-fashioned (pre-0.6) way @return number of sigops this transaction's outputs will produce when spent @@ -279,13 +279,13 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx); @return maximum number of sigops required to validate this transaction's inputs @see CTransaction::FetchInputs */ -unsigned int GetP2SHSigOpCount(const CTransaction& tx, CCoinsViewCache& mapInputs); +unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& mapInputs); // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) // This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it // instead of being performed inline. -bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks = true, unsigned int flags = STANDARD_SCRIPT_VERIFY_FLAGS, std::vector<CScriptCheck> *pvChecks = NULL); @@ -312,64 +312,8 @@ public: READWRITE(vtxundo); ) - bool WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock) - { - // Open history file to append - CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); - if (!fileout) - return error("CBlockUndo::WriteToDisk : OpenUndoFile failed"); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(*this); - fileout << FLATDATA(Params().MessageStart()) << nSize; - - // Write undo data - long fileOutPos = ftell(fileout); - if (fileOutPos < 0) - return error("CBlockUndo::WriteToDisk : ftell failed"); - pos.nPos = (unsigned int)fileOutPos; - fileout << *this; - - // calculate & write checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << *this; - fileout << hasher.GetHash(); - - // Flush stdio buffers and commit to disk before returning - fflush(fileout); - if (!IsInitialBlockDownload()) - FileCommit(fileout); - - return true; - } - - bool ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock) - { - // Open history file to read - CAutoFile filein = CAutoFile(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); - if (!filein) - return error("CBlockUndo::ReadFromDisk : OpenBlockFile failed"); - - // Read block - uint256 hashChecksum; - try { - filein >> *this; - filein >> hashChecksum; - } - catch (std::exception &e) { - return error("%s : Deserialize or I/O error - %s", __func__, e.what()); - } - - // Verify checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << *this; - if (hashChecksum != hasher.GetHash()) - return error("CBlockUndo::ReadFromDisk : Checksum mismatch"); - - return true; - } + bool WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock); + bool ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock); }; @@ -625,9 +569,7 @@ public: SetNull(); } - std::string ToString() const { - return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str()); - } + std::string ToString() const; // update statistics (does not update nSize) void AddBlock(unsigned int nHeightIn, uint64_t nTimeIn) { diff --git a/src/miner.cpp b/src/miner.cpp index 06acff1c33..8696edcf6e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -3,8 +3,6 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <inttypes.h> - #include "miner.h" #include "core.h" @@ -12,10 +10,14 @@ #include "main.h" #include "net.h" #include "pow.h" +#include "util.h" +#include "utilmoneystr.h" #ifdef ENABLE_WALLET #include "wallet.h" #endif +#include <boost/thread.hpp> + using namespace std; ////////////////////////////////////////////////////////////////////////////// diff --git a/src/net.cpp b/src/net.cpp index cae9b70da7..3966706207 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -28,6 +28,7 @@ #endif #include <boost/filesystem.hpp> +#include <boost/thread.hpp> // Dump addresses to peers.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 @@ -66,7 +67,7 @@ namespace { // bool fDiscover = true; bool fListen = true; -uint64_t nLocalServices = NODE_NETWORK | NODE_GETUTXOS; +uint64_t nLocalServices = NODE_NETWORK; CCriticalSection cs_mapLocalHost; map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfReachable[NET_MAX] = {}; @@ -2037,3 +2038,157 @@ bool CAddrDB::Read(CAddrMan& addr) return true; } + +unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } +unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } + +CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), setAddrKnown(5000) +{ + nServices = 0; + hSocket = hSocketIn; + nRecvVersion = INIT_PROTO_VERSION; + nLastSend = 0; + nLastRecv = 0; + nSendBytes = 0; + nRecvBytes = 0; + nTimeConnected = GetTime(); + addr = addrIn; + addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; + nVersion = 0; + strSubVer = ""; + fWhitelisted = false; + fOneShot = false; + fClient = false; // set by version message + fInbound = fInboundIn; + fNetworkNode = false; + fSuccessfullyConnected = false; + fDisconnect = false; + nRefCount = 0; + nSendSize = 0; + nSendOffset = 0; + hashContinue = 0; + pindexLastGetBlocksBegin = 0; + hashLastGetBlocksEnd = 0; + nStartingHeight = -1; + fStartSync = false; + fGetAddr = false; + fRelayTxes = false; + setInventoryKnown.max_size(SendBufferSize() / 1000); + pfilter = new CBloomFilter(); + nPingNonceSent = 0; + nPingUsecStart = 0; + nPingUsecTime = 0; + fPingQueued = false; + + { + LOCK(cs_nLastNodeId); + id = nLastNodeId++; + } + + if (fLogIPs) + LogPrint("net", "Added connection to %s peer=%d\n", addrName, id); + else + LogPrint("net", "Added connection peer=%d\n", id); + + // Be shy and don't send version until we hear + if (hSocket != INVALID_SOCKET && !fInbound) + PushVersion(); + + GetNodeSignals().InitializeNode(GetId(), this); +} + +CNode::~CNode() +{ + CloseSocket(hSocket); + + if (pfilter) + delete pfilter; + + GetNodeSignals().FinalizeNode(GetId()); +} + +void CNode::AskFor(const CInv& inv) +{ + // We're using mapAskFor as a priority queue, + // the key is the earliest time the request can be sent + int64_t nRequestTime; + limitedmap<CInv, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv); + if (it != mapAlreadyAskedFor.end()) + nRequestTime = it->second; + else + nRequestTime = 0; + LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str(), id); + + // Make sure not to reuse time indexes to keep things in the same order + int64_t nNow = GetTimeMicros() - 1000000; + static int64_t nLastTime; + ++nLastTime; + nNow = std::max(nNow, nLastTime); + nLastTime = nNow; + + // Each retry is 2 minutes after the last + nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); + if (it != mapAlreadyAskedFor.end()) + mapAlreadyAskedFor.update(it, nRequestTime); + else + mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime)); + mapAskFor.insert(std::make_pair(nRequestTime, inv)); +} + +void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend) +{ + ENTER_CRITICAL_SECTION(cs_vSend); + assert(ssSend.size() == 0); + ssSend << CMessageHeader(pszCommand, 0); + LogPrint("net", "sending: %s ", pszCommand); +} + +void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend) +{ + ssSend.clear(); + + LEAVE_CRITICAL_SECTION(cs_vSend); + + LogPrint("net", "(aborted)\n"); +} + +void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) +{ + // The -*messagestest options are intentionally not documented in the help message, + // since they are only used during development to debug the networking code and are + // not intended for end-users. + if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) + { + LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n"); + AbortMessage(); + return; + } + if (mapArgs.count("-fuzzmessagestest")) + Fuzz(GetArg("-fuzzmessagestest", 10)); + + if (ssSend.size() == 0) + return; + + // Set the size + unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; + memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize)); + + // Set the checksum + uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); + memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); + + LogPrint("net", "(%d bytes) peer=%d\n", nSize, id); + + std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); + ssSend.GetAndClear(*it); + nSendSize += (*it).size(); + + // If write queue empty, attempt "optimistic write" + if (it == vSendMsg.begin()) + SocketSendData(this); + + LEAVE_CRITICAL_SECTION(cs_vSend); +} @@ -16,7 +16,7 @@ #include "random.h" #include "sync.h" #include "uint256.h" -#include "util.h" +#include "utilstrencodings.h" #include <deque> #include <stdint.h> @@ -25,6 +25,7 @@ #include <arpa/inet.h> #endif +#include <boost/filesystem/path.hpp> #include <boost/foreach.hpp> #include <boost/signals2/signal.hpp> @@ -51,8 +52,8 @@ static const bool DEFAULT_UPNP = USE_UPNP; static const bool DEFAULT_UPNP = false; #endif -inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } -inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } +unsigned int ReceiveFloodSize(); +unsigned int SendBufferSize(); void AddOneShot(std::string strDest); bool RecvLine(SOCKET hSocket, std::string& strLine); @@ -300,70 +301,8 @@ public: // Whether a ping is requested. bool fPingQueued; - CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), setAddrKnown(5000) - { - nServices = 0; - hSocket = hSocketIn; - nRecvVersion = INIT_PROTO_VERSION; - nLastSend = 0; - nLastRecv = 0; - nSendBytes = 0; - nRecvBytes = 0; - nTimeConnected = GetTime(); - addr = addrIn; - addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; - nVersion = 0; - strSubVer = ""; - fWhitelisted = false; - fOneShot = false; - fClient = false; // set by version message - fInbound = fInboundIn; - fNetworkNode = false; - fSuccessfullyConnected = false; - fDisconnect = false; - nRefCount = 0; - nSendSize = 0; - nSendOffset = 0; - hashContinue = 0; - pindexLastGetBlocksBegin = 0; - hashLastGetBlocksEnd = 0; - nStartingHeight = -1; - fStartSync = false; - fGetAddr = false; - fRelayTxes = false; - setInventoryKnown.max_size(SendBufferSize() / 1000); - pfilter = new CBloomFilter(); - nPingNonceSent = 0; - nPingUsecStart = 0; - nPingUsecTime = 0; - fPingQueued = false; - - { - LOCK(cs_nLastNodeId); - id = nLastNodeId++; - } - - if (fLogIPs) - LogPrint("net", "Added connection to %s peer=%d\n", addrName, id); - else - LogPrint("net", "Added connection peer=%d\n", id); - - // Be shy and don't send version until we hear - if (hSocket != INVALID_SOCKET && !fInbound) - PushVersion(); - - GetNodeSignals().InitializeNode(GetId(), this); - } - - ~CNode() - { - CloseSocket(hSocket); - - if (pfilter) - delete pfilter; - - GetNodeSignals().FinalizeNode(GetId()); - } + CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false); + ~CNode(); private: // Network usage totals @@ -452,96 +391,16 @@ public: } } - void AskFor(const CInv& inv) - { - // We're using mapAskFor as a priority queue, - // the key is the earliest time the request can be sent - int64_t nRequestTime; - limitedmap<CInv, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv); - if (it != mapAlreadyAskedFor.end()) - nRequestTime = it->second; - else - nRequestTime = 0; - LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str(), id); - - // Make sure not to reuse time indexes to keep things in the same order - int64_t nNow = GetTimeMicros() - 1000000; - static int64_t nLastTime; - ++nLastTime; - nNow = std::max(nNow, nLastTime); - nLastTime = nNow; - - // Each retry is 2 minutes after the last - nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); - if (it != mapAlreadyAskedFor.end()) - mapAlreadyAskedFor.update(it, nRequestTime); - else - mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime)); - mapAskFor.insert(std::make_pair(nRequestTime, inv)); - } - - + void AskFor(const CInv& inv); // TODO: Document the postcondition of this function. Is cs_vSend locked? - void BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend) - { - ENTER_CRITICAL_SECTION(cs_vSend); - assert(ssSend.size() == 0); - ssSend << CMessageHeader(pszCommand, 0); - LogPrint("net", "sending: %s ", pszCommand); - } + void BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend); // TODO: Document the precondition of this function. Is cs_vSend locked? - void AbortMessage() UNLOCK_FUNCTION(cs_vSend) - { - ssSend.clear(); - - LEAVE_CRITICAL_SECTION(cs_vSend); - - LogPrint("net", "(aborted)\n"); - } + void AbortMessage() UNLOCK_FUNCTION(cs_vSend); // TODO: Document the precondition of this function. Is cs_vSend locked? - void EndMessage() UNLOCK_FUNCTION(cs_vSend) - { - // The -*messagestest options are intentionally not documented in the help message, - // since they are only used during development to debug the networking code and are - // not intended for end-users. - if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) - { - LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n"); - AbortMessage(); - return; - } - if (mapArgs.count("-fuzzmessagestest")) - Fuzz(GetArg("-fuzzmessagestest", 10)); - - if (ssSend.size() == 0) - return; - - // Set the size - unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; - memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize)); - - // Set the checksum - uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); - memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); - - LogPrint("net", "(%d bytes) peer=%d\n", nSize, id); - - std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); - ssSend.GetAndClear(*it); - nSendSize += (*it).size(); - - // If write queue empty, attempt "optimistic write" - if (it == vSendMsg.begin()) - SocketSendData(this); - - LEAVE_CRITICAL_SECTION(cs_vSend); - } + void EndMessage() UNLOCK_FUNCTION(cs_vSend); void PushVersion(); diff --git a/src/netbase.cpp b/src/netbase.cpp index e9207da330..d5821d4465 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -13,6 +13,7 @@ #include "sync.h" #include "uint256.h" #include "util.h" +#include "utilstrencodings.h" #ifdef HAVE_GETADDRINFO_A #include <netdb.h> @@ -27,6 +28,7 @@ #include <boost/algorithm/string/case_conv.hpp> // for to_lower() #include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith() +#include <boost/thread.hpp> #if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 diff --git a/src/noui.cpp b/src/noui.cpp index 32c861b0d9..8b00fd4057 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -8,6 +8,7 @@ #include "ui_interface.h" #include "util.h" +#include <cstdio> #include <stdint.h> #include <string> diff --git a/src/pow.cpp b/src/pow.cpp index c0d0a7ca20..b091cbec3d 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -9,6 +9,7 @@ #include "core.h" #include "main.h" #include "uint256.h" +#include "util.h" unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock) { diff --git a/src/protocol.h b/src/protocol.h index 94d313e9c0..d7565584af 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -64,7 +64,6 @@ class CMessageHeader enum { NODE_NETWORK = (1 << 0), - NODE_GETUTXOS = (1 << 1), // Bits 24-31 are reserved for temporary experiments. Just pick a bit that // isn't getting used, or one not being used much, and notify the diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index a43e7cb756..6cc6b99ceb 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -33,6 +33,7 @@ #include <stdint.h> #include <boost/filesystem/operations.hpp> +#include <boost/thread.hpp> #include <QApplication> #include <QDebug> #include <QLibraryInfo> @@ -414,8 +415,8 @@ void BitcoinApplication::initializeResult(int retval) { walletModel = new WalletModel(pwalletMain, optionsModel); - window->addWallet("~Default", walletModel); - window->setCurrentWallet("~Default"); + window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel); + window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET); connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)), paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray))); diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 6dba62035b..42bb091e25 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -17,6 +17,9 @@ <file alias="transaction_3">res/icons/clock3.png</file> <file alias="transaction_4">res/icons/clock4.png</file> <file alias="transaction_5">res/icons/clock5.png</file> + <file alias="eye">res/icons/eye.png</file> + <file alias="eye_minus">res/icons/eye_minus.png</file> + <file alias="eye_plus">res/icons/eye_plus.png</file> <file alias="options">res/icons/configure.png</file> <file alias="receiving_addresses">res/icons/receive.png</file> <file alias="editpaste">res/icons/editpaste.png</file> diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bfca5e8d1a..790301a1eb 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -24,6 +24,7 @@ #endif #include "init.h" +#include "util.h" #include "ui_interface.h" #include <iostream> @@ -61,10 +62,35 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : QMainWindow(parent), clientModel(0), walletFrame(0), + unitDisplayControl(0), + labelEncryptionIcon(0), + labelConnectionsIcon(0), + labelBlocksIcon(0), + progressBarLabel(0), + progressBar(0), + progressDialog(0), + appMenuBar(0), + overviewAction(0), + historyAction(0), + quitAction(0), + sendCoinsAction(0), + usedSendingAddressesAction(0), + usedReceivingAddressesAction(0), + signMessageAction(0), + verifyMessageAction(0), + aboutAction(0), + receiveCoinsAction(0), + optionsAction(0), + toggleHideAction(0), encryptWalletAction(0), + backupWalletAction(0), changePassphraseAction(0), aboutQtAction(0), + openRPCConsoleAction(0), + openAction(0), + showHelpMessageAction(0), trayIcon(0), + trayIconMenu(0), notificator(0), rpcConsole(0), prevBlocks(0), @@ -425,8 +451,15 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) walletFrame->setClientModel(clientModel); } #endif - - this->unitDisplayControl->setOptionsModel(clientModel->getOptionsModel()); + unitDisplayControl->setOptionsModel(clientModel->getOptionsModel()); + } else { + // Disable possibility to show main window via action + toggleHideAction->setEnabled(false); + if(trayIconMenu) + { + // Disable context menu on tray icon + trayIconMenu->clear(); + } } } @@ -495,7 +528,6 @@ void BitcoinGUI::createTrayIcon(bool fIsTestnet) void BitcoinGUI::createTrayIconMenu() { - QMenu *trayIconMenu; #ifndef Q_OS_MAC // return if trayIcon is unset (only on non-Mac OSes) if (!trayIcon) @@ -536,7 +568,7 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) if(reason == QSystemTrayIcon::Trigger) { // Click on system tray icon triggers show/hide of the main window - toggleHideAction->trigger(); + toggleHidden(); } } #endif @@ -922,6 +954,7 @@ void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) { if(!clientModel) return; + // activateWindow() (sometimes) helps with keyboard focus on Windows if (isHidden()) { @@ -1006,9 +1039,10 @@ void BitcoinGUI::unsubscribeFromCoreSignals() uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); } -UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel() +UnitDisplayStatusBarControl::UnitDisplayStatusBarControl() : + optionsModel(0), + menu(0) { - optionsModel = 0; createContextMenu(); setToolTip(tr("Unit to show amounts in. Click to select another unit.")); } @@ -1068,4 +1102,3 @@ void UnitDisplayStatusBarControl::onMenuSelection(QAction* action) optionsModel->setDisplayUnit(action->data()); } } - diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 30dd7ae317..30b05cb7d9 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -103,6 +103,7 @@ private: QAction *showHelpMessageAction; QSystemTrayIcon *trayIcon; + QMenu *trayIconMenu; Notificator *notificator; RPCConsole *rpcConsole; @@ -219,6 +220,7 @@ protected: private: OptionsModel *optionsModel; QMenu* menu; + /** Shows context menu with Display Unit options by the mouse coordinates */ void onDisplayUnitsClicked(const QPoint& point); /** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */ diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 4c21eb5594..9c9ff5b3a1 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -13,6 +13,7 @@ #include "main.h" #include "net.h" #include "ui_interface.h" +#include "util.h" #include <stdint.h> diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index fd1d446f9b..c775a7f8d6 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -21,6 +21,7 @@ #include "wallet.h" // for CWallet::minTxFee #endif +#include <boost/thread.hpp> #include <QDir> #include <QIntValidator> #include <QLocale> diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index b5a3de48ca..15501b8a8a 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -57,7 +57,15 @@ public: } painter->setPen(foreground); - painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address); + QRect boundingRect; + painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address, &boundingRect); + + if (index.data(TransactionTableModel::WatchonlyRole).toBool()) + { + QIcon iconWatchonly = qvariant_cast<QIcon>(index.data(TransactionTableModel::WatchonlyDecorationRole)); + QRect watchonlyRect(boundingRect.right() + 5, mainRect.top()+ypad+halfheight, 16, halfheight); + iconWatchonly.paint(painter, watchonlyRect); + } if(amount < 0) { @@ -160,18 +168,25 @@ void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 // for the non-mining users bool showImmature = immatureBalance != 0; bool showWatchOnlyImmature = watchImmatureBalance != 0; - bool showWatchOnly = (watchOnlyBalance != 0 || watchUnconfBalance != 0 || showWatchOnlyImmature); // for symmetry reasons also show immature label when the watch-only one is shown ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature); - ui->labelSpendable->setVisible(showWatchOnly); // show spendable label (only when watch-only is active) - ui->labelWatchonly->setVisible(showWatchOnly); // show watch-only label - ui->lineWatchBalance->setVisible(showWatchOnly); // show watch-only balance separator line - ui->labelWatchAvailable->setVisible(showWatchOnly); // show watch-only available balance ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watch-only immature balance - ui->labelWatchPending->setVisible(showWatchOnly); // show watch-only pending balance - ui->labelWatchTotal->setVisible(showWatchOnly); // show watch-only total balance +} + +// show/hide watch-only labels +void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly) +{ + ui->labelSpendable->setVisible(showWatchOnly); // show spendable label (only when watch-only is active) + ui->labelWatchonly->setVisible(showWatchOnly); // show watch-only label + ui->lineWatchBalance->setVisible(showWatchOnly); // show watch-only balance separator line + ui->labelWatchAvailable->setVisible(showWatchOnly); // show watch-only available balance + ui->labelWatchPending->setVisible(showWatchOnly); // show watch-only pending balance + ui->labelWatchTotal->setVisible(showWatchOnly); // show watch-only total balance + + if (!showWatchOnly) + ui->labelWatchImmature->hide(); } void OverviewPage::setClientModel(ClientModel *model) @@ -208,6 +223,9 @@ void OverviewPage::setWalletModel(WalletModel *model) connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64, qint64))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); + + updateWatchOnlyLabels(model->haveWatchOnly()); + connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool))); } // update the display unit, to not use the default ("BTC") diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index fe00106770..f46374efba 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -58,6 +58,7 @@ private slots: void updateDisplayUnit(); void handleTransactionClicked(const QModelIndex &index); void updateAlerts(const QString &warnings); + void updateWatchOnlyLabels(bool showWatchOnly); }; #endif // OVERVIEWPAGE_H diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index a9f1566d62..f6a4b599de 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -11,6 +11,7 @@ #include "base58.h" #include "ui_interface.h" +#include "util.h" #include "wallet.h" #include <cstdlib> diff --git a/src/qt/res/icons/eye.png b/src/qt/res/icons/eye.png Binary files differnew file mode 100644 index 0000000000..c4d182adbf --- /dev/null +++ b/src/qt/res/icons/eye.png diff --git a/src/qt/res/icons/eye_minus.png b/src/qt/res/icons/eye_minus.png Binary files differnew file mode 100644 index 0000000000..08b048eae3 --- /dev/null +++ b/src/qt/res/icons/eye_minus.png diff --git a/src/qt/res/icons/eye_plus.png b/src/qt/res/icons/eye_plus.png Binary files differnew file mode 100644 index 0000000000..4ad653156f --- /dev/null +++ b/src/qt/res/icons/eye_plus.png diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 4f6e3169f5..8258e719a3 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -15,6 +15,7 @@ #include "transactionrecord.h" #include "timedata.h" #include "ui_interface.h" +#include "util.h" #include "wallet.h" #include <stdint.h> diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index f9546fddb5..6ab029173b 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -22,6 +22,7 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : dateTo(MAX_DATE), addrPrefix(), typeFilter(ALL_TYPES), + watchOnlyFilter(WatchOnlyFilter_All), minAmount(0), limitRows(-1), showInactive(true) @@ -34,6 +35,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & int type = index.data(TransactionTableModel::TypeRole).toInt(); QDateTime datetime = index.data(TransactionTableModel::DateRole).toDateTime(); + bool involvesWatchAddress = index.data(TransactionTableModel::WatchonlyRole).toBool(); QString address = index.data(TransactionTableModel::AddressRole).toString(); QString label = index.data(TransactionTableModel::LabelRole).toString(); qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); @@ -43,6 +45,10 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & return false; if(!(TYPE(type) & typeFilter)) return false; + if (involvesWatchAddress && watchOnlyFilter == WatchOnlyFilter_No) + return false; + if (!involvesWatchAddress && watchOnlyFilter == WatchOnlyFilter_Yes) + return false; if(datetime < dateFrom || datetime > dateTo) return false; if (!address.contains(addrPrefix, Qt::CaseInsensitive) && !label.contains(addrPrefix, Qt::CaseInsensitive)) @@ -78,6 +84,12 @@ void TransactionFilterProxy::setMinAmount(qint64 minimum) invalidateFilter(); } +void TransactionFilterProxy::setWatchOnlyFilter(WatchOnlyFilter filter) +{ + this->watchOnlyFilter = filter; + invalidateFilter(); +} + void TransactionFilterProxy::setLimit(int limit) { this->limitRows = limit; diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index 9919bc3fd6..f408317b53 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -25,6 +25,13 @@ public: static quint32 TYPE(int type) { return 1<<type; } + enum WatchOnlyFilter + { + WatchOnlyFilter_All, + WatchOnlyFilter_Yes, + WatchOnlyFilter_No + }; + void setDateRange(const QDateTime &from, const QDateTime &to); void setAddressPrefix(const QString &addrPrefix); /** @@ -32,6 +39,7 @@ public: */ void setTypeFilter(quint32 modes); void setMinAmount(qint64 minimum); + void setWatchOnlyFilter(WatchOnlyFilter filter); /** Set maximum number of rows returned, -1 if unlimited. */ void setLimit(int limit); @@ -49,6 +57,7 @@ private: QDateTime dateTo; QString addrPrefix; quint32 typeFilter; + WatchOnlyFilter watchOnlyFilter; qint64 minAmount; int limitRows; bool showInactive; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 1647bc776f..1a1f726bf8 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -27,6 +27,7 @@ // Amount column is right-aligned it contains numbers static int column_alignments[] = { Qt::AlignLeft|Qt::AlignVCenter, /* status */ + Qt::AlignLeft|Qt::AlignVCenter, /* watchonly */ Qt::AlignLeft|Qt::AlignVCenter, /* date */ Qt::AlignLeft|Qt::AlignVCenter, /* type */ Qt::AlignLeft|Qt::AlignVCenter, /* address */ @@ -234,7 +235,7 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren walletModel(parent), priv(new TransactionTablePriv(wallet, this)) { - columns << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); + columns << QString() << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); priv->refreshWallet(); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); @@ -393,22 +394,19 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const { - // mark transactions involving watch-only addresses: - QString watchAddress = wtx->involvesWatchAddress ? " (w) " : ""; - switch(wtx->type) { case TransactionRecord::RecvFromOther: - return QString::fromStdString(wtx->address) + watchAddress; + return QString::fromStdString(wtx->address); case TransactionRecord::RecvWithAddress: case TransactionRecord::SendToAddress: case TransactionRecord::Generated: - return lookupAddress(wtx->address, tooltip) + watchAddress; + return lookupAddress(wtx->address, tooltip); case TransactionRecord::SendToOther: - return QString::fromStdString(wtx->address) + watchAddress; + return QString::fromStdString(wtx->address); case TransactionRecord::SendToSelf: default: - return tr("(n/a)") + watchAddress; + return tr("(n/a)"); } } @@ -482,6 +480,14 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) return QColor(0,0,0); } +QVariant TransactionTableModel::txWatchonlyDecoration(const TransactionRecord *wtx) const +{ + if (wtx->involvesWatchAddress) + return QIcon(":/icons/eye"); + else + return QVariant(); +} + QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const { QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec); @@ -506,6 +512,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { case Status: return txStatusDecoration(rec); + case Watchonly: + return txWatchonlyDecoration(rec); case ToAddress: return txAddressDecoration(rec); } @@ -533,6 +541,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return rec->time; case Type: return formatTxType(rec); + case Watchonly: + return (rec->involvesWatchAddress ? 1 : 0); case ToAddress: return formatTxToAddress(rec, true); case Amount: @@ -562,6 +572,10 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return rec->type; case DateRole: return QDateTime::fromTime_t(static_cast<uint>(rec->time)); + case WatchonlyRole: + return rec->involvesWatchAddress; + case WatchonlyDecorationRole: + return txWatchonlyDecoration(rec); case LongDescriptionRole: return priv->describe(rec, walletModel->getOptionsModel()->getDisplayUnit()); case AddressRole: @@ -606,6 +620,8 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat return tr("Date and time that the transaction was received."); case Type: return tr("Type of transaction."); + case Watchonly: + return tr("Whether or not a watch-only address is involved in this transaction."); case ToAddress: return tr("Destination address of transaction."); case Amount: diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 2124d3dd1c..413f3f9bf1 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -28,10 +28,11 @@ public: enum ColumnIndex { Status = 0, - Date = 1, - Type = 2, - ToAddress = 3, - Amount = 4 + Watchonly = 1, + Date = 2, + Type = 3, + ToAddress = 4, + Amount = 5 }; /** Roles to get specific information from a transaction row. @@ -42,6 +43,10 @@ public: TypeRole = Qt::UserRole, /** Date and time this transaction was created */ DateRole, + /** Watch-only boolean */ + WatchonlyRole, + /** Watch-only icon */ + WatchonlyDecorationRole, /** Long description (HTML format) */ LongDescriptionRole, /** Address of transaction */ @@ -83,6 +88,7 @@ private: 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 txWatchonlyDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const; public slots: diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 7e8b71d8ea..2d34d58129 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -51,6 +51,13 @@ TransactionView::TransactionView(QWidget *parent) : hlayout->addSpacing(23); #endif + watchOnlyWidget = new QComboBox(this); + watchOnlyWidget->setFixedWidth(24); + watchOnlyWidget->addItem("", TransactionFilterProxy::WatchOnlyFilter_All); + watchOnlyWidget->addItem(QIcon(":/icons/eye_plus"), "", TransactionFilterProxy::WatchOnlyFilter_Yes); + watchOnlyWidget->addItem(QIcon(":/icons/eye_minus"), "", TransactionFilterProxy::WatchOnlyFilter_No); + hlayout->addWidget(watchOnlyWidget); + dateWidget = new QComboBox(this); #ifdef Q_OS_MAC dateWidget->setFixedWidth(121); @@ -150,6 +157,7 @@ TransactionView::TransactionView(QWidget *parent) : connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); + connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int))); connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); @@ -187,6 +195,7 @@ void TransactionView::setModel(WalletModel *model) transactionView->verticalHeader()->hide(); transactionView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH); + transactionView->setColumnWidth(TransactionTableModel::Watchonly, WATCHONLY_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Date, DATE_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH); @@ -211,6 +220,12 @@ void TransactionView::setModel(WalletModel *model) } } } + + // show/hide column Watch-only + updateWatchOnlyColumn(model->haveWatchOnly()); + + // Watch-only signal + connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyColumn(bool))); } } @@ -270,6 +285,14 @@ void TransactionView::chooseType(int idx) typeWidget->itemData(idx).toInt()); } +void TransactionView::chooseWatchonly(int idx) +{ + if(!transactionProxyModel) + return; + transactionProxyModel->setWatchOnlyFilter( + (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt()); +} + void TransactionView::changedPrefix(const QString &prefix) { if(!transactionProxyModel) @@ -307,6 +330,8 @@ void TransactionView::exportClicked() // name, column, role writer.setModel(transactionProxyModel); writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole); + if (model && model->haveWatchOnly()) + writer.addColumn(tr("Watchonly"), TransactionTableModel::Watchonly); writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole); writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole); @@ -501,3 +526,10 @@ bool TransactionView::eventFilter(QObject *obj, QEvent *event) } return QWidget::eventFilter(obj, event); } + +// show/hide column Watch-only +void TransactionView::updateWatchOnlyColumn(bool fHaveWatchOnly) +{ + watchOnlyWidget->setVisible(fHaveWatchOnly); + transactionView->setColumnHidden(TransactionTableModel::Watchonly, !fHaveWatchOnly); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 618efbc565..b249e00411 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -50,6 +50,7 @@ public: enum ColumnWidths { STATUS_COLUMN_WIDTH = 23, + WATCHONLY_COLUMN_WIDTH = 23, DATE_COLUMN_WIDTH = 120, TYPE_COLUMN_WIDTH = 120, AMOUNT_MINIMUM_COLUMN_WIDTH = 120, @@ -63,6 +64,7 @@ private: QComboBox *dateWidget; QComboBox *typeWidget; + QComboBox *watchOnlyWidget; QLineEdit *addressWidget; QLineEdit *amountWidget; @@ -91,6 +93,7 @@ private slots: void copyAmount(); void copyTxID(); void openThirdPartyTxUrl(QString url); + void updateWatchOnlyColumn(bool fHaveWatchOnly); signals: void doubleClicked(const QModelIndex&); @@ -101,6 +104,7 @@ signals: public slots: void chooseDate(int idx); void chooseType(int idx); + void chooseWatchonly(int idx); void changedPrefix(const QString &prefix); void changedAmount(const QString &amount); void exportClicked(); diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 5fb0da145d..7df9d1bc2d 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -10,9 +10,10 @@ #include "clientmodel.h" #include "guiutil.h" -#include "clientversion.h" #include "init.h" -#include "util.h" +#include "version.h" + +#include <stdio.h> #include <QLabel> #include <QRegExp> diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index c5f8fb6a9c..92c22f5692 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -35,6 +35,7 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p cachedNumBlocks(0) { fProcessingQueuedTransactions = false; + fHaveWatchOnly = wallet->HaveWatchOnly(); addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); @@ -80,6 +81,11 @@ qint64 WalletModel::getImmatureBalance() const return wallet->GetImmatureBalance(); } +bool WalletModel::haveWatchOnly() const +{ + return fHaveWatchOnly; +} + qint64 WalletModel::getWatchBalance() const { return wallet->GetWatchOnlyBalance(); @@ -131,9 +137,15 @@ void WalletModel::checkBalanceChanged() qint64 newBalance = getBalance(); qint64 newUnconfirmedBalance = getUnconfirmedBalance(); qint64 newImmatureBalance = getImmatureBalance(); - qint64 newWatchOnlyBalance = getWatchBalance(); - qint64 newWatchUnconfBalance = getWatchUnconfirmedBalance(); - qint64 newWatchImmatureBalance = getWatchImmatureBalance(); + qint64 newWatchOnlyBalance = 0; + qint64 newWatchUnconfBalance = 0; + qint64 newWatchImmatureBalance = 0; + if (haveWatchOnly()) + { + newWatchOnlyBalance = getWatchBalance(); + newWatchUnconfBalance = getWatchUnconfirmedBalance(); + newWatchImmatureBalance = getWatchImmatureBalance(); + } if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance || cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance) @@ -165,6 +177,12 @@ void WalletModel::updateAddressBook(const QString &address, const QString &label addressTableModel->updateEntry(address, label, isMine, purpose, status); } +void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) +{ + fHaveWatchOnly = fHaveWatchonly; + emit notifyWatchonlyChanged(fHaveWatchonly); +} + bool WalletModel::validateAddress(const QString &address) { CBitcoinAddress addressParsed(address.toStdString()); @@ -479,6 +497,12 @@ static void ShowProgress(WalletModel *walletmodel, const std::string &title, int } } +static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly) +{ + QMetaObject::invokeMethod(walletmodel, "updateWatchOnlyFlag", Qt::QueuedConnection, + Q_ARG(bool, fHaveWatchonly)); +} + void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet @@ -486,6 +510,7 @@ void WalletModel::subscribeToCoreSignals() wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); + wallet->NotifyWatchonlyChanged.connect(boost::bind(NotifyWatchonlyChanged, this, _1)); } void WalletModel::unsubscribeFromCoreSignals() @@ -495,6 +520,7 @@ void WalletModel::unsubscribeFromCoreSignals() wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + wallet->NotifyWatchonlyChanged.disconnect(boost::bind(NotifyWatchonlyChanged, this, _1)); } // WalletModel::UnlockContext implementation diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 2a74a6aa79..b3a401e4cc 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -128,6 +128,7 @@ public: qint64 getBalance(const CCoinControl *coinControl = NULL) const; qint64 getUnconfirmedBalance() const; qint64 getImmatureBalance() const; + bool haveWatchOnly() const; qint64 getWatchBalance() const; qint64 getWatchUnconfirmedBalance() const; qint64 getWatchImmatureBalance() const; @@ -197,6 +198,7 @@ public: private: CWallet *wallet; bool fProcessingQueuedTransactions; + bool fHaveWatchOnly; // Wallet has an options model for wallet-specific options // (transaction fee, for example) @@ -244,6 +246,9 @@ signals: // Show progress dialog e.g. for rescan void showProgress(const QString &title, int nProgress); + // Watch-only address added + void notifyWatchonlyChanged(bool fHaveWatchonly); + public slots: /* Wallet status might have changed */ void updateStatus(); @@ -251,6 +256,8 @@ public slots: void updateTransaction(const QString &hash, int status); /* New, updated or removed address book entry */ void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); + /* Watchonly added */ + void updateWatchOnlyFlag(bool fHaveWatchonly); /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ void pollBalanceChanged(); /* Needed to update fProcessingQueuedTransactions through a QueuedConnection */ diff --git a/src/random.cpp b/src/random.cpp index 0d20d205ac..22c942acc0 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -8,12 +8,14 @@ #ifdef WIN32 #include "compat.h" // for Windows API #endif +#include "serialize.h" // for begin_ptr(vec) #include "util.h" // for LogPrint() +#include "utilstrencodings.h" // for GetTime() #ifndef WIN32 #include <sys/time.h> #endif - +#include <limits> #include <openssl/crypto.h> #include <openssl/err.h> #include <openssl/rand.h> diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index e511fe4222..58cab14045 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -7,6 +7,7 @@ #include "main.h" #include "rpcserver.h" #include "sync.h" +#include "util.h" #include <stdint.h> diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index ff2361482b..e68ddee040 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -7,6 +7,8 @@ #include "init.h" #include "main.h" #include "sync.h" +#include "utiltime.h" +#include "util.h" #include "wallet.h" #include <fstream> diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index edab427cbf..6e508abcda 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -11,6 +11,7 @@ #include "miner.h" #include "pow.h" #include "core_io.h" +#include "util.h" #ifdef ENABLE_WALLET #include "db.h" #include "wallet.h" diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index 643208b3b6..808b9bbd2a 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -6,6 +6,10 @@ #include "rpcprotocol.h" #include "util.h" +#include "tinyformat.h" +#include "utilstrencodings.h" +#include "utiltime.h" +#include "version.h" #include <stdint.h> diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 524627e2de..c9133bd3d2 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -23,6 +23,7 @@ #include <boost/iostreams/concepts.hpp> #include <boost/iostreams/stream.hpp> #include <boost/shared_ptr.hpp> +#include <boost/thread.hpp> #include "json/json_spirit_writer_template.h" using namespace boost; @@ -82,6 +83,11 @@ void RPCTypeCheck(const Object& o, } } +static inline int64_t roundint64(double d) +{ + return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); +} + int64_t AmountFromValue(const Value& value) { double dAmount = value.get_real(); diff --git a/src/script.cpp b/src/script.cpp index 942e8810db..28c50a1358 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -18,8 +18,9 @@ #include "util.h" #include <boost/foreach.hpp> -#include <boost/tuple/tuple.hpp> +#include <boost/thread.hpp> #include <boost/tuple/tuple_comparison.hpp> +#include <boost/tuple/tuple.hpp> using namespace std; using namespace boost; @@ -1874,9 +1875,11 @@ bool CScript::IsPushOnly() const const_iterator pc = begin(); while (pc < end()) { + // Note how a script with an invalid PUSHDATA returns False. opcodetype opcode; if (!GetOp(pc, opcode)) return false; + // Note that IsPushOnly() *does* consider OP_RESERVED to be a // push-type opcode, however execution of OP_RESERVED fails, so // it's not relevant to P2SH as the scriptSig would fail prior to diff --git a/src/script.h b/src/script.h index 1e2cc94efe..462e3f2302 100644 --- a/src/script.h +++ b/src/script.h @@ -7,7 +7,8 @@ #define H_BITCOIN_SCRIPT #include "key.h" -#include "util.h" +#include "utilstrencodings.h" +#include "tinyformat.h" #include <stdexcept> #include <stdint.h> diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 5e17555e7a..4ecf6e2535 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -14,6 +14,7 @@ #include "pow.h" #include "script.h" #include "serialize.h" +#include "util.h" #include <stdint.h> diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index e3066a51ab..4af87cf8ef 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -11,6 +11,7 @@ #include "serialize.h" #include "util.h" +#include "utilstrencodings.h" #include "version.h" #include <fstream> diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp index 2752a0b215..69888da3df 100644 --- a/src/test/allocator_tests.cpp +++ b/src/test/allocator_tests.cpp @@ -4,6 +4,8 @@ #include "util.h" +#include "allocators.h" + #include <boost/test/unit_test.hpp> BOOST_AUTO_TEST_SUITE(allocator_tests) diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp index 87473585e3..68617abbdd 100644 --- a/src/test/base32_tests.cpp +++ b/src/test/base32_tests.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "util.h" +#include "utilstrencodings.h" #include <boost/test/unit_test.hpp> diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp index d4a23d9aa0..f2bf3326ad 100644 --- a/src/test/base64_tests.cpp +++ b/src/test/base64_tests.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "util.h" +#include "utilstrencodings.h" #include <boost/test/unit_test.hpp> diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index 10352240f4..fdea12846a 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -9,6 +9,7 @@ #include "main.h" +#include "utiltime.h" #include <cstdio> diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index a17278b803..a3eec270ee 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -6,7 +6,7 @@ #include "crypto/sha1.h" #include "crypto/sha2.h" #include "random.h" -#include "util.h" +#include "utilstrencodings.h" #include <vector> diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp index 4568c8769b..b8e290f071 100644 --- a/src/test/hash_tests.cpp +++ b/src/test/hash_tests.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "hash.h" -#include "util.h" +#include "utilstrencodings.h" #include <vector> diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index bc9f31c077..77c44501ab 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -394,4 +394,15 @@ BOOST_AUTO_TEST_CASE(script_standard_push) } } +BOOST_AUTO_TEST_CASE(script_IsPushOnly_on_invalid_scripts) +{ + // IsPushOnly returns false when given a script containing only pushes that + // are invalid due to truncation. IsPushOnly() is consensus critical + // because P2SH evaluation uses it, although this specific behavior should + // not be consensus critical as the P2SH evaluation would fail first due to + // the invalid push. Still, it doesn't hurt to test it explicitly. + static const unsigned char direct[] = { 1 }; + BOOST_CHECK(!CScript(direct, direct+sizeof(direct)).IsPushOnly()); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 443b5853b2..68fad8d038 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -16,6 +16,7 @@ #include <boost/filesystem.hpp> #include <boost/test/unit_test.hpp> +#include <boost/thread.hpp> CClientUIInterface uiInterface; CWallet* pwalletMain; diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp new file mode 100644 index 0000000000..aa4fa0d500 --- /dev/null +++ b/src/test/timedata_tests.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2011-2014 The Bitcoin Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// +#include "timedata.h" + +#include <boost/test/unit_test.hpp> + +using namespace std; + +BOOST_AUTO_TEST_SUITE(timedata_tests) + +BOOST_AUTO_TEST_CASE(util_MedianFilter) +{ + CMedianFilter<int> filter(5, 15); + + BOOST_CHECK_EQUAL(filter.median(), 15); + + filter.input(20); // [15 20] + BOOST_CHECK_EQUAL(filter.median(), 17); + + filter.input(30); // [15 20 30] + BOOST_CHECK_EQUAL(filter.median(), 20); + + filter.input(3); // [3 15 20 30] + BOOST_CHECK_EQUAL(filter.median(), 17); + + filter.input(7); // [3 7 15 20 30] + BOOST_CHECK_EQUAL(filter.median(), 15); + + filter.input(18); // [3 7 18 20 30] + BOOST_CHECK_EQUAL(filter.median(), 18); + + filter.input(0); // [0 3 7 18 30] + BOOST_CHECK_EQUAL(filter.median(), 7); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 068b9f29c8..e077c9de3b 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -4,8 +4,11 @@ #include "util.h" +#include "core.h" #include "random.h" #include "sync.h" +#include "utilstrencodings.h" +#include "utilmoneystr.h" #include <stdint.h> #include <vector> @@ -36,31 +39,6 @@ BOOST_AUTO_TEST_CASE(util_criticalsection) } while(0); } -BOOST_AUTO_TEST_CASE(util_MedianFilter) -{ - CMedianFilter<int> filter(5, 15); - - BOOST_CHECK_EQUAL(filter.median(), 15); - - filter.input(20); // [15 20] - BOOST_CHECK_EQUAL(filter.median(), 17); - - filter.input(30); // [15 20 30] - BOOST_CHECK_EQUAL(filter.median(), 20); - - filter.input(3); // [3 15 20 30] - BOOST_CHECK_EQUAL(filter.median(), 17); - - filter.input(7); // [3 7 15 20 30] - BOOST_CHECK_EQUAL(filter.median(), 15); - - filter.input(18); // [3 7 18 20 30] - BOOST_CHECK_EQUAL(filter.median(), 18); - - filter.input(0); // [0 3 7 18 30] - BOOST_CHECK_EQUAL(filter.median(), 7); -} - static const unsigned char ParseHex_expected[65] = { 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, diff --git a/src/timedata.cpp b/src/timedata.cpp index 4576786b96..40cdb33f7a 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -8,6 +8,7 @@ #include "sync.h" #include "ui_interface.h" #include "util.h" +#include "utilstrencodings.h" #include <boost/foreach.hpp> @@ -35,6 +36,11 @@ int64_t GetAdjustedTime() return GetTime() + GetTimeOffset(); } +static int64_t abs64(int64_t n) +{ + return (n >= 0 ? n : -n); +} + void AddTimeData(const CNetAddr& ip, int64_t nTime) { int64_t nOffsetSample = nTime - GetTime(); diff --git a/src/timedata.h b/src/timedata.h index 0e7bdc2c1f..d0c84b3183 100644 --- a/src/timedata.h +++ b/src/timedata.h @@ -6,9 +6,68 @@ #define BITCOIN_TIMEDATA_H #include <stdint.h> +#include <vector> +#include <algorithm> +#include <assert.h> class CNetAddr; +/** Median filter over a stream of values. + * Returns the median of the last N numbers + */ +template <typename T> class CMedianFilter +{ +private: + std::vector<T> vValues; + std::vector<T> vSorted; + unsigned int nSize; +public: + CMedianFilter(unsigned int size, T initial_value): + nSize(size) + { + vValues.reserve(size); + vValues.push_back(initial_value); + vSorted = vValues; + } + + void input(T value) + { + if(vValues.size() == nSize) + { + vValues.erase(vValues.begin()); + } + vValues.push_back(value); + + vSorted.resize(vValues.size()); + std::copy(vValues.begin(), vValues.end(), vSorted.begin()); + std::sort(vSorted.begin(), vSorted.end()); + } + + T median() const + { + int size = vSorted.size(); + assert(size>0); + if(size & 1) // Odd number of elements + { + return vSorted[size/2]; + } + else // Even number of elements + { + return (vSorted[size/2-1] + vSorted[size/2]) / 2; + } + } + + int size() const + { + return vValues.size(); + } + + std::vector<T> sorted () const + { + return vSorted; + } +}; + /* Functions to keep track of adjusted P2P time */ int64_t GetTimeOffset(); int64_t GetAdjustedTime(); diff --git a/src/tinyformat.h b/src/tinyformat.h index 929cb66e4d..73d49a1fe4 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -121,6 +121,7 @@ namespace tfm = tinyformat; #include <cassert> #include <iostream> #include <sstream> +#include <stdexcept> #ifndef TINYFORMAT_ERROR # define TINYFORMAT_ERROR(reason) assert(0 && reason) diff --git a/src/txdb.cpp b/src/txdb.cpp index 7c0683aaf3..d4c6007558 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -9,6 +9,7 @@ #include "pow.h" #include "uint256.h" +#include <boost/thread.hpp> #include <stdint.h> using namespace std; @@ -27,7 +28,7 @@ void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { } -bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) { +bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const { return db.Read(make_pair('c', txid), coins); } @@ -37,11 +38,11 @@ bool CCoinsViewDB::SetCoins(const uint256 &txid, const CCoins &coins) { return db.WriteBatch(batch); } -bool CCoinsViewDB::HaveCoins(const uint256 &txid) { +bool CCoinsViewDB::HaveCoins(const uint256 &txid) const { return db.Exists(make_pair('c', txid)); } -uint256 CCoinsViewDB::GetBestBlock() { +uint256 CCoinsViewDB::GetBestBlock() const { uint256 hashBestChain; if (!db.Read('B', hashBestChain)) return uint256(0); @@ -105,8 +106,11 @@ bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { return Read('l', nFile); } -bool CCoinsViewDB::GetStats(CCoinsStats &stats) { - boost::scoped_ptr<leveldb::Iterator> pcursor(db.NewIterator()); +bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { + /* It seems that there are no "const iterators" for LevelDB. Since we + only need read operations on it, use a const-cast to get around + that restriction. */ + leveldb::Iterator *pcursor = const_cast<CLevelDBWrapper*>(&db)->NewIterator(); pcursor->SeekToFirst(); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); diff --git a/src/txdb.h b/src/txdb.h index 099f151776..f0b6b9e1dd 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -32,13 +32,13 @@ protected: public: CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); - bool GetCoins(const uint256 &txid, CCoins &coins); + bool GetCoins(const uint256 &txid, CCoins &coins) const; bool SetCoins(const uint256 &txid, const CCoins &coins); - bool HaveCoins(const uint256 &txid); - uint256 GetBestBlock(); + bool HaveCoins(const uint256 &txid) const; + uint256 GetBestBlock() const; bool SetBestBlock(const uint256 &hashBlock); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); - bool GetStats(CCoinsStats &stats); + bool GetStats(CCoinsStats &stats) const; }; /** Access to the block database (blocks/index/) */ diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 80cae68244..8af1f1c91b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -5,6 +5,7 @@ #include "core.h" #include "txmempool.h" +#include "util.h" #include <boost/circular_buffer.hpp> @@ -484,7 +485,7 @@ void CTxMemPool::clear() ++nTransactionsUpdated; } -void CTxMemPool::check(CCoinsViewCache *pcoins) const +void CTxMemPool::check(const CCoinsViewCache *pcoins) const { if (!fSanityCheck) return; @@ -505,7 +506,7 @@ void CTxMemPool::check(CCoinsViewCache *pcoins) const const CTransaction& tx2 = it2->second.GetTx(); assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); } else { - CCoins &coins = pcoins->GetCoins(txin.prevout.hash); + const CCoins &coins = pcoins->GetCoins(txin.prevout.hash); assert(coins.IsAvailable(txin.prevout.n)); } // Check whether its inputs are marked in mapNextTx. @@ -625,7 +626,7 @@ void CTxMemPool::ClearPrioritisation(const uint256 hash) CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } -bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { +bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const { // 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. @@ -637,7 +638,7 @@ bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { return (base->GetCoins(txid, coins) && !coins.IsPruned()); } -bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) { +bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const { return mempool.exists(txid) || base->HaveCoins(txid); } diff --git a/src/txmempool.h b/src/txmempool.h index 2577397bce..d95c4d970b 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -85,7 +85,7 @@ public: * all inputs are in the mapNextTx array). If sanity-checking is turned off, * check does nothing. */ - void check(CCoinsViewCache *pcoins) const; + void check(const CCoinsViewCache *pcoins) const; void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry); @@ -143,8 +143,8 @@ protected: public: CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn); - bool GetCoins(const uint256 &txid, CCoins &coins); - bool HaveCoins(const uint256 &txid); + bool GetCoins(const uint256 &txid, CCoins &coins) const; + bool HaveCoins(const uint256 &txid) const; }; #endif /* BITCOIN_TXMEMPOOL_H */ diff --git a/src/uint256.cpp b/src/uint256.cpp index 08c05594fd..feda0ca5a9 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -4,7 +4,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "uint256.h" -#include "util.h" + +#include "utilstrencodings.h" #include <stdio.h> #include <string.h> diff --git a/src/util.cpp b/src/util.cpp index ae2145a3a0..5a4e187f9e 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -11,14 +11,13 @@ #include "chainparamsbase.h" #include "random.h" +#include "serialize.h" #include "sync.h" -#include "uint256.h" -#include "version.h" +#include "utilstrencodings.h" +#include "utiltime.h" #include <stdarg.h> -#include <boost/date_time/posix_time/posix_time.hpp> - #ifndef WIN32 // for posix_fallocate #ifdef __linux__ @@ -76,6 +75,7 @@ #include <boost/foreach.hpp> #include <boost/program_options/detail/config_file.hpp> #include <boost/program_options/parsers.hpp> +#include <boost/thread.hpp> #include <openssl/crypto.h> #include <openssl/rand.h> @@ -244,148 +244,6 @@ int LogPrintStr(const std::string &str) return ret; } -string FormatMoney(int64_t n, bool fPlus) -{ - // Note: not using straight sprintf here because we do NOT want - // localized number formatting. - int64_t n_abs = (n > 0 ? n : -n); - int64_t quotient = n_abs/COIN; - int64_t remainder = n_abs%COIN; - string str = strprintf("%d.%08d", quotient, remainder); - - // Right-trim excess zeros before the decimal point: - int nTrim = 0; - for (int i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i) - ++nTrim; - if (nTrim) - str.erase(str.size()-nTrim, nTrim); - - if (n < 0) - str.insert((unsigned int)0, 1, '-'); - else if (fPlus && n > 0) - str.insert((unsigned int)0, 1, '+'); - return str; -} - - -bool ParseMoney(const string& str, int64_t& nRet) -{ - return ParseMoney(str.c_str(), nRet); -} - -bool ParseMoney(const char* pszIn, int64_t& nRet) -{ - string strWhole; - int64_t nUnits = 0; - const char* p = pszIn; - while (isspace(*p)) - p++; - for (; *p; p++) - { - if (*p == '.') - { - p++; - int64_t nMult = CENT*10; - while (isdigit(*p) && (nMult > 0)) - { - nUnits += nMult * (*p++ - '0'); - nMult /= 10; - } - break; - } - if (isspace(*p)) - break; - if (!isdigit(*p)) - return false; - strWhole.insert(strWhole.end(), *p); - } - for (; *p; p++) - if (!isspace(*p)) - return false; - if (strWhole.size() > 10) // guard against 63 bit overflow - return false; - if (nUnits < 0 || nUnits > COIN) - return false; - int64_t nWhole = atoi64(strWhole); - int64_t nValue = nWhole*COIN + nUnits; - - nRet = nValue; - return true; -} - -// safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything -// even possibly remotely dangerous like & or > -static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); -string SanitizeString(const string& str) -{ - string strResult; - for (std::string::size_type i = 0; i < str.size(); i++) - { - if (safeChars.find(str[i]) != std::string::npos) - strResult.push_back(str[i]); - } - return strResult; -} - -const signed char p_util_hexdigit[256] = -{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, - -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; - -signed char HexDigit(char c) -{ - return p_util_hexdigit[(unsigned char)c]; -} - -bool IsHex(const string& str) -{ - BOOST_FOREACH(char c, str) - { - if (HexDigit(c) < 0) - return false; - } - return (str.size() > 0) && (str.size()%2 == 0); -} - -vector<unsigned char> ParseHex(const char* psz) -{ - // convert hex dump to vector - vector<unsigned char> vch; - while (true) - { - while (isspace(*psz)) - psz++; - signed char c = HexDigit(*psz++); - if (c == (signed char)-1) - break; - unsigned char n = (c << 4); - c = HexDigit(*psz++); - if (c == (signed char)-1) - break; - n |= c; - vch.push_back(n); - } - return vch; -} - -vector<unsigned char> ParseHex(const string& str) -{ - return ParseHex(str.c_str()); -} - static void InterpretNegativeSetting(string name, map<string, string>& mapSettingsRet) { // interpret -nofoo as -foo=0 (and -nofoo=0 as -foo=1) as long as -foo not set @@ -483,334 +341,6 @@ bool SoftSetBoolArg(const std::string& strArg, bool fValue) return SoftSetArg(strArg, std::string("0")); } - -string EncodeBase64(const unsigned char* pch, size_t len) -{ - static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - string strRet=""; - strRet.reserve((len+2)/3*4); - - int mode=0, left=0; - const unsigned char *pchEnd = pch+len; - - while (pch<pchEnd) - { - int enc = *(pch++); - switch (mode) - { - case 0: // we have no bits - strRet += pbase64[enc >> 2]; - left = (enc & 3) << 4; - mode = 1; - break; - - case 1: // we have two bits - strRet += pbase64[left | (enc >> 4)]; - left = (enc & 15) << 2; - mode = 2; - break; - - case 2: // we have four bits - strRet += pbase64[left | (enc >> 6)]; - strRet += pbase64[enc & 63]; - mode = 0; - break; - } - } - - if (mode) - { - strRet += pbase64[left]; - strRet += '='; - if (mode == 1) - strRet += '='; - } - - return strRet; -} - -string EncodeBase64(const string& str) -{ - return EncodeBase64((const unsigned char*)str.c_str(), str.size()); -} - -vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid) -{ - static const int decode64_table[256] = - { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, - -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 - }; - - if (pfInvalid) - *pfInvalid = false; - - vector<unsigned char> vchRet; - vchRet.reserve(strlen(p)*3/4); - - int mode = 0; - int left = 0; - - while (1) - { - int dec = decode64_table[(unsigned char)*p]; - if (dec == -1) break; - p++; - switch (mode) - { - case 0: // we have no bits and get 6 - left = dec; - mode = 1; - break; - - case 1: // we have 6 bits and keep 4 - vchRet.push_back((left<<2) | (dec>>4)); - left = dec & 15; - mode = 2; - break; - - case 2: // we have 4 bits and get 6, we keep 2 - vchRet.push_back((left<<4) | (dec>>2)); - left = dec & 3; - mode = 3; - break; - - case 3: // we have 2 bits and get 6 - vchRet.push_back((left<<6) | dec); - mode = 0; - break; - } - } - - if (pfInvalid) - switch (mode) - { - case 0: // 4n base64 characters processed: ok - break; - - case 1: // 4n+1 base64 character processed: impossible - *pfInvalid = true; - break; - - case 2: // 4n+2 base64 characters processed: require '==' - if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1) - *pfInvalid = true; - break; - - case 3: // 4n+3 base64 characters processed: require '=' - if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1) - *pfInvalid = true; - break; - } - - return vchRet; -} - -string DecodeBase64(const string& str) -{ - vector<unsigned char> vchRet = DecodeBase64(str.c_str()); - return string((const char*)&vchRet[0], vchRet.size()); -} - -string EncodeBase32(const unsigned char* pch, size_t len) -{ - static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; - - string strRet=""; - strRet.reserve((len+4)/5*8); - - int mode=0, left=0; - const unsigned char *pchEnd = pch+len; - - while (pch<pchEnd) - { - int enc = *(pch++); - switch (mode) - { - case 0: // we have no bits - strRet += pbase32[enc >> 3]; - left = (enc & 7) << 2; - mode = 1; - break; - - case 1: // we have three bits - strRet += pbase32[left | (enc >> 6)]; - strRet += pbase32[(enc >> 1) & 31]; - left = (enc & 1) << 4; - mode = 2; - break; - - case 2: // we have one bit - strRet += pbase32[left | (enc >> 4)]; - left = (enc & 15) << 1; - mode = 3; - break; - - case 3: // we have four bits - strRet += pbase32[left | (enc >> 7)]; - strRet += pbase32[(enc >> 2) & 31]; - left = (enc & 3) << 3; - mode = 4; - break; - - case 4: // we have two bits - strRet += pbase32[left | (enc >> 5)]; - strRet += pbase32[enc & 31]; - mode = 0; - } - } - - static const int nPadding[5] = {0, 6, 4, 3, 1}; - if (mode) - { - strRet += pbase32[left]; - for (int n=0; n<nPadding[mode]; n++) - strRet += '='; - } - - return strRet; -} - -string EncodeBase32(const string& str) -{ - return EncodeBase32((const unsigned char*)str.c_str(), str.size()); -} - -vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid) -{ - static const int decode32_table[256] = - { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 - }; - - if (pfInvalid) - *pfInvalid = false; - - vector<unsigned char> vchRet; - vchRet.reserve((strlen(p))*5/8); - - int mode = 0; - int left = 0; - - while (1) - { - int dec = decode32_table[(unsigned char)*p]; - if (dec == -1) break; - p++; - switch (mode) - { - case 0: // we have no bits and get 5 - left = dec; - mode = 1; - break; - - case 1: // we have 5 bits and keep 2 - vchRet.push_back((left<<3) | (dec>>2)); - left = dec & 3; - mode = 2; - break; - - case 2: // we have 2 bits and keep 7 - left = left << 5 | dec; - mode = 3; - break; - - case 3: // we have 7 bits and keep 4 - vchRet.push_back((left<<1) | (dec>>4)); - left = dec & 15; - mode = 4; - break; - - case 4: // we have 4 bits, and keep 1 - vchRet.push_back((left<<4) | (dec>>1)); - left = dec & 1; - mode = 5; - break; - - case 5: // we have 1 bit, and keep 6 - left = left << 5 | dec; - mode = 6; - break; - - case 6: // we have 6 bits, and keep 3 - vchRet.push_back((left<<2) | (dec>>3)); - left = dec & 7; - mode = 7; - break; - - case 7: // we have 3 bits, and keep 0 - vchRet.push_back((left<<5) | dec); - mode = 0; - break; - } - } - - if (pfInvalid) - switch (mode) - { - case 0: // 8n base32 characters processed: ok - break; - - case 1: // 8n+1 base32 characters processed: impossible - case 3: // +3 - case 6: // +6 - *pfInvalid = true; - break; - - case 2: // 8n+2 base32 characters processed: require '======' - if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1) - *pfInvalid = true; - break; - - case 4: // 8n+4 base32 characters processed: require '====' - if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1) - *pfInvalid = true; - break; - - case 5: // 8n+5 base32 characters processed: require '===' - if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1) - *pfInvalid = true; - break; - - case 7: // 8n+7 base32 characters processed: require '=' - if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1) - *pfInvalid = true; - break; - } - - return vchRet; -} - -string DecodeBase32(const string& str) -{ - vector<unsigned char> vchRet = DecodeBase32(str.c_str()); - return string((const char*)&vchRet[0], vchRet.size()); -} - static std::string FormatException(std::exception* pex, const char* pszThread) { #ifdef WIN32 @@ -1102,45 +632,6 @@ void ShrinkDebugFile() fclose(file); } -static int64_t nMockTime = 0; // For unit testing - -int64_t GetTime() -{ - if (nMockTime) return nMockTime; - - return time(NULL); -} - -void SetMockTime(int64_t nMockTimeIn) -{ - nMockTime = nMockTimeIn; -} - -string FormatVersion(int nVersion) -{ - if (nVersion%100 == 0) - return strprintf("%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100); - else - return strprintf("%d.%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100, nVersion%100); -} - -string FormatFullVersion() -{ - return CLIENT_BUILD; -} - -// Format the subversion field according to BIP 14 spec (https://en.bitcoin.it/wiki/BIP_0014) -std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments) -{ - std::ostringstream ss; - ss << "/"; - ss << name << ":" << FormatVersion(nClientVersion); - if (!comments.empty()) - ss << "(" << boost::algorithm::join(comments, "; ") << ")"; - ss << "/"; - return ss.str(); -} - #ifdef WIN32 boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) { @@ -1211,20 +702,6 @@ void RenameThread(const char* name) #endif } -bool ParseInt32(const std::string& str, int32_t *out) -{ - char *endp = NULL; - errno = 0; // strtol will not set errno if valid - long int n = strtol(str.c_str(), &endp, 10); - if(out) *out = (int)n; - // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow - // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit - // platforms the size of these types may be different. - return endp && *endp == 0 && !errno && - n >= std::numeric_limits<int32_t>::min() && - n <= std::numeric_limits<int32_t>::max(); -} - void SetupEnvironment() { #ifndef WIN32 @@ -1242,47 +719,15 @@ void SetupEnvironment() #endif } -std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) +void SetThreadPriority(int nPriority) { - // std::locale takes ownership of the pointer - std::locale loc(std::locale::classic(), new boost::posix_time::time_facet(pszFormat)); - std::stringstream ss; - ss.imbue(loc); - ss << boost::posix_time::from_time_t(nTime); - return ss.str(); -} - -std::string FormatParagraph(const std::string in, size_t width, size_t indent) -{ - std::stringstream out; - size_t col = 0; - size_t ptr = 0; - while(ptr < in.size()) - { - // Find beginning of next word - ptr = in.find_first_not_of(' ', ptr); - if (ptr == std::string::npos) - break; - // Find end of next word - size_t endword = in.find_first_of(' ', ptr); - if (endword == std::string::npos) - endword = in.size(); - // Add newline and indentation if this wraps over the allowed width - if (col > 0) - { - if ((col + endword - ptr) > width) - { - out << '\n'; - for(size_t i=0; i<indent; ++i) - out << ' '; - col = 0; - } else - out << ' '; - } - // Append word - out << in.substr(ptr, endword - ptr); - col += endword - ptr; - ptr = endword; - } - return out.str(); +#ifdef WIN32 + SetThreadPriority(GetCurrentThread(), nPriority); +#else // WIN32 +#ifdef PRIO_THREAD + setpriority(PRIO_THREAD, 0, nPriority); +#else // PRIO_THREAD + setpriority(PRIO_PROCESS, 0, nPriority); +#endif // PRIO_THREAD +#endif // WIN32 } diff --git a/src/util.h b/src/util.h index 1fb42a7b7e..30f7c15c89 100644 --- a/src/util.h +++ b/src/util.h @@ -3,6 +3,10 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +/** + * Server/client environment: argument handling, config file parsing, + * logging, thread wrappers + */ #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H @@ -11,84 +15,17 @@ #endif #include "compat.h" -#include "serialize.h" +#include "utiltime.h" #include "tinyformat.h" -#include <cstdio> #include <exception> #include <map> -#include <stdarg.h> #include <stdint.h> #include <string> -#include <utility> #include <vector> -#ifndef WIN32 -#include <sys/resource.h> -#include <sys/time.h> -#include <sys/types.h> -#endif - #include <boost/filesystem/path.hpp> -#include <boost/thread.hpp> - -class uint256; - -static const int64_t COIN = 100000000; -static const int64_t CENT = 1000000; - -#define BEGIN(a) ((char*)&(a)) -#define END(a) ((char*)&((&(a))[1])) -#define UBEGIN(a) ((unsigned char*)&(a)) -#define UEND(a) ((unsigned char*)&((&(a))[1])) -#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) - -// This is needed because the foreach macro can't get over the comma in pair<t1, t2> -#define PAIRTYPE(t1, t2) std::pair<t1, t2> - -// Align by increasing pointer, must have extra space at end of buffer -template <size_t nBytes, typename T> -T* alignup(T* p) -{ - union - { - T* ptr; - size_t n; - } u; - u.ptr = p; - u.n = (u.n + (nBytes-1)) & ~(nBytes-1); - return u.ptr; -} - -#ifdef WIN32 -#define MSG_DONTWAIT 0 - -#ifndef S_IRUSR -#define S_IRUSR 0400 -#define S_IWUSR 0200 -#endif -#else -#define MAX_PATH 1024 -#endif -// As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall, it is defined as 0 -#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) -#define MSG_NOSIGNAL 0 -#endif - -inline void MilliSleep(int64_t n) -{ -// Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 -// until fixed in 1.52. Use the deprecated sleep method for the broken case. -// See: https://svn.boost.org/trac/boost/ticket/7238 -#if defined(HAVE_WORKING_BOOST_SLEEP_FOR) - boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); -#elif defined(HAVE_WORKING_BOOST_SLEEP) - boost::this_thread::sleep(boost::posix_time::milliseconds(n)); -#else -//should never get here -#error missing boost sleep implementation -#endif -} +#include <boost/thread/exceptions.hpp> extern std::map<std::string, std::string> mapArgs; extern std::map<std::string, std::vector<std::string> > mapMultiArgs; @@ -146,22 +83,6 @@ static inline bool error(const char* format) } void PrintExceptionContinue(std::exception* pex, const char* pszThread); -std::string FormatMoney(int64_t n, bool fPlus=false); -bool ParseMoney(const std::string& str, int64_t& nRet); -bool ParseMoney(const char* pszIn, int64_t& nRet); -std::string SanitizeString(const std::string& str); -std::vector<unsigned char> ParseHex(const char* psz); -std::vector<unsigned char> ParseHex(const std::string& str); -signed char HexDigit(char c); -bool IsHex(const std::string& str); -std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = NULL); -std::string DecodeBase64(const std::string& str); -std::string EncodeBase64(const unsigned char* pch, size_t len); -std::string EncodeBase64(const std::string& str); -std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid = NULL); -std::string DecodeBase32(const std::string& str); -std::string EncodeBase32(const unsigned char* pch, size_t len); -std::string EncodeBase32(const std::string& str); void ParseParameters(int argc, const char*const argv[]); void FileCommit(FILE *fileout); bool TruncateFile(FILE *file, unsigned int length); @@ -182,111 +103,8 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif boost::filesystem::path GetTempPath(); void ShrinkDebugFile(); -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); -} - -inline std::string itostr(int n) -{ - return strprintf("%d", n); -} - -inline int64_t atoi64(const char* psz) -{ -#ifdef _MSC_VER - return _atoi64(psz); -#else - return strtoll(psz, NULL, 10); -#endif -} - -inline int64_t atoi64(const std::string& str) -{ -#ifdef _MSC_VER - return _atoi64(str.c_str()); -#else - return strtoll(str.c_str(), NULL, 10); -#endif -} - -inline int atoi(const std::string& str) -{ - return atoi(str.c_str()); -} - -/** - * Convert string to signed 32-bit integer with strict parse error feedback. - * @returns true if the entire string could be parsed as valid integer, - * false if not the entire string could be parsed or when overflow or underflow occured. - */ -bool ParseInt32(const std::string& str, int32_t *out); - -inline int roundint(double d) -{ - return (int)(d > 0 ? d + 0.5 : d - 0.5); -} - -inline int64_t roundint64(double d) -{ - return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); -} - -inline int64_t abs64(int64_t n) -{ - return (n >= 0 ? n : -n); -} - -template<typename T> -std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) -{ - std::string rv; - static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - rv.reserve((itend-itbegin)*3); - for(T it = itbegin; it < itend; ++it) - { - unsigned char val = (unsigned char)(*it); - if(fSpaces && it != itbegin) - rv.push_back(' '); - rv.push_back(hexmap[val>>4]); - rv.push_back(hexmap[val&15]); - } - - return rv; -} - -template<typename T> -inline std::string HexStr(const T& vch, bool fSpaces=false) -{ - return HexStr(vch.begin(), vch.end(), fSpaces); -} - -/** Format a paragraph of text to a fixed width, adding spaces for - * indentation to any added line. - */ -std::string FormatParagraph(const std::string in, size_t width=79, size_t indent=0); - -inline int64_t GetTimeMillis() -{ - return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); -} - -inline int64_t GetTimeMicros() -{ - return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); -} - -std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); - inline bool IsSwitchChar(char c) { #ifdef WIN32 @@ -341,113 +159,9 @@ bool SoftSetArg(const std::string& strArg, const std::string& strValue); */ bool SoftSetBoolArg(const std::string& strArg, bool fValue); -/** - * Timing-attack-resistant comparison. - * Takes time proportional to length - * of first argument. - */ -template <typename T> -bool TimingResistantEqual(const T& a, const T& b) -{ - if (b.size() == 0) return a.size() == 0; - size_t accumulator = a.size() ^ b.size(); - for (size_t i = 0; i < a.size(); i++) - accumulator |= a[i] ^ b[i%b.size()]; - return accumulator == 0; -} - -/** Median filter over a stream of values. - * Returns the median of the last N numbers - */ -template <typename T> class CMedianFilter -{ -private: - std::vector<T> vValues; - std::vector<T> vSorted; - unsigned int nSize; -public: - CMedianFilter(unsigned int size, T initial_value): - nSize(size) - { - vValues.reserve(size); - vValues.push_back(initial_value); - vSorted = vValues; - } - - void input(T value) - { - if(vValues.size() == nSize) - { - vValues.erase(vValues.begin()); - } - vValues.push_back(value); - - vSorted.resize(vValues.size()); - std::copy(vValues.begin(), vValues.end(), vSorted.begin()); - std::sort(vSorted.begin(), vSorted.end()); - } - - T median() const - { - int size = vSorted.size(); - assert(size>0); - if(size & 1) // Odd number of elements - { - return vSorted[size/2]; - } - else // Even number of elements - { - return (vSorted[size/2-1] + vSorted[size/2]) / 2; - } - } - - int size() const - { - return vValues.size(); - } - - std::vector<T> sorted () const - { - return vSorted; - } -}; - -#ifdef WIN32 -inline void SetThreadPriority(int nPriority) -{ - SetThreadPriority(GetCurrentThread(), nPriority); -} -#else - -// PRIO_MAX is not defined on Solaris -#ifndef PRIO_MAX -#define PRIO_MAX 20 -#endif -#define THREAD_PRIORITY_LOWEST PRIO_MAX -#define THREAD_PRIORITY_BELOW_NORMAL 2 -#define THREAD_PRIORITY_NORMAL 0 -#define THREAD_PRIORITY_ABOVE_NORMAL (-2) - -inline void SetThreadPriority(int nPriority) -{ - // It's unclear if it's even possible to change thread priorities on Linux, - // but we really and truly need it for the generation threads. -#ifdef PRIO_THREAD - setpriority(PRIO_THREAD, 0, nPriority); -#else - setpriority(PRIO_PROCESS, 0, nPriority); -#endif -} -#endif - +void SetThreadPriority(int nPriority); void RenameThread(const char* name); -inline uint32_t ByteReverse(uint32_t value) -{ - value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); - return (value<<16) | (value>>16); -} - // Standard wrapper for do-something-forever thread functions. // "Forever" really means until the thread is interrupted. // Use it like: diff --git a/src/utilmoneystr.cpp b/src/utilmoneystr.cpp new file mode 100644 index 0000000000..1bd48d8d4c --- /dev/null +++ b/src/utilmoneystr.cpp @@ -0,0 +1,75 @@ +#include "utilmoneystr.h" + +#include "core.h" +#include "tinyformat.h" + +using namespace std; + +string FormatMoney(int64_t n, bool fPlus) +{ + // Note: not using straight sprintf here because we do NOT want + // localized number formatting. + int64_t n_abs = (n > 0 ? n : -n); + int64_t quotient = n_abs/COIN; + int64_t remainder = n_abs%COIN; + string str = strprintf("%d.%08d", quotient, remainder); + + // Right-trim excess zeros before the decimal point: + int nTrim = 0; + for (int i = str.size()-1; (str[i] == '0' && isdigit(str[i-2])); --i) + ++nTrim; + if (nTrim) + str.erase(str.size()-nTrim, nTrim); + + if (n < 0) + str.insert((unsigned int)0, 1, '-'); + else if (fPlus && n > 0) + str.insert((unsigned int)0, 1, '+'); + return str; +} + + +bool ParseMoney(const string& str, int64_t& nRet) +{ + return ParseMoney(str.c_str(), nRet); +} + +bool ParseMoney(const char* pszIn, int64_t& nRet) +{ + string strWhole; + int64_t nUnits = 0; + const char* p = pszIn; + while (isspace(*p)) + p++; + for (; *p; p++) + { + if (*p == '.') + { + p++; + int64_t nMult = CENT*10; + while (isdigit(*p) && (nMult > 0)) + { + nUnits += nMult * (*p++ - '0'); + nMult /= 10; + } + break; + } + if (isspace(*p)) + break; + if (!isdigit(*p)) + return false; + strWhole.insert(strWhole.end(), *p); + } + for (; *p; p++) + if (!isspace(*p)) + return false; + if (strWhole.size() > 10) // guard against 63 bit overflow + return false; + if (nUnits < 0 || nUnits > COIN) + return false; + int64_t nWhole = atoi64(strWhole); + int64_t nValue = nWhole*COIN + nUnits; + + nRet = nValue; + return true; +} diff --git a/src/utilmoneystr.h b/src/utilmoneystr.h new file mode 100644 index 0000000000..f0c61aa138 --- /dev/null +++ b/src/utilmoneystr.h @@ -0,0 +1,19 @@ +// 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. + +/** + * Money parsing/formatting utilities. + */ +#ifndef BITCOIN_UTILMONEYSTR_H +#define BITCOIN_UTILMONEYSTR_H + +#include <stdint.h> +#include <string> + +std::string FormatMoney(int64_t n, bool fPlus=false); +bool ParseMoney(const std::string& str, int64_t& nRet); +bool ParseMoney(const char* pszIn, int64_t& nRet); + +#endif // BITCOIN_UTILMONEYSTR_H diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp new file mode 100644 index 0000000000..ef13555104 --- /dev/null +++ b/src/utilstrencodings.cpp @@ -0,0 +1,496 @@ +// 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 "utilstrencodings.h" + +#include "tinyformat.h" + +#include <boost/foreach.hpp> +#include <errno.h> +#include <limits> + +using namespace std; + +// safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything +// even possibly remotely dangerous like & or > +static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); +string SanitizeString(const string& str) +{ + string strResult; + for (std::string::size_type i = 0; i < str.size(); i++) + { + if (safeChars.find(str[i]) != std::string::npos) + strResult.push_back(str[i]); + } + return strResult; +} + +const signed char p_util_hexdigit[256] = +{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; + +signed char HexDigit(char c) +{ + return p_util_hexdigit[(unsigned char)c]; +} + +bool IsHex(const string& str) +{ + BOOST_FOREACH(char c, str) + { + if (HexDigit(c) < 0) + return false; + } + return (str.size() > 0) && (str.size()%2 == 0); +} + +vector<unsigned char> ParseHex(const char* psz) +{ + // convert hex dump to vector + vector<unsigned char> vch; + while (true) + { + while (isspace(*psz)) + psz++; + signed char c = HexDigit(*psz++); + if (c == (signed char)-1) + break; + unsigned char n = (c << 4); + c = HexDigit(*psz++); + if (c == (signed char)-1) + break; + n |= c; + vch.push_back(n); + } + return vch; +} + +vector<unsigned char> ParseHex(const string& str) +{ + return ParseHex(str.c_str()); +} + +string EncodeBase64(const unsigned char* pch, size_t len) +{ + static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + string strRet=""; + strRet.reserve((len+2)/3*4); + + int mode=0, left=0; + const unsigned char *pchEnd = pch+len; + + while (pch<pchEnd) + { + int enc = *(pch++); + switch (mode) + { + case 0: // we have no bits + strRet += pbase64[enc >> 2]; + left = (enc & 3) << 4; + mode = 1; + break; + + case 1: // we have two bits + strRet += pbase64[left | (enc >> 4)]; + left = (enc & 15) << 2; + mode = 2; + break; + + case 2: // we have four bits + strRet += pbase64[left | (enc >> 6)]; + strRet += pbase64[enc & 63]; + mode = 0; + break; + } + } + + if (mode) + { + strRet += pbase64[left]; + strRet += '='; + if (mode == 1) + strRet += '='; + } + + return strRet; +} + +string EncodeBase64(const string& str) +{ + return EncodeBase64((const unsigned char*)str.c_str(), str.size()); +} + +vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid) +{ + static const int decode64_table[256] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector<unsigned char> vchRet; + vchRet.reserve(strlen(p)*3/4); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode64_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) + { + case 0: // we have no bits and get 6 + left = dec; + mode = 1; + break; + + case 1: // we have 6 bits and keep 4 + vchRet.push_back((left<<2) | (dec>>4)); + left = dec & 15; + mode = 2; + break; + + case 2: // we have 4 bits and get 6, we keep 2 + vchRet.push_back((left<<4) | (dec>>2)); + left = dec & 3; + mode = 3; + break; + + case 3: // we have 2 bits and get 6 + vchRet.push_back((left<<6) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) + switch (mode) + { + case 0: // 4n base64 characters processed: ok + break; + + case 1: // 4n+1 base64 character processed: impossible + *pfInvalid = true; + break; + + case 2: // 4n+2 base64 characters processed: require '==' + if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1) + *pfInvalid = true; + break; + + case 3: // 4n+3 base64 characters processed: require '=' + if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase64(const string& str) +{ + vector<unsigned char> vchRet = DecodeBase64(str.c_str()); + return string((const char*)&vchRet[0], vchRet.size()); +} + +string EncodeBase32(const unsigned char* pch, size_t len) +{ + static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; + + string strRet=""; + strRet.reserve((len+4)/5*8); + + int mode=0, left=0; + const unsigned char *pchEnd = pch+len; + + while (pch<pchEnd) + { + int enc = *(pch++); + switch (mode) + { + case 0: // we have no bits + strRet += pbase32[enc >> 3]; + left = (enc & 7) << 2; + mode = 1; + break; + + case 1: // we have three bits + strRet += pbase32[left | (enc >> 6)]; + strRet += pbase32[(enc >> 1) & 31]; + left = (enc & 1) << 4; + mode = 2; + break; + + case 2: // we have one bit + strRet += pbase32[left | (enc >> 4)]; + left = (enc & 15) << 1; + mode = 3; + break; + + case 3: // we have four bits + strRet += pbase32[left | (enc >> 7)]; + strRet += pbase32[(enc >> 2) & 31]; + left = (enc & 3) << 3; + mode = 4; + break; + + case 4: // we have two bits + strRet += pbase32[left | (enc >> 5)]; + strRet += pbase32[enc & 31]; + mode = 0; + } + } + + static const int nPadding[5] = {0, 6, 4, 3, 1}; + if (mode) + { + strRet += pbase32[left]; + for (int n=0; n<nPadding[mode]; n++) + strRet += '='; + } + + return strRet; +} + +string EncodeBase32(const string& str) +{ + return EncodeBase32((const unsigned char*)str.c_str(), str.size()); +} + +vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid) +{ + static const int decode32_table[256] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + if (pfInvalid) + *pfInvalid = false; + + vector<unsigned char> vchRet; + vchRet.reserve((strlen(p))*5/8); + + int mode = 0; + int left = 0; + + while (1) + { + int dec = decode32_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) + { + case 0: // we have no bits and get 5 + left = dec; + mode = 1; + break; + + case 1: // we have 5 bits and keep 2 + vchRet.push_back((left<<3) | (dec>>2)); + left = dec & 3; + mode = 2; + break; + + case 2: // we have 2 bits and keep 7 + left = left << 5 | dec; + mode = 3; + break; + + case 3: // we have 7 bits and keep 4 + vchRet.push_back((left<<1) | (dec>>4)); + left = dec & 15; + mode = 4; + break; + + case 4: // we have 4 bits, and keep 1 + vchRet.push_back((left<<4) | (dec>>1)); + left = dec & 1; + mode = 5; + break; + + case 5: // we have 1 bit, and keep 6 + left = left << 5 | dec; + mode = 6; + break; + + case 6: // we have 6 bits, and keep 3 + vchRet.push_back((left<<2) | (dec>>3)); + left = dec & 7; + mode = 7; + break; + + case 7: // we have 3 bits, and keep 0 + vchRet.push_back((left<<5) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) + switch (mode) + { + case 0: // 8n base32 characters processed: ok + break; + + case 1: // 8n+1 base32 characters processed: impossible + case 3: // +3 + case 6: // +6 + *pfInvalid = true; + break; + + case 2: // 8n+2 base32 characters processed: require '======' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1) + *pfInvalid = true; + break; + + case 4: // 8n+4 base32 characters processed: require '====' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1) + *pfInvalid = true; + break; + + case 5: // 8n+5 base32 characters processed: require '===' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1) + *pfInvalid = true; + break; + + case 7: // 8n+7 base32 characters processed: require '=' + if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase32(const string& str) +{ + vector<unsigned char> vchRet = DecodeBase32(str.c_str()); + return string((const char*)&vchRet[0], vchRet.size()); +} + +bool ParseInt32(const std::string& str, int32_t *out) +{ + char *endp = NULL; + errno = 0; // strtol will not set errno if valid + long int n = strtol(str.c_str(), &endp, 10); + if(out) *out = (int)n; + // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit + // platforms the size of these types may be different. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits<int32_t>::min() && + n <= std::numeric_limits<int32_t>::max(); +} + +std::string FormatParagraph(const std::string in, size_t width, size_t indent) +{ + std::stringstream out; + size_t col = 0; + size_t ptr = 0; + while(ptr < in.size()) + { + // Find beginning of next word + ptr = in.find_first_not_of(' ', ptr); + if (ptr == std::string::npos) + break; + // Find end of next word + size_t endword = in.find_first_of(' ', ptr); + if (endword == std::string::npos) + endword = in.size(); + // Add newline and indentation if this wraps over the allowed width + if (col > 0) + { + if ((col + endword - ptr) > width) + { + out << '\n'; + for(size_t i=0; i<indent; ++i) + out << ' '; + col = 0; + } else + out << ' '; + } + // Append word + out << in.substr(ptr, endword - ptr); + col += endword - ptr; + ptr = endword; + } + return out.str(); +} + +std::string i64tostr(int64_t n) +{ + return strprintf("%d", n); +} + +std::string itostr(int n) +{ + return strprintf("%d", n); +} + +int64_t atoi64(const char* psz) +{ +#ifdef _MSC_VER + return _atoi64(psz); +#else + return strtoll(psz, NULL, 10); +#endif +} + +int64_t atoi64(const std::string& str) +{ +#ifdef _MSC_VER + return _atoi64(str.c_str()); +#else + return strtoll(str.c_str(), NULL, 10); +#endif +} + +int atoi(const std::string& str) +{ + return atoi(str.c_str()); +} diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h new file mode 100644 index 0000000000..0b8c1a1781 --- /dev/null +++ b/src/utilstrencodings.h @@ -0,0 +1,97 @@ +// 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. + +/** + * Utilities for converting data from/to strings. + */ +#ifndef BITCOIN_UTILSTRENCODINGS_H +#define BITCOIN_UTILSTRENCODINGS_H + +#include <stdint.h> +#include <string> +#include <vector> + +#define BEGIN(a) ((char*)&(a)) +#define END(a) ((char*)&((&(a))[1])) +#define UBEGIN(a) ((unsigned char*)&(a)) +#define UEND(a) ((unsigned char*)&((&(a))[1])) +#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) + +// This is needed because the foreach macro can't get over the comma in pair<t1, t2> +#define PAIRTYPE(t1, t2) std::pair<t1, t2> + +std::string SanitizeString(const std::string& str); +std::vector<unsigned char> ParseHex(const char* psz); +std::vector<unsigned char> ParseHex(const std::string& str); +signed char HexDigit(char c); +bool IsHex(const std::string& str); +std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase64(const std::string& str); +std::string EncodeBase64(const unsigned char* pch, size_t len); +std::string EncodeBase64(const std::string& str); +std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid = NULL); +std::string DecodeBase32(const std::string& str); +std::string EncodeBase32(const unsigned char* pch, size_t len); +std::string EncodeBase32(const std::string& str); + +std::string i64tostr(int64_t n); +std::string itostr(int n); +int64_t atoi64(const char* psz); +int64_t atoi64(const std::string& str); +int atoi(const std::string& str); + +/** + * Convert string to signed 32-bit integer with strict parse error feedback. + * @returns true if the entire string could be parsed as valid integer, + * false if not the entire string could be parsed or when overflow or underflow occured. + */ +bool ParseInt32(const std::string& str, int32_t *out); + +template<typename T> +std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) +{ + std::string rv; + static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + rv.reserve((itend-itbegin)*3); + for(T it = itbegin; it < itend; ++it) + { + unsigned char val = (unsigned char)(*it); + if(fSpaces && it != itbegin) + rv.push_back(' '); + rv.push_back(hexmap[val>>4]); + rv.push_back(hexmap[val&15]); + } + + return rv; +} + +template<typename T> +inline std::string HexStr(const T& vch, bool fSpaces=false) +{ + return HexStr(vch.begin(), vch.end(), fSpaces); +} + +/** Format a paragraph of text to a fixed width, adding spaces for + * indentation to any added line. + */ +std::string FormatParagraph(const std::string in, size_t width=79, size_t indent=0); + +/** + * Timing-attack-resistant comparison. + * Takes time proportional to length + * of first argument. + */ +template <typename T> +bool TimingResistantEqual(const T& a, const T& b) +{ + if (b.size() == 0) return a.size() == 0; + size_t accumulator = a.size() ^ b.size(); + for (size_t i = 0; i < a.size(); i++) + accumulator |= a[i] ^ b[i%b.size()]; + return accumulator == 0; +} + +#endif // BITCOIN_UTILSTRENCODINGS_H diff --git a/src/utiltime.cpp b/src/utiltime.cpp new file mode 100644 index 0000000000..78f0342cba --- /dev/null +++ b/src/utiltime.cpp @@ -0,0 +1,66 @@ +// 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. + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include "utiltime.h" + +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread.hpp> + +using namespace std; + +static int64_t nMockTime = 0; // For unit testing + +int64_t GetTime() +{ + if (nMockTime) return nMockTime; + + return time(NULL); +} + +void SetMockTime(int64_t nMockTimeIn) +{ + nMockTime = nMockTimeIn; +} + +int64_t GetTimeMillis() +{ + return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); +} + +int64_t GetTimeMicros() +{ + return (boost::posix_time::ptime(boost::posix_time::microsec_clock::universal_time()) - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); +} + +void MilliSleep(int64_t n) +{ +// Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 +// until fixed in 1.52. Use the deprecated sleep method for the broken case. +// See: https://svn.boost.org/trac/boost/ticket/7238 +#if defined(HAVE_WORKING_BOOST_SLEEP_FOR) + boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); +#elif defined(HAVE_WORKING_BOOST_SLEEP) + boost::this_thread::sleep(boost::posix_time::milliseconds(n)); +#else +//should never get here +#error missing boost sleep implementation +#endif +} + +std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) +{ + // std::locale takes ownership of the pointer + std::locale loc(std::locale::classic(), new boost::posix_time::time_facet(pszFormat)); + std::stringstream ss; + ss.imbue(loc); + ss << boost::posix_time::from_time_t(nTime); + return ss.str(); +} diff --git a/src/utiltime.h b/src/utiltime.h new file mode 100644 index 0000000000..500ae4dab9 --- /dev/null +++ b/src/utiltime.h @@ -0,0 +1,20 @@ +// 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_UTILTIME_H +#define BITCOIN_UTILTIME_H + +#include <stdint.h> +#include <string> + +int64_t GetTime(); +int64_t GetTimeMillis(); +int64_t GetTimeMicros(); +void SetMockTime(int64_t nMockTimeIn); +void MilliSleep(int64_t n); + +std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); + +#endif diff --git a/src/version.cpp b/src/version.cpp index d86caa3ac2..8311041ed2 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -4,7 +4,10 @@ #include "version.h" +#include "tinyformat.h" + #include <string> +#include <boost/algorithm/string/join.hpp> // Name of client reported in the 'version' message. Report the same name // for both bitcoind and bitcoin-qt, to make it harder for attackers to @@ -69,3 +72,28 @@ const std::string CLIENT_NAME("Satoshi"); const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX); const std::string CLIENT_DATE(BUILD_DATE); + +static std::string FormatVersion(int nVersion) +{ + if (nVersion%100 == 0) + return strprintf("%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100); + else + return strprintf("%d.%d.%d.%d", nVersion/1000000, (nVersion/10000)%100, (nVersion/100)%100, nVersion%100); +} + +std::string FormatFullVersion() +{ + return CLIENT_BUILD; +} + +// Format the subversion field according to BIP 14 spec (https://en.bitcoin.it/wiki/BIP_0014) +std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments) +{ + std::ostringstream ss; + ss << "/"; + ss << name << ":" << FormatVersion(nClientVersion); + if (!comments.empty()) + ss << "(" << boost::algorithm::join(comments, "; ") << ")"; + ss << "/"; + return ss.str(); +} diff --git a/src/version.h b/src/version.h index fbb731c91d..b87aa8258c 100644 --- a/src/version.h +++ b/src/version.h @@ -7,6 +7,7 @@ #include "clientversion.h" #include <string> +#include <vector> // // client versioning @@ -26,7 +27,7 @@ extern const std::string CLIENT_DATE; // network protocol versioning // -static const int PROTOCOL_VERSION = 70003; +static const int PROTOCOL_VERSION = 70002; // initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -48,4 +49,7 @@ static const int BIP0031_VERSION = 60000; // "mempool" command, enhanced "getdata" behavior starts with this version static const int MEMPOOL_GD_VERSION = 60002; +std::string FormatFullVersion(); +std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments); + #endif diff --git a/src/wallet.cpp b/src/wallet.cpp index 7c04743c0f..786b2f6a92 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -10,8 +10,11 @@ #include "coincontrol.h" #include "net.h" #include "timedata.h" +#include "util.h" +#include "utilmoneystr.h" #include <boost/algorithm/string/replace.hpp> +#include <boost/thread.hpp> using namespace std; @@ -37,6 +40,11 @@ struct CompareValueOnly } }; +std::string COutput::ToString() const +{ + return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue).c_str()); +} + const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const { LOCK(cs_wallet); @@ -153,6 +161,7 @@ bool CWallet::AddWatchOnly(const CScript &dest) if (!CCryptoKeyStore::AddWatchOnly(dest)) return false; nTimeFirstKey = 1; // No birthday information for watch-only keys. + NotifyWatchonlyChanged(true); if (!fFileBacked) return true; return CWalletDB(strWalletFile).WriteWatchOnly(dest); @@ -2168,3 +2177,20 @@ bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, st } return false; } + +CKeyPool::CKeyPool() +{ + nTime = GetTime(); +} + +CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn) +{ + nTime = GetTime(); + vchPubKey = vchPubKeyIn; +} + +CWalletKey::CWalletKey(int64_t nExpires) +{ + nTimeCreated = (nExpires ? GetTime() : 0); + nTimeExpires = nExpires; +} diff --git a/src/wallet.h b/src/wallet.h index 34c699e979..052da24609 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -11,7 +11,6 @@ #include "keystore.h" #include "main.h" #include "ui_interface.h" -#include "util.h" #include "walletdb.h" #include <algorithm> @@ -61,16 +60,8 @@ public: int64_t nTime; CPubKey vchPubKey; - CKeyPool() - { - nTime = GetTime(); - } - - CKeyPool(const CPubKey& vchPubKeyIn) - { - nTime = GetTime(); - vchPubKey = vchPubKeyIn; - } + CKeyPool(); + CKeyPool(const CPubKey& vchPubKeyIn); IMPLEMENT_SERIALIZE ( @@ -408,6 +399,9 @@ public: /** Show progress e.g. for rescan */ boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress; + + /** Watch-only address added */ + boost::signals2::signal<void (bool fHaveWatchOnly)> NotifyWatchonlyChanged; }; /** A key allocated from the key pool. */ @@ -820,10 +814,7 @@ public: tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; } - std::string ToString() const - { - return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue).c_str()); - } + std::string ToString() const; }; @@ -840,11 +831,7 @@ public: //// todo: add something to note what created it (user, getnewaddress, change) //// maybe should have a map<string, string> property map - CWalletKey(int64_t nExpires=0) - { - nTimeCreated = (nExpires ? GetTime() : 0); - nTimeExpires = nExpires; - } + CWalletKey(int64_t nExpires=0); IMPLEMENT_SERIALIZE ( diff --git a/src/walletdb.cpp b/src/walletdb.cpp index a95baf83d0..2fa6071658 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -9,10 +9,13 @@ #include "protocol.h" #include "serialize.h" #include "sync.h" +#include "utiltime.h" +#include "util.h" #include "wallet.h" #include <boost/filesystem.hpp> #include <boost/foreach.hpp> +#include <boost/thread.hpp> using namespace std; using namespace boost; |