diff options
l--------- | README | 1 | ||||
-rw-r--r-- | README.md | 110 | ||||
-rw-r--r-- | bitcoin-qt.pro | 2 | ||||
-rw-r--r-- | contrib/macdeploy/notes.txt | 2 | ||||
-rw-r--r-- | contrib/seeds/README | 9 | ||||
-rwxr-xr-x | contrib/seeds/makeseeds.py | 32 | ||||
-rwxr-xr-x | contrib/verifysfbinaries/verify.sh | 4 | ||||
-rw-r--r-- | doc/files.txt | 19 | ||||
-rw-r--r-- | src/checkpoints.cpp | 1 | ||||
-rw-r--r-- | src/init.cpp | 7 | ||||
-rw-r--r-- | src/leveldb.cpp | 16 | ||||
-rw-r--r-- | src/leveldb.h | 23 | ||||
-rw-r--r-- | src/main.cpp | 402 | ||||
-rw-r--r-- | src/main.h | 92 | ||||
-rw-r--r-- | src/makefile.unix | 2 | ||||
-rw-r--r-- | src/qt/aboutdialog.cpp | 8 | ||||
-rw-r--r-- | src/qt/aboutdialog.h | 1 | ||||
-rw-r--r-- | src/qt/bitcoinstrings.cpp | 14 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 5 | ||||
-rw-r--r-- | src/qt/forms/aboutdialog.ui | 5 | ||||
-rw-r--r-- | src/qt/locale/bitcoin_en.ts | 171 | ||||
-rw-r--r-- | src/rpcmining.cpp | 5 | ||||
-rw-r--r-- | src/rpcrawtransaction.cpp | 5 | ||||
-rw-r--r-- | src/test/miner_tests.cpp | 4 | ||||
-rw-r--r-- | src/test/transaction_tests.cpp | 12 | ||||
-rw-r--r-- | src/wallet.cpp | 2 | ||||
-rw-r--r-- | src/walletdb.cpp | 3 |
27 files changed, 588 insertions, 369 deletions
diff --git a/README b/README deleted file mode 120000 index 42061c01a1..0000000000 --- a/README +++ /dev/null @@ -1 +0,0 @@ -README.md
\ No newline at end of file @@ -1,66 +1,82 @@ - Bitcoin integration/staging tree +================================ + +http://www.bitcoin.org + +Copyright (c) 2009-2012 Bitcoin Developers + +What is Bitcoin? +---------------- + +Bitcoin is an experimental new digital currency that enables instant payments to +anyone, anywhere in the world. Bitcoin uses peer-to-peer technology to operate +with no central authority: managing transactions and issuing money are carried +out collectively by the network. Bitcoin is also the name of the open source +software which enables the use of this currency. + +For more information, as well as an immediately useable, binary version of +the Bitcoin client sofware, see http://www.bitcoin.org. + +License +------- + +Bitcoin is released under the terms of the MIT license. See `COPYING` for more +information or see http://opensource.org/licenses/MIT. Development process -=================== +------------------- -Developers work in their own trees, then submit pull requests when -they think their feature or bug fix is ready. +Developers work in their own trees, then submit pull requests when they think +their feature or bug fix is ready. -If it is a simple/trivial/non-controversial change, then one of the -bitcoin development team members simply pulls it. +If it is a simple/trivial/non-controversial change, then one of the Bitcoin +development team members simply pulls it. -If it is a more complicated or potentially controversial -change, then the patch submitter will be asked to start a -discussion (if they haven't already) on the mailing list: -http://sourceforge.net/mailarchive/forum.php?forum_name=bitcoin-development +If it is a *more complicated or potentially controversial* change, then the patch +submitter will be asked to start a discussion (if they haven't already) on the +[mailing list](http://sourceforge.net/mailarchive/forum.php?forum_name=bitcoin-development). -The patch will be accepted if there is broad consensus that it is a -good thing. Developers should expect to rework and resubmit patches -if they don't match the project's coding conventions (see coding.txt) -or are controversial. +The patch will be accepted if there is broad consensus that it is a good thing. +Developers should expect to rework and resubmit patches if the code doesn't +match the project's coding conventions (see `doc/coding.txt`) or are +controversial. -The master branch is regularly built and tested, but is not guaranteed -to be completely stable. Tags are regularly created to indicate new -official, stable release versions of Bitcoin. +The `master` branch is regularly built and tested, but is not guaranteed to be +completely stable. [Tags](https://github.com/bitcoin/bitcoin/tags) are created +regularly to indicate new official, stable release versions of Bitcoin. Testing -======= +------- + +Testing and code review is the bottleneck for development; we get more pull +requests than we can review and test. Please be patient and help out, and +remember this is a security-critical project where any mistake might cost people +lots of money. + +### Automated Testing -Testing and code review is the bottleneck for development; we get more -pull requests than we can review and test. Please be patient and help -out, and remember this is a security-critical project where any -mistake might cost people lots of money. +Developers are strongly encouraged to write unit tests for new code, and to +submit new unit tests for old code. -Automated Testing ------------------ +Unit tests for the core code are in `src/test/`. To compile and run them: -Developers are strongly encouraged to write unit tests for new code, -and to submit new unit tests for old code. + cd src; make -f makefile.linux test -Unit tests for the core code are in src/test/ -To compile and run them: - cd src; make -f makefile.linux test +Unit tests for the GUI code are in `src/qt/test/`. To compile and run them: -Unit tests for the GUI code are in src/qt/test/ -To compile and run them: - qmake BITCOIN_QT_TEST=1 -o Makefile.test bitcoin-qt.pro - make -f Makefile.test - ./Bitcoin-Qt + qmake BITCOIN_QT_TEST=1 -o Makefile.test bitcoin-qt.pro + make -f Makefile.test + ./Bitcoin-Qt -Every pull request is built for both Windows and -Linux on a dedicated server, and unit and sanity -tests are automatically run. The binaries -produced may be used for manual QA testing -(a link to them will appear in a comment on the pull request -from 'BitcoinPullTester'). -See https://github.com/TheBlueMatt/test-scripts for the -build/test scripts. +Every pull request is built for both Windows and Linux on a dedicated server, +and unit and sanity tests are automatically run. The binaries produced may be +used for manual QA testing -- a link to them will appear in a comment on the +pull request posted by 'BitcoinPullTester'. See `https://github.com/TheBlueMatt/test-scripts` +for the build/test scripts. -Manual Quality Assurance (QA) Testing -------------------------------------- +### Manual Quality Assurance (QA) Testing -Large changes should have a test plan, and should be tested -by somebody other than the developer who wrote the code. +Large changes should have a test plan, and should be tested by somebody other +than the developer who wrote the code. -See https://github.com/bitcoin/QA/ for how to create a test plan. +See `https://github.com/bitcoin/QA/` for how to create a test plan. diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 6c1c4a78cc..8a6f9efebd 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -372,6 +372,8 @@ win32:!contains(MINGW_THREAD_BUGFIX, 0) { !win32:!macx { DEFINES += LINUX LIBS += -lrt + # _FILE_OFFSET_BITS=64 lets 32-bit fopen transparently support large files. + DEFINES += _FILE_OFFSET_BITS=64 } macx:HEADERS += src/qt/macdockiconhandler.h diff --git a/contrib/macdeploy/notes.txt b/contrib/macdeploy/notes.txt index a3f0b5447d..3d74901437 100644 --- a/contrib/macdeploy/notes.txt +++ b/contrib/macdeploy/notes.txt @@ -5,7 +5,7 @@ Python 2.7 and make it your default Python installation. You will need the appscript package for the fancy disk image creation to work. Install it by invoking "sudo easy_install appscript". -Ths script should be invoked in the target directory like this: +This script should be invoked in the target directory like this: $source_dir/contrib/macdeploy/macdeployqtplus Bitcoin-Qt.app -add-qt-tr da,de,es,hu,ru,uk,zh_CN,zh_TW -dmg -fancy $source_dir/contrib/macdeploy/fancy.plist -verbose 2 During the process, the disk image window will pop up briefly where the fancy diff --git a/contrib/seeds/README b/contrib/seeds/README new file mode 100644 index 0000000000..97e3e1ec71 --- /dev/null +++ b/contrib/seeds/README @@ -0,0 +1,9 @@ +Utility to generate the pnSeed[] array that is compiled into the client +(see src/net.cpp). + +The 600 seeds compiled into the 0.8 release were created from sipa's DNS seed data, like this: + +curl -s http://bitcoin.sipa.be/seeds.txt | head -1000 | makeseeds.py + +The input to makeseeds.py is assumed to be approximately sorted from most-reliable to least-reliable, +with IP:port first on each line (lines that don't match IPv4:port are ignored). diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py new file mode 100755 index 0000000000..1d01fd7d20 --- /dev/null +++ b/contrib/seeds/makeseeds.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# +# Generate pnSeed[] from Pieter's DNS seeder +# + +NSEEDS=600 + +import re +import sys +from subprocess import check_output + +def main(): + lines = sys.stdin.readlines() + + ips = [] + pattern = re.compile(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}):8333") + for line in lines: + m = pattern.match(line) + if m is None: + continue + ip = 0 + for i in range(0,4): + ip = ip + (int(m.group(i+1)) << (8*(i))) + if ip == 0: + continue + ips.append(ip) + + for row in range(0, min(NSEEDS,len(ips)), 8): + print " " + ", ".join([ "0x%08x"%i for i in ips[row:row+8] ]) + "," + +if __name__ == '__main__': + main() diff --git a/contrib/verifysfbinaries/verify.sh b/contrib/verifysfbinaries/verify.sh index 336de3ec1f..149b7b394c 100755 --- a/contrib/verifysfbinaries/verify.sh +++ b/contrib/verifysfbinaries/verify.sh @@ -18,7 +18,7 @@ WORKINGDIR="/tmp/bitcoin" TMPFILE="hashes.tmp" #this URL is used if a version number is not specified as an argument to the script -SIGNATUREFILE="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/bitcoin-0.7.1/test/SHA256SUMS.asc" +SIGNATUREFILE="http://downloads.sourceforge.net/project/bitcoin/Bitcoin/bitcoin-0.7.2/SHA256SUMS.asc" SIGNATUREFILENAME="SHA256SUMS.asc" RCSUBDIR="test/" @@ -81,7 +81,7 @@ if [ $RET -ne 0 ]; then #and notify the user if it's bad echo "Bad signature." elif [ $RET -eq 2 ]; then - #or if a gpg error has occured + #or if a gpg error has occurred echo "gpg error. Do you have Gavin's code signing key installed?" fi diff --git a/doc/files.txt b/doc/files.txt new file mode 100644 index 0000000000..5d4cdabf8d --- /dev/null +++ b/doc/files.txt @@ -0,0 +1,19 @@ +Used in 0.8.0: +* wallet.dat: personal wallet (BDB) with keys and transactions +* peers.dat: peer IP address database (custom format); since 0.7.0 +* blocks/blk000??.dat: block data (custom, 128 MiB per file); since 0.8.0 +* blocks/rev000??.dat; block undo data (custom); since 0.8.0 (format changed since pre-0.8) +* blocks/index/*; block index (LevelDB); since 0.8.0 +* chainstate/*; block chain state database (LevelDB); since 0.8.0 +* database/*: BDB database environment; only used for wallet since 0.8.0 + +Only used in pre-0.8.0: +* blktree/*; block chain index (LevelDB); since pre-0.8, replaced by blocks/index/* in 0.8.0 +* coins/*; unspent transaction output database (LevelDB); since pre-0.8, replaced by chainstate/* in 0.8.0 + +Only used before 0.8.0: +* blkindex.dat: block chain index database (BDB); replaced by {chainstate/*,blocks/index/*,blocks/rev000??.dat} in 0.8.0 +* blk000?.dat: block data (custom, 2 GiB per file); replaced by blocks/blk000??.dat in 0.8.0 + +Only used before 0.7.0: +* addr.dat: peer IP address database (BDB); replaced by peers.dat in 0.7.0 diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 4d2096ef07..e2c420edd7 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -31,6 +31,7 @@ namespace Checkpoints (168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")) (193000, uint256("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")) (210000, uint256("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")) + (216116, uint256("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")) ; static MapCheckpoints mapCheckpointsTestnet = diff --git a/src/init.cpp b/src/init.cpp index 9b9415a8ed..2f37dad56c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -187,9 +187,9 @@ bool AppInit(int argc, char* argv[]) fRet = AppInit2(); } catch (std::exception& e) { - PrintException(&e, "AppInit()"); + PrintExceptionContinue(&e, "AppInit()"); } catch (...) { - PrintException(NULL, "AppInit()"); + PrintExceptionContinue(NULL, "AppInit()"); } if (!fRet) Shutdown(NULL); @@ -936,7 +936,8 @@ bool AppInit2() // scan for better chains in the block chain database, that are not yet connected in the active best chain uiInterface.InitMessage(_("Importing blocks from block database...")); - if (!ConnectBestBlock()) + CValidationState state; + if (!ConnectBestBlock(state)) strErrors << "Failed to connect best block"; CImportData *pimport = new CImportData(); diff --git a/src/leveldb.cpp b/src/leveldb.cpp index 9e2f32a171..b41764f51f 100644 --- a/src/leveldb.cpp +++ b/src/leveldb.cpp @@ -12,6 +12,18 @@ #include <boost/filesystem.hpp> +void HandleError(const leveldb::Status &status) throw(leveldb_error) { + if (status.ok()) + return; + if (status.IsCorruption()) + throw leveldb_error("Database corrupted"); + if (status.IsIOError()) + throw leveldb_error("Database I/O error"); + if (status.IsNotFound()) + throw leveldb_error("Database entry missing"); + throw leveldb_error("Unknown database error"); +} + static leveldb::Options GetOptions(size_t nCacheSize) { leveldb::Options options; options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); @@ -57,12 +69,12 @@ CLevelDB::~CLevelDB() { options.env = NULL; } -bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) { +bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) { leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); if (!status.ok()) { printf("LevelDB write failure: %s\n", status.ToString().c_str()); + HandleError(status); return false; } return true; } - diff --git a/src/leveldb.h b/src/leveldb.h index 0b83432072..79262edbb5 100644 --- a/src/leveldb.h +++ b/src/leveldb.h @@ -11,6 +11,14 @@ #include <boost/filesystem/path.hpp> +class leveldb_error : public std::runtime_error +{ +public: + leveldb_error(const std::string &msg) : std::runtime_error(msg) {} +}; + +void HandleError(const leveldb::Status &status) throw(leveldb_error); + // Batch of changes queued to be written to a CLevelDB class CLevelDBBatch { @@ -72,7 +80,7 @@ public: CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); ~CLevelDB(); - template<typename K, typename V> bool Read(const K& key, V& value) { + template<typename K, typename V> bool Read(const K& key, V& value) throw(leveldb_error) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); ssKey << key; @@ -84,6 +92,7 @@ public: if (status.IsNotFound()) return false; printf("LevelDB read failure: %s\n", status.ToString().c_str()); + HandleError(status); } try { CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); @@ -94,13 +103,13 @@ public: return true; } - template<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) { + template<typename K, typename V> bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) { CLevelDBBatch batch; batch.Write(key, value); return WriteBatch(batch, fSync); } - template<typename K> bool Exists(const K& key) { + template<typename K> bool Exists(const K& key) throw(leveldb_error) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); ssKey << key; @@ -112,24 +121,25 @@ public: if (status.IsNotFound()) return false; printf("LevelDB read failure: %s\n", status.ToString().c_str()); + HandleError(status); } return true; } - template<typename K> bool Erase(const K& key, bool fSync = false) { + template<typename K> bool Erase(const K& key, bool fSync = false) throw(leveldb_error) { CLevelDBBatch batch; batch.Erase(key); return WriteBatch(batch, fSync); } - bool WriteBatch(CLevelDBBatch &batch, bool fSync = false); + bool WriteBatch(CLevelDBBatch &batch, bool fSync = false) throw(leveldb_error); // not available for LevelDB; provide for compatibility with BDB bool Flush() { return true; } - bool Sync() { + bool Sync() throw(leveldb_error) { CLevelDBBatch batch; return WriteBatch(batch, true); } @@ -141,4 +151,3 @@ public: }; #endif // BITCOIN_LEVELDB_H -
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index fe35fbaf29..847b1ea8a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -93,15 +93,6 @@ void UnregisterWallet(CWallet* pwalletIn) } } -// check whether the passed transaction is from us -bool static IsFromMe(CTransaction& tx) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - if (pwallet->IsFromMe(tx)) - return true; - return false; -} - // get the wallet transaction with the given hash (if it exists) bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) { @@ -524,28 +515,28 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -bool CTransaction::CheckTransaction() const +bool CTransaction::CheckTransaction(CValidationState &state) const { // Basic checks that don't depend on any context if (vin.empty()) - return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); + return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty")); if (vout.empty()) - return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); + return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty")); // Size limits if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); + return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); // Check for negative or overflow output values int64 nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, vout) { if (txout.nValue < 0) - return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); if (txout.nValue > MAX_MONEY) - return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); } // Check for duplicate inputs @@ -553,20 +544,20 @@ bool CTransaction::CheckTransaction() const BOOST_FOREACH(const CTxIn& txin, vin) { if (vInOutPoints.count(txin.prevout)) - return false; + return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); vInOutPoints.insert(txin.prevout); } if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); + return state.DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); } else { BOOST_FOREACH(const CTxIn& txin, vin) if (txin.prevout.IsNull()) - return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); + return state.DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); } return true; @@ -633,18 +624,18 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) } } -bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, +bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) { if (pfMissingInputs) *pfMissingInputs = false; - if (!tx.CheckTransaction()) + if (!tx.CheckTransaction(state)) return error("CTxMemPool::accept() : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); + return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); // To help v0.1.5 clients who would see it as a negative number if ((int64)tx.nLockTime > std::numeric_limits<int>::max()) @@ -717,7 +708,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, // are the actual inputs available? if (!tx.HaveInputs(view)) - return error("CTxMemPool::accept() : inputs already spent"); + return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); // Bring the best block into scope view.GetBestBlock(); @@ -769,7 +760,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(view, true, SCRIPT_VERIFY_P2SH)) + if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } @@ -798,9 +789,13 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, return true; } -bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) +bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) { - return mempool.accept(*this, fCheckInputs, fLimitFree, pfMissingInputs); + try { + return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } } bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) @@ -913,7 +908,8 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree) { - return CTransaction::AcceptToMemoryPool(fCheckInputs, fLimitFree); + CValidationState state; + return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree); } @@ -1209,11 +1205,13 @@ void static InvalidBlockFound(CBlockIndex *pindex) { pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); setBlockIndexValid.erase(pindex); InvalidChainFound(pindex); - if (pindex->pnext) - ConnectBestBlock(); // reorganise away from the failed block + if (pindex->pnext) { + CValidationState stateDummy; + ConnectBestBlock(stateDummy); // reorganise away from the failed block + } } -bool ConnectBestBlock() { +bool ConnectBestBlock(CValidationState &state) { do { CBlockIndex *pindexNewBest; @@ -1252,8 +1250,13 @@ bool ConnectBestBlock() { BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { if (fRequestShutdown) break; - if (!SetBestChain(pindexSwitch)) - return false; + CValidationState state; + try { + if (!SetBestChain(state, pindexSwitch)) + return false; + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } } return true; } @@ -1315,22 +1318,20 @@ unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const return nSigOps; } -bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const +bool CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const { // mark inputs spent if (!IsCoinBase()) { BOOST_FOREACH(const CTxIn &txin, vin) { CCoins &coins = inputs.GetCoins(txin.prevout.hash); CTxInUndo undo; - if (!coins.Spend(txin.prevout, undo)) - return error("UpdateCoins() : cannot spend input"); + assert(coins.Spend(txin.prevout, undo)); txundo.vprevout.push_back(undo); } } // add outputs - if (!inputs.SetCoins(txhash, CCoins(*this, nHeight))) - return error("UpdateCoins() : cannot update output"); + assert(inputs.SetCoins(txhash, CCoins(*this, nHeight))); return true; } @@ -1368,7 +1369,7 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); } -bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const +bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const { if (!IsCoinBase()) { @@ -1378,7 +1379,7 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!HaveInputs(inputs)) - return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str()); + return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str())); // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. @@ -1393,26 +1394,26 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi // If prev is coinbase, check that it's matured if (coins.IsCoinBase()) { if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) - return error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight); + return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight)); } // Check for negative or overflow input values nValueIn += coins.vout[prevout.n].nValue; if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return DoS(100, error("CheckInputs() : txin values out of range")); + return state.DoS(100, error("CheckInputs() : txin values out of range")); } if (nValueIn < GetValueOut()) - return DoS(100, error("ChecktInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); + return state.DoS(100, error("ChecktInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); // Tally transaction fees int64 nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) - return DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); nFees += nTxFee; if (!MoneyRange(nFees)) - return DoS(100, error("CheckInputs() : nFees out of range")); + return state.DoS(100, error("CheckInputs() : nFees out of range")); // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. @@ -1432,7 +1433,7 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); } else if (!check()) - return DoS(100,false); + return state.DoS(100,false); } } } @@ -1441,56 +1442,9 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi } -bool CTransaction::ClientCheckInputs() const -{ - if (IsCoinBase()) - return false; - - // Take over previous transactions' spent pointers - { - LOCK(mempool.cs); - int64 nValueIn = 0; - for (unsigned int i = 0; i < vin.size(); i++) - { - // Get prev tx from single transactions in memory - COutPoint prevout = vin[i].prevout; - if (!mempool.exists(prevout.hash)) - return false; - CTransaction& txPrev = mempool.lookup(prevout.hash); - - if (prevout.n >= txPrev.vout.size()) - return false; - - // Verify signature - if (!VerifySignature(CCoins(txPrev, -1), *this, i, SCRIPT_VERIFY_P2SH, 0)) - return error("ConnectInputs() : VerifySignature failed"); - - ///// this is redundant with the mempool.mapNextTx stuff, - ///// not sure which I want to get rid of - ///// this has to go away now that posNext is gone - // // Check for conflicts - // if (!txPrev.vout[prevout.n].posNext.IsNull()) - // return error("ConnectInputs() : prev tx already used"); - // - // // Flag outpoints as used - // txPrev.vout[prevout.n].posNext = posThisTx; - - nValueIn += txPrev.vout[prevout.n].nValue; - - if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return error("ClientConnectInputs() : txin values out of range"); - } - if (GetValueOut() > nValueIn) - return false; - } - - return true; -} - - -bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) +bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) { assert(pindex == view.GetBestBlock()); @@ -1591,7 +1545,7 @@ void static FlushBlockFile() } } -bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize); +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); static CCheckQueue<CScriptCheck> scriptcheckqueue(128); @@ -1606,10 +1560,10 @@ void ThreadScriptCheckQuit() { scriptcheckqueue.Quit(); } -bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) +bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) { // Check it again in case a previous version let a bad block in - if (!CheckBlock(!fJustCheck, !fJustCheck)) + if (!CheckBlock(state, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block @@ -1674,12 +1628,12 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust nInputs += tx.vin.size(); nSigOps += tx.GetLegacySigOpCount(); if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops")); if (!tx.IsCoinBase()) { if (!tx.HaveInputs(view)) - return DoS(100, error("ConnectBlock() : inputs missing/spent")); + return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); if (fStrictPayToScriptHash) { @@ -1688,19 +1642,19 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust // an incredibly-expensive-to-validate block. nSigOps += tx.GetP2SHSigOpCount(view); if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops")); } nFees += tx.GetValueIn(view)-tx.GetValueOut(); std::vector<CScriptCheck> vChecks; - if (!tx.CheckInputs(view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) + if (!tx.CheckInputs(state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } CTxUndo txundo; - if (!tx.UpdateCoins(view, txundo, pindex->nHeight, GetTxHash(i))) + if (!tx.UpdateCoins(state, view, txundo, pindex->nHeight, GetTxHash(i))) return error("ConnectBlock() : UpdateInputs failed"); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); @@ -1713,10 +1667,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) - return error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)); + return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees))); if (!control.Wait()) - return DoS(100, false); + return state.DoS(100, false); int64 nTime2 = GetTimeMicros() - nStart; if (fBenchmark) printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1)); @@ -1729,10 +1683,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust { if (pindex->GetUndoPos().IsNull()) { CDiskBlockPos pos; - if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) + if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock() : FindUndoPos failed"); if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) - return error("ConnectBlock() : CBlockUndo::WriteToDisk failed"); + return state.Abort(_("Failed to write undo data")); // update nUndoPos in block index pindex->nUndoPos = pos.nPos; @@ -1743,15 +1697,15 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust CDiskBlockIndex blockindex(pindex); if (!pblocktree->WriteBlockIndex(blockindex)) - return error("ConnectBlock() : WriteBlockIndex failed"); + return state.Abort(_("Failed to write block index")); } if (fTxIndex) - pblocktree->WriteTxIndex(vPos); + if (!pblocktree->WriteTxIndex(vPos)) + return state.Abort(_("Failed to write transaction index")); // add this block to the view's block chain - if (!view.SetBestBlock(pindex)) - return false; + assert(view.SetBestBlock(pindex)); // Watch for transactions paying to me for (unsigned int i=0; i<vtx.size(); i++) @@ -1760,7 +1714,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust return true; } -bool SetBestChain(CBlockIndex* pindexNew) +bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) { // All modifications to the coin state will be done in this cache. // Only when all have succeeded, we push it to pcoinsTip. @@ -1771,13 +1725,14 @@ bool SetBestChain(CBlockIndex* pindexNew) CBlockIndex* plonger = pindexNew; while (pfork && pfork != plonger) { - while (plonger->nHeight > pfork->nHeight) - if (!(plonger = plonger->pprev)) - return error("SetBestChain() : plonger->pprev is null"); + while (plonger->nHeight > pfork->nHeight) { + plonger = plonger->pprev; + assert(plonger != NULL); + } if (pfork == plonger) break; - if (!(pfork = pfork->pprev)) - return error("SetBestChain() : pfork->pprev is null"); + pfork = pfork->pprev; + assert(pfork != NULL); } // List of what to disconnect (typically nothing) @@ -1801,9 +1756,9 @@ bool SetBestChain(CBlockIndex* pindexNew) BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { CBlock block; if (!block.ReadFromDisk(pindex)) - return error("SetBestBlock() : ReadFromDisk for disconnect failed"); + return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.DisconnectBlock(pindex, view)) + if (!block.DisconnectBlock(state, pindex, view)) return error("SetBestBlock() : DisconnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str()); if (fBenchmark) printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); @@ -1821,11 +1776,13 @@ bool SetBestChain(CBlockIndex* pindexNew) BOOST_FOREACH(CBlockIndex *pindex, vConnect) { CBlock block; if (!block.ReadFromDisk(pindex)) - return error("SetBestBlock() : ReadFromDisk for connect failed"); + return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.ConnectBlock(pindex, view)) { - InvalidChainFound(pindexNew); - InvalidBlockFound(pindex); + if (!block.ConnectBlock(state, pindex, view)) { + if (state.IsInvalid()) { + InvalidChainFound(pindexNew); + InvalidBlockFound(pindex); + } return error("SetBestBlock() : ConnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str()); } if (fBenchmark) @@ -1839,8 +1796,7 @@ bool SetBestChain(CBlockIndex* pindexNew) // Flush changes to global coin state int64 nStart = GetTimeMicros(); int nModified = view.GetCacheSize(); - if (!view.Flush()) - return error("SetBestBlock() : unable to modify coin state"); + assert(view.Flush()); int64 nTime = GetTimeMicros() - nStart; if (fBenchmark) printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); @@ -1848,10 +1804,17 @@ bool SetBestChain(CBlockIndex* pindexNew) // Make sure it's successfully written to disk before changing memory structure bool fIsInitialDownload = IsInitialBlockDownload(); if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) { + // Typical CCoins structures on disk are around 100 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error(); FlushBlockFile(); pblocktree->Sync(); if (!pcoinsTip->Flush()) - return false; + return state.Abort(_("Failed to write to coin database")); } // At this point, all changes have been done to the database. @@ -1868,8 +1831,11 @@ bool SetBestChain(CBlockIndex* pindexNew) pindex->pprev->pnext = pindex; // Resurrect memory transactions that were in the disconnected branch - BOOST_FOREACH(CTransaction& tx, vResurrect) - tx.AcceptToMemoryPool(true, false); + BOOST_FOREACH(CTransaction& tx, vResurrect) { + // ignore validation errors in resurrected transactions + CValidationState stateDummy; + tx.AcceptToMemoryPool(stateDummy, true, false); + } // Delete redundant memory transactions that are in the connected branch BOOST_FOREACH(CTransaction& tx, vDelete) { @@ -1926,17 +1892,16 @@ bool SetBestChain(CBlockIndex* pindexNew) } -bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) +bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) { // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) - return error("AddToBlockIndex() : %s already exists", BlockHashStr(hash).c_str()); + return state.Invalid(error("AddToBlockIndex() : %s already exists", BlockHashStr(hash).c_str())); // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(*this); - if (!pindexNew) - return error("AddToBlockIndex() : new CBlockIndex failed"); + assert(pindexNew); map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock); @@ -1954,10 +1919,11 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; setBlockIndexValid.insert(pindexNew); - pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)); + if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) + return state.Abort(_("Failed to write block index")); // New best? - if (!ConnectBestBlock()) + if (!ConnectBestBlock(state)) return false; if (pindexNew == pindexBest) @@ -1968,14 +1934,15 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) hashPrevBestCoinBase = GetTxHash(0); } - pblocktree->Flush(); + if (!pblocktree->Flush()) + return state.Abort(_("Failed to sync block index")); uiInterface.NotifyBlocksChanged(); return true; } -bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) +bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) { bool fUpdatedLast = false; @@ -2017,19 +1984,19 @@ bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeigh } } else - return error("FindBlockPos() : out of disk space"); + return state.Error(); } } if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return error("FindBlockPos() : cannot write updated block info"); + return state.Abort(_("Failed to write file info")); if (fUpdatedLast) pblocktree->WriteLastBlockFile(nLastBlockFile); return true; } -bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) { pos.nFile = nFile; @@ -2040,15 +2007,15 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) pos.nPos = infoLastBlockFile.nUndoSize; nNewSize = (infoLastBlockFile.nUndoSize += nAddSize); if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return error("FindUndoPos() : cannot write updated block info"); + return state.Abort(_("Failed to write block info")); } else { CBlockFileInfo info; if (!pblocktree->ReadBlockFileInfo(nFile, info)) - return error("FindUndoPos() : cannot read block info"); + return state.Abort(_("Failed to read block info")); pos.nPos = info.nUndoSize; nNewSize = (info.nUndoSize += nAddSize); if (!pblocktree->WriteBlockFileInfo(nFile, info)) - return error("FindUndoPos() : cannot write updated block info"); + return state.Abort(_("Failed to write block info")); } unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; @@ -2063,41 +2030,41 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) } } else - return error("FindUndoPos() : out of disk space"); + return state.Error(); } return true; } -bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const +bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const { // These are checks that are independent of context // that can be verified before saving an orphan block. // Size limits if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return DoS(100, error("CheckBlock() : size limits failed")); + return state.DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) - return DoS(50, error("CheckBlock() : proof of work failed")); + return state.DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return error("CheckBlock() : block timestamp too far in the future"); + return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); // First transaction must be coinbase, the rest must not be if (vtx.empty() || !vtx[0].IsCoinBase()) - return DoS(100, error("CheckBlock() : first tx is not coinbase")); + return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); for (unsigned int i = 1; i < vtx.size(); i++) if (vtx[i].IsCoinBase()) - return DoS(100, error("CheckBlock() : more than one coinbase")); + return state.DoS(100, error("CheckBlock() : more than one coinbase")); // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.CheckTransaction()) - return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); + if (!tx.CheckTransaction(state)) + return error("CheckBlock() : CheckTransaction failed"); // Build the merkle tree already. We need it anyway later, and it makes the // block cache the transaction hashes, which means they don't need to be @@ -2111,7 +2078,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const uniqueTx.insert(GetTxHash(i)); } if (uniqueTx.size() != vtx.size()) - return DoS(100, error("CheckBlock() : duplicate transaction")); + return state.DoS(100, error("CheckBlock() : duplicate transaction")); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, vtx) @@ -2119,21 +2086,21 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const nSigOps += tx.GetLegacySigOpCount(); } if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); + return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkle root if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) - return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); + return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); return true; } -bool CBlock::AcceptBlock(CDiskBlockPos *dbp) +bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) - return error("AcceptBlock() : block already in mapBlockIndex"); + return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex")); // Get prev block index CBlockIndex* pindexPrev = NULL; @@ -2141,26 +2108,26 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) if (hash != hashGenesisBlock) { map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); if (mi == mapBlockIndex.end()) - return DoS(10, error("AcceptBlock() : prev block not found")); + return state.DoS(10, error("AcceptBlock() : prev block not found")); pindexPrev = (*mi).second; nHeight = pindexPrev->nHeight+1; // Check proof of work if (nBits != GetNextWorkRequired(pindexPrev, this)) - return DoS(100, error("AcceptBlock() : incorrect proof of work")); + return state.DoS(100, error("AcceptBlock() : incorrect proof of work")); // Check timestamp against prev if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return error("AcceptBlock() : block's timestamp is too early"); + return state.Invalid(error("AcceptBlock() : block's timestamp is too early")); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.IsFinal(nHeight, GetBlockTime())) - return DoS(10, error("AcceptBlock() : contains a non-final transaction")); + return state.DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(nHeight, hash)) - return DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); + return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (nVersion < 2) @@ -2168,7 +2135,7 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) { - return error("AcceptBlock() : rejected nVersion=1 block"); + return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height @@ -2180,23 +2147,27 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) { CScript expect = CScript() << nHeight; if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) - return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); } } } // Write block to history file - unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (dbp != NULL) - blockPos = *dbp; - if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) - return error("AcceptBlock() : FindBlockPos failed"); - if (dbp == NULL) - if (!WriteToDisk(blockPos)) - return error("AcceptBlock() : WriteToDisk failed"); - if (!AddToBlockIndex(blockPos)) - return error("AcceptBlock() : AddToBlockIndex failed"); + try { + unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (dbp != NULL) + blockPos = *dbp; + if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) + return error("AcceptBlock() : FindBlockPos failed"); + if (dbp == NULL) + if (!WriteToDisk(blockPos)) + return state.Abort(_("Failed to write block")); + if (!AddToBlockIndex(state, blockPos)) + return error("AcceptBlock() : AddToBlockIndex failed"); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } // Relay inventory, but don't relay old inventory during initial block download int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); @@ -2223,17 +2194,17 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } -bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) +bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = pblock->GetHash(); if (mapBlockIndex.count(hash)) - return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, BlockHashStr(hash).c_str()); + return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, BlockHashStr(hash).c_str())); if (mapOrphanBlocks.count(hash)) - return error("ProcessBlock() : already have block (orphan) %s", BlockHashStr(hash).c_str()); + return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", BlockHashStr(hash).c_str())); // Preliminary checks - if (!pblock->CheckBlock()) + if (!pblock->CheckBlock(state)) return error("ProcessBlock() : CheckBlock FAILED"); CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); @@ -2243,9 +2214,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; if (deltaTime < 0) { - if (pfrom) - pfrom->Misbehaving(100); - return error("ProcessBlock() : block with timestamp before last checkpoint"); + return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint")); } CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); @@ -2253,9 +2222,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); if (bnNewBlock > bnRequired) { - if (pfrom) - pfrom->Misbehaving(100); - return error("ProcessBlock() : block with too little proof-of-work"); + return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work")); } } @@ -2278,7 +2245,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) } // Store to disk - if (!pblock->AcceptBlock(dbp)) + if (!pblock->AcceptBlock(state, dbp)) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one @@ -2292,7 +2259,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) ++mi) { CBlock* pblockOrphan = (*mi).second; - if (pblockOrphan->AcceptBlock()) + if (pblockOrphan->AcceptBlock(state)) vWorkQueue.push_back(pblockOrphan->GetHash()); mapOrphanBlocks.erase(pblockOrphan->GetHash()); delete pblockOrphan; @@ -2464,6 +2431,14 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) { +bool AbortNode(const std::string &strMessage) { + fRequestShutdown = true; + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MODAL); + StartShutdown(); + return false; +} bool CheckDiskSpace(uint64 nAdditionalBytes) { @@ -2471,15 +2446,8 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) - { - fShutdown = true; - string strMessage = _("Error: Disk space is low!"); - strMiscWarning = strMessage; - printf("*** %s\n", strMessage.c_str()); - uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; - } + return AbortNode(_("Error: Disk space is low!")); + return true; } @@ -2621,6 +2589,7 @@ bool VerifyDB() { CBlockIndex* pindexState = pindexBest; CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; + CValidationState state; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) @@ -2630,7 +2599,7 @@ bool VerifyDB() { if (!block.ReadFromDisk(pindex)) return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !block.CheckBlock()) + if (nCheckLevel >= 1 && !block.CheckBlock(state)) return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -2644,7 +2613,7 @@ bool VerifyDB() { // check level 3: check for inconsistencies during memory-only disconnect of tip blocks if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { bool fClean = true; - if (!block.DisconnectBlock(pindex, coins, &fClean)) + if (!block.DisconnectBlock(state, pindex, coins, &fClean)) return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); pindexState = pindex->pprev; if (!fClean) { @@ -2665,7 +2634,7 @@ bool VerifyDB() { CBlock block; if (!block.ReadFromDisk(pindex)) return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - if (!block.ConnectBlock(pindex, coins)) + if (!block.ConnectBlock(state, pindex, coins)) return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); } } @@ -2746,11 +2715,12 @@ bool LoadBlockIndex() // Start new block file unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; - if (!FindBlockPos(blockPos, nBlockSize+8, 0, block.nTime)) + CValidationState state; + if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime)) return error("AcceptBlock() : FindBlockPos failed"); if (!block.WriteToDisk(blockPos)) return error("LoadBlockIndex() : writing genesis block to disk failed"); - if (!block.AddToBlockIndex(blockPos)) + if (!block.AddToBlockIndex(state, blockPos)) return error("LoadBlockIndex() : genesis block not accepted"); } @@ -2834,7 +2804,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) int64 nStart = GetTimeMillis(); int nLoaded = 0; - { + try { CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); uint64 nStartByte = 0; if (dbp) { @@ -2880,14 +2850,19 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) LOCK(cs_main); if (dbp) dbp->nPos = nBlockPos; - if (ProcessBlock(NULL, &block, dbp)) + CValidationState state; + if (ProcessBlock(state, NULL, &block, dbp)) nLoaded++; + if (state.IsError()) + break; } } catch (std::exception &e) { printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); } } fclose(fileIn); + } catch(std::runtime_error &e) { + AbortNode(_("Error: system error: ") + e.what()); } if (nLoaded > 0) printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); @@ -3457,7 +3432,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); bool fMissingInputs = false; - if (tx.AcceptToMemoryPool(true, true, &fMissingInputs)) + CValidationState state; + if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs)) { RelayTransaction(tx, inv.hash, vMsg); mapAlreadyAskedFor.erase(inv); @@ -3478,7 +3454,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CInv inv(MSG_TX, tx.GetHash()); bool fMissingInputs2 = false; - if (tx.AcceptToMemoryPool(true, true, &fMissingInputs2)) + if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); RelayTransaction(tx, inv.hash, vMsg); @@ -3507,7 +3483,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (nEvicted > 0) printf("mapOrphan overflow, removed %u tx\n", nEvicted); } - if (tx.nDoS) pfrom->Misbehaving(tx.nDoS); + int nDoS; + if (state.IsInvalid(nDoS)) + pfrom->Misbehaving(nDoS); } @@ -3522,9 +3500,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CInv inv(MSG_BLOCK, block.GetHash()); pfrom->AddInventoryKnown(inv); - if (ProcessBlock(pfrom, &block)) + CValidationState state; + if (ProcessBlock(state, pfrom, &block)) mapAlreadyAskedFor.erase(inv); - if (block.nDoS) pfrom->Misbehaving(block.nDoS); + int nDoS; + if (state.IsInvalid(nDoS)) + pfrom->Misbehaving(nDoS); } @@ -4274,12 +4255,13 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; - if (!tx.CheckInputs(viewTemp, true, SCRIPT_VERIFY_P2SH)) + CValidationState state; + if (!tx.CheckInputs(state, viewTemp, true, SCRIPT_VERIFY_P2SH)) continue; CTxUndo txundo; uint256 hash = tx.GetHash(); - if (!tx.UpdateCoins(viewTemp, txundo, pindexPrev->nHeight+1, hash)) + if (!tx.UpdateCoins(state, viewTemp, txundo, pindexPrev->nHeight+1, hash)) continue; // push changes from the second layer cache to the first one @@ -4337,7 +4319,8 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; CCoinsViewCache viewNew(*pcoinsTip, true); - if (!pblock->ConnectBlock(&indexDummy, viewNew, true)) + CValidationState state; + if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true)) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); } @@ -4439,7 +4422,8 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) } // Process this block the same as if we had received it from another node - if (!ProcessBlock(NULL, pblock)) + CValidationState state; + if (!ProcessBlock(state, NULL, pblock)) return error("BitcoinMiner : ProcessBlock, block not accepted"); } diff --git a/src/main.h b/src/main.h index 9dbcac0b5a..23a4d3ba31 100644 --- a/src/main.h +++ b/src/main.h @@ -106,15 +106,17 @@ static const uint64 nMinDiskSpace = 52428800; class CReserveKey; class CCoinsDB; class CBlockTreeDB; -class CDiskBlockPos; +struct CDiskBlockPos; class CCoins; class CTxUndo; class CCoinsView; class CCoinsViewCache; class CScriptCheck; +class CValidationState; struct CBlockTemplate; + /** Register a wallet to receive updates from core */ void RegisterWallet(CWallet* pwalletIn); /** Unregister a wallet from core */ @@ -122,7 +124,7 @@ void UnregisterWallet(CWallet* pwalletIn); /** Push an updated transaction to all registered wallets */ void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); /** Process an incoming block */ -bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); +bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64 nAdditionalBytes = 0); /** Open a block file (blk?????.dat) */ @@ -172,13 +174,15 @@ std::string GetWarnings(std::string strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); /** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */ -bool SetBestChain(CBlockIndex* pindexNew); +bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew); /** Find the best known block, and make it the tip of the block chain */ -bool ConnectBestBlock(); +bool ConnectBestBlock(CValidationState &state); /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); /** Verify a signature */ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); +/** Abort with a message */ +bool AbortNode(const std::string &msg); @@ -454,7 +458,6 @@ public: - enum GetMinFee_mode { GMF_BLOCK, @@ -474,10 +477,6 @@ public: std::vector<CTxOut> vout; unsigned int nLockTime; - // Denial-of-service detection: - mutable int nDoS; - bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } - CTransaction() { SetNull(); @@ -498,7 +497,6 @@ public: vin.clear(); vout.clear(); nLockTime = 0; - nDoS = 0; // Denial-of-service prevention } bool IsNull() const @@ -654,27 +652,24 @@ public: } - // Do all possible client-mode checks - bool ClientCheckInputs() const; - // Check whether all prevouts of this transaction are present in the UTXO set represented by view bool HaveInputs(CCoinsViewCache &view) const; // 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(CCoinsViewCache &view, bool fScriptChecks = true, + bool CheckInputs(CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, std::vector<CScriptCheck> *pvChecks = NULL) const; // Apply the effects of this transaction on the UTXO set represented by view - bool UpdateCoins(CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; + bool UpdateCoins(CValidationState &state, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; // Context-independent validity checks - bool CheckTransaction() const; + bool CheckTransaction(CValidationState &state) const; // Try to accept this transaction into the memory pool - bool AcceptToMemoryPool(bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); + bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); protected: static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); @@ -1320,10 +1315,6 @@ public: // memory only mutable std::vector<uint256> vMerkleTree; - // Denial-of-service detection: - mutable int nDoS; - bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; } - CBlock() { SetNull(); @@ -1346,7 +1337,6 @@ public: CBlockHeader::SetNull(); vtx.clear(); vMerkleTree.clear(); - nDoS = 0; } CBlockHeader GetBlockHeader() const @@ -1494,23 +1484,23 @@ public: * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean * will be true if no problems were found. Otherwise, the return value will be false in case * of problems. Note that in any case, coins may be modified. */ - bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL); + bool DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL); // Apply the effects of this block (with given index) on the UTXO set represented by coins - bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); + bool ConnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); // Read a block from disk bool ReadFromDisk(const CBlockIndex* pindex); // Add this block to the block index, and if necessary, switch the active block chain to this - bool AddToBlockIndex(const CDiskBlockPos &pos); + bool AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos); // Context-independent validity checks - bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; + bool CheckBlock(CValidationState &state, bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; // Store block on disk // if dbp is provided, the file is known to already reside on disk - bool AcceptBlock(CDiskBlockPos *dbp = NULL); + bool AcceptBlock(CValidationState &state, CDiskBlockPos *dbp = NULL); }; @@ -1877,6 +1867,52 @@ public: } }; +/** Capture information about block/transaction validation */ +class CValidationState { +private: + enum mode_state { + MODE_VALID, // everything ok + MODE_INVALID, // network rule violation (DoS value may be set) + MODE_ERROR, // run-time error + } mode; + int nDoS; +public: + CValidationState() : mode(MODE_VALID), nDoS(0) {} + bool DoS(int level, bool ret = false) { + if (mode == MODE_ERROR) + return ret; + nDoS += level; + mode = MODE_INVALID; + return ret; + } + bool Invalid(bool ret = false) { + return DoS(0, ret); + } + bool Error() { + mode = MODE_ERROR; + return false; + } + bool Abort(const std::string &msg) { + AbortNode(msg); + return Error(); + } + bool IsValid() { + return mode == MODE_VALID; + } + bool IsInvalid() { + return mode == MODE_INVALID; + } + bool IsError() { + return mode == MODE_ERROR; + } + bool IsInvalid(int &nDoSOut) { + if (IsInvalid()) { + nDoSOut = nDoS; + return true; + } + return false; + } +}; @@ -2025,7 +2061,7 @@ public: std::map<uint256, CTransaction> mapTx; std::map<COutPoint, CInPoint> mapNextTx; - bool accept(CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs); + bool accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs); bool addUnchecked(const uint256& hash, CTransaction &tx); bool remove(const CTransaction &tx, bool fRecursive = false); bool removeConflicts(const CTransaction &tx); diff --git a/src/makefile.unix b/src/makefile.unix index 14cf1b8fa5..b52a0f8aea 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -7,7 +7,7 @@ USE_IPV6:=1 LINK:=$(CXX) -DEFS=-DBOOST_SPIRIT_THREADSAFE +DEFS=-DBOOST_SPIRIT_THREADSAFE -D_FILE_OFFSET_BITS=64 DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/obj $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) LIBS = $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) diff --git a/src/qt/aboutdialog.cpp b/src/qt/aboutdialog.cpp index 0b98befe8d..755413b2bb 100644 --- a/src/qt/aboutdialog.cpp +++ b/src/qt/aboutdialog.cpp @@ -1,14 +1,20 @@ #include "aboutdialog.h" #include "ui_aboutdialog.h" + #include "clientmodel.h" -#include "version.h" +// Copyright year (2009-this) +// Todo: update this when changing our copyright comments in the source +const int ABOUTDIALOG_COPYRIGHT_YEAR = 2013; AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog) { ui->setupUi(this); + + // Set current copyright year + ui->copyrightLabel->setText(tr("Copyright") + QString(" © ") + tr("2009-%1 The Bitcoin developers").arg(ABOUTDIALOG_COPYRIGHT_YEAR)); } void AboutDialog::setModel(ClientModel *model) diff --git a/src/qt/aboutdialog.h b/src/qt/aboutdialog.h index 2ed9e9e7c4..33b1437674 100644 --- a/src/qt/aboutdialog.h +++ b/src/qt/aboutdialog.h @@ -18,6 +18,7 @@ public: ~AboutDialog(); void setModel(ClientModel *model); + private: Ui::AboutDialog *ui; diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 92a511adb9..5bd1517091 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -109,13 +109,24 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Transaction creation failed!"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: Wallet locked, unable to create transaction!"), QT_TRANSLATE_NOOP("bitcoin-core", "Error: could not start node"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error: system error: "), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to read block info"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to read block"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to sync block index"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block index"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block info"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write block"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write file info"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write to coin database"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write transaction index"), +QT_TRANSLATE_NOOP("bitcoin-core", "Failed to write undo data"), QT_TRANSLATE_NOOP("bitcoin-core", "Fee per KB to add to transactions you send"), QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using DNS lookup (default: 1 unless -connect)"), QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using internet relay chat (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins"), QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"), -QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 2500, 0 = all)"), +QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 288, 0 = all)"), QT_TRANSLATE_NOOP("bitcoin-core", "How thorough the block verification is (0-4, default: 3)"), QT_TRANSLATE_NOOP("bitcoin-core", "Importing blocks from block database..."), QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file"), @@ -163,6 +174,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify connection timeout in milliseconds (d QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: bitcoind.pid)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), +QT_TRANSLATE_NOOP("bitcoin-core", "System error: "), QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: 100)"), QT_TRANSLATE_NOOP("bitcoin-core", "To use the %s option"), diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 12bd989338..084ad12a56 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -48,7 +48,10 @@ int ClientModel::getNumBlocksAtStartup() QDateTime ClientModel::getLastBlockDate() const { - return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + if (pindexBest) + return QDateTime::fromTime_t(pindexBest->GetBlockTime()); + else + return QDateTime::fromTime_t(1231006505); // Genesis block's time } void ClientModel::updateTimer() diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui index b59c2445de..80768f89b0 100644 --- a/src/qt/forms/aboutdialog.ui +++ b/src/qt/forms/aboutdialog.ui @@ -91,7 +91,10 @@ <cursorShape>IBeamCursor</cursorShape> </property> <property name="text"> - <string>Copyright © 2009-2012 The Bitcoin developers</string> + <string notr="true">Copyright &copy; 2009-YYYY The Bitcoin developers</string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 4895e9ba4e..18249b1669 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -15,12 +15,7 @@ <translation><b>Bitcoin</b> version</translation> </message> <message> - <location line="+41"/> - <source>Copyright © 2009-2012 The Bitcoin developers</source> - <translation>Copyright © 2009-2012 The Bitcoin developers</translation> - </message> - <message> - <location line="+13"/> + <location line="+57"/> <source> This is experimental software. @@ -34,6 +29,16 @@ Distributed under the MIT/X11 software license, see the accompanying file COPYIN This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard.</translation> </message> + <message> + <location filename="../aboutdialog.cpp" line="+17"/> + <source>Copyright</source> + <translation>Copyright</translation> + </message> + <message> + <location line="+0"/> + <source>2009-%1 The Bitcoin developers</source> + <translation>2009-%1 The Bitcoin developers</translation> + </message> </context> <context> <name>AddressBookPage</name> @@ -713,7 +718,7 @@ Address: %4 <context> <name>ClientModel</name> <message> - <location filename="../clientmodel.cpp" line="+86"/> + <location filename="../clientmodel.cpp" line="+89"/> <source>Network Alert</source> <translation>Network Alert</translation> </message> @@ -2174,12 +2179,12 @@ Address: %4 <translation>Bitcoin version</translation> </message> <message> - <location line="+83"/> + <location line="+95"/> <source>Usage:</source> <translation>Usage:</translation> </message> <message> - <location line="-23"/> + <location line="-24"/> <source>Send command to -server or bitcoind</source> <translation>Send command to -server or bitcoind</translation> </message> @@ -2214,12 +2219,12 @@ Address: %4 <translation>Generate coins</translation> </message> <message> - <location line="-15"/> + <location line="-26"/> <source>Don't generate coins</source> <translation>Don't generate coins</translation> </message> <message> - <location line="+62"/> + <location line="+73"/> <source>Specify data directory</source> <translation>Specify data directory</translation> </message> @@ -2239,22 +2244,22 @@ Address: %4 <translation>Maintain at most <n> connections to peers (default: 125)</translation> </message> <message> - <location line="-35"/> + <location line="-46"/> <source>Connect to a node to retrieve peer addresses, and disconnect</source> <translation>Connect to a node to retrieve peer addresses, and disconnect</translation> </message> <message> - <location line="+66"/> + <location line="+77"/> <source>Specify your own public address</source> <translation>Specify your own public address</translation> </message> <message> - <location line="+2"/> + <location line="+3"/> <source>Threshold for disconnecting misbehaving peers (default: 100)</source> <translation>Threshold for disconnecting misbehaving peers (default: 100)</translation> </message> <message> - <location line="-117"/> + <location line="-129"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</source> <translation>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</translation> </message> @@ -2274,17 +2279,17 @@ Address: %4 <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="+63"/> + <location line="+74"/> <source>Run in the background as a daemon and accept commands</source> <translation>Run in the background as a daemon and accept commands</translation> </message> <message> - <location line="+31"/> + <location line="+32"/> <source>Use the test network</source> <translation>Use the test network</translation> </message> <message> - <location line="-93"/> + <location line="-105"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> @@ -2425,16 +2430,76 @@ If the file does not exist, create it with owner-readable-only file permissions. </message> <message> <location line="+2"/> + <source>Error: system error: </source> + <translation>Error: system error: </translation> + </message> + <message> + <location line="+1"/> <source>Failed to listen on any port. Use -listen=0 if you want this.</source> <translation>Failed to listen on any port. Use -listen=0 if you want this.</translation> </message> <message> + <location line="+1"/> + <source>Failed to read block info</source> + <translation>Failed to read block info</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to read block</source> + <translation>Failed to read block</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to sync block index</source> + <translation>Failed to sync block index</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write block index</source> + <translation>Failed to write block index</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write block info</source> + <translation>Failed to write block info</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write block</source> + <translation>Failed to write block</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write file info</source> + <translation>Failed to write file info</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write to coin database</source> + <translation>Failed to write to coin database</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write transaction index</source> + <translation>Failed to write transaction index</translation> + </message> + <message> + <location line="+1"/> + <source>Failed to write undo data</source> + <translation>Failed to write undo data</translation> + </message> + <message> <location line="+2"/> <source>Find peers using DNS lookup (default: 1 unless -connect)</source> <translation>Find peers using DNS lookup (default: 1 unless -connect)</translation> </message> <message> - <location line="+5"/> + <location line="+4"/> + <source>How many blocks to check at startup (default: 288, 0 = all)</source> + <translation>How many blocks to check at startup (default: 288, 0 = all)</translation> + </message> + <message> + <location line="+1"/> <source>How thorough the block verification is (0-4, default: 3)</source> <translation>How thorough the block verification is (0-4, default: 3)</translation> </message> @@ -2544,7 +2609,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Specify connection timeout in milliseconds (default: 5000)</translation> </message> <message> - <location line="+13"/> + <location line="+4"/> + <source>System error: </source> + <translation>System error: </translation> + </message> + <message> + <location line="+10"/> <source>Use UPnP to map the listening port (default: 0)</source> <translation>Use UPnP to map the listening port (default: 0)</translation> </message> @@ -2584,32 +2654,32 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>wallet.dat corrupt, salvage failed</translation> </message> <message> - <location line="-44"/> + <location line="-45"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-54"/> + <location line="-65"/> <source>Allow JSON-RPC connections from specified IP address</source> <translation>Allow JSON-RPC connections from specified IP address</translation> </message> <message> - <location line="+63"/> + <location line="+74"/> <source>Send commands to node running on <ip> (default: 127.0.0.1)</source> <translation>Send commands to node running on <ip> (default: 127.0.0.1)</translation> </message> <message> - <location line="-106"/> + <location line="-117"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+127"/> + <location line="+139"/> <source>Upgrade wallet to latest format</source> <translation>Upgrade wallet to latest format</translation> </message> <message> - <location line="-15"/> + <location line="-16"/> <source>Set key pool size to <n> (default: 100)</source> <translation>Set key pool size to <n> (default: 100)</translation> </message> @@ -2619,17 +2689,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Rescan the block chain for missing wallet transactions</translation> </message> <message> - <location line="-27"/> - <source>How many blocks to check at startup (default: 2500, 0 = all)</source> - <translation>How many blocks to check at startup (default: 2500, 0 = all)</translation> - </message> - <message> - <location line="+56"/> + <location line="+30"/> <source>Use OpenSSL (https) for JSON-RPC connections</source> <translation>Use OpenSSL (https) for JSON-RPC connections</translation> </message> <message> - <location line="-20"/> + <location line="-21"/> <source>Server certificate file (default: server.cert)</source> <translation>Server certificate file (default: server.cert)</translation> </message> @@ -2639,12 +2704,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Server private key (default: server.pem)</translation> </message> <message> - <location line="-136"/> + <location line="-147"/> <source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source> <translation>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation> </message> <message> - <location line="+147"/> + <location line="+159"/> <source>This help message</source> <translation>This help message</translation> </message> @@ -2654,7 +2719,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Unable to bind to %s on this computer (bind returned error %d, %s)</translation> </message> <message> - <location line="-71"/> + <location line="-83"/> <source>Connect through socks proxy</source> <translation>Connect through socks proxy</translation> </message> @@ -2664,12 +2729,12 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+43"/> + <location line="+54"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> <message> - <location line="-25"/> + <location line="-36"/> <source>Error loading wallet.dat: Wallet corrupted</source> <translation>Error loading wallet.dat: Wallet corrupted</translation> </message> @@ -2679,7 +2744,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Error loading wallet.dat: Wallet requires newer version of Bitcoin</translation> </message> <message> - <location line="+74"/> + <location line="+86"/> <source>Verifying block database integrity...</source> <translation>Verifying block database integrity...</translation> </message> @@ -2694,17 +2759,17 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Wallet needed to be rewritten: restart Bitcoin to complete</translation> </message> <message> - <location line="-78"/> + <location line="-90"/> <source>Error loading wallet.dat</source> <translation>Error loading wallet.dat</translation> </message> <message> - <location line="+20"/> + <location line="+31"/> <source>Invalid -proxy address: '%s'</source> <translation>Invalid -proxy address: '%s'</translation> </message> <message> - <location line="+47"/> + <location line="+48"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Unknown network specified in -onlynet: '%s'</translation> </message> @@ -2714,7 +2779,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Unknown -socks proxy version requested: %i</translation> </message> <message> - <location line="-76"/> + <location line="-88"/> <source>Cannot resolve -bind address: '%s'</source> <translation>Cannot resolve -bind address: '%s'</translation> </message> @@ -2724,17 +2789,17 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Cannot resolve -externalip address: '%s'</translation> </message> <message> - <location line="+31"/> + <location line="+42"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> </message> <message> - <location line="-15"/> + <location line="-26"/> <source>Error: could not start node</source> <translation>Error: could not start node</translation> </message> <message> - <location line="+16"/> + <location line="+27"/> <source>Invalid amount</source> <translation>Invalid amount</translation> </message> @@ -2749,7 +2814,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Loading block index...</translation> </message> <message> - <location line="-45"/> + <location line="-56"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Add a node to connect to and attempt to keep the connection open</translation> </message> @@ -2759,7 +2824,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Unable to bind to %s on this computer. Bitcoin is probably already running.</translation> </message> <message> - <location line="+54"/> + <location line="+65"/> <source>Find peers using internet relay chat (default: 0)</source> <translation>Find peers using internet relay chat (default: 0)</translation> </message> @@ -2774,7 +2839,7 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Loading wallet...</translation> </message> <message> - <location line="-40"/> + <location line="-51"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> @@ -2789,22 +2854,22 @@ If the file does not exist, create it with owner-readable-only file permissions. <translation>Cannot write default address</translation> </message> <message> - <location line="+50"/> + <location line="+61"/> <source>Rescanning...</source> <translation>Rescanning...</translation> </message> <message> - <location line="-44"/> + <location line="-55"/> <source>Done loading</source> <translation>Done loading</translation> </message> <message> - <location line="+66"/> + <location line="+78"/> <source>To use the %s option</source> <translation>To use the %s option</translation> </message> <message> - <location line="-61"/> + <location line="-73"/> <source>Error</source> <translation>Error</translation> </message> diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 778e0acbd2..b9ebcb4001 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -365,9 +365,10 @@ Value submitblock(const Array& params, bool fHelp) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); } - bool fAccepted = ProcessBlock(NULL, &pblock); + CValidationState state; + bool fAccepted = ProcessBlock(state, NULL, &pblock); if (!fAccepted) - return "rejected"; + return "rejected"; // TODO: report validation state return Value::null; } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 8d89c2f302..5224051ace 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -546,8 +546,9 @@ Value sendrawtransaction(const Array& params, bool fHelp) fHave = view.GetCoins(hashTx, existingCoins); if (!fHave) { // push to local node - if (!tx.AcceptToMemoryPool(true, false)) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); + CValidationState state; + if (!tx.AcceptToMemoryPool(state, true, false)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state } } if (fHave) { diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index bc2a05a6b3..af284653dd 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -73,7 +73,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) txFirst.push_back(new CTransaction(pblock->vtx[0])); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); pblock->nNonce = blockinfo[i].nonce; - assert(ProcessBlock(NULL, pblock)); + CValidationState state; + BOOST_CHECK(ProcessBlock(state, NULL, pblock)); + BOOST_CHECK(state.IsValid()); pblock->hashPrevBlock = pblock->GetHash(); } delete pblocktemplate; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 23837c6c15..f44d46fdb8 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -66,7 +66,9 @@ BOOST_AUTO_TEST_CASE(tx_valid) CTransaction tx; stream >> tx; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(), strTest); + CValidationState state; + BOOST_CHECK_MESSAGE(tx.CheckTransaction(state), strTest); + BOOST_CHECK(state.IsValid()); for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -133,7 +135,8 @@ BOOST_AUTO_TEST_CASE(tx_invalid) CTransaction tx; stream >> tx; - fValid = tx.CheckTransaction(); + CValidationState state; + fValid = tx.CheckTransaction(state) && state.IsValid(); for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) { @@ -159,11 +162,12 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) CDataStream stream(vch, SER_DISK, CLIENT_VERSION); CTransaction tx; stream >> tx; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(), "Simple deserialized transaction should be valid."); + CValidationState state; + BOOST_CHECK_MESSAGE(tx.CheckTransaction(state) && state.IsValid(), "Simple deserialized transaction should be valid."); // Check that duplicate txins fail tx.vin.push_back(tx.vin[0]); - BOOST_CHECK_MESSAGE(!tx.CheckTransaction(), "Transaction with duplicate txins should be invalid."); + BOOST_CHECK_MESSAGE(!tx.CheckTransaction(state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); } // diff --git a/src/wallet.cpp b/src/wallet.cpp index 557784e5c2..b8ef2a20bf 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1190,7 +1190,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW scriptChange.SetDestination(vchPubKey.GetID()); // Insert change txn at random position: - vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); + vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1); wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); } else diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 2282bed18a..fe9bce21e8 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -203,7 +203,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssKey >> hash; CWalletTx& wtx = pwallet->mapWallet[hash]; ssValue >> wtx; - if (wtx.CheckTransaction() && (wtx.GetHash() == hash)) + CValidationState state; + if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid()) wtx.BindWallet(pwallet); else { |