diff options
-rw-r--r-- | .travis.yml | 57 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | doc/travis-ci.txt | 39 | ||||
-rw-r--r-- | src/coins.cpp | 44 | ||||
-rw-r--r-- | src/coins.h | 41 | ||||
-rw-r--r-- | src/leveldbwrapper.h | 4 | ||||
-rw-r--r-- | src/m4/bitcoin_qt.m4 | 10 | ||||
-rw-r--r-- | src/main.cpp | 98 | ||||
-rw-r--r-- | src/main.h | 6 | ||||
-rw-r--r-- | src/net.cpp | 2 | ||||
-rw-r--r-- | src/protocol.h | 1 | ||||
-rw-r--r-- | src/rpcrawtransaction.cpp | 4 | ||||
-rw-r--r-- | src/txdb.cpp | 13 | ||||
-rw-r--r-- | src/txdb.h | 8 | ||||
-rw-r--r-- | src/txmempool.cpp | 8 | ||||
-rw-r--r-- | src/txmempool.h | 6 | ||||
-rw-r--r-- | src/version.h | 2 |
17 files changed, 272 insertions, 74 deletions
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..2073e71769 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,57 @@ +# errata: +# - A travis bug causes caches to trample eachother when using the same +# compiler key (which we don't use anyway). This is worked around for now by +# using the phony compilers "true X". These can be removed when the travis +# bug is fixed. See: https://github.com/travis-ci/casher/issues/6 + +os: linux +language: cpp +env: + global: + - MAKEJOBS=-j3 + - RUN_TESTS=false + - CCACHE_SIZE=100M + - CCACHE_TEMPDIR=/tmp/.ccache-temp + - CCACHE_COMPRESS=1 + - BASE_OUTDIR=$TRAVIS_BUILD_DIR/out +cache: + apt: true + directories: + - depends/built + - $HOME/.ccache +matrix: + fast_finish: true + include: + - compiler: "true 1" + env: HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=1" GOAL="install" BITCOIN_CONFIG"--enable-glibc-back-compat" + - compiler: "true 2" + env: HOST=x86_64-unknown-linux-gnu DEP_OPTS="NO_QT=1 NO_WALLET=1 NO_UPNP=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat" + - compiler: "true 3" + env: HOST=x86_64-unknown-linux-gnu RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat" + - compiler: "true 4" + env: HOST=i686-pc-linux-gnu PACKAGES="g++-multilib" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat" + - compiler: "true 6" + env: HOST=x86_64-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64 mingw-w64-dev" GOAL="deploy" + - compiler: "true 7" + env: HOST=i686-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-i686 g++-mingw-w64-i686 binutils-mingw-w64-i686 mingw-w64-dev" GOAL="deploy" +install: + - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi + - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-upgrade -qq $PACKAGES; fi +before_script: + - unset CC; unset CXX + - mkdir -p depends/SDKs + - make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS || (echo "Build failure. Verbose build follows." && make -C depends V=1 HOST=$HOST $DEP_OPTS) +script: + - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST + - BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib" + - depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE + - if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then export CCACHE_READONLY=1; fi + - ./autogen.sh + - ./configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) + - make distdir PACKAGE=bitcoin VERSION=$HOST + - cd bitcoin-$HOST + - ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) + - make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL V=1 ; false ) + - if [ "$RUN_TESTS" = "true" ]; then make check; fi +after_script: + - if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then (echo "Upload goes here. Something like: scp -r $BASE_OUTDIR server" || echo "upload failed"); fi diff --git a/configure.ac b/configure.ac index 601ccf0a79..700f4ab709 100644 --- a/configure.ac +++ b/configure.ac @@ -656,6 +656,9 @@ if test "x$use_ccache" != "xno"; then fi AC_MSG_RESULT($use_ccache) fi +if test "x$use_ccache" = "xyes"; then + AX_CHECK_PREPROC_FLAG([-Qunused-arguments],[CPPFLAGS="-Qunused-arguments $CPPFLAGS"]) +fi dnl enable wallet AC_MSG_CHECKING([if wallet should be enabled]) diff --git a/doc/travis-ci.txt b/doc/travis-ci.txt new file mode 100644 index 0000000000..01f7d02a86 --- /dev/null +++ b/doc/travis-ci.txt @@ -0,0 +1,39 @@ +Support for using travis-ci has been added in order to automate pull-testing. +See https://travis-ci.org/ for more info + +This procedure is different than the pull-tester that came before it in a few +ways. + +There is nothing to administer. This is a major feature as it means +that builds have no local state. Because there is no ability to login to the +builders to install packages (tools, dependencies, etc), the entire build +procedure must instead be controlled by a declarative script (.travis.yml). +This script declares each build configuration, creates virtual machines as +necessary, builds, then discards the virtual machines. + +A build matrix is constructed to test a wide range of configurations, rather +than a single pass/fail. This helps to catch build failures and logic errors +that present on platforms other than the ones the author has tested. This +matrix is defined in the build script and can be changed at any time. + +All builders use the dependency-generator in the depends dir, rather than +using apt-get to install build dependencies. This guarantees that the tester +is using the same versions as Gitian, so the build results are nearly identical +to what would be found in a final release. However, this also means that builds +will fail if new dependencies are introduced without being added to the +dependency generator. + +In order to avoid rebuilding all dependencies for each build, the binaries are +cached and re-used when possible. Changes in the dependency-generator will +trigger cache-invalidation and rebuilds as necessary. + +These caches can be manually removed if necessary. This is one of the the very few +manual operations that is possible with Travis, and it can be done by the +Bitcoin Core committer via the Travis web interface. + +In some cases, secure strings may be needed for hiding sensitive info such as +private keys or URLs. The travis client may be used to create these strings: +http://docs.travis-ci.com/user/encryption-keys/ + +For the details of the build descriptor, see the official docs: +http://docs.travis-ci.com/user/build-configuration/ 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/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/m4/bitcoin_qt.m4 b/src/m4/bitcoin_qt.m4 index 27000ecbac..e141033b19 100644 --- a/src/m4/bitcoin_qt.m4 +++ b/src/m4/bitcoin_qt.m4 @@ -112,11 +112,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) if test x$qt_plugin_path != x; then QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" - if test x$bitcoin_qt_got_major_vers == x5; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" - else - QT_LIBS="$QT_LIBS -L$qt_plugin_path/codecs" - fi + QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" fi if test x$use_pkgconfig = xyes; then PKG_CHECK_MODULES([QTPLATFORM], [Qt5PlatformSupport], [QT_LIBS="$QTPLATFORM_LIBS $QT_LIBS"]) @@ -141,6 +137,10 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ else if test x$TARGET_OS == xwindows; then AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) + if test x$qt_plugin_path != x; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" + QT_LIBS="$QT_LIBS -L$qt_plugin_path/codecs" + fi _BITCOIN_QT_CHECK_STATIC_PLUGINS([ Q_IMPORT_PLUGIN(qcncodecs) Q_IMPORT_PLUGIN(qjpcodecs) diff --git a/src/main.cpp b/src/main.cpp index 01f3e06a60..1c9b3f861b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ #include <sstream> +#include <boost/dynamic_bitset.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> @@ -643,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 @@ -716,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; @@ -1022,7 +1023,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa pool.addUnchecked(hash, entry); } - g_signals.SyncTransaction(tx, NULL); + SyncWithWallets(tx, NULL); return true; } @@ -1469,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()) { @@ -1857,10 +1858,6 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2; LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); - // Watch for transactions paying to me - BOOST_FOREACH(const CTransaction& tx, block.vtx) - g_signals.SyncTransaction(tx, &block); - // Watch for changes to the previous coinbase transaction. static uint256 hashPrevBestCoinBase; g_signals.UpdatedTransaction(hashPrevBestCoinBase); @@ -3521,6 +3518,75 @@ 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(); @@ -3869,6 +3935,22 @@ 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; diff --git a/src/main.h b/src/main.h index 886cac1507..f93b78d8e5 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); diff --git a/src/net.cpp b/src/net.cpp index 2443740c45..cae9b70da7 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -66,7 +66,7 @@ namespace { // bool fDiscover = true; bool fListen = true; -uint64_t nLocalServices = NODE_NETWORK; +uint64_t nLocalServices = NODE_NETWORK | NODE_GETUTXOS; CCriticalSection cs_mapLocalHost; map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfReachable[NET_MAX] = {}; diff --git a/src/protocol.h b/src/protocol.h index d7565584af..94d313e9c0 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -64,6 +64,7 @@ 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/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 763615120a..7cd704193d 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -738,9 +738,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) if (!fHaveMempool && !fHaveChain) { // push to local node and sync with wallets CValidationState state; - if (AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) - SyncWithWallets(tx, NULL); - else { + if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) { if(state.IsInvalid()) throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); else diff --git a/src/txdb.cpp b/src/txdb.cpp index 7c0683aaf3..2349514def 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -27,7 +27,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 +37,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 +105,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..f75e2212d5 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -484,7 +484,7 @@ void CTxMemPool::clear() ++nTransactionsUpdated; } -void CTxMemPool::check(CCoinsViewCache *pcoins) const +void CTxMemPool::check(const CCoinsViewCache *pcoins) const { if (!fSanityCheck) return; @@ -505,7 +505,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 +625,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 +637,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/version.h b/src/version.h index 85c5dbf8d7..fbb731c91d 100644 --- a/src/version.h +++ b/src/version.h @@ -26,7 +26,7 @@ extern const std::string CLIENT_DATE; // network protocol versioning // -static const int PROTOCOL_VERSION = 70002; +static const int PROTOCOL_VERSION = 70003; // initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; |