diff options
Diffstat (limited to 'src')
58 files changed, 1006 insertions, 921 deletions
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 1ea039adb3..f8f4439159 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -178,6 +178,7 @@ BITCOIN_QT_H = \ qt/macdockiconhandler.h \ qt/macnotificationhandler.h \ qt/monitoreddatamapper.h \ + qt/networkstyle.h \ qt/notificator.h \ qt/openuridialog.h \ qt/optionsdialog.h \ @@ -269,6 +270,7 @@ BITCOIN_QT_CPP = \ qt/guiutil.cpp \ qt/intro.cpp \ qt/monitoreddatamapper.cpp \ + qt/networkstyle.cpp \ qt/notificator.cpp \ qt/optionsdialog.cpp \ qt/optionsmodel.cpp \ @@ -366,6 +368,7 @@ if USE_LIBSECP256K1 qt_bitcoin_qt_LDADD += secp256k1/libsecp256k1.la endif qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) +qt_bitcoin_qt_LIBTOOLFLAGS = --tag CXX #locale/foo.ts -> locale/foo.qm QT_QM=$(QT_TS:.ts=.qm) diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 2cba5b7e1e..23375bef82 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -13,7 +13,7 @@ TEST_QT_H = \ qt/test/paymentservertests.h qt_test_test_bitcoin_qt_CPPFLAGS = $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ - $(QT_INCLUDES) $(QT_TEST_INCLUDES) + $(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS) qt_test_test_bitcoin_qt_SOURCES = \ qt/test/test_main.cpp \ diff --git a/src/alert.cpp b/src/alert.cpp index 3271ecfbfd..d495849206 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -233,25 +233,30 @@ bool CAlert::ProcessAlert(bool fThread) if(AppliesToMe()) { uiInterface.NotifyAlertChanged(GetHash(), CT_NEW); - std::string strCmd = GetArg("-alertnotify", ""); - if (!strCmd.empty()) - { - // Alert text should be plain ascii coming from a trusted source, but to - // be safe we first strip anything not in safeChars, then add single quotes around - // the whole string before passing it to the shell: - std::string singleQuote("'"); - std::string safeStatus = SanitizeString(strStatusBar); - safeStatus = singleQuote+safeStatus+singleQuote; - boost::replace_all(strCmd, "%s", safeStatus); - - if (fThread) - boost::thread t(runCommand, strCmd); // thread runs free - else - runCommand(strCmd); - } + Notify(strStatusBar, fThread); } } LogPrint("alert", "accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); return true; } + +void +CAlert::Notify(const std::string& strMessage, bool fThread) +{ + std::string strCmd = GetArg("-alertnotify", ""); + if (strCmd.empty()) return; + + // Alert text should be plain ascii coming from a trusted source, but to + // be safe we first strip anything not in safeChars, then add single quotes around + // the whole string before passing it to the shell: + std::string singleQuote("'"); + std::string safeStatus = SanitizeString(strMessage); + safeStatus = singleQuote+safeStatus+singleQuote; + boost::replace_all(strCmd, "%s", safeStatus); + + if (fThread) + boost::thread t(runCommand, strCmd); // thread runs free + else + runCommand(strCmd); +} diff --git a/src/alert.h b/src/alert.h index 5ecf94cea8..ba3235858d 100644 --- a/src/alert.h +++ b/src/alert.h @@ -101,7 +101,8 @@ public: bool AppliesToMe() const; bool RelayTo(CNode* pnode) const; bool CheckSignature() const; - bool ProcessAlert(bool fThread = true); + bool ProcessAlert(bool fThread = true); // fThread means run -alertnotify in a free-running thread + static void Notify(const std::string& strMessage, bool fThread); /* * Get copy of (active) alert object by hash. Returns a null alert if it is not found. diff --git a/src/chain.h b/src/chain.h index 0aafb40b98..4e6a466c6a 100644 --- a/src/chain.h +++ b/src/chain.h @@ -49,12 +49,29 @@ struct CDiskBlockPos }; enum BlockStatus { + // Unused. BLOCK_VALID_UNKNOWN = 0, - BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future - BLOCK_VALID_TREE = 2, // parent found, difficulty matches, timestamp >= median previous, checkpoint - BLOCK_VALID_TRANSACTIONS = 3, // only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, sigops, size, merkle root - BLOCK_VALID_CHAIN = 4, // outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30 - BLOCK_VALID_SCRIPTS = 5, // scripts/signatures ok + + // Parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future + BLOCK_VALID_HEADER = 1, + + // All parent headers found, difficulty matches, timestamp >= median previous, checkpoint. Implies all parents + // are also at least TREE. + BLOCK_VALID_TREE = 2, + + // Only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, + // sigops, size, merkle root. Implies all parents are at least TREE but not necessarily TRANSACTIONS. When all + // parent blocks also have TRANSACTIONS, CBlockIndex::nChainTx will be set. + BLOCK_VALID_TRANSACTIONS = 3, + + // Outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30. + // Implies all parents are also at least CHAIN. + BLOCK_VALID_CHAIN = 4, + + // Scripts & signatures ok. Implies all parents are also at least SCRIPTS. + BLOCK_VALID_SCRIPTS = 5, + + // All validity bits. BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS | BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS, @@ -103,7 +120,8 @@ public: // Note: in a potential headers-first mode, this number cannot be relied upon unsigned int nTx; - // (memory only) Number of transactions in the chain up to and including this block + // (memory only) Number of transactions in the chain up to and including this block. + // This value will be non-zero only if and only if transactions for this block and all its parents are available. unsigned int nChainTx; // change to 64-bit type when necessary; won't happen before 2030 // Verification status of this block. See enum BlockStatus @@ -146,7 +164,7 @@ public: SetNull(); } - CBlockIndex(CBlockHeader& block) + CBlockIndex(const CBlockHeader& block) { SetNull(); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 31c67715c8..dfb4c59d87 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -44,6 +44,57 @@ static void convertSeed6(std::vector<CAddress> &vSeedsOut, const SeedSpec6 *data } } + // What makes a good checkpoint block? + // + Is surrounded by blocks with reasonable timestamps + // (no blocks before with a timestamp after, none after with + // timestamp before) + // + Contains no strange transactions +static Checkpoints::MapCheckpoints mapCheckpoints = + boost::assign::map_list_of + ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) + ( 33333, uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) + ( 74000, uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) + (105000, uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) + (134444, uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")) + (168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")) + (193000, uint256("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")) + (210000, uint256("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")) + (216116, uint256("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")) + (225430, uint256("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")) + (250000, uint256("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")) + (279000, uint256("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")) + (295000, uint256("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")) + ; +static const Checkpoints::CCheckpointData data = { + &mapCheckpoints, + 1397080064, // * UNIX timestamp of last checkpoint block + 36544669, // * total number of transactions between genesis and last checkpoint + // (the tx=... number in the SetBestChain debug.log lines) + 60000.0 // * estimated number of transactions per day after checkpoint + }; + +static Checkpoints::MapCheckpoints mapCheckpointsTestnet = + boost::assign::map_list_of + ( 546, uint256("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")) + ; +static const Checkpoints::CCheckpointData dataTestnet = { + &mapCheckpointsTestnet, + 1337966069, + 1488, + 300 + }; + +static Checkpoints::MapCheckpoints mapCheckpointsRegtest = + boost::assign::map_list_of + ( 0, uint256("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")) + ; +static const Checkpoints::CCheckpointData dataRegtest = { + &mapCheckpointsRegtest, + 0, + 0, + 0 + }; + class CMainParams : public CChainParams { public: CMainParams() { @@ -116,6 +167,12 @@ public: fRequireStandard = true; fMineBlocksOnDemand = false; fSkipProofOfWorkCheck = false; + fTestnetToBeDeprecatedFieldRPC = false; + } + + const Checkpoints::CCheckpointData& Checkpoints() const + { + return data; } }; static CMainParams mainParams; @@ -172,6 +229,11 @@ public: fAllowMinDifficultyBlocks = true; fRequireStandard = false; fMineBlocksOnDemand = false; + fTestnetToBeDeprecatedFieldRPC = true; + } + const Checkpoints::CCheckpointData& Checkpoints() const + { + return dataTestnet; } }; static CTestNetParams testNetParams; @@ -211,6 +273,11 @@ public: fAllowMinDifficultyBlocks = true; fRequireStandard = false; fMineBlocksOnDemand = true; + fTestnetToBeDeprecatedFieldRPC = false; + } + const Checkpoints::CCheckpointData& Checkpoints() const + { + return dataRegtest; } }; static CRegTestParams regTestParams; @@ -233,7 +300,13 @@ public: fAllowMinDifficultyBlocks = false; fMineBlocksOnDemand = true; } -public: + + const Checkpoints::CCheckpointData& Checkpoints() const + { + // UnitTest share the same checkpoints as MAIN + return data; + } + // Published setters to allow changing values in unit test cases virtual void setSubsidyHalvingInterval(int anSubsidyHalvingInterval) { nSubsidyHalvingInterval=anSubsidyHalvingInterval; } virtual void setEnforceBlockUpgradeMajority(int anEnforceBlockUpgradeMajority) { nEnforceBlockUpgradeMajority=anEnforceBlockUpgradeMajority; } @@ -281,10 +354,13 @@ void SelectParams(CBaseChainParams::Network network) { pCurrentParams = &Params(network); } -bool SelectParamsFromCommandLine() { - if (!SelectBaseParamsFromCommandLine()) +bool SelectParamsFromCommandLine() +{ + CBaseChainParams::Network network = NetworkIdFromCommandLine(); + if (network == CBaseChainParams::MAX_NETWORK_TYPES) return false; - SelectParams(BaseParams().NetworkID()); + SelectBaseParams(network); + SelectParams(network); return true; } diff --git a/src/chainparams.h b/src/chainparams.h index 50441a89f3..f157419bb2 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -8,6 +8,7 @@ #include "core.h" #include "chainparamsbase.h" +#include "checkpoints.h" #include "protocol.h" #include "uint256.h" @@ -71,12 +72,14 @@ public: /* Make miner stop after a block is found. In RPC, don't return * until nGenProcLimit blocks are generated */ bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } - CBaseChainParams::Network NetworkID() const { return networkID; } + /* In the future use NetworkIDString() for RPC fields */ + bool TestnetToBeDeprecatedFieldRPC() const { return fTestnetToBeDeprecatedFieldRPC; } /* Return the BIP70 network string (main, test or regtest) */ std::string NetworkIDString() const { return strNetworkID; } const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::vector<CAddress>& FixedSeeds() const { return vFixedSeeds; } + virtual const Checkpoints::CCheckpointData& Checkpoints() const = 0; protected: CChainParams() {} @@ -106,6 +109,7 @@ protected: bool fRequireStandard; bool fMineBlocksOnDemand; bool fSkipProofOfWorkCheck; + bool fTestnetToBeDeprecatedFieldRPC; }; /** Modifiable parameters interface is used by test cases to adapt the parameters in order diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index e9d63197bd..5d9ec7927b 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -100,22 +100,27 @@ void SelectBaseParams(CBaseChainParams::Network network) } } -bool SelectBaseParamsFromCommandLine() +CBaseChainParams::Network NetworkIdFromCommandLine() { bool fRegTest = GetBoolArg("-regtest", false); bool fTestNet = GetBoolArg("-testnet", false); - if (fTestNet && fRegTest) { + if (fTestNet && fRegTest) + return CBaseChainParams::MAX_NETWORK_TYPES; + if (fRegTest) + return CBaseChainParams::REGTEST; + if (fTestNet) + return CBaseChainParams::TESTNET; + return CBaseChainParams::MAIN; +} + +bool SelectBaseParamsFromCommandLine() +{ + CBaseChainParams::Network network = NetworkIdFromCommandLine(); + if (network == CBaseChainParams::MAX_NETWORK_TYPES) return false; - } - if (fRegTest) { - SelectBaseParams(CBaseChainParams::REGTEST); - } else if (fTestNet) { - SelectBaseParams(CBaseChainParams::TESTNET); - } else { - SelectBaseParams(CBaseChainParams::MAIN); - } + SelectBaseParams(network); return true; } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index cc154cf501..911d1181ac 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -26,7 +26,6 @@ public: const std::string& DataDir() const { return strDataDir; } int RPCPort() const { return nRPCPort; } - Network NetworkID() const { return networkID; } protected: CBaseChainParams() {} @@ -46,7 +45,13 @@ const CBaseChainParams& BaseParams(); void SelectBaseParams(CBaseChainParams::Network network); /** - * Looks for -regtest or -testnet and then calls SelectParams as appropriate. + * Looks for -regtest or -testnet and returns the appropriate Network ID. + * Returns MAX_NETWORK_TYPES if an invalid combination is given. + */ +CBaseChainParams::Network NetworkIdFromCommandLine(); + +/** + * Calls NetworkIdFromCommandLine() and then calls SelectParams as appropriate. * Returns false if an invalid combination is given. */ bool SelectBaseParamsFromCommandLine(); diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 9a6bc05e63..fbde47339d 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -4,18 +4,16 @@ #include "checkpoints.h" +#include "chainparams.h" #include "main.h" #include "uint256.h" #include <stdint.h> -#include <boost/assign/list_of.hpp> // for 'map_list_of()' #include <boost/foreach.hpp> namespace Checkpoints { - typedef std::map<int, uint256> MapCheckpoints; - // How many times we expect transactions after the last checkpoint to // be slower. This number is a compromise, as it can't be accurate for // every system. When reindexing from a fast disk with a slow CPU, it @@ -23,83 +21,14 @@ namespace Checkpoints { // fast multicore CPU, it won't be much higher than 1. static const double SIGCHECK_VERIFICATION_FACTOR = 5.0; - struct CCheckpointData { - const MapCheckpoints *mapCheckpoints; - int64_t nTimeLastCheckpoint; - int64_t nTransactionsLastCheckpoint; - double fTransactionsPerDay; - }; - bool fEnabled = true; - // What makes a good checkpoint block? - // + Is surrounded by blocks with reasonable timestamps - // (no blocks before with a timestamp after, none after with - // timestamp before) - // + Contains no strange transactions - static MapCheckpoints mapCheckpoints = - boost::assign::map_list_of - ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) - ( 33333, uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) - ( 74000, uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) - (105000, uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) - (134444, uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")) - (168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")) - (193000, uint256("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")) - (210000, uint256("0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")) - (216116, uint256("0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")) - (225430, uint256("0x00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")) - (250000, uint256("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")) - (279000, uint256("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")) - (295000, uint256("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")) - ; - static const CCheckpointData data = { - &mapCheckpoints, - 1397080064, // * UNIX timestamp of last checkpoint block - 36544669, // * total number of transactions between genesis and last checkpoint - // (the tx=... number in the SetBestChain debug.log lines) - 60000.0 // * estimated number of transactions per day after checkpoint - }; - - static MapCheckpoints mapCheckpointsTestnet = - boost::assign::map_list_of - ( 546, uint256("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")) - ; - static const CCheckpointData dataTestnet = { - &mapCheckpointsTestnet, - 1337966069, - 1488, - 300 - }; - - static MapCheckpoints mapCheckpointsRegtest = - boost::assign::map_list_of - ( 0, uint256("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")) - ; - static const CCheckpointData dataRegtest = { - &mapCheckpointsRegtest, - 0, - 0, - 0 - }; - - const CCheckpointData &Checkpoints() { - if (Params().NetworkID() == CBaseChainParams::TESTNET) - return dataTestnet; - else if (Params().NetworkID() == CBaseChainParams::MAIN) - return data; - else if (Params().NetworkID() == CBaseChainParams::UNITTEST) // UnitTest share the same checkpoints as MAIN - return data; - else - return dataRegtest; - } - bool CheckBlock(int nHeight, const uint256& hash) { if (!fEnabled) return true; - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + const MapCheckpoints& checkpoints = *Params().Checkpoints().mapCheckpoints; MapCheckpoints::const_iterator i = checkpoints.find(nHeight); if (i == checkpoints.end()) return true; @@ -119,7 +48,7 @@ namespace Checkpoints { // Work is defined as: 1.0 per transaction before the last checkpoint, and // fSigcheckVerificationFactor per transaction after. - const CCheckpointData &data = Checkpoints(); + const CCheckpointData &data = Params().Checkpoints(); if (pindex->nChainTx <= data.nTransactionsLastCheckpoint) { double nCheapBefore = pindex->nChainTx; @@ -143,7 +72,7 @@ namespace Checkpoints { if (!fEnabled) return 0; - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + const MapCheckpoints& checkpoints = *Params().Checkpoints().mapCheckpoints; return checkpoints.rbegin()->first; } @@ -153,7 +82,7 @@ namespace Checkpoints { if (!fEnabled) return NULL; - const MapCheckpoints& checkpoints = *Checkpoints().mapCheckpoints; + const MapCheckpoints& checkpoints = *Params().Checkpoints().mapCheckpoints; BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints) { diff --git a/src/checkpoints.h b/src/checkpoints.h index fca046559a..b5b620fa6b 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -5,16 +5,26 @@ #ifndef BITCOIN_CHECKPOINT_H #define BITCOIN_CHECKPOINT_H +#include "uint256.h" + #include <map> class CBlockIndex; -class uint256; /** Block-chain checkpoints are compiled-in sanity checks. * They are updated every release or three. */ namespace Checkpoints { +typedef std::map<int, uint256> MapCheckpoints; + +struct CCheckpointData { + const MapCheckpoints *mapCheckpoints; + int64_t nTimeLastCheckpoint; + int64_t nTransactionsLastCheckpoint; + double fTransactionsPerDay; +}; + // Returns true if block passes checkpoint checks bool CheckBlock(int nHeight, const uint256& hash); diff --git a/src/core.cpp b/src/core.cpp index 380b1c38e0..6a7a9ff378 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -7,8 +7,6 @@ #include "tinyformat.h" -#include <boost/foreach.hpp> - std::string COutPoint::ToString() const { return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n); @@ -113,10 +111,10 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) { CAmount CTransaction::GetValueOut() const { CAmount nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) + for (std::vector<CTxOut>::const_iterator it(vout.begin()); it != vout.end(); ++it) { - nValueOut += txout.nValue; - if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + nValueOut += it->nValue; + if (!MoneyRange(it->nValue) || !MoneyRange(nValueOut)) throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); } return nValueOut; @@ -139,10 +137,9 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const // risk encouraging people to create junk outputs to redeem later. if (nTxSize == 0) nTxSize = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); - - BOOST_FOREACH(const CTxIn& txin, vin) + for (std::vector<CTxIn>::const_iterator it(vin.begin()); it != vin.end(); ++it) { - unsigned int offset = 41U + std::min(110U, (unsigned int)txin.scriptSig.size()); + unsigned int offset = 41U + std::min(110U, (unsigned int)it->scriptSig.size()); if (nTxSize > offset) nTxSize -= offset; } @@ -263,8 +260,8 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const */ vMerkleTree.clear(); vMerkleTree.reserve(vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes. - BOOST_FOREACH(const CTransaction& tx, vtx) - vMerkleTree.push_back(tx.GetHash()); + for (std::vector<CTransaction>::const_iterator it(vtx.begin()); it != vtx.end(); ++it) + vMerkleTree.push_back(it->GetHash()); int j = 0; bool mutated = false; for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) @@ -307,12 +304,12 @@ uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMer { if (nIndex == -1) return 0; - BOOST_FOREACH(const uint256& otherside, vMerkleBranch) + for (std::vector<uint256>::const_iterator it(vMerkleBranch.begin()); it != vMerkleBranch.end(); ++it) { if (nIndex & 1) - hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); + hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash)); else - hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); + hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it)); nIndex >>= 1; } return hash; diff --git a/src/core.h b/src/core.h index a348293578..a024dad740 100644 --- a/src/core.h +++ b/src/core.h @@ -61,19 +61,6 @@ public: std::string ToString() const; }; -/** An inpoint - a combination of a transaction and an index n into its vin */ -class CInPoint -{ -public: - const CTransaction* ptx; - uint32_t n; - - CInPoint() { SetNull(); } - CInPoint(const CTransaction* ptxIn, uint32_t nIn) { ptx = ptxIn; n = nIn; } - void SetNull() { ptx = NULL; n = (uint32_t) -1; } - bool IsNull() const { return (ptx == NULL && n == (uint32_t) -1); } -}; - /** An input of a transaction. It contains the location of the previous * transaction's output that it claims and a signature that matches the * output's public key. diff --git a/src/init.cpp b/src/init.cpp index ce72b415aa..8b2a32532c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -47,6 +47,7 @@ using namespace std; #ifdef ENABLE_WALLET CWallet* pwalletMain = NULL; #endif +bool fFeeEstimatesInitialized = false; #ifdef WIN32 // Win32 LevelDB doesn't use filedescriptors, and the ones used for @@ -119,6 +120,10 @@ void Shutdown() if (!lockShutdown) return; + /// Note: Shutdown() must be able to handle cases in which AppInit2() failed part of the way, + /// for example if the data directory was found to be locked. + /// Be sure that anything that writes files or flushes caches only does this if the respective + /// module was initialized. RenameThread("bitcoin-shutoff"); mempool.AddTransactionsUpdated(1); StopRPCThreads(); @@ -130,6 +135,7 @@ void Shutdown() StopNode(); UnregisterNodeSignals(GetNodeSignals()); + if (fFeeEstimatesInitialized) { boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_fileout(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION); @@ -137,6 +143,7 @@ void Shutdown() mempool.WriteFeeEstimates(est_fileout); else LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string()); + fFeeEstimatesInitialized = false; } { @@ -163,7 +170,7 @@ void Shutdown() #ifndef WIN32 boost::filesystem::remove(GetPidFile()); #endif - UnregisterAllWallets(); + UnregisterAllValidationInterfaces(); #ifdef ENABLE_WALLET delete pwalletMain; pwalletMain = NULL; @@ -967,7 +974,7 @@ bool AppInit2(boost::thread_group& threadGroup) // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way around). - if (!mapBlockIndex.empty() && chainActive.Genesis() == NULL) + if (!mapBlockIndex.empty() && mapBlockIndex.count(Params().HashGenesisBlock()) == 0) return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); // Initialize the block index (no-op if non-empty database was already loaded) @@ -1059,6 +1066,7 @@ bool AppInit2(boost::thread_group& threadGroup) // Allowed to fail as this file IS missing on first startup. if (est_filein) mempool.ReadFeeEstimates(est_filein); + fFeeEstimatesInitialized = true; // ********************************************************* Step 8: load wallet #ifdef ENABLE_WALLET @@ -1146,7 +1154,7 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf("%s", strErrors.str()); LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); - RegisterWallet(pwalletMain); + RegisterValidationInterface(pwalletMain); CBlockIndex *pindexRescan = chainActive.Tip(); if (GetBoolArg("-rescan", false)) @@ -1215,22 +1223,7 @@ bool AppInit2(boost::thread_group& threadGroup) } threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); - // ********************************************************* Step 10: load peers - - uiInterface.InitMessage(_("Loading addresses...")); - - nStart = GetTimeMillis(); - - { - CAddrDB adb; - if (!adb.Read(addrman)) - LogPrintf("Invalid or missing peers.dat; recreating\n"); - } - - LogPrintf("Loaded %i addresses from peers.dat %dms\n", - addrman.size(), GetTimeMillis() - nStart); - - // ********************************************************* Step 11: start node + // ********************************************************* Step 10: start node if (!CheckDiskSpace()) return false; @@ -1259,7 +1252,7 @@ bool AppInit2(boost::thread_group& threadGroup) GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", -1)); #endif - // ********************************************************* Step 12: finished + // ********************************************************* Step 11: finished uiInterface.InitMessage(_("Done loading")); diff --git a/src/key.cpp b/src/key.cpp index c2251b4f2a..079e2c6540 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -179,19 +179,17 @@ public: BN_clear_free(&bn); } - void GetPrivKey(CPrivKey &privkey, bool fCompressed) { + int GetPrivKeySize(bool fCompressed) { EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); - int nSize = i2d_ECPrivateKey(pkey, NULL); - assert(nSize); - privkey.resize(nSize); - unsigned char* pbegin = &privkey[0]; - int nSize2 = i2d_ECPrivateKey(pkey, &pbegin); - assert(nSize == nSize2); + return i2d_ECPrivateKey(pkey, NULL); + } + int GetPrivKey(unsigned char* privkey, bool fCompressed) { + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + return i2d_ECPrivateKey(pkey, &privkey); } - bool SetPrivKey(const CPrivKey &privkey, bool fSkipCheck=false) { - const unsigned char* pbegin = &privkey[0]; - if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) { + bool SetPrivKey(const unsigned char* privkey, size_t size, bool fSkipCheck=false) { + if (d2i_ECPrivateKey(&pkey, &privkey, size)) { if(fSkipCheck) return true; @@ -424,7 +422,7 @@ bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { return false; #else CECKey key; - if (!key.SetPrivKey(privkey)) + if (!key.SetPrivKey(&privkey[0], privkey.size())) return false; key.GetSecretBytes(vch); #endif @@ -436,16 +434,21 @@ bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { CPrivKey CKey::GetPrivKey() const { assert(fValid); CPrivKey privkey; + int privkeylen, ret; #ifdef USE_SECP256K1 privkey.resize(279); - int privkeylen = 279; - int ret = secp256k1_ecdsa_privkey_export(begin(), (unsigned char*)&privkey[0], &privkeylen, fCompressed); + privkeylen = 279; + ret = secp256k1_ecdsa_privkey_export(begin(), (unsigned char*)&privkey[0], &privkeylen, fCompressed); assert(ret); privkey.resize(privkeylen); #else CECKey key; key.SetSecretBytes(vch); - key.GetPrivKey(privkey, fCompressed); + privkeylen = key.GetPrivKeySize(fCompressed); + assert(privkeylen); + privkey.resize(privkeylen); + ret = key.GetPrivKey(&privkey[0], fCompressed); + assert(ret == (int)privkey.size()); #endif return privkey; } @@ -517,7 +520,7 @@ bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { return false; #else CECKey key; - if (!key.SetPrivKey(privkey, fSkipCheck)) + if (!key.SetPrivKey(&privkey[0], privkey.size(), fSkipCheck)) return false; key.GetSecretBytes(vch); #endif diff --git a/src/keystore.cpp b/src/keystore.cpp index 98bc0e9e28..755defa26d 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -67,6 +67,13 @@ bool CBasicKeyStore::AddWatchOnly(const CScript &dest) return true; } +bool CBasicKeyStore::RemoveWatchOnly(const CScript &dest) +{ + LOCK(cs_KeyStore); + setWatchOnly.erase(dest); + return true; +} + bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const { LOCK(cs_KeyStore); diff --git a/src/keystore.h b/src/keystore.h index 3b49e282d9..d3478f7672 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -40,6 +40,7 @@ public: // Support for Watch-only addresses virtual bool AddWatchOnly(const CScript &dest) =0; + virtual bool RemoveWatchOnly(const CScript &dest) =0; virtual bool HaveWatchOnly(const CScript &dest) const =0; virtual bool HaveWatchOnly() const =0; }; @@ -98,6 +99,7 @@ public: virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; virtual bool AddWatchOnly(const CScript &dest); + virtual bool RemoveWatchOnly(const CScript &dest); virtual bool HaveWatchOnly(const CScript &dest) const; virtual bool HaveWatchOnly() const; }; diff --git a/src/main.cpp b/src/main.cpp index de3876944e..630891bd3f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,7 @@ CCriticalSection cs_main; BlockMap mapBlockIndex; CChain chainActive; +CBlockIndex *pindexBestHeader = NULL; int64_t nTimeBestReceived = 0; CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; @@ -51,19 +52,12 @@ bool fTxIndex = false; bool fIsBareMultisigStd = true; unsigned int nCoinCacheSize = 5000; + /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ CFeeRate minRelayTxFee = CFeeRate(1000); CTxMemPool mempool(::minRelayTxFee); -struct COrphanBlock { - uint256 hashBlock; - uint256 hashPrev; - vector<unsigned char> vchBlock; -}; -map<uint256, COrphanBlock*> mapOrphanBlocks; -multimap<uint256, COrphanBlock*> mapOrphanBlocksByPrev; - struct COrphanTx { CTransaction tx; NodeId fromPeer; @@ -105,7 +99,11 @@ namespace { // The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS or better that are at least // as good as our current tip. Entries may be failed, though. - set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; + 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. + multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked; CCriticalSection cs_LastBlockFile; CBlockFileInfo infoLastBlockFile; @@ -125,11 +123,10 @@ namespace { // Protected by cs_main. struct QueuedBlock { uint256 hash; + CBlockIndex *pindex; // Optional. int64_t nTime; // Time of "getdata" request in microseconds. - int nQueuedBefore; // Number of blocks in flight at the time of request. }; map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight; - map<uint256, pair<NodeId, list<uint256>::iterator> > mapBlocksToDownload; } // anon namespace @@ -159,25 +156,25 @@ struct CMainSignals { } // anon namespace -void RegisterWallet(CWalletInterface* pwalletIn) { - g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2)); - g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.UpdatedTransaction.connect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.SetBestChain.connect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); - g_signals.Inventory.connect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1)); - g_signals.Broadcast.connect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn)); +void RegisterValidationInterface(CValidationInterface* pwalletIn) { + g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); + g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); + g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); + g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); + g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); + g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); } -void UnregisterWallet(CWalletInterface* pwalletIn) { - g_signals.Broadcast.disconnect(boost::bind(&CWalletInterface::ResendWalletTransactions, pwalletIn)); - g_signals.Inventory.disconnect(boost::bind(&CWalletInterface::Inventory, pwalletIn, _1)); - g_signals.SetBestChain.disconnect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); - g_signals.UpdatedTransaction.disconnect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.EraseTransaction.disconnect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.SyncTransaction.disconnect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2)); +void UnregisterValidationInterface(CValidationInterface* pwalletIn) { + g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); + g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); + g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); + g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); + g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); + g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); } -void UnregisterAllWallets() { +void UnregisterAllValidationInterfaces() { g_signals.Broadcast.disconnect_all_slots(); g_signals.Inventory.disconnect_all_slots(); g_signals.SetBestChain.disconnect_all_slots(); @@ -220,22 +217,24 @@ struct CNodeState { CBlockIndex *pindexBestKnownBlock; // The hash of the last unknown block this peer has announced. uint256 hashLastUnknownBlock; + // The last full block we both have. + CBlockIndex *pindexLastCommonBlock; + // Whether we've started headers synchronization with this peer. + bool fSyncStarted; + // Since when we're stalling block download progress (in microseconds), or 0. + int64_t nStallingSince; list<QueuedBlock> vBlocksInFlight; int nBlocksInFlight; - list<uint256> vBlocksToDownload; - int nBlocksToDownload; - int64_t nLastBlockReceive; - int64_t nLastBlockProcess; CNodeState() { nMisbehavior = 0; fShouldBan = false; pindexBestKnownBlock = NULL; hashLastUnknownBlock = uint256(0); - nBlocksToDownload = 0; + pindexLastCommonBlock = NULL; + fSyncStarted = false; + nStallingSince = 0; nBlocksInFlight = 0; - nLastBlockReceive = 0; - nLastBlockProcess = 0; } }; @@ -266,64 +265,37 @@ void FinalizeNode(NodeId nodeid) { LOCK(cs_main); CNodeState *state = State(nodeid); + if (state->fSyncStarted) + nSyncStarted--; + BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) mapBlocksInFlight.erase(entry.hash); - BOOST_FOREACH(const uint256& hash, state->vBlocksToDownload) - mapBlocksToDownload.erase(hash); EraseOrphansFor(nodeid); mapNodeState.erase(nodeid); } // Requires cs_main. -void MarkBlockAsReceived(const uint256 &hash, NodeId nodeFrom = -1) { - map<uint256, pair<NodeId, list<uint256>::iterator> >::iterator itToDownload = mapBlocksToDownload.find(hash); - if (itToDownload != mapBlocksToDownload.end()) { - CNodeState *state = State(itToDownload->second.first); - state->vBlocksToDownload.erase(itToDownload->second.second); - state->nBlocksToDownload--; - mapBlocksToDownload.erase(itToDownload); - } - +void MarkBlockAsReceived(const uint256& hash) { map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); state->vBlocksInFlight.erase(itInFlight->second.second); state->nBlocksInFlight--; - if (itInFlight->second.first == nodeFrom) - state->nLastBlockReceive = GetTimeMicros(); + state->nStallingSince = 0; mapBlocksInFlight.erase(itInFlight); } } // Requires cs_main. -bool AddBlockToQueue(NodeId nodeid, const uint256 &hash) { - if (mapBlocksToDownload.count(hash) || mapBlocksInFlight.count(hash)) - return false; - - CNodeState *state = State(nodeid); - if (state == NULL) - return false; - - list<uint256>::iterator it = state->vBlocksToDownload.insert(state->vBlocksToDownload.end(), hash); - state->nBlocksToDownload++; - if (state->nBlocksToDownload > 5000) - Misbehaving(nodeid, 10); - mapBlocksToDownload[hash] = std::make_pair(nodeid, it); - return true; -} - -// Requires cs_main. -void MarkBlockAsInFlight(NodeId nodeid, const uint256 &hash) { +void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, CBlockIndex *pindex = NULL) { CNodeState *state = State(nodeid); assert(state != NULL); // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); - QueuedBlock newentry = {hash, GetTimeMicros(), state->nBlocksInFlight}; - if (state->nBlocksInFlight == 0) - state->nLastBlockReceive = newentry.nTime; // Reset when a first request is sent. + QueuedBlock newentry = {hash, pindex, GetTimeMicros()}; list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); state->nBlocksInFlight++; mapBlocksInFlight[hash] = std::make_pair(nodeid, it); @@ -362,6 +334,104 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { } } +/** Find the last common ancestor two blocks have. + * Both pa and pb must be non-NULL. */ +CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { + if (pa->nHeight > pb->nHeight) { + pa = pa->GetAncestor(pb->nHeight); + } else if (pb->nHeight > pa->nHeight) { + pb = pb->GetAncestor(pa->nHeight); + } + + while (pa != pb && pa && pb) { + pa = pa->pprev; + pb = pb->pprev; + } + + // Eventually all chain branches meet at the genesis block. + assert(pa == pb); + return pa; +} + +/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has + * at most count entries. */ +void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller) { + if (count == 0) + return; + + vBlocks.reserve(vBlocks.size() + count); + CNodeState *state = State(nodeid); + assert(state != NULL); + + // Make sure pindexBestKnownBlock is up to date, we'll need it. + ProcessBlockAvailability(nodeid); + + if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) { + // This peer has nothing interesting. + return; + } + + if (state->pindexLastCommonBlock == NULL) { + // Bootstrap quickly by guessing a parent of our best tip is the forking point. + // Guessing wrong in either direction is not a problem. + state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; + } + + // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor + // of their current tip anymore. Go back enough to fix that. + state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); + if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) + return; + + std::vector<CBlockIndex*> vToFetch; + CBlockIndex *pindexWalk = state->pindexLastCommonBlock; + // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last + // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to + // download that next block if the window were 1 larger. + int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW; + int nMaxHeight = std::min<int>(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); + NodeId waitingfor = -1; + while (pindexWalk->nHeight < nMaxHeight) { + // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards + // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive + // as iterating over ~100 CBlockIndex* entries anyway. + int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max<int>(count - vBlocks.size(), 128)); + vToFetch.resize(nToFetch); + pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch); + vToFetch[nToFetch - 1] = pindexWalk; + for (unsigned int i = nToFetch - 1; i > 0; i--) { + vToFetch[i - 1] = vToFetch[i]->pprev; + } + + // Iterate over those blocks in vToFetch (in forward direction), adding the ones that + // are not yet downloaded and not in flight to vBlocks. In the mean time, update + // pindexLastCommonBlock as long as all ancestors are already downloaded. + BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { + if (pindex->nStatus & BLOCK_HAVE_DATA) { + if (pindex->nChainTx) + state->pindexLastCommonBlock = pindex; + } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { + // The block is not already downloaded, and not yet in flight. + if (pindex->nHeight > nWindowEnd) { + // We reached the end of the window. + if (vBlocks.size() == 0 && waitingfor != nodeid) { + // We aren't able to fetch anything, but we would be if the download window was one larger. + nodeStaller = waitingfor; + } + return; + } + vBlocks.push_back(pindex); + if (vBlocks.size() == count) { + return; + } + } else if (waitingfor == -1) { + // This is the first already-in-flight block. + waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; + } + } + } +} + } // anon namespace bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { @@ -371,6 +441,11 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { return false; stats.nMisbehavior = state->nMisbehavior; stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; + stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; + BOOST_FOREACH(const QueuedBlock& queue, state->vBlocksInFlight) { + if (queue.pindex) + stats.vHeightInFlight.push_back(queue.pindex->nHeight); + } return true; } @@ -1086,46 +1161,6 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return true; } -uint256 static GetOrphanRoot(const uint256& hash) -{ - map<uint256, COrphanBlock*>::iterator it = mapOrphanBlocks.find(hash); - if (it == mapOrphanBlocks.end()) - return hash; - - // Work back to the first block in the orphan chain - do { - map<uint256, COrphanBlock*>::iterator it2 = mapOrphanBlocks.find(it->second->hashPrev); - if (it2 == mapOrphanBlocks.end()) - return it->first; - it = it2; - } while(true); -} - -// Remove a random orphan block (which does not have any dependent orphans). -void static PruneOrphanBlocks() -{ - if (mapOrphanBlocksByPrev.size() <= (size_t)std::max((int64_t)0, GetArg("-maxorphanblocks", DEFAULT_MAX_ORPHAN_BLOCKS))) - return; - - // Pick a random orphan block. - int pos = insecure_rand() % mapOrphanBlocksByPrev.size(); - std::multimap<uint256, COrphanBlock*>::iterator it = mapOrphanBlocksByPrev.begin(); - while (pos--) it++; - - // As long as this block has other orphans depending on it, move to one of those successors. - do { - std::multimap<uint256, COrphanBlock*>::iterator it2 = mapOrphanBlocksByPrev.find(it->second->hashBlock); - if (it2 == mapOrphanBlocksByPrev.end()) - break; - it = it2; - } while(1); - - uint256 hash = it->second->hashBlock; - delete it->second; - mapOrphanBlocksByPrev.erase(it); - mapOrphanBlocks.erase(hash); -} - CAmount GetBlockValue(int nHeight, const CAmount& nFees) { int64_t nSubsidy = 50 * COIN; @@ -1178,14 +1213,9 @@ void CheckForkWarningConditions() { if (!fLargeWorkForkFound) { - std::string strCmd = GetArg("-alertnotify", ""); - if (!strCmd.empty()) - { - std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + - pindexBestForkBase->phashBlock->ToString() + std::string("'"); - boost::replace_all(strCmd, "%s", warning); - boost::thread t(runCommand, strCmd); // thread runs free - } + std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + + pindexBestForkBase->phashBlock->ToString() + std::string("'"); + CAlert::Notify(warning, true); } if (pindexBestForkTip) { @@ -1289,7 +1319,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); - setBlockIndexValid.erase(pindex); + setBlockIndexCandidates.erase(pindex); InvalidChainFound(pindex); } } @@ -1669,11 +1699,6 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C if (fJustCheck) return true; - // Correct transaction counts. - pindex->nTx = block.vtx.size(); - if (pindex->pprev) - pindex->nChainTx = pindex->pprev->nChainTx + block.vtx.size(); - // Write undo information to disk if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) { @@ -1753,7 +1778,8 @@ void static UpdateTip(CBlockIndex *pindexNew) { cvBlockChange.notify_all(); // Check the version of the last 100 blocks to see if we need to upgrade: - if (!IsInitialBlockDownload()) + static bool fWarned = false; + if (!IsInitialBlockDownload() && !fWarned) { int nUpgraded = 0; const CBlockIndex* pindex = chainActive.Tip(); @@ -1766,8 +1792,12 @@ void static UpdateTip(CBlockIndex *pindexNew) { if (nUpgraded > 0) LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", 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: strMiscWarning = _("Warning: This version is obsolete, upgrade required!"); + CAlert::Notify(strMiscWarning, true); + fWarned = true; + } } } @@ -1889,8 +1919,8 @@ static CBlockIndex* FindMostWorkChain() { // Find the best candidate header. { - std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexValid.rbegin(); - if (it == setBlockIndexValid.rend()) + std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexCandidates.rbegin(); + if (it == setBlockIndexCandidates.rend()) return NULL; pindexNew = *it; } @@ -1900,6 +1930,8 @@ 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) @@ -1907,10 +1939,10 @@ static CBlockIndex* FindMostWorkChain() { CBlockIndex *pindexFailed = pindexNew; while (pindexTest != pindexFailed) { pindexFailed->nStatus |= BLOCK_FAILED_CHILD; - setBlockIndexValid.erase(pindexFailed); + setBlockIndexCandidates.erase(pindexFailed); pindexFailed = pindexFailed->pprev; } - setBlockIndexValid.erase(pindexTest); + setBlockIndexCandidates.erase(pindexTest); fInvalidAncestor = true; break; } @@ -1937,12 +1969,20 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo // Build list of new blocks to connect. std::vector<CBlockIndex*> vpindexToConnect; - vpindexToConnect.reserve(pindexMostWork->nHeight - (pindexFork ? pindexFork->nHeight : -1)); - CBlockIndex *pindexIter = pindexMostWork; - while (pindexIter && pindexIter != pindexFork) { + bool fContinue = true; + int nHeight = pindexFork ? pindexFork->nHeight : -1; + while (fContinue && nHeight != pindexMostWork->nHeight) { + // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need + // a few blocks along the way. + int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); + vpindexToConnect.clear(); + vpindexToConnect.reserve(nTargetHeight - nHeight); + CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); + while (pindexIter && pindexIter->nHeight != nHeight) { vpindexToConnect.push_back(pindexIter); pindexIter = pindexIter->pprev; } + nHeight = nTargetHeight; // Connect new blocks. BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { @@ -1953,27 +1993,30 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo InvalidChainFound(vpindexToConnect.back()); state = CValidationState(); fInvalidFound = true; + fContinue = false; break; } else { // A system error occurred (disk space, database error, ...). return false; } } else { - // Delete all entries in setBlockIndexValid that are worse than our new current block. + // Delete all entries in setBlockIndexCandidates that are worse than our new current block. // Note that we can't delete the current block itself, as we may need to return to it later in case a // reorganization to a better block fails. - std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexValid.begin(); - while (setBlockIndexValid.value_comp()(*it, chainActive.Tip())) { - setBlockIndexValid.erase(it++); + std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexCandidates.begin(); + while (setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) { + setBlockIndexCandidates.erase(it++); } - // Either the current tip or a successor of it we're working towards is left in setBlockIndexValid. - assert(!setBlockIndexValid.empty()); + // Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates. + assert(!setBlockIndexCandidates.empty()); if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { // We're in a better position than we were. Return temporarily to release the lock. + fContinue = false; break; } } } + } // Callbacks/notifications for a new best chain. if (fInvalidFound) @@ -2032,7 +2075,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { return true; } -CBlockIndex* AddToBlockIndex(CBlockHeader& block) +CBlockIndex* AddToBlockIndex(const CBlockHeader& block) { // Check for duplicate uint256 hash = block.GetHash(); @@ -2043,10 +2086,10 @@ CBlockIndex* AddToBlockIndex(CBlockHeader& block) // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(block); assert(pindexNew); - { - LOCK(cs_nBlockSequenceId); - pindexNew->nSequenceId = nBlockSequenceId++; - } + // We assign the sequence id to blocks only when the full data is available, + // to avoid miners withholding blocks but broadcasting headers, to get a + // competitive advantage. + pindexNew->nSequenceId = 0; BlockMap::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); @@ -2058,6 +2101,11 @@ CBlockIndex* AddToBlockIndex(CBlockHeader& block) } pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork(); pindexNew->RaiseValidity(BLOCK_VALID_TREE); + if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) + pindexBestHeader = pindexNew; + + // Ok if it fails, we'll download the header again next time. + pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)); return pindexNew; } @@ -2066,30 +2114,45 @@ CBlockIndex* AddToBlockIndex(CBlockHeader& block) bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos) { pindexNew->nTx = block.vtx.size(); - if (pindexNew->pprev) { - // Not the genesis block. - if (pindexNew->pprev->nChainTx) { - // This parent's block's total number transactions is known, so compute outs. - pindexNew->nChainTx = pindexNew->pprev->nChainTx + pindexNew->nTx; - } else { - // The total number of transactions isn't known yet. - // We will compute it when the block is connected. - pindexNew->nChainTx = 0; - } - } else { - // Genesis block. - pindexNew->nChainTx = pindexNew->nTx; - } + pindexNew->nChainTx = 0; pindexNew->nFile = pos.nFile; pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; pindexNew->nStatus |= BLOCK_HAVE_DATA; + pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); + { + LOCK(cs_nBlockSequenceId); + pindexNew->nSequenceId = nBlockSequenceId++; + } - if (pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS)) - setBlockIndexValid.insert(pindexNew); - - if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) - return state.Abort("Failed to write block index"); + if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { + // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. + deque<CBlockIndex*> queue; + queue.push_back(pindexNew); + + // Recursively process any descendant blocks that now may be eligible to be connected. + while (!queue.empty()) { + CBlockIndex *pindex = queue.front(); + queue.pop_front(); + pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + setBlockIndexCandidates.insert(pindex); + std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex); + while (range.first != range.second) { + std::multimap<CBlockIndex*, CBlockIndex*>::iterator it = range.first; + queue.push_back(it->second); + range.first++; + mapBlocksUnlinked.erase(it); + } + if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))) + return state.Abort("Failed to write block index"); + } + } else { + if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) { + mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); + } + if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) + return state.Abort("Failed to write block index"); + } return true; } @@ -2205,12 +2268,31 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot) { - // These are checks that are independent of context - // that can be verified before saving an orphan block. + // These are checks that are independent of context. if (!CheckBlockHeader(block, state, fCheckPOW)) return false; + // Check the merkle root. + if (fCheckMerkleRoot) { + bool mutated; + uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated); + if (block.hashMerkleRoot != hashMerkleRoot2) + return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), + REJECT_INVALID, "bad-txnmrklroot", true); + + // Check for merkle tree malleability (CVE-2012-2459): repeating sequences + // of transactions in a block without affecting the merkle root of a block, + // while still invalidating it. + if (mutated) + return state.DoS(100, error("CheckBlock() : duplicate transaction"), + REJECT_INVALID, "bad-txns-duplicate", true); + } + + // All potential-corruption validation must be done before we do any + // transaction validation, as otherwise we may mark the header as invalid + // because we receive the wrong transactions for it. + // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CheckBlock() : size limits failed"), @@ -2230,15 +2312,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo if (!CheckTransaction(tx, state)) return error("CheckBlock() : CheckTransaction failed"); - // Check for merkle tree malleability (CVE-2012-2459): repeating sequences - // of transactions in a block without affecting the merkle root of a block, - // while still invalidating it. - bool mutated; - uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated); - if (mutated) - return state.DoS(100, error("CheckBlock() : duplicate transaction"), - REJECT_INVALID, "bad-txns-duplicate", true); - unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) { @@ -2248,15 +2321,10 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); - // Check merkle root - if (fCheckMerkleRoot && block.hashMerkleRoot != hashMerkleRoot2) - return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), - REJECT_INVALID, "bad-txnmrklroot", true); - return true; } -bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) +bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) { AssertLockHeld(cs_main); // Check for duplicate @@ -2264,26 +2332,13 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex BlockMap::iterator miSelf = mapBlockIndex.find(hash); CBlockIndex *pindex = NULL; if (miSelf != mapBlockIndex.end()) { + // Block header is already known. pindex = miSelf->second; + if (ppindex) + *ppindex = pindex; if (pindex->nStatus & BLOCK_FAILED_MASK) return state.Invalid(error("%s : block is marked invalid", __func__), 0, "duplicate"); - } - - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); - if (pcheckpoint && block.hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) - { - // Extra checks to prevent "fill up memory by spamming with bogus blocks" - int64_t deltaTime = block.GetBlockTime() - pcheckpoint->GetBlockTime(); - if (deltaTime < 0) - { - return state.DoS(100, error("%s : block with timestamp before last checkpoint", __func__), - REJECT_CHECKPOINT, "time-too-old"); - } - if (!CheckMinWork(block.nBits, pcheckpoint->nBits, deltaTime)) - { - return state.DoS(100, error("%s : block with too little proof-of-work", __func__), - REJECT_INVALID, "bad-diffbits"); - } + return true; } // Get prev block index @@ -2344,6 +2399,12 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (!AcceptBlockHeader(block, state, &pindex)) return false; + if (pindex->nStatus & BLOCK_HAVE_DATA) { + // 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; + } + if (!CheckBlock(block, state)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; @@ -2456,93 +2517,26 @@ void CBlockIndex::BuildSkip() pskip = pprev->GetAncestor(GetSkipHeight(nHeight)); } -void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) -{ - AssertLockHeld(cs_main); - // Filter out duplicate requests - if (pindexBegin == pnode->pindexLastGetBlocksBegin && hashEnd == pnode->hashLastGetBlocksEnd) - return; - pnode->pindexLastGetBlocksBegin = pindexBegin; - pnode->hashLastGetBlocksEnd = hashEnd; - - pnode->PushMessage("getblocks", chainActive.GetLocator(pindexBegin), hashEnd); -} - bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { - // Check for duplicate - uint256 hash = pblock->GetHash(); - - { - LOCK(cs_main); - if (mapBlockIndex.count(hash)) - return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString()), 0, "duplicate"); - if (mapOrphanBlocks.count(hash)) - return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString()), 0, "duplicate"); - // Preliminary checks - if (!CheckBlock(*pblock, state)) - return error("ProcessBlock() : CheckBlock FAILED"); + bool checked = CheckBlock(*pblock, state); - // If we don't already have its previous block (with full data), shunt it off to holding area until we get it - BlockMap::iterator it = mapBlockIndex.find(pblock->hashPrevBlock); - if (pblock->hashPrevBlock != 0 && (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA))) { - LogPrintf("ProcessBlock: ORPHAN BLOCK %lu, prev=%s\n", (unsigned long)mapOrphanBlocks.size(), pblock->hashPrevBlock.ToString()); - - // Accept orphans as long as there is a node to request its parents from - if (pfrom) { - PruneOrphanBlocks(); - COrphanBlock* pblock2 = new COrphanBlock(); - { - CDataStream ss(SER_DISK, CLIENT_VERSION); - ss << *pblock; - pblock2->vchBlock = std::vector<unsigned char>(ss.begin(), ss.end()); - } - pblock2->hashBlock = hash; - pblock2->hashPrev = pblock->hashPrevBlock; - mapOrphanBlocks.insert(make_pair(hash, pblock2)); - mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrev, pblock2)); - - // Ask this guy to fill in what we're missing - PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(hash)); + LOCK(cs_main); + MarkBlockAsReceived(pblock->GetHash()); + if (!checked) { + return error("ProcessBlock() : CheckBlock FAILED"); } - return true; - } - // Store to disk - CBlockIndex *pindex = NULL; - bool ret = AcceptBlock(*pblock, state, &pindex, dbp); - if (!ret) - return error("ProcessBlock() : AcceptBlock FAILED"); - - // Recursively process any orphan blocks that depended on this one - vector<uint256> vWorkQueue; - vWorkQueue.push_back(hash); - for (unsigned int i = 0; i < vWorkQueue.size(); i++) - { - uint256 hashPrev = vWorkQueue[i]; - for (multimap<uint256, COrphanBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); - mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); - ++mi) - { - CBlock block; - { - CDataStream ss(mi->second->vchBlock, SER_DISK, CLIENT_VERSION); - ss >> block; - } - block.BuildMerkleTree(); - // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned) - CValidationState stateDummy; - CBlockIndex *pindexChild = NULL; - if (AcceptBlock(block, stateDummy, &pindexChild)) - vWorkQueue.push_back(mi->second->hashBlock); - mapOrphanBlocks.erase(mi->second->hashBlock); - delete mi->second; + // Store to disk + CBlockIndex *pindex = NULL; + bool ret = AcceptBlock(*pblock, state, &pindex, dbp); + if (pindex && pfrom) { + mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); } - mapOrphanBlocksByPrev.erase(hashPrev); - } - + if (!ret) + return error("ProcessBlock() : AcceptBlock FAILED"); } if (!ActivateBestChain(state, pblock)) @@ -2808,13 +2802,26 @@ bool static LoadBlockIndexDB() { CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + pindex->GetBlockWork(); - pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; - if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) - setBlockIndexValid.insert(pindex); + if (pindex->nStatus & BLOCK_HAVE_DATA) { + if (pindex->pprev) { + if (pindex->pprev->nChainTx) { + pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; + } else { + pindex->nChainTx = 0; + mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); + } + } else { + pindex->nChainTx = pindex->nTx; + } + } + if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL)) + setBlockIndexCandidates.insert(pindex); if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindex; if (pindex->pprev) pindex->BuildSkip(); + if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) + pindexBestHeader = pindex; } // Load block file info @@ -2952,7 +2959,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth void UnloadBlockIndex() { mapBlockIndex.clear(); - setBlockIndexValid.clear(); + setBlockIndexCandidates.clear(); chainActive.SetTip(NULL); pindexBestInvalid = NULL; } @@ -3075,21 +3082,14 @@ void PrintBlockTree() bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) { + // Map of disk positions for blocks with unknown parent (only used for reindex) + static std::multimap<uint256, CDiskBlockPos> mapBlocksUnknownParent; int64_t nStart = GetTimeMillis(); int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); - uint64_t nStartByte = 0; - if (dbp) { - // (try to) skip already indexed part - CBlockFileInfo info; - if (pblocktree->ReadBlockFileInfo(dbp->nFile, info)) { - nStartByte = info.nSize; - blkdat.Seek(info.nSize); - } - } uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); @@ -3117,21 +3117,57 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) try { // read block uint64_t nBlockPos = blkdat.GetPos(); + if (dbp) + dbp->nPos = nBlockPos; blkdat.SetLimit(nBlockPos + nSize); + blkdat.SetPos(nBlockPos); CBlock block; blkdat >> block; nRewind = blkdat.GetPos(); - // process block - if (nBlockPos >= nStartByte) { + // detect out of order blocks, and store them for later + uint256 hash = block.GetHash(); + if (hash != Params().HashGenesisBlock() && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { + LogPrint("reindex", "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), + block.hashPrevBlock.ToString()); if (dbp) - dbp->nPos = nBlockPos; + mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); + continue; + } + + // process in case the block isn't known yet + if (mapBlockIndex.count(hash) == 0) { CValidationState state; if (ProcessBlock(state, NULL, &block, dbp)) nLoaded++; if (state.IsError()) break; } + + // Recursively process earlier encountered successors of this block + deque<uint256> queue; + queue.push_back(hash); + while (!queue.empty()) { + uint256 head = queue.front(); + queue.pop_front(); + std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head); + while (range.first != range.second) { + std::multimap<uint256, CDiskBlockPos>::iterator it = range.first; + if (ReadBlockFromDisk(block, it->second)) + { + LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), + head.ToString()); + CValidationState dummy; + if (ProcessBlock(dummy, NULL, &block, &it->second)) + { + nLoaded++; + queue.push_back(block.GetHash()); + } + } + range.first++; + mapBlocksUnknownParent.erase(it); + } + } } catch (std::exception &e) { LogPrintf("%s : Deserialize or I/O error - %s", __func__, e.what()); } @@ -3226,8 +3262,7 @@ bool static AlreadyHave(const CInv& inv) pcoinsTip->HaveCoins(inv.hash); } case MSG_BLOCK: - return mapBlockIndex.count(inv.hash) || - mapOrphanBlocks.count(inv.hash); + return mapBlockIndex.count(inv.hash); } // Don't know what it is, just say we already got one return true; @@ -3375,10 +3410,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - { - LOCK(cs_main); - State(pfrom->GetId())->nLastBlockProcess = GetTimeMicros(); - } @@ -3587,6 +3618,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_main); + std::vector<CInv> vToFetch; + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; @@ -3597,19 +3630,30 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool fAlreadyHave = AlreadyHave(inv); LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); - if (!fAlreadyHave) { - if (!fImporting && !fReindex) { - if (inv.type == MSG_BLOCK) - AddBlockToQueue(pfrom->GetId(), inv.hash); - else - pfrom->AskFor(inv); - } - } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { - PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(inv.hash)); - } + if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK) + pfrom->AskFor(inv); - if (inv.type == MSG_BLOCK) + if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); + if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { + // First request the headers preceeding the announced block. In the normal fully-synced + // case where a new block is announced that succeeds the current tip (no reorganization), + // there are no such headers. + // Secondly, and only when we are close to being synced, we request the announced block directly, + // to avoid an extra round-trip. Note that we must *first* ask for the headers, so by the + // time the block arrives, the header chain leading up to it is already validated. Not + // doing this will result in the received block being rejected as an orphan in case it is + // not a direct successor. + pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash); + if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - Params().TargetSpacing() * 20) { + vToFetch.push_back(inv); + // Mark block as in flight already, even though the actual "getdata" message only goes out + // later (within the same cs_main lock, though). + MarkBlockAsInFlight(pfrom->GetId(), inv.hash); + } + LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); + } + } // Track requests for our stuff g_signals.Inventory(inv.hash); @@ -3619,6 +3663,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return error("send buffer size() = %u", pfrom->nSendSize); } } + + if (!vToFetch.empty()) + pfrom->PushMessage("getdata", vToFetch); } @@ -3706,8 +3753,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end vector<CBlock> vHeaders; - int nLimit = 2000; - LogPrint("net", "getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString()); + int nLimit = MAX_HEADERS_RESULTS; + LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) { vHeaders.push_back(pindex->GetBlockHeader()); @@ -3826,22 +3873,67 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } + else if (strCommand == "headers" && !fImporting && !fReindex) // Ignore headers received while importing + { + std::vector<CBlockHeader> headers; + + // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. + unsigned int nCount = ReadCompactSize(vRecv); + if (nCount > MAX_HEADERS_RESULTS) { + Misbehaving(pfrom->GetId(), 20); + return error("headers message size = %u", nCount); + } + headers.resize(nCount); + for (unsigned int n = 0; n < nCount; n++) { + vRecv >> headers[n]; + ReadCompactSize(vRecv); // ignore tx count; assume it is 0. + } + + LOCK(cs_main); + + if (nCount == 0) { + // Nothing interesting. Stop asking this peers for more headers. + return true; + } + + CBlockIndex *pindexLast = NULL; + BOOST_FOREACH(const CBlockHeader& header, headers) { + CValidationState state; + if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash()) { + Misbehaving(pfrom->GetId(), 20); + return error("non-continuous headers sequence"); + } + if (!AcceptBlockHeader(header, state, &pindexLast)) { + int nDoS; + if (state.IsInvalid(nDoS)) { + if (nDoS > 0) + Misbehaving(pfrom->GetId(), nDoS); + return error("invalid header received"); + } + } + } + + if (pindexLast) + UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); + + if (nCount == MAX_HEADERS_RESULTS && pindexLast) { + // Headers message had its maximum size; the peer may have more headers. + // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue + // from there instead. + LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); + pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256(0)); + } + } + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing { CBlock block; vRecv >> block; - LogPrint("net", "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id); - CInv inv(MSG_BLOCK, block.GetHash()); - pfrom->AddInventoryKnown(inv); + LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - { - LOCK(cs_main); - // Remember who we got this block from. - mapBlockSource[inv.hash] = pfrom->GetId(); - MarkBlockAsReceived(inv.hash, pfrom->GetId()); - } + pfrom->AddInventoryKnown(inv); CValidationState state; ProcessBlock(state, pfrom, &block); @@ -4323,9 +4415,18 @@ bool SendMessages(CNode* pto, bool fSendTrickle) state.rejects.clear(); // Start block sync - if (pto->fStartSync && !fImporting && !fReindex) { - pto->fStartSync = false; - PushGetBlocks(pto, chainActive.Tip(), uint256(0)); + if (pindexBestHeader == NULL) + pindexBestHeader = chainActive.Tip(); + bool fFetch = !pto->fInbound || (pindexBestHeader && (state.pindexLastCommonBlock ? state.pindexLastCommonBlock->nHeight : 0) + 144 > pindexBestHeader->nHeight); + if (!state.fSyncStarted && !pto->fClient && fFetch && !fImporting && !fReindex) { + // Only actively request headers from a single peer, unless we're close to today. + if (nSyncStarted == 0 || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { + state.fSyncStarted = true; + nSyncStarted++; + CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; + LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); + pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256(0)); + } } // Resend wallet transactions that haven't gotten in a block yet @@ -4384,35 +4485,35 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!vInv.empty()) pto->PushMessage("inv", vInv); - - // Detect stalled peers. Require that blocks are in flight, we haven't - // received a (requested) block in one minute, and that all blocks are - // in flight for over two minutes, since we first had a chance to - // process an incoming block. + // Detect whether we're stalling int64_t nNow = GetTimeMicros(); - if (!pto->fDisconnect && state.nBlocksInFlight && - state.nLastBlockReceive < state.nLastBlockProcess - BLOCK_DOWNLOAD_TIMEOUT*1000000 && - state.vBlocksInFlight.front().nTime < state.nLastBlockProcess - 2*BLOCK_DOWNLOAD_TIMEOUT*1000000) { - LogPrintf("Peer %s is stalling block download, disconnecting\n", state.name); + if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { + // Stalling only triggers when the block download window cannot move. During normal steady state, + // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection + // should only happen during initial block download. + LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); pto->fDisconnect = true; } - // Update knowledge of peer's block availability. - ProcessBlockAvailability(pto->GetId()); - // // Message: getdata (blocks) // vector<CInv> vGetData; - while (!pto->fDisconnect && state.nBlocksToDownload && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - uint256 hash = state.vBlocksToDownload.front(); - vGetData.push_back(CInv(MSG_BLOCK, hash)); - MarkBlockAsInFlight(pto->GetId(), hash); - LogPrint("net", "Requesting block %s peer=%d\n", hash.ToString(), pto->id); - if (vGetData.size() >= 1000) - { - pto->PushMessage("getdata", vGetData); - vGetData.clear(); + if (!pto->fDisconnect && !pto->fClient && fFetch && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + vector<CBlockIndex*> vToDownload; + NodeId staller = -1; + FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); + BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { + vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); + MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex); + LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), + pindex->nHeight, pto->id); + } + if (state.nBlocksInFlight == 0 && staller != -1) { + if (State(staller)->nStallingSince == 0) { + State(staller)->nStallingSince = nNow; + LogPrint("net", "Stall started peer=%d\n", staller); + } } } @@ -4519,12 +4620,6 @@ public: delete (*it1).second; mapBlockIndex.clear(); - // orphan blocks - std::map<uint256, COrphanBlock*>::iterator it2 = mapOrphanBlocks.begin(); - for (; it2 != mapOrphanBlocks.end(); it2++) - delete (*it2).second; - mapOrphanBlocks.clear(); - // orphan transactions mapOrphanTransactions.clear(); mapOrphanTransactionsByPrev.clear(); diff --git a/src/main.h b/src/main.h index cad7eebfb7..c0c1fb2707 100644 --- a/src/main.h +++ b/src/main.h @@ -72,9 +72,17 @@ static const int MAX_SCRIPTCHECK_THREADS = 16; /** -par default (number of script-checking threads, 0 = auto) */ static const int DEFAULT_SCRIPTCHECK_THREADS = 0; /** Number of blocks that can be requested at any given time from a single peer. */ -static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 128; -/** Timeout in seconds before considering a block download peer unresponsive. */ -static const unsigned int BLOCK_DOWNLOAD_TIMEOUT = 60; +static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16; +/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */ +static const unsigned int BLOCK_STALLING_TIMEOUT = 2; +/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends + * less than this number, we reached their tip. Changing this value is a protocol upgrade. */ +static const unsigned int MAX_HEADERS_RESULTS = 2000; +/** Size of the "block download window": how far ahead of our current height do we fetch? + * Larger windows tolerate larger download speed differences between peer, but increase the potential + * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning + * harder). We'll probably want to make this a per-peer adaptive value at some point. */ +static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; /** "reject" message codes **/ static const unsigned char REJECT_MALFORMED = 0x01; @@ -110,6 +118,9 @@ extern bool fIsBareMultisigStd; extern unsigned int nCoinCacheSize; extern CFeeRate minRelayTxFee; +// Best header we've seen so far (used for getheaders queries' starting points). +extern CBlockIndex *pindexBestHeader; + // Minimum disk space required - used in CheckDiskSpace() static const uint64_t nMinDiskSpace = 52428800; @@ -118,17 +129,17 @@ class CBlockTreeDB; class CTxUndo; class CScriptCheck; class CValidationState; -class CWalletInterface; +class CValidationInterface; struct CNodeStateStats; struct CBlockTemplate; /** Register a wallet to receive updates from core */ -void RegisterWallet(CWalletInterface* pwalletIn); +void RegisterValidationInterface(CValidationInterface* pwalletIn); /** Unregister a wallet from core */ -void UnregisterWallet(CWalletInterface* pwalletIn); +void UnregisterValidationInterface(CValidationInterface* pwalletIn); /** Unregister all wallets from core */ -void UnregisterAllWallets(); +void UnregisterAllValidationInterfaces(); /** Push an updated transaction to all registered wallets */ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL); @@ -137,8 +148,6 @@ void RegisterNodeSignals(CNodeSignals& nodeSignals); /** Unregister a network node */ void UnregisterNodeSignals(CNodeSignals& nodeSignals); -void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd); - /** Process an incoming block */ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); /** Check whether enough disk space is available for an incoming block */ @@ -193,6 +202,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa struct CNodeStateStats { int nMisbehavior; int nSyncHeight; + int nCommonHeight; + std::vector<int> vHeightInFlight; }; struct CDiskTxPos : public CDiskBlockPos @@ -439,9 +450,6 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // Apply the effects of this block (with given index) on the UTXO set represented by coins bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); -// Add this block to the block index, and if necessary, switch the active block chain to this -bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos); - // Context-independent validity checks bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true); @@ -449,7 +457,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = t // Store block on disk // if dbp is provided, the file is known to already reside on disk bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL); -bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL); +bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL); @@ -632,17 +640,17 @@ public: }; -class CWalletInterface { +class CValidationInterface { protected: - virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) =0; - virtual void EraseFromWallet(const uint256 &hash) =0; - virtual void SetBestChain(const CBlockLocator &locator) =0; - virtual void UpdatedTransaction(const uint256 &hash) =0; - virtual void Inventory(const uint256 &hash) =0; - virtual void ResendWalletTransactions() =0; - friend void ::RegisterWallet(CWalletInterface*); - friend void ::UnregisterWallet(CWalletInterface*); - friend void ::UnregisterAllWallets(); + virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {}; + virtual void EraseFromWallet(const uint256 &hash) {}; + virtual void SetBestChain(const CBlockLocator &locator) {}; + virtual void UpdatedTransaction(const uint256 &hash) {}; + virtual void Inventory(const uint256 &hash) {}; + virtual void ResendWalletTransactions() {}; + friend void ::RegisterValidationInterface(CValidationInterface*); + friend void ::UnregisterValidationInterface(CValidationInterface*); + friend void ::UnregisterAllValidationInterfaces(); }; #endif // BITCOIN_MAIN_H diff --git a/src/miner.cpp b/src/miner.cpp index fd6c52ca69..c2762bf44e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -83,6 +83,11 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) return NULL; CBlock *pblock = &pblocktemplate->block; // pointer for convenience + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios + if (Params().MineBlocksOnDemand()) + pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + // Create coinbase tx CMutableTransaction txNew; txNew.vin.resize(1); diff --git a/src/net.cpp b/src/net.cpp index 866bac2c0e..50b435cf14 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -73,11 +73,11 @@ map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfReachable[NET_MAX] = {}; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; -static CNode* pnodeSync = NULL; uint64_t nLocalHostNonce = 0; static std::vector<ListenSocket> vhListenSocket; CAddrMan addrman; int nMaxConnections = 125; +bool fAddressesInitialized = false; vector<CNode*> vNodes; CCriticalSection cs_vNodes; @@ -518,10 +518,6 @@ void CNode::CloseSocketDisconnect() TRY_LOCK(cs_vRecvMsg, lockRecv); if (lockRecv) vRecvMsg.clear(); - - // if this was the sync node, we'll need a new one - if (this == pnodeSync) - pnodeSync = NULL; } void CNode::PushVersion() @@ -614,7 +610,6 @@ void CNode::copyStats(CNodeStats &stats) X(nSendBytes); X(nRecvBytes); X(fWhitelisted); - stats.fSyncNode = (this == pnodeSync); // It is common for nodes with good ping times to suddenly become lagged, // due to a new block arriving or other large transfer. @@ -1486,61 +1481,20 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu } -// for now, use a very simple selection metric: the node from which we received -// most recently -static int64_t NodeSyncScore(const CNode *pnode) { - return pnode->nLastRecv; -} - -void static StartSync(const vector<CNode*> &vNodes) { - CNode *pnodeNewSync = NULL; - int64_t nBestScore = 0; - - int nBestHeight = g_signals.GetHeight().get_value_or(0); - - // Iterate over all nodes - BOOST_FOREACH(CNode* pnode, vNodes) { - // check preconditions for allowing a sync - if (!pnode->fClient && !pnode->fOneShot && - !pnode->fDisconnect && pnode->fSuccessfullyConnected && - (pnode->nStartingHeight > (nBestHeight - 144)) && - (pnode->nVersion < NOBLKS_VERSION_START || pnode->nVersion >= NOBLKS_VERSION_END)) { - // if ok, compare node's score with the best so far - int64_t nScore = NodeSyncScore(pnode); - if (pnodeNewSync == NULL || nScore > nBestScore) { - pnodeNewSync = pnode; - nBestScore = nScore; - } - } - } - // if a new sync candidate was found, start sync! - if (pnodeNewSync) { - pnodeNewSync->fStartSync = true; - pnodeSync = pnodeNewSync; - } -} - void ThreadMessageHandler() { SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); while (true) { - bool fHaveSyncNode = false; - vector<CNode*> vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; BOOST_FOREACH(CNode* pnode, vNodesCopy) { pnode->AddRef(); - if (pnode == pnodeSync) - fHaveSyncNode = true; } } - if (!fHaveSyncNode) - StartSync(vNodesCopy); - // Poll the connected nodes for messages CNode* pnodeTrickle = NULL; if (!vNodesCopy.empty()) @@ -1739,6 +1693,18 @@ void static Discover(boost::thread_group& threadGroup) void StartNode(boost::thread_group& threadGroup) { + uiInterface.InitMessage(_("Loading addresses...")); + // Load addresses for peers.dat + int64_t nStart = GetTimeMillis(); + { + CAddrDB adb; + if (!adb.Read(addrman)) + LogPrintf("Invalid or missing peers.dat; recreating\n"); + } + LogPrintf("Loaded %i addresses from peers.dat %dms\n", + addrman.size(), GetTimeMillis() - nStart); + fAddressesInitialized = true; + if (semOutbound == NULL) { // initialize semaphore int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); @@ -1785,7 +1751,12 @@ bool StopNode() if (semOutbound) for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) semOutbound->post(); - DumpAddresses(); + + if (fAddressesInitialized) + { + DumpAddresses(); + fAddressesInitialized = false; + } return true; } @@ -2060,10 +2031,7 @@ CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fIn nSendSize = 0; nSendOffset = 0; hashContinue = 0; - pindexLastGetBlocksBegin = 0; - hashLastGetBlocksEnd = 0; nStartingHeight = -1; - fStartSync = false; fGetAddr = false; fRelayTxes = false; setInventoryKnown.max_size(SendBufferSize() / 1000); @@ -158,7 +158,6 @@ public: int nStartingHeight; uint64_t nSendBytes; uint64_t nRecvBytes; - bool fSyncNode; bool fWhitelisted; double dPingTime; double dPingWait; @@ -276,10 +275,7 @@ protected: public: uint256 hashContinue; - CBlockIndex* pindexLastGetBlocksBegin; - uint256 hashLastGetBlocksEnd; int nStartingHeight; - bool fStartSync; // flood relay std::vector<CAddress> vAddrToSend; diff --git a/src/noui.cpp b/src/noui.cpp index f786a20db5..8f3b0275b0 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -14,6 +14,9 @@ static bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { + bool fSecure = style & CClientUIInterface::SECURE; + style &= ~CClientUIInterface::SECURE; + std::string strCaption; // Check for usage of predefined caption switch (style) { @@ -30,7 +33,8 @@ static bool noui_ThreadSafeMessageBox(const std::string& message, const std::str strCaption += caption; // Use supplied caption (can be empty) } - LogPrintf("%s: %s\n", strCaption, message); + if (!fSecure) + LogPrintf("%s: %s\n", strCaption, message); fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str()); return false; } diff --git a/src/pow.cpp b/src/pow.cpp index d50222849c..75fbfc6a6d 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -98,39 +98,6 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits) return true; } -// -// true if nBits is greater than the minimum amount of work that could -// possibly be required deltaTime after minimum work required was nBase -// -bool CheckMinWork(unsigned int nBits, unsigned int nBase, int64_t deltaTime) -{ - bool fOverflow = false; - uint256 bnNewBlock; - bnNewBlock.SetCompact(nBits, NULL, &fOverflow); - if (fOverflow) - return false; - - const uint256 &bnLimit = Params().ProofOfWorkLimit(); - // Testnet has min-difficulty blocks - // after Params().TargetSpacing()*2 time between blocks: - if (Params().AllowMinDifficultyBlocks() && deltaTime > Params().TargetSpacing()*2) - return bnNewBlock <= bnLimit; - - uint256 bnResult; - bnResult.SetCompact(nBase); - while (deltaTime > 0 && bnResult < bnLimit) - { - // Maximum 400% adjustment... - bnResult *= 4; - // ... in best-case exactly 4-times-normal target time - deltaTime -= Params().TargetTimespan()*4; - } - if (bnResult > bnLimit) - bnResult = bnLimit; - - return bnNewBlock <= bnResult; -} - void UpdateTime(CBlockHeader* pblock, const CBlockIndex* pindexPrev) { pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); @@ -16,8 +16,6 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead /** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ bool CheckProofOfWork(uint256 hash, unsigned int nBits); -/** Check the work is more than the minimum a received block needs, without knowing its direct parent */ -bool CheckMinWork(unsigned int nBits, unsigned int nBase, int64_t deltaTime); void UpdateTime(CBlockHeader* block, const CBlockIndex* pindexPrev); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 5e2fdc6c30..9872ebc1f6 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -12,6 +12,7 @@ #include "guiconstants.h" #include "guiutil.h" #include "intro.h" +#include "networkstyle.h" #include "optionsmodel.h" #include "splashscreen.h" #include "utilitydialog.h" @@ -190,9 +191,9 @@ public: /// Create options model void createOptionsModel(); /// Create main window - void createWindow(bool isaTestNet); + void createWindow(const NetworkStyle *networkStyle); /// Create splash screen - void createSplashScreen(bool isaTestNet); + void createSplashScreen(const NetworkStyle *networkStyle); /// Request core initialization void requestInitialize(); @@ -331,18 +332,18 @@ void BitcoinApplication::createOptionsModel() optionsModel = new OptionsModel(); } -void BitcoinApplication::createWindow(bool isaTestNet) +void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) { - window = new BitcoinGUI(isaTestNet, 0); + window = new BitcoinGUI(networkStyle, 0); pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); pollShutdownTimer->start(200); } -void BitcoinApplication::createSplashScreen(bool isaTestNet) +void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) { - SplashScreen *splash = new SplashScreen(0, isaTestNet); + SplashScreen *splash = new SplashScreen(0, networkStyle); // We don't hold a direct pointer to the splash screen after creation, so use // Qt::WA_DeleteOnClose to make sure that the window will be deleted eventually. splash->setAttribute(Qt::WA_DeleteOnClose); @@ -572,12 +573,10 @@ int main(int argc, char *argv[]) if (!PaymentServer::ipcParseCommandLine(argc, argv)) exit(0); #endif - bool isaTestNet = Params().NetworkID() != CBaseChainParams::MAIN; + QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString()))); + assert(!networkStyle.isNull()); // Allow for separate UI settings for testnets - if (isaTestNet) - QApplication::setApplicationName(QAPP_APP_NAME_TESTNET); - else - QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT); + QApplication::setApplicationName(networkStyle->getAppName()); // Re-initialize translations after changing application name (language in network-specific settings can be different) initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); @@ -617,11 +616,11 @@ int main(int argc, char *argv[]) uiInterface.InitMessage.connect(InitMessage); if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false)) - app.createSplashScreen(isaTestNet); + app.createSplashScreen(networkStyle.data()); try { - app.createWindow(isaTestNet); + app.createWindow(networkStyle.data()); app.requestInitialize(); #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("Bitcoin Core didn't yet exit safely..."), (HWND)app.getMainWinId()); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 7380fbd240..9d6d07a56b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -8,6 +8,7 @@ #include "clientmodel.h" #include "guiconstants.h" #include "guiutil.h" +#include "networkstyle.h" #include "notificator.h" #include "openuridialog.h" #include "optionsdialog.h" @@ -59,7 +60,7 @@ const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; -BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : +BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : QMainWindow(parent), clientModel(0), walletFrame(0), @@ -112,26 +113,13 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : } else { windowTitle += tr("Node"); } - - if (!fIsTestnet) - { + windowTitle += " " + networkStyle->getTitleAddText(); #ifndef Q_OS_MAC - QApplication::setWindowIcon(QIcon(":icons/bitcoin")); - setWindowIcon(QIcon(":icons/bitcoin")); + QApplication::setWindowIcon(networkStyle->getAppIcon()); + setWindowIcon(networkStyle->getAppIcon()); #else - MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin")); + MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon()); #endif - } - else - { - windowTitle += " " + tr("[testnet]"); -#ifndef Q_OS_MAC - QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet")); - setWindowIcon(QIcon(":icons/bitcoin_testnet")); -#else - MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); -#endif - } setWindowTitle(windowTitle); #if defined(Q_OS_MAC) && QT_VERSION < 0x050000 @@ -161,7 +149,7 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : // Create actions for the toolbar, menu bar and tray/dock icon // Needs walletFrame to be initialized - createActions(fIsTestnet); + createActions(networkStyle); // Create application menu bar createMenuBar(); @@ -170,7 +158,7 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : createToolBars(); // Create system tray icon and notification - createTrayIcon(fIsTestnet); + createTrayIcon(networkStyle); // Create status bar statusBar(); @@ -248,7 +236,7 @@ BitcoinGUI::~BitcoinGUI() #endif } -void BitcoinGUI::createActions(bool fIsTestnet) +void BitcoinGUI::createActions(const NetworkStyle *networkStyle) { QActionGroup *tabGroup = new QActionGroup(this); @@ -295,10 +283,7 @@ void BitcoinGUI::createActions(bool fIsTestnet) quitAction->setStatusTip(tr("Quit application")); quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); quitAction->setMenuRole(QAction::QuitRole); - if (!fIsTestnet) - aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin Core"), this); - else - aboutAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&About Bitcoin Core"), this); + aboutAction = new QAction(networkStyle->getAppIcon(), tr("&About Bitcoin Core"), this); aboutAction->setStatusTip(tr("Show information about Bitcoin Core")); aboutAction->setMenuRole(QAction::AboutRole); #if QT_VERSION < 0x050000 @@ -311,10 +296,7 @@ void BitcoinGUI::createActions(bool fIsTestnet) optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin")); optionsAction->setMenuRole(QAction::PreferencesRole); - if (!fIsTestnet) - toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this); - else - toggleHideAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&Show / Hide"), this); + toggleHideAction = new QAction(networkStyle->getAppIcon(), tr("&Show / Hide"), this); toggleHideAction->setStatusTip(tr("Show or hide the main Window")); encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); @@ -505,22 +487,13 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled) openAction->setEnabled(enabled); } -void BitcoinGUI::createTrayIcon(bool fIsTestnet) +void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle) { #ifndef Q_OS_MAC trayIcon = new QSystemTrayIcon(this); - - if (!fIsTestnet) - { - trayIcon->setToolTip(tr("Bitcoin Core client")); - trayIcon->setIcon(QIcon(":/icons/bitcoin")); - } - else - { - trayIcon->setToolTip(tr("Bitcoin Core client") + " " + tr("[testnet]")); - trayIcon->setIcon(QIcon(":/icons/bitcoin_testnet")); - } - + QString toolTip = tr("Bitcoin Core client") + " " + networkStyle->getTitleAddText(); + trayIcon->setToolTip(toolTip); + trayIcon->setIcon(networkStyle->getAppIcon()); trayIcon->show(); #endif @@ -1019,6 +992,9 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress) static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style) { bool modal = (style & CClientUIInterface::MODAL); + // The SECURE flag has no effect in the Qt GUI. + // bool secure = (style & CClientUIInterface::SECURE); + style &= ~CClientUIInterface::SECURE; bool ret = false; // In case of modal message, use blocking connection to wait for user to click a button QMetaObject::invokeMethod(gui, "message", diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 8af6eda867..f65f0e9137 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -19,6 +19,7 @@ #include <QSystemTrayIcon> class ClientModel; +class NetworkStyle; class Notificator; class OptionsModel; class RPCConsole; @@ -46,7 +47,7 @@ class BitcoinGUI : public QMainWindow public: static const QString DEFAULT_WALLET; - explicit BitcoinGUI(bool fIsTestnet = false, QWidget *parent = 0); + explicit BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent = 0); ~BitcoinGUI(); /** Set the client model. @@ -114,13 +115,13 @@ private: int spinnerFrame; /** Create the main UI actions. */ - void createActions(bool fIsTestnet); + void createActions(const NetworkStyle *networkStyle); /** Create the menu bar and sub-menus. */ void createMenuBar(); /** Create the toolbars */ void createToolBars(); /** Create system tray icon and notification */ - void createTrayIcon(bool fIsTestnet); + void createTrayIcon(const NetworkStyle *networkStyle); /** Create system tray menu (or setup the dock menu) */ void createTrayIconMenu(); diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index 7f28209c9a..898df2b080 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -836,29 +836,6 @@ </property> </widget> </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_25"> - <property name="text"> - <string>Sync Node</string> - </property> - </widget> - </item> - <item row="4" column="2"> - <widget class="QLabel" name="peerSyncNode"> - <property name="cursor"> - <cursorShape>IBeamCursor</cursorShape> - </property> - <property name="text"> - <string>N/A</string> - </property> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - <property name="textInteractionFlags"> - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> - </property> - </widget> - </item> <item row="5" column="0"> <widget class="QLabel" name="label_29"> <property name="text"> diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp new file mode 100644 index 0000000000..62c44703f4 --- /dev/null +++ b/src/qt/networkstyle.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "networkstyle.h" + +#include "guiconstants.h" + +#include <QApplication> + +static const struct { + const char *networkId; + const char *appName; + const char *appIcon; + const char *titleAddText; + const char *splashImage; +} network_styles[] = { + {"main", QAPP_APP_NAME_DEFAULT, ":/icons/bitcoin", "", ":/images/splash"}, + {"test", QAPP_APP_NAME_TESTNET, ":/icons/bitcoin_testnet", QT_TRANSLATE_NOOP("SplashScreen", "[testnet]"), ":/images/splash_testnet"}, + {"regtest", QAPP_APP_NAME_TESTNET, ":/icons/bitcoin_testnet", "[regtest]", ":/images/splash_testnet"} +}; +static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles); + +// titleAddText needs to be const char* for tr() +NetworkStyle::NetworkStyle(const QString &appName, const QString &appIcon, const char *titleAddText, const QString &splashImage): + appName(appName), + appIcon(appIcon), + titleAddText(qApp->translate("SplashScreen", titleAddText)), + splashImage(splashImage) +{ +} + +const NetworkStyle *NetworkStyle::instantiate(const QString &networkId) +{ + for (unsigned x=0; x<network_styles_count; ++x) + { + if (networkId == network_styles[x].networkId) + { + return new NetworkStyle( + network_styles[x].appName, + network_styles[x].appIcon, + network_styles[x].titleAddText, + network_styles[x].splashImage); + } + } + return 0; +} diff --git a/src/qt/networkstyle.h b/src/qt/networkstyle.h new file mode 100644 index 0000000000..99304d61a3 --- /dev/null +++ b/src/qt/networkstyle.h @@ -0,0 +1,33 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef H_NETWORKSTYLE +#define H_NETWORKSTYLE + +#include <QIcon> +#include <QPixmap> +#include <QString> + +/* Coin network-specific GUI style information */ +class NetworkStyle +{ +public: + /** Get style associated with provided BIP70 network id, or 0 if not known */ + static const NetworkStyle *instantiate(const QString &networkId); + + const QString &getAppName() const { return appName; } + const QIcon &getAppIcon() const { return appIcon; } + const QString &getTitleAddText() const { return titleAddText; } + const QPixmap &getSplashImage() const { return splashImage; } + +private: + NetworkStyle(const QString &appName, const QString &appIcon, const char *titleAddText, const QString &splashImage); + + QString appName; + QIcon appIcon; + QString titleAddText; + QPixmap splashImage; +}; + +#endif // H_NETWORKSTYLE diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 8129353d4b..2d2d448b49 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -611,7 +611,6 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound")); ui->peerHeight->setText(QString("%1").arg(stats->nodeStats.nStartingHeight)); - ui->peerSyncNode->setText(stats->nodeStats.fSyncNode ? tr("Yes") : tr("No")); // This check fails for example if the lock was busy and // nodeStateStats couldn't be fetched. diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 4fe610794f..b4b81440ca 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -6,6 +6,7 @@ #include "clientversion.h" #include "init.h" +#include "networkstyle.h" #include "ui_interface.h" #include "util.h" #include "version.h" @@ -19,7 +20,7 @@ #include <QDesktopWidget> #include <QPainter> -SplashScreen::SplashScreen(Qt::WindowFlags f, bool isTestNet) : +SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) : QWidget(0, f), curAlignment(0) { // set reference point, paddings @@ -34,17 +35,12 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, bool isTestNet) : QString titleText = tr("Bitcoin Core"); QString versionText = QString("Version %1").arg(QString::fromStdString(FormatFullVersion())); QString copyrightText = QChar(0xA9)+QString(" 2009-%1 ").arg(COPYRIGHT_YEAR) + QString(tr("The Bitcoin Core developers")); - QString testnetAddText = QString(tr("[testnet]")); // define text to place as single text object + QString titleAddText = networkStyle->getTitleAddText(); QString font = "Arial"; // load the bitmap for writing some text over it - if(isTestNet) { - pixmap = QPixmap(":/images/splash_testnet"); - } - else { - pixmap = QPixmap(":/images/splash"); - } + pixmap = networkStyle->getSplashImage(); QPainter pixPaint(&pixmap); pixPaint.setPen(QColor(100,100,100)); @@ -78,23 +74,20 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, bool isTestNet) : pixPaint.setFont(QFont(font, 10*fontFactor)); pixPaint.drawText(pixmap.width()-titleTextWidth-paddingRight,paddingTop+titleCopyrightVSpace,copyrightText); - // draw testnet string if testnet is on - if(isTestNet) { + // draw additional text if special network + if(!titleAddText.isEmpty()) { QFont boldFont = QFont(font, 10*fontFactor); boldFont.setWeight(QFont::Bold); pixPaint.setFont(boldFont); fm = pixPaint.fontMetrics(); - int testnetAddTextWidth = fm.width(testnetAddText); - pixPaint.drawText(pixmap.width()-testnetAddTextWidth-10,15,testnetAddText); + int titleAddTextWidth = fm.width(titleAddText); + pixPaint.drawText(pixmap.width()-titleAddTextWidth-10,15,titleAddText); } pixPaint.end(); // Set window title - if(isTestNet) - setWindowTitle(titleText + " " + testnetAddText); - else - setWindowTitle(titleText); + setWindowTitle(titleText + " " + titleAddText); // Resize window and move to center of desktop, disallow resizing QRect r(QPoint(), pixmap.size()); @@ -177,5 +170,6 @@ void SplashScreen::paintEvent(QPaintEvent *event) void SplashScreen::closeEvent(QCloseEvent *event) { + StartShutdown(); // allows an "emergency" shutdown during startup event->ignore(); } diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index 89c21e6457..128edadbee 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -7,6 +7,8 @@ #include <QSplashScreen> +class NetworkStyle; + /** Class for the splashscreen with information of the running client. * * @note this is intentionally not a QSplashScreen. Bitcoin Core initialization @@ -18,7 +20,7 @@ class SplashScreen : public QWidget Q_OBJECT public: - explicit SplashScreen(Qt::WindowFlags f, bool isTestNet); + explicit SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle); ~SplashScreen(); protected: diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index afb343f349..5278c8673a 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -32,7 +32,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * { QList<TransactionRecord> parts; int64_t nTime = wtx.GetTxTime(); - CAmount nCredit = wtx.GetCredit(true); + CAmount nCredit = wtx.GetCredit(ISMINE_ALL); CAmount nDebit = wtx.GetDebit(ISMINE_ALL); CAmount nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index b8701a23a6..b4733d369e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -605,7 +605,8 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); - vCoins.push_back(out); + if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE) + vCoins.push_back(out); } BOOST_FOREACH(const COutput& out, vCoins) diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 24175215bf..5beac0512a 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -445,6 +445,7 @@ Value getblockchaininfo(const Array& params, bool fHelp) "{\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" + " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" @@ -458,6 +459,7 @@ Value getblockchaininfo(const Array& params, bool fHelp) Object obj; obj.push_back(Pair("chain", Params().NetworkIDString())); obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(chainActive.Tip()))); diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 1ac7024550..9da0a7d091 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -114,8 +114,6 @@ Value importprivkey(const Array& params, bool fHelp) CPubKey pubkey = key.GetPubKey(); CKeyID vchAddress = pubkey.GetID(); { - LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->MarkDirty(); pwalletMain->SetAddressBook(vchAddress, strLabel, "receive"); @@ -181,7 +179,8 @@ Value importaddress(const Array& params, bool fHelp) fRescan = params[2].get_bool(); { - LOCK2(cs_main, pwalletMain->cs_wallet); + if (::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE) + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); // add to address book or update label if (address.IsValid()) diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index e794bf69e0..c767835a27 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -239,7 +239,7 @@ Value getmininginfo(const Array& params, bool fHelp) obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("testnet", Params().NetworkID() == CBaseChainParams::TESTNET)); + obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); obj.push_back(Pair("chain", Params().NetworkIDString())); #ifdef ENABLE_WALLET obj.push_back(Pair("generate", getgenerate(params, false))); diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index dd45eefd58..8be14b567c 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -87,7 +87,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", Params().NetworkID() == CBaseChainParams::TESTNET)); + obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); #ifdef ENABLE_WALLET if (pwalletMain) { obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index bc19d1372a..12dcd5b540 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -97,7 +97,12 @@ Value getpeerinfo(const Array& params, bool fHelp) " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" " \"startingheight\": n, (numeric) The starting height (block) of the peer\n" " \"banscore\": n, (numeric) The ban score\n" - " \"syncnode\": true|false (boolean) if sync node\n" + " \"synced_headers\": n, (numeric) The last header we have in common with this peer\n" + " \"synced_blocks\": n, (numeric) The last block we have in common with this peer\n" + " \"inflight\": [\n" + " n, (numeric) The heights of blocks we're currently asking from this peer\n" + " ...\n" + " ]\n" " }\n" " ,...\n" "]\n" @@ -137,9 +142,14 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("startingheight", stats.nStartingHeight)); if (fStateStats) { obj.push_back(Pair("banscore", statestats.nMisbehavior)); - obj.push_back(Pair("syncheight", statestats.nSyncHeight)); + obj.push_back(Pair("synced_headers", statestats.nSyncHeight)); + obj.push_back(Pair("synced_blocks", statestats.nCommonHeight)); + Array heights; + BOOST_FOREACH(int height, statestats.vHeightInFlight) { + heights.push_back(height); + } + obj.push_back(Pair("inflight", heights)); } - obj.push_back(Pair("syncnode", stats.fSyncNode)); obj.push_back(Pair("whitelisted", stats.fWhitelisted)); ret.push_back(obj); diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 1a41344da5..9668c78831 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -581,7 +581,7 @@ void StartRPCThreads() strWhatAmI, GetConfigFile().string(), EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32)), - "", CClientUIInterface::MSG_ERROR); + "", CClientUIInterface::MSG_ERROR | CClientUIInterface::SECURE); StartShutdown(); return; } diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index d7c0c0ef5c..d11455e389 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1539,7 +1539,7 @@ Value gettransaction(const Array& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - CAmount nCredit = wtx.GetCredit(filter != 0); + CAmount nCredit = wtx.GetCredit(filter); CAmount nDebit = wtx.GetDebit(filter); CAmount nNet = nCredit - nDebit; CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); diff --git a/src/serialize.h b/src/serialize.h index ff11edc06c..55b6891394 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -20,9 +20,6 @@ #include <utility> #include <vector> -#include <boost/tuple/tuple.hpp> -#include <boost/type_traits/is_fundamental.hpp> - class CAutoFile; class CDataStream; class CScript; @@ -432,14 +429,15 @@ template<typename Stream, typename C> void Serialize(Stream& os, const std::basi template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string<C>& str, int, int=0); // vector -template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&); -template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&); +// vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. +template<typename T, typename A> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const unsigned char&); +template<typename T, typename A, typename V> unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const V&); template<typename T, typename A> inline unsigned int GetSerializeSize(const std::vector<T, A>& v, int nType, int nVersion); -template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&); -template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&); +template<typename Stream, typename T, typename A> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const unsigned char&); +template<typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const V&); template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v, int nType, int nVersion); -template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&); -template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&); +template<typename Stream, typename T, typename A> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const unsigned char&); +template<typename Stream, typename T, typename A, typename V> void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const V&); template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersion); // others derived from vector @@ -452,16 +450,6 @@ template<typename K, typename T> unsigned int GetSerializeSize(const std::pair<K template<typename Stream, typename K, typename T> void Serialize(Stream& os, const std::pair<K, T>& item, int nType, int nVersion); template<typename Stream, typename K, typename T> void Unserialize(Stream& is, std::pair<K, T>& item, int nType, int nVersion); -// 3 tuple -template<typename T0, typename T1, typename T2> unsigned int GetSerializeSize(const boost::tuple<T0, T1, T2>& item, int nType, int nVersion); -template<typename Stream, typename T0, typename T1, typename T2> void Serialize(Stream& os, const boost::tuple<T0, T1, T2>& item, int nType, int nVersion); -template<typename Stream, typename T0, typename T1, typename T2> void Unserialize(Stream& is, boost::tuple<T0, T1, T2>& item, int nType, int nVersion); - -// 4 tuple -template<typename T0, typename T1, typename T2, typename T3> unsigned int GetSerializeSize(const boost::tuple<T0, T1, T2, T3>& item, int nType, int nVersion); -template<typename Stream, typename T0, typename T1, typename T2, typename T3> void Serialize(Stream& os, const boost::tuple<T0, T1, T2, T3>& item, int nType, int nVersion); -template<typename Stream, typename T0, typename T1, typename T2, typename T3> void Unserialize(Stream& is, boost::tuple<T0, T1, T2, T3>& item, int nType, int nVersion); - // map template<typename K, typename T, typename Pred, typename A> unsigned int GetSerializeSize(const std::map<K, T, Pred, A>& m, int nType, int nVersion); template<typename Stream, typename K, typename T, typename Pred, typename A> void Serialize(Stream& os, const std::map<K, T, Pred, A>& m, int nType, int nVersion); @@ -536,13 +524,13 @@ void Unserialize(Stream& is, std::basic_string<C>& str, int, int) // vector // template<typename T, typename A> -unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&) +unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const unsigned char&) { return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); } -template<typename T, typename A> -unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&) +template<typename T, typename A, typename V> +unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nVersion, const V&) { unsigned int nSize = GetSizeOfCompactSize(v.size()); for (typename std::vector<T, A>::const_iterator vi = v.begin(); vi != v.end(); ++vi) @@ -553,20 +541,20 @@ unsigned int GetSerializeSize_impl(const std::vector<T, A>& v, int nType, int nV template<typename T, typename A> inline unsigned int GetSerializeSize(const std::vector<T, A>& v, int nType, int nVersion) { - return GetSerializeSize_impl(v, nType, nVersion, boost::is_fundamental<T>()); + return GetSerializeSize_impl(v, nType, nVersion, T()); } template<typename Stream, typename T, typename A> -void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&) +void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const unsigned char&) { WriteCompactSize(os, v.size()); if (!v.empty()) os.write((char*)&v[0], v.size() * sizeof(T)); } -template<typename Stream, typename T, typename A> -void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&) +template<typename Stream, typename T, typename A, typename V> +void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVersion, const V&) { WriteCompactSize(os, v.size()); for (typename std::vector<T, A>::const_iterator vi = v.begin(); vi != v.end(); ++vi) @@ -576,12 +564,12 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, int nType, int nVers template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v, int nType, int nVersion) { - Serialize_impl(os, v, nType, nVersion, boost::is_fundamental<T>()); + Serialize_impl(os, v, nType, nVersion, T()); } template<typename Stream, typename T, typename A> -void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::true_type&) +void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const unsigned char&) { // Limit size per read so bogus size value won't cause out of memory v.clear(); @@ -596,8 +584,8 @@ void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, } } -template<typename Stream, typename T, typename A> -void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const boost::false_type&) +template<typename Stream, typename T, typename A, typename V> +void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, const V&) { v.clear(); unsigned int nSize = ReadCompactSize(is); @@ -617,7 +605,7 @@ void Unserialize_impl(Stream& is, std::vector<T, A>& v, int nType, int nVersion, template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersion) { - Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental<T>()); + Unserialize_impl(is, v, nType, nVersion, T()); } @@ -670,71 +658,6 @@ void Unserialize(Stream& is, std::pair<K, T>& item, int nType, int nVersion) // -// 3 tuple -// -template<typename T0, typename T1, typename T2> -unsigned int GetSerializeSize(const boost::tuple<T0, T1, T2>& item, int nType, int nVersion) -{ - unsigned int nSize = 0; - nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); - return nSize; -} - -template<typename Stream, typename T0, typename T1, typename T2> -void Serialize(Stream& os, const boost::tuple<T0, T1, T2>& item, int nType, int nVersion) -{ - Serialize(os, boost::get<0>(item), nType, nVersion); - Serialize(os, boost::get<1>(item), nType, nVersion); - Serialize(os, boost::get<2>(item), nType, nVersion); -} - -template<typename Stream, typename T0, typename T1, typename T2> -void Unserialize(Stream& is, boost::tuple<T0, T1, T2>& item, int nType, int nVersion) -{ - Unserialize(is, boost::get<0>(item), nType, nVersion); - Unserialize(is, boost::get<1>(item), nType, nVersion); - Unserialize(is, boost::get<2>(item), nType, nVersion); -} - - - -// -// 4 tuple -// -template<typename T0, typename T1, typename T2, typename T3> -unsigned int GetSerializeSize(const boost::tuple<T0, T1, T2, T3>& item, int nType, int nVersion) -{ - unsigned int nSize = 0; - nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); - nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); - return nSize; -} - -template<typename Stream, typename T0, typename T1, typename T2, typename T3> -void Serialize(Stream& os, const boost::tuple<T0, T1, T2, T3>& item, int nType, int nVersion) -{ - Serialize(os, boost::get<0>(item), nType, nVersion); - Serialize(os, boost::get<1>(item), nType, nVersion); - Serialize(os, boost::get<2>(item), nType, nVersion); - Serialize(os, boost::get<3>(item), nType, nVersion); -} - -template<typename Stream, typename T0, typename T1, typename T2, typename T3> -void Unserialize(Stream& is, boost::tuple<T0, T1, T2, T3>& item, int nType, int nVersion) -{ - Unserialize(is, boost::get<0>(item), nType, nVersion); - Unserialize(is, boost::get<1>(item), nType, nVersion); - Unserialize(is, boost::get<2>(item), nType, nVersion); - Unserialize(is, boost::get<3>(item), nType, nVersion); -} - - - -// // map // template<typename K, typename T, typename Pred, typename A> diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 7bec12b665..f9746fdaa5 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -106,51 +106,6 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) BOOST_CHECK(!CNode::IsBanned(addr)); } -static bool CheckNBits(unsigned int nbits1, int64_t time1, unsigned int nbits2, int64_t time2)\ -{ - if (time1 > time2) - return CheckNBits(nbits2, time2, nbits1, time1); - int64_t deltaTime = time2-time1; - - return CheckMinWork(nbits2, nbits1, deltaTime); -} - -BOOST_AUTO_TEST_CASE(DoS_checknbits) -{ - using namespace boost::assign; // for 'map_list_of()' - - // Timestamps,nBits from the bitcoin block chain. - // These are the block-chain checkpoint blocks - typedef std::map<int64_t, unsigned int> BlockData; - BlockData chainData = - map_list_of(1239852051,486604799)(1262749024,486594666) - (1279305360,469854461)(1280200847,469830746)(1281678674,469809688) - (1296207707,453179945)(1302624061,453036989)(1309640330,437004818) - (1313172719,436789733); - - // Make sure CheckNBits considers every combination of block-chain-lock-in-points - // "sane": - BOOST_FOREACH(const BlockData::value_type& i, chainData) - { - BOOST_FOREACH(const BlockData::value_type& j, chainData) - { - BOOST_CHECK(CheckNBits(i.second, i.first, j.second, j.first)); - } - } - - // Test a couple of insane combinations: - BlockData::value_type firstcheck = *(chainData.begin()); - BlockData::value_type lastcheck = *(chainData.rbegin()); - - // First checkpoint difficulty at or a while after the last checkpoint time should fail when - // compared to last checkpoint - BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*10, lastcheck.second, lastcheck.first)); - BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*60*24*14, lastcheck.second, lastcheck.first)); - - // ... but OK if enough time passed for difficulty to adjust downward: - BOOST_CHECK(CheckNBits(firstcheck.second, lastcheck.first+60*60*24*365*4, lastcheck.second, lastcheck.first)); -} - CTransaction RandomOrphan() { std::map<uint256, COrphanTx>::iterator it; diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 203c20731a..b32f3774fe 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(key_test1) CKey key1C = bsecret1C.GetKey(); BOOST_CHECK(key1C.IsCompressed() == true); CKey key2C = bsecret2C.GetKey(); - BOOST_CHECK(key1C.IsCompressed() == true); + BOOST_CHECK(key2C.IsCompressed() == true); CPubKey pubkey1 = key1. GetPubKey(); CPubKey pubkey2 = key2. GetPubKey(); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index afd63d2717..e50218d8ef 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -47,7 +47,7 @@ struct TestingSetup { bool fFirstRun; pwalletMain = new CWallet("wallet.dat"); pwalletMain->LoadWallet(fFirstRun); - RegisterWallet(pwalletMain); + RegisterValidationInterface(pwalletMain); #endif nScriptCheckThreads = 3; for (int i=0; i < nScriptCheckThreads-1; i++) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 6378bd0941..61daa0a3fe 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -9,6 +9,7 @@ #include "sync.h" #include "utilstrencodings.h" #include "utilmoneystr.h" +#include "version.h" #include <stdint.h> #include <vector> @@ -341,4 +342,15 @@ BOOST_AUTO_TEST_CASE(test_FormatParagraph) BOOST_CHECK_EQUAL(FormatParagraph("test test", 4, 4), "test\n test"); } +BOOST_AUTO_TEST_CASE(test_FormatSubVersion) +{ + std::vector<std::string> comments; + comments.push_back(std::string("comment1")); + std::vector<std::string> comments2; + comments2.push_back(std::string("comment1")); + comments2.push_back(std::string("comment2")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, std::vector<std::string>()),std::string("/Test:0.9.99/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments),std::string("/Test:0.9.99(comment1)/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments2),std::string("/Test:0.9.99(comment1; comment2)/")); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/src/txmempool.h b/src/txmempool.h index c63fd6f590..ad190eea9d 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -52,6 +52,19 @@ public: class CMinerPolicyEstimator; +/** An inpoint - a combination of a transaction and an index n into its vin */ +class CInPoint +{ +public: + const CTransaction* ptx; + uint32_t n; + + CInPoint() { SetNull(); } + CInPoint(const CTransaction* ptxIn, uint32_t nIn) { ptx = ptxIn; n = nIn; } + void SetNull() { ptx = NULL; n = (uint32_t) -1; } + bool IsNull() const { return (ptx == NULL && n == (uint32_t) -1); } +}; + /* * CTxMemPool stores valid-according-to-the-current-best-chain * transactions that may be included in the next block. diff --git a/src/ui_interface.h b/src/ui_interface.h index f5224ba57d..1231d5ed0b 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -63,6 +63,9 @@ public: /** Force blocking, modal message box dialog (not just OS notification) */ MODAL = 0x10000000U, + /** Do not print contents of message to debug log */ + SECURE = 0x40000000U, + /** Predefined combinations for certain default usage cases */ MSG_INFORMATION = ICON_INFORMATION, MSG_WARNING = (ICON_WARNING | BTN_OK | MODAL), diff --git a/src/util.cpp b/src/util.cpp index 632d0965bf..544ffc98b8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -395,7 +395,8 @@ boost::filesystem::path GetDefaultDataDir() #endif } -static boost::filesystem::path pathCached[CBaseChainParams::MAX_NETWORK_TYPES+1]; +static boost::filesystem::path pathCached; +static boost::filesystem::path pathCachedNetSpecific; static CCriticalSection csPathCached; const boost::filesystem::path &GetDataDir(bool fNetSpecific) @@ -404,10 +405,7 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) LOCK(csPathCached); - int nNet = CBaseChainParams::MAX_NETWORK_TYPES; - if (fNetSpecific) nNet = BaseParams().NetworkID(); - - fs::path &path = pathCached[nNet]; + fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; // This can be called during exceptions by LogPrintf(), so we cache the // value so we don't have to do memory allocations after that. @@ -433,8 +431,8 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) void ClearDatadirCache() { - std::fill(&pathCached[0], &pathCached[CBaseChainParams::MAX_NETWORK_TYPES+1], - boost::filesystem::path()); + pathCached = boost::filesystem::path(); + pathCachedNetSpecific = boost::filesystem::path(); } boost::filesystem::path GetConfigFile() diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index b9e64c5fe1..81e156f43f 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -9,8 +9,8 @@ #include <errno.h> #include <limits> - -#include <boost/foreach.hpp> +#include <cstdlib> +#include <cstring> using namespace std; @@ -53,9 +53,9 @@ signed char HexDigit(char c) bool IsHex(const string& str) { - BOOST_FOREACH(char c, str) + for(std::string::const_iterator it(str.begin()); it != str.end(); ++it) { - if (HexDigit(c) < 0) + if (HexDigit(*it) < 0) return false; } return (str.size() > 0) && (str.size()%2 == 0); diff --git a/src/version.cpp b/src/version.cpp index 95632fdab7..d12b681e5c 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -8,8 +8,6 @@ #include <string> -#include <boost/algorithm/string/join.hpp> - // Name of client reported in the 'version' message. Report the same name // for both bitcoind and bitcoin-qt, to make it harder for attackers to // target servers or GUI users specifically. @@ -94,7 +92,13 @@ std::string FormatSubVersion(const std::string& name, int nClientVersion, const ss << "/"; ss << name << ":" << FormatVersion(nClientVersion); if (!comments.empty()) - ss << "(" << boost::algorithm::join(comments, "; ") << ")"; + { + std::vector<std::string>::const_iterator it(comments.begin()); + ss << "(" << *it; + for(++it; it != comments.end(); ++it) + ss << "; " << *it; + ss << ")"; + } ss << "/"; return ss.str(); } diff --git a/src/version.h b/src/version.h index 75cbec39b7..a1e440de24 100644 --- a/src/version.h +++ b/src/version.h @@ -33,8 +33,11 @@ static const int PROTOCOL_VERSION = 70002; // initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; +// In this version, 'getheaders' was introduced. +static const int GETHEADERS_VERSION = 31800; + // disconnect from peers older than this proto version -static const int MIN_PEER_PROTO_VERSION = 209; +static const int MIN_PEER_PROTO_VERSION = GETHEADERS_VERSION; // nTime field added to CAddress, starting with this version; // if possible, avoid requesting addresses nodes older than this diff --git a/src/wallet.cpp b/src/wallet.cpp index 026c53aa2e..19e43f6ec2 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -89,6 +89,13 @@ bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) AssertLockHeld(cs_wallet); // mapKeyMetadata if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) return false; + + // check if we need to remove from watch-only + CScript script; + script = GetScriptForDestination(pubkey.GetID()); + if (HaveWatchOnly(script)) + RemoveWatchOnly(script); + if (!fFileBacked) return true; if (!IsCrypted()) { @@ -171,6 +178,20 @@ bool CWallet::AddWatchOnly(const CScript &dest) return CWalletDB(strWalletFile).WriteWatchOnly(dest); } +bool CWallet::RemoveWatchOnly(const CScript &dest) +{ + AssertLockHeld(cs_wallet); + if (!CCryptoKeyStore::RemoveWatchOnly(dest)) + return false; + if (!HaveWatchOnly()) + NotifyWatchonlyChanged(false); + if (fFileBacked) + if (!CWalletDB(strWalletFile).EraseWatchOnly(dest)) + return false; + + return true; +} + bool CWallet::LoadWatchOnly(const CScript &dest) { return CCryptoKeyStore::AddWatchOnly(dest); diff --git a/src/wallet.h b/src/wallet.h index 58e285b7eb..06706655f8 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -95,7 +95,7 @@ public: /** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. */ -class CWallet : public CCryptoKeyStore, public CWalletInterface +class CWallet : public CCryptoKeyStore, public CValidationInterface { private: bool SelectCoins(const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const; @@ -230,6 +230,7 @@ public: // Adds a watch-only address to the store, and saves it to disk. bool AddWatchOnly(const CScript &dest); + bool RemoveWatchOnly(const CScript &dest); // Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) bool LoadWatchOnly(const CScript &dest); @@ -709,18 +710,37 @@ public: return debit; } - CAmount GetCredit(bool fUseCache=true) const + CAmount GetCredit(const isminefilter& filter) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; - // GetBalance can assume transactions in mapWallet won't change - if (fUseCache && fCreditCached) - return nCreditCached; - nCreditCached = pwallet->GetCredit(*this, ISMINE_ALL); - fCreditCached = true; - return nCreditCached; + int64_t credit = 0; + if (filter & ISMINE_SPENDABLE) + { + // GetBalance can assume transactions in mapWallet won't change + if (fCreditCached) + credit += nCreditCached; + else + { + nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fCreditCached = true; + credit += nCreditCached; + } + } + if (filter & ISMINE_WATCH_ONLY) + { + if (fWatchCreditCached) + credit += nWatchCreditCached; + else + { + nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + fWatchCreditCached = true; + credit += nWatchCreditCached; + } + } + return credit; } CAmount GetImmatureCredit(bool fUseCache=true) const diff --git a/src/walletdb.cpp b/src/walletdb.cpp index e09bb8d1b2..ffddd8106b 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -121,6 +121,12 @@ bool CWalletDB::WriteWatchOnly(const CScript &dest) return Write(std::make_pair(std::string("watchs"), dest), '1'); } +bool CWalletDB::EraseWatchOnly(const CScript &dest) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("watchs"), dest)); +} + bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; @@ -179,7 +185,7 @@ bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) { - return Write(boost::make_tuple(string("acentry"), acentry.strAccount, nAccEntryNum), acentry); + return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); } bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) @@ -212,7 +218,7 @@ void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountin // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (fFlags == DB_SET_RANGE) - ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64_t(0)); + ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? string("") : strAccount), uint64_t(0))); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); fFlags = DB_NEXT; @@ -971,11 +977,11 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) { nWalletDBUpdated++; - return Write(boost::make_tuple(std::string("destdata"), address, key), value); + return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); } bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) { nWalletDBUpdated++; - return Erase(boost::make_tuple(string("destdata"), address, key)); + return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } diff --git a/src/walletdb.h b/src/walletdb.h index f3d6e61f8b..7ff41c7c8d 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -96,6 +96,7 @@ public: bool WriteCScript(const uint160& hash, const CScript& redeemScript); bool WriteWatchOnly(const CScript &script); + bool EraseWatchOnly(const CScript &script); bool WriteBestBlock(const CBlockLocator& locator); bool ReadBestBlock(CBlockLocator& locator); |