diff options
| -rw-r--r-- | depends/packages/native_comparisontool.mk | 6 | ||||
| -rw-r--r-- | doc/release-process.md | 4 | ||||
| -rwxr-xr-x | qa/pull-tester/run-bitcoind-for-test.sh.in | 4 | ||||
| -rw-r--r-- | src/amount.h | 4 | ||||
| -rw-r--r-- | src/chainparams.cpp | 41 | ||||
| -rw-r--r-- | src/chainparams.h | 24 | ||||
| -rw-r--r-- | src/chainparamsbase.cpp | 17 | ||||
| -rw-r--r-- | src/chainparamsbase.h | 1 | ||||
| -rw-r--r-- | src/checkpoints.cpp | 2 | ||||
| -rw-r--r-- | src/core.cpp | 53 | ||||
| -rw-r--r-- | src/core.h | 6 | ||||
| -rw-r--r-- | src/init.cpp | 9 | ||||
| -rw-r--r-- | src/main.cpp | 24 | ||||
| -rw-r--r-- | src/pow.cpp | 4 | ||||
| -rw-r--r-- | src/test/base58_tests.cpp | 4 | ||||
| -rw-r--r-- | src/test/miner_tests.cpp | 1 | ||||
| -rw-r--r-- | src/test/test_bitcoin.cpp | 2 | ||||
| -rw-r--r-- | src/util.h | 1 |
18 files changed, 174 insertions, 33 deletions
diff --git a/depends/packages/native_comparisontool.mk b/depends/packages/native_comparisontool.mk index d9f772219..4c40f8b12 100644 --- a/depends/packages/native_comparisontool.mk +++ b/depends/packages/native_comparisontool.mk @@ -1,8 +1,8 @@ package=native_comparisontool -$(package)_version=adfd3de7 -$(package)_download_path=https://github.com/TheBlueMatt/test-scripts/raw/10222bfdace65a0c5f3bd4a766eeb6b3a8b869fb/ +$(package)_version=5caed78 +$(package)_download_path=https://github.com/TheBlueMatt/test-scripts/raw/2d76ce92d68e6746988adde731318605a70e252c $(package)_file_name=pull-tests-$($(package)_version).jar -$(package)_sha256_hash=fd2282b112e35f339dbe3729b08a04834ad719f8c9c10eeec1178465e6d36a18 +$(package)_sha256_hash=b55a98828b17060e327c5dabe5e4631898f422c0cba07c46170930a9eaf5e7c0 $(package)_install_dirname=BitcoindComparisonTool_jar $(package)_install_filename=BitcoindComparisonTool.jar diff --git a/doc/release-process.md b/doc/release-process.md index 940f934ba..7699af90b 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -154,6 +154,10 @@ repackage gitian builds for release as stand-alone zip/tar/installer exe zip -r bitcoin-${VERSION}-win.zip bitcoin-${VERSION}-win rm -rf bitcoin-${VERSION}-win +**Mac OS X .dmg:** + + mv Bitcoin-Qt.dmg bitcoin-${VERSION}-osx.dmg + ###Next steps: Commit your signature to gitian.sigs: diff --git a/qa/pull-tester/run-bitcoind-for-test.sh.in b/qa/pull-tester/run-bitcoind-for-test.sh.in index 67318e5a4..210fc3c42 100755 --- a/qa/pull-tester/run-bitcoind-for-test.sh.in +++ b/qa/pull-tester/run-bitcoind-for-test.sh.in @@ -29,4 +29,8 @@ fi kill $BITCOIND && wait $BITCOIND # timeout returns 124 on timeout, otherwise the return value of the child + +# If $RETURN is not 0, the test failed. Dump the tail of the debug log. +if [ $RETURN -ne 0 ]; then tail -n 200 $DATADIR/regtest/debug.log; fi + exit $RETURN diff --git a/src/amount.h b/src/amount.h index 42006a038..831fa1f6c 100644 --- a/src/amount.h +++ b/src/amount.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_AMOUNT_H @@ -10,4 +10,4 @@ typedef int64_t CAmount; -#endif +#endif // BITCOIN_AMOUNT_H diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 179db5a81..31c67715c 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -115,6 +115,7 @@ public: fAllowMinDifficultyBlocks = false; fRequireStandard = true; fMineBlocksOnDemand = false; + fSkipProofOfWorkCheck = false; } }; static CMainParams mainParams; @@ -214,8 +215,46 @@ public: }; static CRegTestParams regTestParams; +// +// Unit test +// +class CUnitTestParams : public CMainParams, public CModifiableParams { +public: + CUnitTestParams() { + networkID = CBaseChainParams::UNITTEST; + strNetworkID = "unittest"; + nDefaultPort = 18445; + vFixedSeeds.clear(); + vSeeds.clear(); // Regtest mode doesn't have any DNS seeds. + + fRequireRPCPassword = false; + fMiningRequiresPeers = false; + fDefaultCheckMemPool = true; + fAllowMinDifficultyBlocks = false; + fMineBlocksOnDemand = true; + } +public: + // Published setters to allow changing values in unit test cases + virtual void setSubsidyHalvingInterval(int anSubsidyHalvingInterval) { nSubsidyHalvingInterval=anSubsidyHalvingInterval; } + virtual void setEnforceBlockUpgradeMajority(int anEnforceBlockUpgradeMajority) { nEnforceBlockUpgradeMajority=anEnforceBlockUpgradeMajority; } + virtual void setRejectBlockOutdatedMajority(int anRejectBlockOutdatedMajority) { nRejectBlockOutdatedMajority=anRejectBlockOutdatedMajority; } + virtual void setToCheckBlockUpgradeMajority(int anToCheckBlockUpgradeMajority) { nToCheckBlockUpgradeMajority=anToCheckBlockUpgradeMajority; } + virtual void setDefaultCheckMemPool(bool afDefaultCheckMemPool) { fDefaultCheckMemPool=afDefaultCheckMemPool; } + virtual void setAllowMinDifficultyBlocks(bool afAllowMinDifficultyBlocks) { fAllowMinDifficultyBlocks=afAllowMinDifficultyBlocks; } + virtual void setSkipProofOfWorkCheck(bool afSkipProofOfWorkCheck) { fSkipProofOfWorkCheck = afSkipProofOfWorkCheck; } +}; +static CUnitTestParams unitTestParams; + + static CChainParams *pCurrentParams = 0; +CModifiableParams *ModifiableParams() +{ + assert(pCurrentParams); + assert(pCurrentParams==&unitTestParams); + return (CModifiableParams*)&unitTestParams; +} + const CChainParams &Params() { assert(pCurrentParams); return *pCurrentParams; @@ -229,6 +268,8 @@ CChainParams &Params(CBaseChainParams::Network network) { return testNetParams; case CBaseChainParams::REGTEST: return regTestParams; + case CBaseChainParams::UNITTEST: + return unitTestParams; default: assert(false && "Unimplemented network"); return mainParams; diff --git a/src/chainparams.h b/src/chainparams.h index e5dfc87c6..50441a89f 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -61,6 +61,8 @@ public: bool DefaultCheckMemPool() const { return fDefaultCheckMemPool; } /* Allow mining of a min-difficulty block */ bool AllowMinDifficultyBlocks() const { return fAllowMinDifficultyBlocks; } + /* Skip proof-of-work check: allow mining of any difficulty block */ + bool SkipProofOfWorkCheck() const { return fSkipProofOfWorkCheck; } /* Make standard checks */ bool RequireStandard() const { return fRequireStandard; } int64_t TargetTimespan() const { return nTargetTimespan; } @@ -103,8 +105,27 @@ protected: bool fAllowMinDifficultyBlocks; bool fRequireStandard; bool fMineBlocksOnDemand; + bool fSkipProofOfWorkCheck; }; +/** Modifiable parameters interface is used by test cases to adapt the parameters in order +*** to test specific features more easily. Test cases should always restore the previous +*** values after finalization. +**/ + +class CModifiableParams { +public: + // Published setters to allow changing values in unit test cases + virtual void setSubsidyHalvingInterval(int anSubsidyHalvingInterval) =0; + virtual void setEnforceBlockUpgradeMajority(int anEnforceBlockUpgradeMajority)=0; + virtual void setRejectBlockOutdatedMajority(int anRejectBlockOutdatedMajority)=0; + virtual void setToCheckBlockUpgradeMajority(int anToCheckBlockUpgradeMajority)=0; + virtual void setDefaultCheckMemPool(bool aDefaultCheckMemPool)=0; + virtual void setAllowMinDifficultyBlocks(bool aAllowMinDifficultyBlocks)=0; + virtual void setSkipProofOfWorkCheck(bool aSkipProofOfWorkCheck)=0; +}; + + /** * Return the currently selected parameters. This won't change after app startup * outside of the unit tests. @@ -114,6 +135,9 @@ const CChainParams &Params(); /** Return parameters for the given network. */ CChainParams &Params(CBaseChainParams::Network network); +/** Get modifyable network parameters (UNITTEST only) */ +CModifiableParams *ModifiableParams(); + /** Sets the params returned by Params() to those for the given network. */ void SelectParams(CBaseChainParams::Network network); diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 98bb5b855..e9d63197b 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -57,6 +57,20 @@ public: }; static CBaseRegTestParams regTestParams; +// +// Unit test +// +class CBaseUnitTestParams : public CBaseMainParams +{ +public: + CBaseUnitTestParams() + { + networkID = CBaseChainParams::UNITTEST; + strDataDir = "unittest"; + } +}; +static CBaseUnitTestParams unitTestParams; + static CBaseChainParams* pCurrentBaseParams = 0; const CBaseChainParams& BaseParams() @@ -77,6 +91,9 @@ void SelectBaseParams(CBaseChainParams::Network network) case CBaseChainParams::REGTEST: pCurrentBaseParams = ®TestParams; break; + case CBaseChainParams::UNITTEST: + pCurrentBaseParams = &unitTestParams; + break; default: assert(false && "Unimplemented network"); return; diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index c054f03f1..cc154cf50 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -19,6 +19,7 @@ public: MAIN, TESTNET, REGTEST, + UNITTEST, MAX_NETWORK_TYPES }; diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index c41deea7c..9a6bc05e6 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -88,6 +88,8 @@ namespace Checkpoints { 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; } diff --git a/src/core.cpp b/src/core.cpp index 1489d77bb..380b1c38e 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -224,29 +224,66 @@ uint256 CBlockHeader::GetHash() const return Hash(BEGIN(nVersion), END(nNonce)); } -uint256 CBlock::BuildMerkleTree() const +uint256 CBlock::BuildMerkleTree(bool* fMutated) const { - // WARNING! If you're reading this because you're learning about crypto - // and/or designing a new system that will use merkle trees, keep in mind - // that the following merkle tree algorithm has a serious flaw related to - // duplicate txids, resulting in a vulnerability. (CVE-2012-2459) Bitcoin - // has since worked around the flaw, but for new applications you should - // use something different; don't just copy-and-paste this code without - // understanding the problem first. + /* WARNING! If you're reading this because you're learning about crypto + and/or designing a new system that will use merkle trees, keep in mind + that the following merkle tree algorithm has a serious flaw related to + duplicate txids, resulting in a vulnerability (CVE-2012-2459). + + The reason is that if the number of hashes in the list at a given time + is odd, the last one is duplicated before computing the next level (which + is unusual in Merkle trees). This results in certain sequences of + transactions leading to the same merkle root. For example, these two + trees: + + A A + / \ / \ + B C B C + / \ | / \ / \ + D E F D E F F + / \ / \ / \ / \ / \ / \ / \ + 1 2 3 4 5 6 1 2 3 4 5 6 5 6 + + for transaction lists [1,2,3,4,5,6] and [1,2,3,4,5,6,5,6] (where 5 and + 6 are repeated) result in the same root hash A (because the hash of both + of (F) and (F,F) is C). + + The vulnerability results from being able to send a block with such a + transaction list, with the same merkle root, and the same block hash as + the original without duplication, resulting in failed validation. If the + receiving node proceeds to mark that block as permanently invalid + however, it will fail to accept further unmodified (and thus potentially + valid) versions of the same block. We defend against this by detecting + the case where we would hash two identical hashes at the end of the list + together, and treating that identically to the block having an invalid + merkle root. Assuming no double-SHA256 collisions, this will detect all + known ways of changing the transactions without affecting the merkle + root. + */ 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()); int j = 0; + bool mutated = false; for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) { for (int i = 0; i < nSize; i += 2) { int i2 = std::min(i+1, nSize-1); + if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j+i] == vMerkleTree[j+i2]) { + // Two identical hashes at the end of the list at a particular level. + mutated = true; + } vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); } j += nSize; } + if (fMutated) { + *fMutated = mutated; + } return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); } diff --git a/src/core.h b/src/core.h index e8435c8b0..a34829357 100644 --- a/src/core.h +++ b/src/core.h @@ -529,7 +529,11 @@ public: return block; } - uint256 BuildMerkleTree() const; + // Build the in-memory merkle tree for this block and return the merkle root. + // If non-NULL, *mutated is set to whether mutation was detected in the merkle + // tree (a duplication of transactions in the block leading to an identical + // merkle root). + uint256 BuildMerkleTree(bool* mutated = NULL) const; std::vector<uint256> GetMerkleBranch(int nIndex) const; static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex); diff --git a/src/init.cpp b/src/init.cpp index 503df7ad3..980c589b2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -45,7 +45,7 @@ using namespace boost; using namespace std; #ifdef ENABLE_WALLET -CWallet* pwalletMain; +CWallet* pwalletMain = NULL; #endif #ifdef WIN32 @@ -109,7 +109,7 @@ bool ShutdownRequested() return fRequestShutdown; } -static CCoinsViewDB *pcoinsdbview; +static CCoinsViewDB *pcoinsdbview = NULL; void Shutdown() { @@ -165,8 +165,8 @@ void Shutdown() #endif UnregisterAllWallets(); #ifdef ENABLE_WALLET - if (pwalletMain) - delete pwalletMain; + delete pwalletMain; + pwalletMain = NULL; #endif LogPrintf("%s: done\n", __func__); } @@ -701,6 +701,7 @@ bool AppInit2(boost::thread_group& threadGroup) fIsBareMultisigStd = GetArg("-permitbaremultisig", true) != 0; // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log + // Sanity check if (!InitSanityCheck()) return InitError(_("Initialization sanity check failed. Bitcoin Core is shutting down.")); diff --git a/src/main.cpp b/src/main.cpp index 5a5c70057..55485c86f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ #include "txmempool.h" #include "ui_interface.h" #include "util.h" +#include "utilmoneystr.h" #include <sstream> @@ -1316,7 +1317,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() const { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags)) - return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString()); + return error("CScriptCheck() : %s:%d VerifySignature failed", ptxTo->GetHash().ToString(), nIn); return true; } @@ -1361,7 +1362,8 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi } if (nValueIn < tx.GetValueOut()) - return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString()), + return state.DoS(100, error("CheckInputs() : %s value in (%s) < value out (%s)", + tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), REJECT_INVALID, "bad-txns-in-belowout"); // Tally transaction fees @@ -2236,13 +2238,12 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo if (!CheckTransaction(tx, state)) return error("CheckBlock() : CheckTransaction failed"); - // Check for duplicate txids. This is caught by ConnectInputs(), - // but catching it earlier avoids a potential DoS attack: - set<uint256> uniqueTx; - BOOST_FOREACH(const CTransaction &tx, block.vtx) { - uniqueTx.insert(tx.GetHash()); - } - if (uniqueTx.size() != block.vtx.size()) + // 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); @@ -2256,7 +2257,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo REJECT_INVALID, "bad-blk-sigops", true); // Check merkle root - if (fCheckMerkleRoot && block.hashMerkleRoot != block.BuildMerkleTree()) + if (fCheckMerkleRoot && block.hashMerkleRoot != hashMerkleRoot2) return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), REJECT_INVALID, "bad-txnmrklroot", true); @@ -2304,7 +2305,8 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex nHeight = pindexPrev->nHeight+1; // Check proof of work - if (block.nBits != GetNextWorkRequired(pindexPrev, &block)) + if ((!Params().SkipProofOfWorkCheck()) && + (block.nBits != GetNextWorkRequired(pindexPrev, &block))) return state.DoS(100, error("AcceptBlock() : incorrect proof of work"), REJECT_INVALID, "bad-diffbits"); diff --git a/src/pow.cpp b/src/pow.cpp index 893f6c18b..d50222849 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -81,6 +81,10 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits) bool fNegative; bool fOverflow; uint256 bnTarget; + + if (Params().SkipProofOfWorkCheck()) + return true; + bnTarget.SetCompact(nBits, &fNegative, &fOverflow); // Check range diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 58fffb6df..c298c805d 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest); } } - SelectParams(CBaseChainParams::MAIN); + SelectParams(CBaseChainParams::UNITTEST); } // Goal: check that generated keys match test vectors @@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) CTxDestination nodest = CNoDestination(); BOOST_CHECK(!dummyAddr.Set(nodest)); - SelectParams(CBaseChainParams::MAIN); + SelectParams(CBaseChainParams::UNITTEST); } // Goal: check that base58 parsing code is robust against a variety of corrupted data diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 9e4669eba..bad5c13ac 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -253,6 +253,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) chainActive.Tip()->nHeight--; SetMockTime(0); + mempool.clear(); BOOST_FOREACH(CTransaction *tx, txFirst) delete tx; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 68fad8d03..6e5f0e3fa 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -31,7 +31,7 @@ struct TestingSetup { TestingSetup() { fPrintToDebugLog = false; // don't want to write to debug.log file - SelectParams(CBaseChainParams::MAIN); + SelectParams(CBaseChainParams::UNITTEST); noui_connect(); #ifdef ENABLE_WALLET bitdb.MakeMock(); diff --git a/src/util.h b/src/util.h index fb1d3eacb..4b2415278 100644 --- a/src/util.h +++ b/src/util.h @@ -14,7 +14,6 @@ #include "config/bitcoin-config.h" #endif -#include "amount.h" #include "compat.h" #include "tinyformat.h" #include "utiltime.h" |