diff options
Diffstat (limited to 'src')
41 files changed, 616 insertions, 220 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 52ff3f224f..9aec13ccef 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -39,6 +39,7 @@ BITCOIN_TESTS =\ test/base32_tests.cpp \ test/base58_tests.cpp \ test/base64_tests.cpp \ + test/bip32_tests.cpp \ test/bloom_tests.cpp \ test/checkblock_tests.cpp \ test/Checkpoints_tests.cpp \ diff --git a/src/addrman.cpp b/src/addrman.cpp index cefd70a1b1..c41ee3f9fc 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -67,9 +67,8 @@ double CAddrInfo::GetChance(int64_t nNow) const if (nSinceLastTry < 60 * 10) fChance *= 0.01; - // deprioritize 50% after each failed attempt - for (int n = 0; n < nAttempts; n++) - fChance /= 1.5; + // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages. + fChance *= pow(0.66, min(nAttempts, 8)); return fChance; } diff --git a/src/arith_uint256.h b/src/arith_uint256.h index b6ba3a1087..103c78bb8e 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -287,4 +287,4 @@ public: uint256 ArithToUint256(const arith_uint256 &); arith_uint256 UintToArith256(const uint256 &); -#endif // BITCOIN_UINT256_H +#endif // BITCOIN_ARITH_UINT256_H diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index a0a96c2dfa..2172f4a21b 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -21,7 +21,7 @@ * * \section intro_sec Introduction * - * This is the developer documentation of the reference client for an experimental new digital currency called Bitcoin (http://www.bitcoin.org/), + * This is the developer documentation of the reference client for an experimental new digital currency called Bitcoin (https://www.bitcoin.org/), * which 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. * diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 97312b366d..589c7b5472 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -121,6 +121,7 @@ public: vAlertPubKey = ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"); nDefaultPort = 8333; nMinerThreads = 0; + nPruneAfterHeight = 100000; /** * Build the genesis block. Note that the output of the genesis coinbase cannot @@ -151,11 +152,11 @@ public: assert(consensus.hashGenesisBlock == uint256S("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")); assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); - vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be")); - vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me")); - vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org")); - vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com")); - vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org")); + vSeeds.push_back(CDNSSeedData("bitcoin.sipa.be", "seed.bitcoin.sipa.be")); // Pieter Wuille + vSeeds.push_back(CDNSSeedData("bluematt.me", "dnsseed.bluematt.me")); // Matt Corallo + vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org")); // Luke Dashjr + vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com")); // Addy Yeow + vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org")); // Jeff Garzik base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0); base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5); @@ -198,6 +199,7 @@ public: vAlertPubKey = ParseHex("04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"); nDefaultPort = 18333; nMinerThreads = 0; + nPruneAfterHeight = 1000; //! Modify the testnet genesis block so the timestamp is valid for a later start. genesis.nTime = 1296688602; @@ -257,6 +259,7 @@ public: consensus.hashGenesisBlock = genesis.GetHash(); nDefaultPort = 18444; assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); + nPruneAfterHeight = 1000; vFixedSeeds.clear(); //! Regtest mode doesn't have any fixed seeds. vSeeds.clear(); //! Regtest mode doesn't have any DNS seeds. diff --git a/src/chainparams.h b/src/chainparams.h index 0692094478..5e974123dc 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -58,6 +58,7 @@ public: bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; } /** Make standard checks */ bool RequireStandard() const { return fRequireStandard; } + int64_t PruneAfterHeight() const { return nPruneAfterHeight; } /** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */ bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } /** In the future use NetworkIDString() for RPC fields */ @@ -77,6 +78,7 @@ protected: std::vector<unsigned char> vAlertPubKey; int nDefaultPort; int nMinerThreads; + uint64_t nPruneAfterHeight; std::vector<CDNSSeedData> vSeeds; std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; std::string strNetworkID; diff --git a/src/consensus/params.h b/src/consensus/params.h index 35d447b712..c480a1cce1 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_CONSENSUS_CONSENSUS_PARAMS_H -#define BITCOIN_CONSENSUS_CONSENSUS_PARAMS_H +#ifndef BITCOIN_CONSENSUS_PARAMS_H +#define BITCOIN_CONSENSUS_PARAMS_H #include "uint256.h" @@ -28,4 +28,4 @@ struct Params { }; } // namespace Consensus -#endif // BITCOIN_CONSENSUS_CONSENSUS_PARAMS_H +#endif // BITCOIN_CONSENSUS_PARAMS_H diff --git a/src/init.cpp b/src/init.cpp index 4d9c233c8c..b648a237bf 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -123,7 +123,7 @@ public: LogPrintf("Error reading from database: %s\n", e.what()); // Starting the shutdown sequence and returning false to the caller would be // interpreted as 'entry not found' (as opposed to unable to read data), and - // could lead to invalid interpration. Just exit immediately, as we can't + // could lead to invalid interpretation. Just exit immediately, as we can't // continue anyway, and all writes should be atomic. abort(); } @@ -275,7 +275,12 @@ std::string HelpMessage(HelpMessageMode mode) #ifndef WIN32 strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), "bitcoind.pid")); #endif - strUsage += HelpMessageOpt("-reindex", _("Rebuild block chain index from current blk000??.dat files") + " " + _("on startup")); + strUsage += HelpMessageOpt("-prune=<n>", _("Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex.") + " " + + _("Warning: Reverting this setting requires re-downloading the entire blockchain.") + " " + + _("(default: 0 = disable pruning blocks,") + " " + + strprintf(_(">%u = target size in MiB to use for block files)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); +strUsage += HelpMessageOpt("-reindex", _("Rebuild block chain index from current blk000??.dat files") + " " + _("on startup")); + #if !defined(WIN32) strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); #endif @@ -301,6 +306,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), 1)); strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), 8333, 18333)); strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy")); + strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), 1)); strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect")); strUsage += HelpMessageOpt("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT)); #ifdef USE_UPNP @@ -351,7 +357,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-flushwallet", strprintf(_("Run a thread to flush wallet periodically (default: %u)"), 1)); strUsage += HelpMessageOpt("-stopafterblockimport", strprintf(_("Stop running after importing blocks from disk (default: %u)"), 0)); } - string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, net"; // Don't translate these and qt below + string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, net, proxy, prune"; // Don't translate these and qt below if (mode == HMM_BITCOIN_QT) debugCategories += ", qt"; strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + @@ -365,8 +371,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), 1)); if (GetBoolArg("-help-debug", false)) { - strUsage += HelpMessageOpt("-limitfreerelay=<n>", strprintf(_("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default:%u)"), 15)); - strUsage += HelpMessageOpt("-relaypriority", strprintf(_("Require high priority for relaying free or low-fee transactions (default:%u)"), 1)); + strUsage += HelpMessageOpt("-limitfreerelay=<n>", strprintf(_("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)"), 15)); + strUsage += HelpMessageOpt("-relaypriority", strprintf(_("Require high priority for relaying free or low-fee transactions (default: %u)"), 1)); strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf(_("Limit size of signature cache to <n> entries (default: %u)"), 50000)); } strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for relaying (default: %s)"), FormatMoney(::minRelayTxFee.GetFeePerK()))); @@ -457,10 +463,33 @@ struct CImportingNow } }; + +// If we're using -prune with -reindex, then delete block files that will be ignored by the +// reindex. Since reindexing works by starting at block file 0 and looping until a blockfile +// is missing, and since pruning works by deleting the oldest block file first, just check +// for block file 0, and if it doesn't exist, delete all the block files in the +// directory (since they won't be read by the reindex but will take up disk space). +void DeleteAllBlockFiles() +{ + if (boost::filesystem::exists(GetBlockPosFilename(CDiskBlockPos(0, 0), "blk"))) + return; + + LogPrintf("Removing all blk?????.dat and rev?????.dat files for -reindex with -prune\n"); + boost::filesystem::path blocksdir = GetDataDir() / "blocks"; + for (boost::filesystem::directory_iterator it(blocksdir); it != boost::filesystem::directory_iterator(); it++) { + if (is_regular_file(*it)) { + if ((it->path().filename().string().length() == 12) && + (it->path().filename().string().substr(8,4) == ".dat") && + ((it->path().filename().string().substr(0,3) == "blk") || + (it->path().filename().string().substr(0,3) == "rev"))) + boost::filesystem::remove(it->path()); + } + } +} + void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) { RenameThread("bitcoin-loadblk"); - // -reindex if (fReindex) { CImportingNow imp; @@ -673,6 +702,21 @@ bool AppInit2(boost::thread_group& threadGroup) if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections) nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS; + // if using block pruning, then disable txindex + // also disable the wallet (for now, until SPV support is implemented in wallet) + if (GetArg("-prune", 0)) { + if (GetBoolArg("-txindex", false)) + return InitError(_("Prune mode is incompatible with -txindex.")); +#ifdef ENABLE_WALLET + if (!GetBoolArg("-disablewallet", false)) { + if (SoftSetBoolArg("-disablewallet", true)) + LogPrintf("%s : parameter interaction: -prune -> setting -disablewallet=1\n", __func__); + else + return InitError(_("Can't run with a wallet in prune mode.")); + } +#endif + } + // ********************************************************* Step 3: parameter-to-internal-flags fDebug = !mapMultiArgs["-debug"].empty(); @@ -709,6 +753,21 @@ bool AppInit2(boost::thread_group& threadGroup) nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; fServer = GetBoolArg("-server", false); + + // block pruning; get the amount of disk space (in MB) to allot for block & undo files + int64_t nSignedPruneTarget = GetArg("-prune", 0) * 1024 * 1024; + if (nSignedPruneTarget < 0) { + return InitError(_("Prune cannot be configured with a negative value.")); + } + nPruneTarget = (uint64_t) nSignedPruneTarget; + if (nPruneTarget) { + if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { + return InitError(strprintf(_("Prune configured below the minimum of %d MB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); + } + LogPrintf("Prune configured to target %uMiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024); + fPruneMode = true; + } + #ifdef ENABLE_WALLET bool fDisableWallet = GetBoolArg("-disablewallet", false); #endif @@ -891,10 +950,10 @@ bool AppInit2(boost::thread_group& threadGroup) } } - CService addrProxy; + proxyType addrProxy; bool fProxy = false; if (mapArgs.count("-proxy")) { - addrProxy = CService(mapArgs["-proxy"], 9050); + addrProxy = proxyType(CService(mapArgs["-proxy"], 9050), GetArg("-proxyrandomize", true)); if (!addrProxy.IsValid()) return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"])); @@ -904,14 +963,14 @@ bool AppInit2(boost::thread_group& threadGroup) fProxy = true; } - // -onion can override normal proxy, -noonion disables tor entirely + // -onion can override normal proxy, -noonion disables connecting to .onion entirely if (!(mapArgs.count("-onion") && mapArgs["-onion"] == "0") && (fProxy || mapArgs.count("-onion"))) { - CService addrOnion; + proxyType addrOnion; if (!mapArgs.count("-onion")) addrOnion = addrProxy; else - addrOnion = CService(mapArgs["-onion"], 9050); + addrOnion = proxyType(CService(mapArgs["-onion"], 9050), GetArg("-proxyrandomize", true)); if (!addrOnion.IsValid()) return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs["-onion"])); SetProxy(NET_TOR, addrOnion); @@ -1029,8 +1088,12 @@ bool AppInit2(boost::thread_group& threadGroup) pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinscatcher); - if (fReindex) + if (fReindex) { pblocktree->WriteReindexing(true); + //If we're reindexing in prune mode, wipe away all our block and undo data files + if (fPruneMode) + DeleteAllBlockFiles(); + } if (!LoadBlockIndex()) { strLoadError = _("Error loading block database"); @@ -1054,7 +1117,18 @@ bool AppInit2(boost::thread_group& threadGroup) break; } + // Check for changed -prune state. What we are concerned about is a user who has pruned blocks + // in the past, but is now trying to run unpruned. + if (fHavePruned && !fPruneMode) { + strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain"); + break; + } + uiInterface.InitMessage(_("Verifying blocks...")); + if (fHavePruned && GetArg("-checkblocks", 288) > MIN_BLOCKS_TO_KEEP) { + LogPrintf("Prune: pruned datadir may not have more than %d blocks; -checkblocks=%d may fail\n", + MIN_BLOCKS_TO_KEEP, GetArg("-checkblocks", 288)); + } if (!CVerifyDB().VerifyDB(pcoinsdbview, GetArg("-checklevel", 3), GetArg("-checkblocks", 288))) { strLoadError = _("Corrupted block database detected"); @@ -1105,6 +1179,15 @@ bool AppInit2(boost::thread_group& threadGroup) mempool.ReadFeeEstimates(est_filein); fFeeEstimatesInitialized = true; + // if prune mode, unset NODE_NETWORK and prune block files + if (fPruneMode) { + LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); + nLocalServices &= ~NODE_NETWORK; + if (!fReindex) { + PruneAndFlush(); + } + } + // ********************************************************* Step 8: load wallet #ifdef ENABLE_WALLET if (fDisableWallet) { diff --git a/src/main.cpp b/src/main.cpp index 8dd61a3720..bf32ac91e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -52,9 +52,12 @@ int nScriptCheckThreads = 0; bool fImporting = false; bool fReindex = false; bool fTxIndex = false; +bool fHavePruned = false; +bool fPruneMode = false; bool fIsBareMultisigStd = true; bool fCheckBlockIndex = false; unsigned int nCoinCacheSize = 5000; +uint64_t nPruneTarget = 0; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ CFeeRate minRelayTxFee = CFeeRate(1000); @@ -110,17 +113,25 @@ namespace { /** * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and - * as good as our current tip or better. Entries may be failed, though. + * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be + * missing the data for the block. */ set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates; /** Number of nodes with fSyncStarted. */ int nSyncStarted = 0; - /** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. */ + /** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. + * Pruned nodes may have entries where B is missing data. + */ multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked; CCriticalSection cs_LastBlockFile; std::vector<CBlockFileInfo> vinfoBlockFile; int nLastBlockFile = 0; + /** Global flag to indicate we should check to see if there are + * block/undo files that should be deleted. Set on startup + * or if we allocate more file space when we're in prune mode + */ + bool fCheckForPruning = false; /** * Every received block is assigned a unique and increasing identifier, so we @@ -1233,14 +1244,14 @@ void CheckForkWarningConditions() } if (pindexBestForkTip && pindexBestForkBase) { - LogPrintf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", + LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__, pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); fLargeWorkForkFound = true; } else { - LogPrintf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n"); + LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__); fLargeWorkInvalidChainFound = true; } } @@ -1298,10 +1309,10 @@ void Misbehaving(NodeId pnode, int howmuch) int banscore = GetArg("-banscore", 100); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { - LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); + LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); state->fShouldBan = true; } else - LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); + LogPrintf("%s: %s (%d -> %d)\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); } void static InvalidChainFound(CBlockIndex* pindexNew) @@ -1309,11 +1320,11 @@ void static InvalidChainFound(CBlockIndex* pindexNew) if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) pindexBestInvalid = pindexNew; - LogPrintf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", + LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime())); - LogPrintf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", + LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime())); CheckForkWarningConditions(); @@ -1849,6 +1860,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } enum FlushStateMode { + FLUSH_STATE_NONE, FLUSH_STATE_IF_NEEDED, FLUSH_STATE_PERIODIC, FLUSH_STATE_ALWAYS @@ -1856,16 +1868,30 @@ enum FlushStateMode { /** * Update the on-disk chain state. - * The caches and indexes are flushed if either they're too large, forceWrite is set, or - * fast is not set and it's been a while since the last write. + * The caches and indexes are flushed depending on the mode we're called with + * if they're too large, if it's been a while since the last write, + * or always and in all cases if we're in prune mode and are deleting files. */ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { - LOCK(cs_main); + LOCK2(cs_main, cs_LastBlockFile); static int64_t nLastWrite = 0; + std::set<int> setFilesToPrune; + bool fFlushForPrune = false; try { + if (fPruneMode && fCheckForPruning) { + FindFilesToPrune(setFilesToPrune); + if (!setFilesToPrune.empty()) { + fFlushForPrune = true; + if (!fHavePruned) { + pblocktree->WriteFlag("prunedblockfiles", true); + fHavePruned = true; + } + } + } if ((mode == FLUSH_STATE_ALWAYS) || ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) || - (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000)) { + (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000) || + fFlushForPrune) { // 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 @@ -1893,9 +1919,16 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { return state.Abort("Files to write to block index database"); } } - // Finally flush the chainstate (which may refer to block index entries). + // Flush the chainstate (which may refer to block index entries). if (!pcoinsTip->Flush()) return state.Abort("Failed to write to coin database"); + + // Finally remove any pruned files + if (fFlushForPrune) { + UnlinkPrunedFiles(setFilesToPrune); + fCheckForPruning = false; + } + // Update best block in wallet (so we can detect restored wallets). if (mode != FLUSH_STATE_IF_NEEDED) { GetMainSignals().SetBestChain(chainActive.GetLocator()); @@ -1913,6 +1946,12 @@ void FlushStateToDisk() { FlushStateToDisk(state, FLUSH_STATE_ALWAYS); } +void PruneAndFlush() { + CValidationState state; + fCheckForPruning = true; + FlushStateToDisk(state, FLUSH_STATE_NONE); +} + /** Update chainActive and related internal data structures. */ void static UpdateTip(CBlockIndex *pindexNew) { chainActive.SetTip(pindexNew); @@ -1921,7 +1960,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", + LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); @@ -1941,7 +1980,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, (int)CBlock::CURRENT_VERSION); + LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION); if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: @@ -2083,15 +2122,29 @@ static CBlockIndex* FindMostWorkChain() { CBlockIndex *pindexTest = pindexNew; bool fInvalidAncestor = false; while (pindexTest && !chainActive.Contains(pindexTest)) { - assert(pindexTest->nStatus & BLOCK_HAVE_DATA); assert(pindexTest->nChainTx || pindexTest->nHeight == 0); - if (pindexTest->nStatus & BLOCK_FAILED_MASK) { - // Candidate has an invalid ancestor, remove entire chain from the set. - if (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork) + + // Pruned nodes may have entries in setBlockIndexCandidates for + // which block files have been deleted. Remove those as candidates + // for the most work chain if we come across them; we can't switch + // to a chain unless we have all the non-active-chain parent blocks. + bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK; + bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); + if (fFailedChain || fMissingData) { + // Candidate chain is not usable (either invalid or missing data) + if (fFailedChain && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindexNew; CBlockIndex *pindexFailed = pindexNew; + // Remove the entire chain from the set. while (pindexTest != pindexFailed) { - pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + if (fFailedChain) { + pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + } else if (fMissingData) { + // If we're missing data, then add back to mapBlocksUnlinked, + // so that if the block arrives in the future we can try adding + // to setBlockIndexCandidates again. + mapBlocksUnlinked.insert(std::make_pair(pindexFailed->pprev, pindexFailed)); + } setBlockIndexCandidates.erase(pindexFailed); pindexFailed = pindexFailed->pprev; } @@ -2219,7 +2272,9 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { uint256 hashNewTip = pindexNewTip->GetBlockHash(); // Relay inventory, but don't relay old inventory during initial block download. int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); - { + // Don't relay blocks if pruning -- could cause a peer to try to download, resulting + // in a stalled download if the block file is pruned before the request. + if (nLocalServices & NODE_NETWORK) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) @@ -2350,10 +2405,6 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl pindexNew->nUndoPos = 0; pindexNew->nStatus |= BLOCK_HAVE_DATA; pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); - { - LOCK(cs_nBlockSequenceId); - pindexNew->nSequenceId = nBlockSequenceId++; - } setDirtyBlockIndex.insert(pindexNew); if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { @@ -2366,6 +2417,10 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl CBlockIndex *pindex = queue.front(); queue.pop_front(); pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + { + LOCK(cs_nBlockSequenceId); + pindex->nSequenceId = nBlockSequenceId++; + } if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { setBlockIndexCandidates.insert(pindex); } @@ -2419,6 +2474,8 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; if (nNewChunks > nOldChunks) { + if (fPruneMode) + fCheckForPruning = true; if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) { FILE *file = OpenBlockFile(pos); if (file) { @@ -2450,6 +2507,8 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; if (nNewChunks > nOldChunks) { + if (fPruneMode) + fCheckForPruning = true; if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) { FILE *file = OpenUndoFile(pos); if (file) { @@ -2665,7 +2724,10 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!AcceptBlockHeader(block, state, &pindex)) return false; - if (pindex->nStatus & BLOCK_HAVE_DATA) { + // If we're pruning, ensure that we don't allow a peer to dump a copy + // of old blocks. But we might need blocks that are not on the main chain + // to handle a reorg, even if we've processed once. + if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { // TODO: deal better with duplicate blocks. // return state.DoS(20, error("AcceptBlock(): already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate"); return true; @@ -2698,6 +2760,9 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return state.Abort(std::string("System error: ") + e.what()); } + if (fCheckForPruning) + FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files + return true; } @@ -2785,6 +2850,112 @@ bool AbortNode(const std::string &strMessage, const std::string &userMessage) { return false; } + +/** + * BLOCK PRUNING CODE + */ + +/* Calculate the amount of disk space the block & undo files currently use */ +uint64_t CalculateCurrentUsage() +{ + uint64_t retval = 0; + BOOST_FOREACH(const CBlockFileInfo &file, vinfoBlockFile) { + retval += file.nSize + file.nUndoSize; + } + return retval; +} + +/* Prune a block file (modify associated database entries)*/ +void PruneOneBlockFile(const int fileNumber) +{ + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) { + CBlockIndex* pindex = it->second; + if (pindex->nFile == fileNumber) { + pindex->nStatus &= ~BLOCK_HAVE_DATA; + pindex->nStatus &= ~BLOCK_HAVE_UNDO; + pindex->nFile = 0; + pindex->nDataPos = 0; + pindex->nUndoPos = 0; + setDirtyBlockIndex.insert(pindex); + + // Prune from mapBlocksUnlinked -- any block we prune would have + // to be downloaded again in order to consider its chain, at which + // point it would be considered as a candidate for + // mapBlocksUnlinked or setBlockIndexCandidates. + std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex->pprev); + while (range.first != range.second) { + std::multimap<CBlockIndex *, CBlockIndex *>::iterator it = range.first; + range.first++; + if (it->second == pindex) { + mapBlocksUnlinked.erase(it); + } + } + } + } + + vinfoBlockFile[fileNumber].SetNull(); + setDirtyFileInfo.insert(fileNumber); +} + + +void UnlinkPrunedFiles(std::set<int>& setFilesToPrune) +{ + for (set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { + CDiskBlockPos pos(*it, 0); + boost::filesystem::remove(GetBlockPosFilename(pos, "blk")); + boost::filesystem::remove(GetBlockPosFilename(pos, "rev")); + LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it); + } +} + +/* Calculate the block/rev files that should be deleted to remain under target*/ +void FindFilesToPrune(std::set<int>& setFilesToPrune) +{ + LOCK2(cs_main, cs_LastBlockFile); + if (chainActive.Tip() == NULL || nPruneTarget == 0) { + return; + } + if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) { + return; + } + + unsigned int nLastBlockWeMustKeep = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; + uint64_t nCurrentUsage = CalculateCurrentUsage(); + // We don't check to prune until after we've allocated new space for files + // So we should leave a buffer under our target to account for another allocation + // before the next pruning. + uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; + uint64_t nBytesToPrune; + int count=0; + + if (nCurrentUsage + nBuffer >= nPruneTarget) { + for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { + nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; + + if (vinfoBlockFile[fileNumber].nSize == 0) + continue; + + if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? + break; + + // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip + if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeMustKeep) + break; + + PruneOneBlockFile(fileNumber); + // Queue up the files for removal + setFilesToPrune.insert(fileNumber); + nCurrentUsage -= nBytesToPrune; + count++; + } + } + + LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB min_must_keep=%d removed %d blk/rev pairs\n", + nPruneTarget/1024/1024, nCurrentUsage/1024/1024, + ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, + nLastBlockWeMustKeep, count); +} + bool CheckDiskSpace(uint64_t nAdditionalBytes) { uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; @@ -2872,7 +3043,9 @@ bool static LoadBlockIndexDB() { CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); - if (pindex->nStatus & BLOCK_HAVE_DATA) { + // We can link the chain of blocks for which we've received transactions at some point. + // Pruned nodes may have deleted the block. + if (pindex->nTx > 0) { if (pindex->pprev) { if (pindex->pprev->nChainTx) { pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; @@ -2929,6 +3102,11 @@ bool static LoadBlockIndexDB() } } + // Check whether we have ever pruned block & undo files + pblocktree->ReadFlag("prunedblockfiles", fHavePruned); + if (fHavePruned) + LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n"); + // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); @@ -2936,7 +3114,7 @@ bool static LoadBlockIndexDB() // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); - LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); + LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); // Load pointer to end of best chain BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); @@ -2946,7 +3124,7 @@ bool static LoadBlockIndexDB() PruneBlockIndexCandidates(); - LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s progress=%f\n", + LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainActive.Tip())); @@ -3069,6 +3247,7 @@ void UnloadBlockIndex() delete entry.second; } mapBlockIndex.clear(); + fHavePruned = false; } bool LoadBlockIndex() @@ -3260,6 +3439,7 @@ void static CheckBlockIndex() int nHeight = 0; CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid. CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. + CBlockIndex* pindexFirstNeverProcessed = NULL; // Oldest ancestor of pindex for which nTx == 0. CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). CBlockIndex* pindexFirstNotTransactionsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TRANSACTIONS (regardless of being valid or not). CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). @@ -3268,6 +3448,7 @@ void static CheckBlockIndex() nNodes++; if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; + if (pindexFirstNeverProcessed == NULL && pindex->nTx == 0) pindexFirstNeverProcessed = pindex; if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; @@ -3279,11 +3460,21 @@ void static CheckBlockIndex() assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match. assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. } - // HAVE_DATA is equivalent to VALID_TRANSACTIONS and equivalent to nTx > 0 (we stored the number of transactions in the block) - assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); - assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); - // All parents having data is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. - assert((pindexFirstMissing != NULL) == (pindex->nChainTx == 0)); // nChainTx == 0 is used to signal that all parent block's transaction data is available. + if (pindex->nChainTx == 0) assert(pindex->nSequenceId == 0); // nSequenceId can't be set for blocks that aren't linked + // VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred). + // HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred. + if (!fHavePruned) { + // If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0 + assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); + assert(pindexFirstMissing == pindexFirstNeverProcessed); + } else { + // If we have pruned, then we can only say that HAVE_DATA implies nTx > 0 + if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0); + } + if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA); + assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent. + // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. + assert((pindexFirstNeverProcessed != NULL) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned). assert((pindexFirstNotTransactionsValid != NULL) == (pindex->nChainTx == 0)); assert(pindex->nHeight == nHeight); // nHeight must be consistent. assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. @@ -3296,11 +3487,20 @@ void static CheckBlockIndex() // Checks for not-invalid blocks. assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents. } - if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstMissing == NULL) { - if (pindexFirstInvalid == NULL) { // If this block sorts at least as good as the current tip and is valid, it must be in setBlockIndexCandidates. - assert(setBlockIndexCandidates.count(pindex)); + if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == NULL) { + if (pindexFirstInvalid == NULL) { + // If this block sorts at least as good as the current tip and + // is valid and we have all data for its parents, it must be in + // setBlockIndexCandidates. chainActive.Tip() must also be there + // even if some data has been pruned. + if (pindexFirstMissing == NULL || pindex == chainActive.Tip()) { + assert(setBlockIndexCandidates.count(pindex)); + } + // If some parent is missing, then it could be that this block was in + // setBlockIndexCandidates but had to be removed because of the missing data. + // In this case it must be in mapBlocksUnlinked -- see test below. } - } else { // If this block sorts worse than the current tip, it cannot be in setBlockIndexCandidates. + } else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates. assert(setBlockIndexCandidates.count(pindex) == 0); } // Check whether this block is in mapBlocksUnlinked. @@ -3314,12 +3514,28 @@ void static CheckBlockIndex() } rangeUnlinked.first++; } - if (pindex->pprev && pindex->nStatus & BLOCK_HAVE_DATA && pindexFirstMissing != NULL) { - if (pindexFirstInvalid == NULL) { // If this block has block data available, some parent doesn't, and has no invalid parents, it must be in mapBlocksUnlinked. - assert(foundInUnlinked); + if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != NULL && pindexFirstInvalid == NULL) { + // If this block has block data available, some parent was never received, and has no invalid parents, it must be in mapBlocksUnlinked. + assert(foundInUnlinked); + } + if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in mapBlocksUnlinked if we don't HAVE_DATA + if (pindexFirstMissing == NULL) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in mapBlocksUnlinked. + if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == NULL && pindexFirstMissing != NULL) { + // We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent. + assert(fHavePruned); // We must have pruned. + // This block may have entered mapBlocksUnlinked if: + // - it has a descendant that at some point had more work than the + // tip, and + // - we tried switching to that descendant but were missing + // data for some intermediate block between chainActive and the + // tip. + // So if this block is itself better than chainActive.Tip() and it wasn't in + // setBlockIndexCandidates, then it must be in mapBlocksUnlinked. + if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && setBlockIndexCandidates.count(pindex) == 0) { + if (pindexFirstInvalid == NULL) { + assert(foundInUnlinked); + } } - } else { // If this block does not have block data available, or all parents do, it cannot be in mapBlocksUnlinked. - assert(!foundInUnlinked); } // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow // End: actual consistency checks. @@ -3339,6 +3555,7 @@ void static CheckBlockIndex() // If pindex was the first with a certain property, unset the corresponding variable. if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL; if (pindex == pindexFirstMissing) pindexFirstMissing = NULL; + if (pindex == pindexFirstNeverProcessed) pindexFirstNeverProcessed = NULL; if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL; if (pindex == pindexFirstNotTransactionsValid) pindexFirstNotTransactionsValid = NULL; if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL; @@ -3492,11 +3709,13 @@ void static ProcessGetData(CNode* pfrom) send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && (mi->second->GetBlockTime() > pindexBestHeader->GetBlockTime() - 30 * 24 * 60 * 60); if (!send) { - LogPrintf("ProcessGetData(): ignoring request from peer=%i for old block that isn't in the main chain\n", pfrom->GetId()); + LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } } } - if (send) + // Pruned nodes may have deleted the block, so check whether + // it's available before trying to send. + if (send && (mi->second->nStatus & BLOCK_HAVE_DATA)) { // Send block from disk CBlock block; @@ -4394,7 +4613,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool ProcessMessages(CNode* pfrom) { //if (fDebug) - // LogPrintf("ProcessMessages(%u messages)\n", pfrom->vRecvMsg.size()); + // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); // // Message format @@ -4422,7 +4641,7 @@ bool ProcessMessages(CNode* pfrom) CNetMessage& msg = *it; //if (fDebug) - // LogPrintf("ProcessMessages(message %u msgsz, %u bytes, complete:%s)\n", + // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__, // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); @@ -4458,7 +4677,7 @@ bool ProcessMessages(CNode* pfrom) unsigned int nChecksum = ReadLE32((unsigned char*)&hash); if (nChecksum != hdr.nChecksum) { - LogPrintf("ProcessMessages(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", __func__, SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); continue; } @@ -4476,12 +4695,12 @@ bool ProcessMessages(CNode* pfrom) if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", SanitizeString(strCommand), nMessageSize, e.what()); + LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size - LogPrintf("ProcessMessages(%s, %u bytes): Exception '%s' caught\n", SanitizeString(strCommand), nMessageSize, e.what()); + LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else { @@ -4498,7 +4717,7 @@ bool ProcessMessages(CNode* pfrom) } if (!fRet) - LogPrintf("ProcessMessage(%s, %u bytes) FAILED peer=%d\n", SanitizeString(strCommand), nMessageSize, pfrom->id); + LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); break; } diff --git a/src/main.h b/src/main.h index bb6fd6f204..d34f6c30d1 100644 --- a/src/main.h +++ b/src/main.h @@ -132,6 +132,26 @@ extern CBlockIndex *pindexBestHeader; /** Minimum disk space required - used in CheckDiskSpace() */ static const uint64_t nMinDiskSpace = 52428800; +/** Pruning-related variables and constants */ +/** True if any block files have ever been pruned. */ +extern bool fHavePruned; +/** True if we're running in -prune mode. */ +extern bool fPruneMode; +/** Number of MiB of block files that we're trying to stay below. */ +extern uint64_t nPruneTarget; +/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */ +static const signed int MIN_BLOCKS_TO_KEEP = 288; + +// Require that user allocate at least 550MB for block & undo files (blk???.dat and rev???.dat) +// At 1MB per block, 288 blocks = 288MB. +// Add 15% for Undo data = 331MB +// Add 20% for Orphan block rate = 397MB +// We want the low water mark after pruning to be at least 397 MB and since we prune in +// full block file chunks, we need the high water mark which triggers the prune to be +// one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB +// Setting the target to > than 550MB will make it likely we can respect the target. +static const signed int MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024; + /** Register with a network node to receive its signals */ void RegisterNodeSignals(CNodeSignals& nodeSignals); /** Unregister a network node */ @@ -186,6 +206,28 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, b bool ActivateBestChain(CValidationState &state, CBlock *pblock = NULL); CAmount GetBlockValue(int nHeight, const CAmount& nFees); +/** + * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target. + * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new + * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex + * (which in this case means the blockchain must be re-downloaded.) + * + * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set. + * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.) + * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 10 on regtest). + * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip. + * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files. + * A db flag records the fact that at least some block files have been pruned. + * + * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned + */ +void FindFilesToPrune(std::set<int>& setFilesToPrune); + +/** + * Actually unlink the specified files + */ +void UnlinkPrunedFiles(std::set<int>& setFilesToPrune); + /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(uint256 hash); /** Abort with a message */ @@ -196,7 +238,8 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); void Misbehaving(NodeId nodeid, int howmuch); /** Flush all state, indexes and buffers to disk. */ void FlushStateToDisk(); - +/** Prune block files and flush state to disk. */ +void PruneAndFlush(); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, diff --git a/src/miner.h b/src/miner.h index 0be3152d97..96a6b70ecd 100644 --- a/src/miner.h +++ b/src/miner.h @@ -14,7 +14,7 @@ class CBlockIndex; class CReserveKey; class CScript; class CWallet; -namespace Consensus { class Params; }; +namespace Consensus { struct Params; }; struct CBlockTemplate { diff --git a/src/netbase.cpp b/src/netbase.cpp index a2ac6575bc..1837cfa9c3 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -12,6 +12,7 @@ #include "hash.h" #include "sync.h" #include "uint256.h" +#include "random.h" #include "util.h" #include "utilstrencodings.h" @@ -38,7 +39,7 @@ using namespace std; // Settings static proxyType proxyInfo[NET_MAX]; -static CService nameProxy; +static proxyType nameProxy; static CCriticalSection cs_proxyInfos; int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; bool fNameLookup = false; @@ -285,59 +286,100 @@ bool static InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSock return len == 0; } -bool static Socks5(string strDest, int port, SOCKET& hSocket) +struct ProxyCredentials +{ + std::string username; + std::string password; +}; + +/** Connect using SOCKS5 (as described in RFC1928) */ +bool static Socks5(string strDest, int port, const ProxyCredentials *auth, SOCKET& hSocket) { LogPrintf("SOCKS5 connecting %s\n", strDest); - if (strDest.size() > 255) - { + if (strDest.size() > 255) { CloseSocket(hSocket); return error("Hostname too long"); } - char pszSocks5Init[] = "\5\1\0"; - ssize_t nSize = sizeof(pszSocks5Init) - 1; - - ssize_t ret = send(hSocket, pszSocks5Init, nSize, MSG_NOSIGNAL); - if (ret != nSize) - { + // Accepted authentication methods + std::vector<uint8_t> vSocks5Init; + vSocks5Init.push_back(0x05); + if (auth) { + vSocks5Init.push_back(0x02); // # METHODS + vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED + vSocks5Init.push_back(0x02); // X'02' USERNAME/PASSWORD (RFC1929) + } else { + vSocks5Init.push_back(0x01); // # METHODS + vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED + } + ssize_t ret = send(hSocket, (const char*)begin_ptr(vSocks5Init), vSocks5Init.size(), MSG_NOSIGNAL); + if (ret != (ssize_t)vSocks5Init.size()) { CloseSocket(hSocket); return error("Error sending to proxy"); } char pchRet1[2]; - if (!InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) - { + if (!InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) { CloseSocket(hSocket); return error("Error reading proxy response"); } - if (pchRet1[0] != 0x05 || pchRet1[1] != 0x00) - { + if (pchRet1[0] != 0x05) { CloseSocket(hSocket); return error("Proxy failed to initialize"); } - string strSocks5("\5\1"); - strSocks5 += '\000'; strSocks5 += '\003'; - strSocks5 += static_cast<char>(std::min((int)strDest.size(), 255)); - strSocks5 += strDest; - strSocks5 += static_cast<char>((port >> 8) & 0xFF); - strSocks5 += static_cast<char>((port >> 0) & 0xFF); - ret = send(hSocket, strSocks5.data(), strSocks5.size(), MSG_NOSIGNAL); - if (ret != (ssize_t)strSocks5.size()) - { + if (pchRet1[1] == 0x02 && auth) { + // Perform username/password authentication (as described in RFC1929) + std::vector<uint8_t> vAuth; + vAuth.push_back(0x01); + if (auth->username.size() > 255 || auth->password.size() > 255) + return error("Proxy username or password too long"); + vAuth.push_back(auth->username.size()); + vAuth.insert(vAuth.end(), auth->username.begin(), auth->username.end()); + vAuth.push_back(auth->password.size()); + vAuth.insert(vAuth.end(), auth->password.begin(), auth->password.end()); + ret = send(hSocket, (const char*)begin_ptr(vAuth), vAuth.size(), MSG_NOSIGNAL); + if (ret != (ssize_t)vAuth.size()) { + CloseSocket(hSocket); + return error("Error sending authentication to proxy"); + } + LogPrint("proxy", "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password); + char pchRetA[2]; + if (!InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) { + CloseSocket(hSocket); + return error("Error reading proxy authentication response"); + } + if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) { + CloseSocket(hSocket); + return error("Proxy authentication unsuccesful"); + } + } else if (pchRet1[1] == 0x00) { + // Perform no authentication + } else { + CloseSocket(hSocket); + return error("Proxy requested wrong authentication method %02x", pchRet1[1]); + } + std::vector<uint8_t> vSocks5; + vSocks5.push_back(0x05); // VER protocol version + vSocks5.push_back(0x01); // CMD CONNECT + vSocks5.push_back(0x00); // RSV Reserved + vSocks5.push_back(0x03); // ATYP DOMAINNAME + vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function + vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end()); + vSocks5.push_back((port >> 8) & 0xFF); + vSocks5.push_back((port >> 0) & 0xFF); + ret = send(hSocket, (const char*)begin_ptr(vSocks5), vSocks5.size(), MSG_NOSIGNAL); + if (ret != (ssize_t)vSocks5.size()) { CloseSocket(hSocket); return error("Error sending to proxy"); } char pchRet2[4]; - if (!InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) - { + if (!InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) { CloseSocket(hSocket); return error("Error reading proxy response"); } - if (pchRet2[0] != 0x05) - { + if (pchRet2[0] != 0x05) { CloseSocket(hSocket); return error("Proxy failed to accept request"); } - if (pchRet2[1] != 0x00) - { + if (pchRet2[1] != 0x00) { CloseSocket(hSocket); switch (pchRet2[1]) { @@ -352,8 +394,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket) default: return error("Proxy error: unknown"); } } - if (pchRet2[2] != 0x00) - { + if (pchRet2[2] != 0x00) { CloseSocket(hSocket); return error("Error: malformed proxy response"); } @@ -375,13 +416,11 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket) } default: CloseSocket(hSocket); return error("Error: malformed proxy response"); } - if (!ret) - { + if (!ret) { CloseSocket(hSocket); return error("Error reading from proxy"); } - if (!InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) - { + if (!InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) { CloseSocket(hSocket); return error("Error reading from proxy"); } @@ -471,7 +510,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe return true; } -bool SetProxy(enum Network net, CService addrProxy) { +bool SetProxy(enum Network net, const proxyType &addrProxy) { assert(net >= 0 && net < NET_MAX); if (!addrProxy.IsValid()) return false; @@ -489,7 +528,7 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut) { return true; } -bool SetNameProxy(CService addrProxy) { +bool SetNameProxy(const proxyType &addrProxy) { if (!addrProxy.IsValid()) return false; LOCK(cs_proxyInfos); @@ -497,7 +536,7 @@ bool SetNameProxy(CService addrProxy) { return true; } -bool GetNameProxy(CService &nameProxyOut) { +bool GetNameProxy(proxyType &nameProxyOut) { LOCK(cs_proxyInfos); if(!nameProxy.IsValid()) return false; @@ -513,37 +552,49 @@ bool HaveNameProxy() { bool IsProxy(const CNetAddr &addr) { LOCK(cs_proxyInfos); for (int i = 0; i < NET_MAX; i++) { - if (addr == (CNetAddr)proxyInfo[i]) + if (addr == (CNetAddr)proxyInfo[i].proxy) return true; } return false; } -bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) +static bool ConnectThroughProxy(const proxyType &proxy, const std::string strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) { - proxyType proxy; - if (outProxyConnectionFailed) - *outProxyConnectionFailed = false; - // no proxy needed (none set for target network) - if (!GetProxy(addrDest.GetNetwork(), proxy)) - return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); - SOCKET hSocket = INVALID_SOCKET; - // first connect to proxy server - if (!ConnectSocketDirectly(proxy, hSocket, nTimeout)) { + if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout)) { if (outProxyConnectionFailed) *outProxyConnectionFailed = true; return false; } // do socks negotiation - if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket)) - return false; + if (proxy.randomize_credentials) { + ProxyCredentials random_auth; + random_auth.username = strprintf("%i", insecure_rand()); + random_auth.password = strprintf("%i", insecure_rand()); + if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket)) + return false; + } else { + if (!Socks5(strDest, (unsigned short)port, 0, hSocket)) + return false; + } hSocketRet = hSocket; return true; } +bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) +{ + proxyType proxy; + if (outProxyConnectionFailed) + *outProxyConnectionFailed = false; + + if (GetProxy(addrDest.GetNetwork(), proxy)) + return ConnectThroughProxy(proxy, addrDest.ToStringIP(), addrDest.GetPort(), hSocketRet, nTimeout, outProxyConnectionFailed); + else // no proxy needed (none set for target network) + return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); +} + bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed) { string strDest; @@ -554,9 +605,7 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest SplitHostPort(string(pszDest), port, strDest); - SOCKET hSocket = INVALID_SOCKET; - - CService nameProxy; + proxyType nameProxy; GetNameProxy(nameProxy); CService addrResolved(CNetAddr(strDest, fNameLookup && !HaveNameProxy()), port); @@ -569,18 +618,7 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest if (!HaveNameProxy()) return false; - // first connect to name proxy server - if (!ConnectSocketDirectly(nameProxy, hSocket, nTimeout)) { - if (outProxyConnectionFailed) - *outProxyConnectionFailed = true; - return false; - } - // do socks negotiation - if (!Socks5(strDest, (unsigned short)port, hSocket)) - return false; - - hSocketRet = hSocket; - return true; + return ConnectThroughProxy(nameProxy, strDest, port, hSocketRet, nTimeout, outProxyConnectionFailed); } void CNetAddr::Init() diff --git a/src/netbase.h b/src/netbase.h index b42c2dffa4..6d2ca4afb2 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -168,15 +168,25 @@ class CService : public CNetAddr } }; -typedef CService proxyType; +class proxyType +{ +public: + proxyType(): randomize_credentials(false) {} + proxyType(const CService &proxy, bool randomize_credentials=false): proxy(proxy), randomize_credentials(randomize_credentials) {} + + bool IsValid() const { return proxy.IsValid(); } + + CService proxy; + bool randomize_credentials; +}; enum Network ParseNetwork(std::string net); std::string GetNetworkName(enum Network net); void SplitHostPort(std::string in, int &portOut, std::string &hostOut); -bool SetProxy(enum Network net, CService addrProxy); +bool SetProxy(enum Network net, const proxyType &addrProxy); bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); -bool SetNameProxy(CService addrProxy); +bool SetNameProxy(const proxyType &addrProxy); bool HaveNameProxy(); bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true); bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 3ae780abfd..069601ab67 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -107,7 +107,6 @@ static QString GetLangTerritory() /** Set up translations */ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator) { - // Remove old translators QApplication::removeTranslator(&qtTranslatorBase); QApplication::removeTranslator(&qtTranslator); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 198dd1fdf9..670d54c7e3 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -164,7 +164,7 @@ BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : // Create status bar statusBar(); - + // Disable size grip because it looks ugly and nobody needs it statusBar()->setSizeGripEnabled(false); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index e4e9015c85..0a60632bfa 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -8,12 +8,12 @@ #include "addresstablemodel.h" #include "bitcoinunits.h" #include "guiutil.h" -#include "init.h" #include "optionsmodel.h" #include "scicon.h" #include "walletmodel.h" #include "coincontrol.h" +#include "init.h" #include "main.h" #include "wallet/wallet.h" diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index b362928438..48d0dd093c 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -195,7 +195,7 @@ </item> </layout> </widget> - <widget class="QFrame" name="SendCoins_InsecurePaymentRequest"> + <widget class="QFrame" name="SendCoins_UnauthenticatedPaymentRequest"> <property name="palette"> <palette> <active> @@ -612,7 +612,7 @@ </palette> </property> <property name="toolTip"> - <string>This is an unverified payment request.</string> + <string>This is an unauthenticated payment request.</string> </property> <property name="autoFillBackground"> <bool>true</bool> @@ -700,7 +700,7 @@ </item> </layout> </widget> - <widget class="QFrame" name="SendCoins_SecurePaymentRequest"> + <widget class="QFrame" name="SendCoins_AuthenticatedPaymentRequest"> <property name="palette"> <palette> <active> @@ -1144,7 +1144,7 @@ </palette> </property> <property name="toolTip"> - <string>This is a verified payment request.</string> + <string>This is an authenticated payment request.</string> </property> <property name="autoFillBackground"> <bool>true</bool> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index a5ee81db6c..4a1f728e18 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -741,14 +741,14 @@ LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef CFURLRef currentItemURL = NULL; #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100 - if(&LSSharedFileListItemCopyResolvedURL) - currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, NULL); + if(&LSSharedFileListItemCopyResolvedURL) + currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, NULL); #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100 - else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); + else + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); #endif #else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); #endif if(currentItemURL && CFEqual(currentItemURL, findUrl)) { diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index a169ed6b55..41d6acf358 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -335,8 +335,8 @@ bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const proxyType curProxy; if (GetProxy(NET_IPV4, curProxy)) { proxy.setType(QNetworkProxy::Socks5Proxy); - proxy.setHostName(QString::fromStdString(curProxy.ToStringIP())); - proxy.setPort(curProxy.GetPort()); + proxy.setHostName(QString::fromStdString(curProxy.proxy.ToStringIP())); + proxy.setPort(curProxy.proxy.GetPort()); return true; } diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 5221ec3e24..543b977d8f 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -5,9 +5,10 @@ #include "recentrequeststablemodel.h" #include "bitcoinunits.h" -#include "clientversion.h" #include "guiutil.h" #include "optionsmodel.h" + +#include "clientversion.h" #include "streams.h" #include <boost/foreach.hpp> diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 59939fa871..0360f160d8 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -274,11 +274,11 @@ void SendCoinsDialog::on_sendButton_clicked() recipientElement = tr("%1 to %2").arg(amount, address); } } - else if(!rcp.authenticatedMerchant.isEmpty()) // secure payment request + else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request { recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); } - else // insecure payment request + else // unauthenticated payment request { recipientElement = tr("%1 to %2").arg(amount, address); } diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 6ac650e74f..ea35ed1d53 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -99,11 +99,11 @@ void SendCoinsEntry::clear() ui->messageTextLabel->clear(); ui->messageTextLabel->hide(); ui->messageLabel->hide(); - // clear UI elements for insecure payment request + // clear UI elements for unauthenticated payment request ui->payTo_is->clear(); ui->memoTextLabel_is->clear(); ui->payAmount_is->clear(); - // clear UI elements for secure payment request + // clear UI elements for authenticated payment request ui->payTo_s->clear(); ui->memoTextLabel_s->clear(); ui->payAmount_s->clear(); @@ -190,21 +190,21 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value) if (recipient.paymentRequest.IsInitialized()) // payment request { - if (recipient.authenticatedMerchant.isEmpty()) // insecure + if (recipient.authenticatedMerchant.isEmpty()) // unauthenticated { ui->payTo_is->setText(recipient.address); ui->memoTextLabel_is->setText(recipient.message); ui->payAmount_is->setValue(recipient.amount); ui->payAmount_is->setReadOnly(true); - setCurrentWidget(ui->SendCoins_InsecurePaymentRequest); + setCurrentWidget(ui->SendCoins_UnauthenticatedPaymentRequest); } - else // secure + else // authenticated { ui->payTo_s->setText(recipient.authenticatedMerchant); ui->memoTextLabel_s->setText(recipient.message); ui->payAmount_s->setValue(recipient.amount); ui->payAmount_s->setReadOnly(true); - setCurrentWidget(ui->SendCoins_SecurePaymentRequest); + setCurrentWidget(ui->SendCoins_AuthenticatedPaymentRequest); } } else // normal payment diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 414fe02ff9..8430e017c1 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -4,11 +4,12 @@ #include "splashscreen.h" +#include "networkstyle.h" + #include "clientversion.h" #include "init.h" -#include "networkstyle.h" -#include "ui_interface.h" #include "util.h" +#include "ui_interface.h" #include "version.h" #ifdef ENABLE_WALLET diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 9b235f9130..4a2c51477e 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -10,7 +10,6 @@ #include "transactionrecord.h" #include "base58.h" -#include "wallet/db.h" #include "main.h" #include "script/script.h" #include "timedata.h" diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 09ed8ce9fd..9b8be76beb 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -12,7 +12,6 @@ #include "transactiontablemodel.h" #include "base58.h" -#include "wallet/db.h" #include "keystore.h" #include "main.h" #include "sync.h" diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 851d113f3b..aba250ba49 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -196,6 +196,8 @@ Value setgenerate(const Array& params, bool fHelp) if (pwalletMain == NULL) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); + if (Params().MineBlocksOnDemand()) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Use the generate method instead of setgenerate on this network"); bool fGenerate = true; if (params.size() > 0) diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 938d79513f..f5bef2a077 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -90,7 +90,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("timeoffset", GetTimeOffset())); obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string()))); + obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); #ifdef ENABLE_WALLET diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 6306fd4406..bdee5b9f2e 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -371,7 +371,8 @@ static Array GetNetworksInfo() obj.push_back(Pair("name", GetNetworkName(network))); obj.push_back(Pair("limited", IsLimited(network))); obj.push_back(Pair("reachable", IsReachable(network))); - obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.ToStringIPPort() : string())); + obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())); + obj.push_back(Pair("proxy_randomize_credentials", proxy.randomize_credentials)); networks.push_back(obj); } return networks; diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index c979217a13..8393a8502e 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -419,7 +419,7 @@ Value signrawtransaction(const Array& params, bool fHelp) "\nResult:\n" "{\n" " \"hex\": \"value\", (string) The raw transaction with signature(s) (hex-encoded string)\n" - " \"complete\": n (numeric) if transaction has a complete set of signature (0 if not)\n" + " \"complete\": true|false (boolean) if transaction has a complete set of signature\n" "}\n" "\nExamples:\n" diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h index 7a74d87bb4..5e7bb66ea2 100644 --- a/src/support/allocators/secure.h +++ b/src/support/allocators/secure.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_ALLOCATORS_SECURE_H -#define BITCOIN_ALLOCATORS_SECURE_H +#ifndef BITCOIN_SUPPORT_ALLOCATORS_SECURE_H +#define BITCOIN_SUPPORT_ALLOCATORS_SECURE_H #include "support/pagelocker.h" @@ -59,4 +59,4 @@ struct secure_allocator : public std::allocator<T> { // This is exactly like std::string, but with a custom allocator. typedef std::basic_string<char, std::char_traits<char>, secure_allocator<char> > SecureString; -#endif // BITCOIN_ALLOCATORS_SECURE_H +#endif // BITCOIN_SUPPORT_ALLOCATORS_SECURE_H diff --git a/src/support/allocators/zeroafterfree.h b/src/support/allocators/zeroafterfree.h index b01fcd088b..41e23392e8 100644 --- a/src/support/allocators/zeroafterfree.h +++ b/src/support/allocators/zeroafterfree.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_ALLOCATORS_ZEROAFTERFREE_H -#define BITCOIN_ALLOCATORS_ZEROAFTERFREE_H +#ifndef BITCOIN_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H +#define BITCOIN_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H #include "support/cleanse.h" @@ -45,4 +45,4 @@ struct zero_after_free_allocator : public std::allocator<T> { // Byte-vector that clears its contents before deletion. typedef std::vector<char, zero_after_free_allocator<char> > CSerializeData; -#endif // BITCOIN_ALLOCATORS_ZEROAFTERFREE_H +#endif // BITCOIN_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H diff --git a/src/support/pagelocker.h b/src/support/pagelocker.h index 964be1aec4..3fd793072f 100644 --- a/src/support/pagelocker.h +++ b/src/support/pagelocker.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_ALLOCATORS_PAGELOCKER_H -#define BITCOIN_ALLOCATORS_PAGELOCKER_H +#ifndef BITCOIN_SUPPORT_PAGELOCKER_H +#define BITCOIN_SUPPORT_PAGELOCKER_H #include "support/cleanse.h" @@ -175,4 +175,4 @@ void UnlockObject(const T& t) LockedPageManager::Instance().UnlockRange((void*)(&t), sizeof(T)); } -#endif // BITCOIN_ALLOCATORS_PAGELOCKER_H +#endif // BITCOIN_SUPPORT_PAGELOCKER_H diff --git a/src/sync.h b/src/sync.h index 27e80e813b..78b9043477 100644 --- a/src/sync.h +++ b/src/sync.h @@ -21,9 +21,6 @@ //////////////////////////////////////////////// /* - - - CCriticalSection mutex; boost::recursive_mutex mutex; @@ -42,20 +39,18 @@ ENTER_CRITICAL_SECTION(mutex); // no RAII LEAVE_CRITICAL_SECTION(mutex); // no RAII mutex.unlock(); - - - */ - /////////////////////////////// // // // THE ACTUAL IMPLEMENTATION // // // /////////////////////////////// -// Template mixin that adds -Wthread-safety locking annotations to a -// subset of the mutex API. +/** + * Template mixin that adds -Wthread-safety locking + * annotations to a subset of the mutex API. + */ template <typename PARENT> class LOCKABLE AnnotatedMixin : public PARENT { @@ -76,8 +71,10 @@ public: } }; -/** Wrapped boost mutex: supports recursive locking, but no waiting */ -// TODO: We should move away from using the recursive lock by default. +/** + * Wrapped boost mutex: supports recursive locking, but no waiting + * TODO: We should move away from using the recursive lock by default. + */ typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection; /** Wrapped boost mutex: supports waiting but not recursive locking */ @@ -92,9 +89,7 @@ void LeaveCritical(); std::string LocksHeld(); void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); #else -void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) -{ -} +void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} void static inline LeaveCritical() {} void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} #endif diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index d738851c1f..0d815c27fd 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -8,6 +8,7 @@ #include "key.h" #include "uint256.h" #include "util.h" +#include "utilstrencodings.h" #include "test/test_bitcoin.h" #include <string> diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 32746b00df..70aeb76723 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_CRYPTER_H -#define BITCOIN_CRYPTER_H +#ifndef BITCOIN_WALLET_CRYPTER_H +#define BITCOIN_WALLET_CRYPTER_H #include "keystore.h" #include "serialize.h" @@ -193,4 +193,4 @@ public: boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged; }; -#endif // BITCOIN_CRYPTER_H +#endif // BITCOIN_WALLET_CRYPTER_H diff --git a/src/wallet/db.h b/src/wallet/db.h index 790ae50413..9596723b21 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_DB_H -#define BITCOIN_DB_H +#ifndef BITCOIN_WALLET_DB_H +#define BITCOIN_WALLET_DB_H #include "clientversion.h" #include "serialize.h" @@ -307,4 +307,4 @@ public: bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); }; -#endif // BITCOIN_DB_H +#endif // BITCOIN_WALLET_DB_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e03cd5b84e..c31c09d922 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2301,7 +2301,7 @@ Value listunspent(const Array& params, bool fHelp) vector<COutput> vecOutputs; assert(pwalletMain != NULL); LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->AvailableCoins(vecOutputs, false); + pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); BOOST_FOREACH(const COutput& out, vecOutputs) { if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2566b2712b..f0ee834913 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1481,7 +1481,7 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const /** * populate vCoins with vector of available COutputs. */ -void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const +void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const { vCoins.clear(); @@ -1508,7 +1508,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const for (unsigned int i = 0; i < pcoin->vout.size(); i++) { isminetype mine = IsMine(pcoin->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && - !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 && + !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO)); } @@ -2038,7 +2038,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) setKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation - // the requires a new key. + // that requires a new key. } } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4dbb0e2e57..1c900b0315 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_WALLET_H -#define BITCOIN_WALLET_H +#ifndef BITCOIN_WALLET_WALLET_H +#define BITCOIN_WALLET_WALLET_H #include "amount.h" #include "key.h" @@ -540,7 +540,7 @@ public: //! check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } - void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL) const; + void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const; bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const; bool IsSpent(const uint256& hash, unsigned int n) const; @@ -869,4 +869,4 @@ private: std::vector<char> _ssExtra; }; -#endif // BITCOIN_WALLET_H +#endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/wallet_ismine.h b/src/wallet/wallet_ismine.h index 6293df8b10..5b9b0e0841 100644 --- a/src/wallet/wallet_ismine.h +++ b/src/wallet/wallet_ismine.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_WALLET_ISMINE_H -#define BITCOIN_WALLET_ISMINE_H +#ifndef BITCOIN_WALLET_WALLET_ISMINE_H +#define BITCOIN_WALLET_WALLET_ISMINE_H #include "key.h" #include "script/standard.h" @@ -26,4 +26,4 @@ typedef uint8_t isminefilter; isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); -#endif // BITCOIN_WALLET_ISMINE_H +#endif // BITCOIN_WALLET_WALLET_ISMINE_H diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index e5f64ffaae..bc1a104b5b 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -3,8 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_WALLETDB_H -#define BITCOIN_WALLETDB_H +#ifndef BITCOIN_WALLET_WALLETDB_H +#define BITCOIN_WALLET_WALLETDB_H #include "amount.h" #include "wallet/db.h" @@ -140,4 +140,4 @@ private: bool BackupWallet(const CWallet& wallet, const std::string& strDest); void ThreadFlushWalletDB(const std::string& strFile); -#endif // BITCOIN_WALLETDB_H +#endif // BITCOIN_WALLET_WALLETDB_H |