diff options
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 593 |
1 files changed, 329 insertions, 264 deletions
diff --git a/src/main.cpp b/src/main.cpp index f0c08d273..d35891440 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include "init.h" #include "ui_interface.h" #include "checkqueue.h" +#include "chainparams.h" #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> @@ -32,8 +33,6 @@ unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; std::vector<CBlockIndex*> vBlockIndexByHeight; -uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); -static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; uint256 nBestChainWork = 0; @@ -100,9 +99,16 @@ void UnregisterWallet(CWallet* pwalletIn) } } +void UnregisterAllWallets() +{ + LOCK(cs_setpwalletRegistered); + setpwalletRegistered.clear(); +} + // get the wallet transaction with the given hash (if it exists) bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) { + LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) if (pwallet->GetTransaction(hashTx,wtx)) return true; @@ -112,6 +118,7 @@ bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) // erases transaction with the given hash from all wallets void static EraseFromWallets(uint256 hash) { + LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->EraseFromWallet(hash); } @@ -119,6 +126,7 @@ void static EraseFromWallets(uint256 hash) // make sure all wallets know about the given transaction, in the given block void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) { + LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate); } @@ -126,6 +134,7 @@ void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* // notify wallets about a new best chain void static SetBestChain(const CBlockLocator& loc) { + LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->SetBestChain(loc); } @@ -133,6 +142,7 @@ void static SetBestChain(const CBlockLocator& loc) // notify wallets about an updated transaction void static UpdatedTransaction(const uint256& hashTx) { + LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->UpdatedTransaction(hashTx); } @@ -140,6 +150,7 @@ void static UpdatedTransaction(const uint256& hashTx) // dump all wallets void static PrintWallets(const CBlock& block) { + LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->PrintWallet(block); } @@ -147,6 +158,7 @@ void static PrintWallets(const CBlock& block) // notify wallets about an incoming inventory (for request counts) void static Inventory(const uint256& hash) { + LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->Inventory(hash); } @@ -154,12 +166,11 @@ void static Inventory(const uint256& hash) // ask wallets to resend their transactions void static ResendWalletTransactions() { + LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->ResendWalletTransactions(); } - - ////////////////////////////////////////////////////////////////////////////// // // Registration of network node signals. @@ -177,7 +188,95 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals) nodeSignals.SendMessages.disconnect(&SendMessages); } +////////////////////////////////////////////////////////////////////////////// +// +// CBlockLocator implementation +// +CBlockLocator::CBlockLocator(uint256 hashBlock) +{ + std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end()) + Set((*mi).second); +} + +void CBlockLocator::Set(const CBlockIndex* pindex) +{ + vHave.clear(); + int nStep = 1; + while (pindex) + { + vHave.push_back(pindex->GetBlockHash()); + + // Exponentially larger steps back + for (int i = 0; pindex && i < nStep; i++) + pindex = pindex->pprev; + if (vHave.size() > 10) + nStep *= 2; + } + vHave.push_back(Params().HashGenesisBlock()); +} + +int CBlockLocator::GetDistanceBack() +{ + // Retrace how far back it was in the sender's branch + int nDistance = 0; + int nStep = 1; + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return nDistance; + } + nDistance += nStep; + if (nDistance > 10) + nStep *= 2; + } + return nDistance; +} + +CBlockIndex *CBlockLocator::GetBlockIndex() +{ + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return pindex; + } + } + return pindexGenesisBlock; +} + +uint256 CBlockLocator::GetBlockHash() +{ + // Find the first block the caller has in the main chain + BOOST_FOREACH(const uint256& hash, vHave) + { + std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) + { + CBlockIndex* pindex = (*mi).second; + if (pindex->IsInMainChain()) + return hash; + } + } + return Params().HashGenesisBlock(); +} + +int CBlockLocator::GetHeight() +{ + CBlockIndex* pindex = GetBlockIndex(); + if (!pindex) + return 0; + return pindex->nHeight; +} ////////////////////////////////////////////////////////////////////////////// // @@ -372,38 +471,53 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) -bool IsStandardTx(const CTransaction& tx) +bool IsStandardTx(const CTransaction& tx, string& reason) { - if (tx.nVersion > CTransaction::CURRENT_VERSION) + if (tx.nVersion > CTransaction::CURRENT_VERSION) { + reason = "version"; return false; + } - if (!IsFinalTx(tx)) + if (!IsFinalTx(tx)) { + reason = "non-final"; return false; + } // Extremely large transactions with lots of inputs can cost the network // almost as much to process as they cost the sender in fees, because // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) + if (sz >= MAX_STANDARD_TX_SIZE) { + reason = "tx-size"; return false; + } BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG // pay-to-script-hash, which is 3 ~80-byte signatures, 3 // ~65-byte public keys, plus a few script ops. - if (txin.scriptSig.size() > 500) + if (txin.scriptSig.size() > 500) { + reason = "scriptsig-size"; return false; - if (!txin.scriptSig.IsPushOnly()) + } + if (!txin.scriptSig.IsPushOnly()) { + reason = "scriptsig-not-pushonly"; return false; + } } BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey)) + if (!::IsStandard(txout.scriptPubKey)) { + reason = "scriptpubkey"; return false; - if (txout.IsDust(CTransaction::nMinRelayTxFee)) + } + if (txout.IsDust(CTransaction::nMinRelayTxFee)) { + reason = "dust"; return false; + } } + return true; } @@ -542,7 +656,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) if (pcoinsTip->GetCoins(GetHash(), coins)) { CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); if (pindex) { - if (!blockTmp.ReadFromDisk(pindex)) + if (!ReadBlockFromDisk(blockTmp, pindex)) return 0; pblock = &blockTmp; } @@ -634,30 +748,23 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) return true; } -int64 GetMinFee(const CTransaction& tx, unsigned int nBlockSize, bool fAllowFree, enum GetMinFee_mode mode) +int64 GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mode) { // Base fee is either nMinTxFee or nMinRelayTxFee int64 nBaseFee = (mode == GMF_RELAY) ? tx.nMinRelayTxFee : tx.nMinTxFee; unsigned int nBytes = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - unsigned int nNewBlockSize = nBlockSize + nBytes; int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; if (fAllowFree) { - if (nBlockSize == 1) - { - // Transactions under 10K are free - // (about 4500 BTC if made of 50 BTC inputs) - if (nBytes < 10000) - nMinFee = 0; - } - else - { - // Free transaction area - if (nNewBlockSize < 27000) - nMinFee = 0; - } + // There is a free transaction area in blocks created by most miners, + // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 + // to be considered to fall into this category + // * If we are creating a transaction we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 17000 + // (= 10000) to be considered safe and assume they can likely make it into this section + if (nBytes < (mode == GMF_SEND ? (DEFAULT_BLOCK_PRIORITY_SIZE - 17000) : (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))) + nMinFee = 0; } // To limit dust spam, require base fee if any output is less than 0.01 @@ -668,14 +775,6 @@ int64 GetMinFee(const CTransaction& tx, unsigned int nBlockSize, bool fAllowFree nMinFee = nBaseFee; } - // Raise the price as the block approaches full - if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) - { - if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) - return MAX_MONEY; - nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); - } - if (!MoneyRange(nMinFee)) nMinFee = MAX_MONEY; return nMinFee; @@ -712,8 +811,10 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) - if (!fTestNet && !IsStandardTx(tx)) - return error("CTxMemPool::accept() : nonstandard transaction type"); + string reason; + if (!TestNet() && !IsStandardTx(tx, reason)) + return error("CTxMemPool::accept() : nonstandard transaction: %s", + reason.c_str()); // is it already in the memory pool? uint256 hash = tx.GetHash(); @@ -787,7 +888,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr } // Check for non-standard pay-to-script-hash in inputs - if (!AreInputsStandard(tx, view) && !fTestNet) + if (!TestNet() && !AreInputsStandard(tx, view)) return error("CTxMemPool::accept() : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then @@ -798,7 +899,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block - int64 txMinFee = GetMinFee(tx, 1000, true, GMF_RELAY); + int64 txMinFee = GetMinFee(tx, true, GMF_RELAY); if (fLimitFree && nFees < txMinFee) return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, hash.ToString().c_str(), @@ -1044,7 +1145,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock if (pindexSlow) { CBlock block; - if (block.ReadFromDisk(pindexSlow)) { + if (ReadBlockFromDisk(block, pindexSlow)) { BOOST_FOREACH(const CTransaction &tx, block.vtx) { if (tx.GetHash() == hash) { txOut = tx; @@ -1076,12 +1177,62 @@ CBlockIndex* FindBlockByHeight(int nHeight) return vBlockIndexByHeight[nHeight]; } -bool CBlock::ReadFromDisk(const CBlockIndex* pindex) +bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) +{ + // Open history file to append + CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("WriteBlockToDisk() : OpenBlockFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(block); + fileout << FLATDATA(Params().MessageStart()) << nSize; + + // Write block + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) + return error("WriteBlockToDisk() : ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << block; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload()) + FileCommit(fileout); + + return true; +} + +bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) { - if (!ReadFromDisk(pindex->GetBlockPos())) + block.SetNull(); + + // Open history file to read + CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : OpenBlockFile failed"); + + // Read block + try { + filein >> block; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + + // Check the header + if (!CheckProofOfWork(block.GetHash(), block.nBits)) + return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : errors in block header"); + + return true; +} + +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) +{ + if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) return false; - if (GetHash() != pindex->GetBlockHash()) - return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); + if (block.GetHash() != pindex->GetBlockHash()) + return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index"); return true; } @@ -1097,8 +1248,8 @@ int64 static GetBlockValue(int nHeight, int64 nFees) { int64 nSubsidy = 50 * COIN; - // Subsidy is cut in half every 210000 blocks, which will occur approximately every 4 years - nSubsidy >>= (nHeight / 210000); + // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years. + nSubsidy >>= (nHeight / Params().SubsidyHalvingInterval()); return nSubsidy + nFees; } @@ -1113,28 +1264,29 @@ static const int64 nInterval = nTargetTimespan / nTargetSpacing; // unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) { + const CBigNum &bnLimit = Params().ProofOfWorkLimit(); // Testnet has min-difficulty blocks // after nTargetSpacing*2 time between blocks: - if (fTestNet && nTime > nTargetSpacing*2) - return bnProofOfWorkLimit.GetCompact(); + if (TestNet() && nTime > nTargetSpacing*2) + return bnLimit.GetCompact(); CBigNum bnResult; bnResult.SetCompact(nBase); - while (nTime > 0 && bnResult < bnProofOfWorkLimit) + while (nTime > 0 && bnResult < bnLimit) { // Maximum 400% adjustment... bnResult *= 4; // ... in best-case exactly 4-times-normal target time nTime -= nTargetTimespan*4; } - if (bnResult > bnProofOfWorkLimit) - bnResult = bnProofOfWorkLimit; + if (bnResult > bnLimit) + bnResult = bnLimit; return bnResult.GetCompact(); } unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock) { - unsigned int nProofOfWorkLimit = bnProofOfWorkLimit.GetCompact(); + unsigned int nProofOfWorkLimit = Params().ProofOfWorkLimit().GetCompact(); // Genesis block if (pindexLast == NULL) @@ -1143,9 +1295,9 @@ unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBl // Only change once per interval if ((pindexLast->nHeight+1) % nInterval != 0) { - // Special difficulty rule for testnet: - if (fTestNet) + if (TestNet()) { + // Special difficulty rule for testnet: // If the new block's timestamp is more than 2* 10 minutes // then allow mining of a min-difficulty block. if (pblock->nTime > pindexLast->nTime + nTargetSpacing*2) @@ -1159,7 +1311,6 @@ unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBl return pindex->nBits; } } - return pindexLast->nBits; } @@ -1183,8 +1334,8 @@ unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBl bnNew *= nActualTimespan; bnNew /= nTargetTimespan; - if (bnNew > bnProofOfWorkLimit) - bnNew = bnProofOfWorkLimit; + if (bnNew > Params().ProofOfWorkLimit()) + bnNew = Params().ProofOfWorkLimit(); /// debug print printf("GetNextWorkRequired RETARGET\n"); @@ -1201,7 +1352,7 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits) bnTarget.SetCompact(nBits); // Check range - if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) + if (bnTarget <= 0 || bnTarget > Params().ProofOfWorkLimit()) return error("CheckProofOfWork() : nBits below minimum work"); // Check proof of work matches claimed amount @@ -1319,7 +1470,7 @@ void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) block.nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); // Updating time can change work required on testnet: - if (fTestNet) + if (TestNet()) block.nBits = GetNextWorkRequired(pindexPrev, &block); } @@ -1483,7 +1634,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach -bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) +bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex == view.GetBestBlock()); @@ -1499,12 +1650,12 @@ bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoin if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) return error("DisconnectBlock() : failure reading undo data"); - if (blockUndo.vtxundo.size() + 1 != vtx.size()) + if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) return error("DisconnectBlock() : block and undo data inconsistent"); // undo transactions in reverse order - for (int i = vtx.size() - 1; i >= 0; i--) { - const CTransaction &tx = vtx[i]; + for (int i = block.vtx.size() - 1; i >= 0; i--) { + const CTransaction &tx = block.vtx[i]; uint256 hash = tx.GetHash(); // check that all outputs are available @@ -1597,10 +1748,10 @@ void ThreadScriptCheck() { scriptcheckqueue.Thread(); } -bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) +bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) { // Check it again in case a previous version let a bad block in - if (!CheckBlock(state, !fJustCheck, !fJustCheck)) + if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block @@ -1608,7 +1759,7 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) - if (GetHash() == hashGenesisBlock) { + if (block.GetHash() == Params().HashGenesisBlock()) { view.SetBestBlock(pindex); pindexGenesisBlock = pindex; return true; @@ -1632,8 +1783,8 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); if (fEnforceBIP30) { - for (unsigned int i=0; i<vtx.size(); i++) { - uint256 hash = GetTxHash(i); + for (unsigned int i = 0; i < block.vtx.size(); i++) { + uint256 hash = block.GetTxHash(i); if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction")); } @@ -1654,12 +1805,12 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi int64 nFees = 0; int nInputs = 0; unsigned int nSigOps = 0; - CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(vtx.size())); + CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector<std::pair<uint256, CDiskTxPos> > vPos; - vPos.reserve(vtx.size()); - for (unsigned int i=0; i<vtx.size(); i++) + vPos.reserve(block.vtx.size()); + for (unsigned int i = 0; i < block.vtx.size(); i++) { - const CTransaction &tx = vtx[i]; + const CTransaction &tx = block.vtx[i]; nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); @@ -1690,19 +1841,19 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi } CTxUndo txundo; - UpdateCoins(tx, state, view, txundo, pindex->nHeight, GetTxHash(i)); + UpdateCoins(tx, state, view, txundo, pindex->nHeight, block.GetTxHash(i)); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); - vPos.push_back(std::make_pair(GetTxHash(i), pos)); + vPos.push_back(std::make_pair(block.GetTxHash(i), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64 nTime = GetTimeMicros() - nStart; if (fBenchmark) - printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); + printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); - if (GetValueOut(vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) - return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(vtx[0]), GetBlockValue(pindex->nHeight, nFees))); + if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) + return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees))); if (!control.Wait()) return state.DoS(100, false); @@ -1743,8 +1894,8 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi assert(view.SetBestBlock(pindex)); // Watch for transactions paying to me - for (unsigned int i=0; i<vtx.size(); i++) - SyncWithWallets(GetTxHash(i), vtx[i], this, true); + for (unsigned int i = 0; i < block.vtx.size(); i++) + SyncWithWallets(block.GetTxHash(i), block.vtx[i], &block, true); return true; } @@ -1790,10 +1941,10 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) vector<CTransaction> vResurrect; BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { CBlock block; - if (!block.ReadFromDisk(pindex)) + if (!ReadBlockFromDisk(block, pindex)) return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.DisconnectBlock(state, pindex, view)) + if (!DisconnectBlock(block, state, pindex, view)) return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); if (fBenchmark) printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); @@ -1810,10 +1961,10 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) vector<CTransaction> vDelete; BOOST_FOREACH(CBlockIndex *pindex, vConnect) { CBlock block; - if (!block.ReadFromDisk(pindex)) + if (!ReadBlockFromDisk(block, pindex)) return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.ConnectBlock(state, pindex, view)) { + if (!ConnectBlock(block, state, pindex, view)) { if (state.IsInvalid()) { InvalidChainFound(pindexNew); InvalidBlockFound(pindex); @@ -1923,25 +2074,25 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) } -bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) +bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos) { // Check for duplicate - uint256 hash = GetHash(); + uint256 hash = block.GetHash(); if (mapBlockIndex.count(hash)) return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString().c_str())); // Construct new block index object - CBlockIndex* pindexNew = new CBlockIndex(*this); + CBlockIndex* pindexNew = new CBlockIndex(block); assert(pindexNew); map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); - map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock); + map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); if (miPrev != mapBlockIndex.end()) { pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } - pindexNew->nTx = vtx.size(); + pindexNew->nTx = block.vtx.size(); pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork().getuint256(); pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx; pindexNew->nFile = pos.nFile; @@ -1962,7 +2113,7 @@ bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; UpdatedTransaction(hashPrevBestCoinBase); - hashPrevBestCoinBase = GetTxHash(0); + hashPrevBestCoinBase = block.GetTxHash(0); } if (!pblocktree->Flush()) @@ -2068,51 +2219,51 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne } -bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const +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. // Size limits - if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + 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")); // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) + if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits)) return state.DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp - if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); // First transaction must be coinbase, the rest must not be - if (vtx.empty() || !vtx[0].IsCoinBase()) + if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); - for (unsigned int i = 1; i < vtx.size(); i++) - if (vtx[i].IsCoinBase()) + for (unsigned int i = 1; i < block.vtx.size(); i++) + if (block.vtx[i].IsCoinBase()) return state.DoS(100, error("CheckBlock() : more than one coinbase")); // Check transactions - BOOST_FOREACH(const CTransaction& tx, vtx) + BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!CheckTransaction(tx, state)) return error("CheckBlock() : CheckTransaction failed"); // Build the merkle tree already. We need it anyway later, and it makes the // block cache the transaction hashes, which means they don't need to be // recalculated many times during this block's validation. - BuildMerkleTree(); + block.BuildMerkleTree(); // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: set<uint256> uniqueTx; - for (unsigned int i=0; i<vtx.size(); i++) { - uniqueTx.insert(GetTxHash(i)); + for (unsigned int i = 0; i < block.vtx.size(); i++) { + uniqueTx.insert(block.GetTxHash(i)); } - if (uniqueTx.size() != vtx.size()) + if (uniqueTx.size() != block.vtx.size()) return state.DoS(100, error("CheckBlock() : duplicate transaction")); unsigned int nSigOps = 0; - BOOST_FOREACH(const CTransaction& tx, vtx) + BOOST_FOREACH(const CTransaction& tx, block.vtx) { nSigOps += GetLegacySigOpCount(tx); } @@ -2120,40 +2271,40 @@ bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerk return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkle root - if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) + if (fCheckMerkleRoot && block.hashMerkleRoot != block.BuildMerkleTree()) return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); return true; } -bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) +bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) { // Check for duplicate - uint256 hash = GetHash(); + uint256 hash = block.GetHash(); if (mapBlockIndex.count(hash)) return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex")); // Get prev block index CBlockIndex* pindexPrev = NULL; int nHeight = 0; - if (hash != hashGenesisBlock) { - map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); + if (hash != Params().HashGenesisBlock()) { + map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) return state.DoS(10, error("AcceptBlock() : prev block not found")); pindexPrev = (*mi).second; nHeight = pindexPrev->nHeight+1; // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev, this)) + if (block.nBits != GetNextWorkRequired(pindexPrev, &block)) return state.DoS(100, error("AcceptBlock() : incorrect proof of work")); // Check timestamp against prev - if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) + if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) return state.Invalid(error("AcceptBlock() : block's timestamp is too early")); // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, vtx) - if (!IsFinalTx(tx, nHeight, GetBlockTime())) + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) return state.DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint @@ -2161,23 +2312,23 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (nVersion < 2) + if (block.nVersion < 2) { - if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || - (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) + if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) { return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - if (nVersion >= 2) + if (block.nVersion >= 2) { // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || - (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) + if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) { CScript expect = CScript() << nHeight; - if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) + if (!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); } } @@ -2185,16 +2336,16 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) // Write block to history file try { - unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; if (dbp != NULL) blockPos = *dbp; - if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) + if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.nTime, dbp != NULL)) return error("AcceptBlock() : FindBlockPos failed"); if (dbp == NULL) - if (!WriteToDisk(blockPos)) + if (!WriteBlockToDisk(block, blockPos)) return state.Abort(_("Failed to write block")); - if (!AddToBlockIndex(state, blockPos)) + if (!AddToBlockIndex(block, state, blockPos)) return error("AcceptBlock() : AddToBlockIndex failed"); } catch(std::runtime_error &e) { return state.Abort(_("System error: ") + e.what()); @@ -2246,7 +2397,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString().c_str())); // Preliminary checks - if (!pblock->CheckBlock(state)) + if (!CheckBlock(*pblock, state)) return error("ProcessBlock() : CheckBlock FAILED"); CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); @@ -2287,7 +2438,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl } // Store to disk - if (!pblock->AcceptBlock(state, dbp)) + if (!AcceptBlock(*pblock, state, dbp)) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one @@ -2303,7 +2454,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl CBlock* pblockOrphan = (*mi).second; // 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; - if (pblockOrphan->AcceptBlock(stateDummy)) + if (AcceptBlock(*pblockOrphan, stateDummy)) vWorkQueue.push_back(pblockOrphan->GetHash()); mapOrphanBlocks.erase(pblockOrphan->GetHash()); delete pblockOrphan; @@ -2616,14 +2767,13 @@ bool static LoadBlockIndexDB() return true; } -bool VerifyDB() { +bool VerifyDB(int nCheckLevel, int nCheckDepth) +{ if (pindexBest == NULL || pindexBest->pprev == NULL) return true; // Verify blocks in the best chain - int nCheckLevel = GetArg("-checklevel", 3); - int nCheckDepth = GetArg( "-checkblocks", 288); - if (nCheckDepth == 0) + if (nCheckDepth <= 0) nCheckDepth = 1000000000; // suffices until the year 19000 if (nCheckDepth > nBestHeight) nCheckDepth = nBestHeight; @@ -2641,10 +2791,10 @@ bool VerifyDB() { break; CBlock block; // check level 0: read from disk - if (!block.ReadFromDisk(pindex)) - return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + if (!ReadBlockFromDisk(block, pindex)) + return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !block.CheckBlock(state)) + if (nCheckLevel >= 1 && !CheckBlock(block, state)) return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -2658,7 +2808,7 @@ bool VerifyDB() { // check level 3: check for inconsistencies during memory-only disconnect of tip blocks if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { bool fClean = true; - if (!block.DisconnectBlock(state, pindex, coins, &fClean)) + if (!DisconnectBlock(block, state, pindex, coins, &fClean)) return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); pindexState = pindex->pprev; if (!fClean) { @@ -2678,9 +2828,9 @@ bool VerifyDB() { boost::this_thread::interruption_point(); pindex = pindex->GetNextInMainChain(); CBlock block; - if (!block.ReadFromDisk(pindex)) - return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - if (!block.ConnectBlock(state, pindex, coins)) + if (!ReadBlockFromDisk(block, pindex)) + return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + if (!ConnectBlock(block, state, pindex, coins)) return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); } } @@ -2704,21 +2854,9 @@ void UnloadBlockIndex() bool LoadBlockIndex() { - if (fTestNet) - { - pchMessageStart[0] = 0x0b; - pchMessageStart[1] = 0x11; - pchMessageStart[2] = 0x09; - pchMessageStart[3] = 0x07; - hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"); - } - - // // Load block index from databases - // if (!fReindex && !LoadBlockIndexDB()) return false; - return true; } @@ -2735,55 +2873,17 @@ bool InitBlockIndex() { // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) if (!fReindex) { - // Genesis Block: - // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) - // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) - // CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) - // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) - // vMerkleTree: 4a5e1e - - // Genesis block - const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; - CTransaction txNew; - txNew.vin.resize(1); - txNew.vout.resize(1); - txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); - txNew.vout[0].nValue = 50 * COIN; - txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; - CBlock block; - block.vtx.push_back(txNew); - block.hashPrevBlock = 0; - block.hashMerkleRoot = block.BuildMerkleTree(); - block.nVersion = 1; - block.nTime = 1231006505; - block.nBits = 0x1d00ffff; - block.nNonce = 2083236893; - - if (fTestNet) - { - block.nTime = 1296688602; - block.nNonce = 414098458; - } - - //// debug print - uint256 hash = block.GetHash(); - printf("%s\n", hash.ToString().c_str()); - printf("%s\n", hashGenesisBlock.ToString().c_str()); - printf("%s\n", block.hashMerkleRoot.ToString().c_str()); - assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); - block.print(); - assert(hash == hashGenesisBlock); - - // Start new block file try { + CBlock &block = const_cast<CBlock&>(Params().GenesisBlock()); + // Start new block file unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; CValidationState state; if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime)) return error("LoadBlockIndex() : FindBlockPos failed"); - if (!block.WriteToDisk(blockPos)) + if (!WriteBlockToDisk(block, blockPos)) return error("LoadBlockIndex() : writing genesis block to disk failed"); - if (!block.AddToBlockIndex(state, blockPos)) + if (!AddToBlockIndex(block, state, blockPos)) return error("LoadBlockIndex() : genesis block not accepted"); } catch(std::runtime_error &e) { return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); @@ -2839,7 +2939,7 @@ void PrintBlockTree() // print item CBlock block; - block.ReadFromDisk(pindex); + ReadBlockFromDisk(block, pindex); printf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"", pindex->nHeight, pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos, @@ -2892,10 +2992,10 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) try { // locate a header unsigned char buf[4]; - blkdat.FindByte(pchMessageStart[0]); + blkdat.FindByte(Params().MessageStart()[0]); nRewind = blkdat.GetPos()+1; blkdat >> FLATDATA(buf); - if (memcmp(buf, pchMessageStart, 4)) + if (memcmp(buf, Params().MessageStart(), 4)) continue; // read size blkdat >> nSize; @@ -3040,12 +3140,6 @@ bool static AlreadyHave(const CInv& inv) -// The message start string is designed to be unlikely to occur in normal data. -// The characters are rarely used upper ASCII, not valid as UTF-8, and produce -// a large 4-byte int at any alignment. -unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; - - void static ProcessGetData(CNode* pfrom) { std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); @@ -3069,7 +3163,7 @@ void static ProcessGetData(CNode* pfrom) if (mi != mapBlockIndex.end()) { CBlock block; - block.ReadFromDisk((*mi).second); + ReadBlockFromDisk(block, (*mi).second); if (inv.type == MSG_BLOCK) pfrom->PushMessage("block", block); else // MSG_FILTERED_BLOCK) @@ -3504,6 +3598,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); + // Truncate messages to the size of the tx in them + unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + unsigned int oldSize = vMsg.size(); + if (nSize < oldSize) { + vMsg.resize(nSize); + printf("truncating oversized TX %s (%u -> %u)\n", + tx.GetHash().ToString().c_str(), + oldSize, nSize); + } + bool fMissingInputs = false; CValidationState state; if (mempool.accept(state, tx, true, &fMissingInputs)) @@ -3768,7 +3872,7 @@ bool ProcessMessages(CNode* pfrom) it++; // Scan for message start - if (memcmp(msg.hdr.pchMessageStart, pchMessageStart, sizeof(pchMessageStart)) != 0) { + if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { printf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); fOk = false; break; @@ -4186,7 +4290,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay - unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000); + unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); // Minimum block size you want to create; block will be filled with free transactions @@ -4314,7 +4418,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) // Prioritize by fee once past the priority size or we run out of high-priority // transactions: if (!fSortedByFee && - ((nBlockSize + nTxSize >= nBlockPrioritySize) || (dPriority < COIN * 144 / 250))) + ((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority))) { fSortedByFee = true; comparer = TxPriorityCompare(fSortedByFee); @@ -4391,7 +4495,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) indexDummy.nHeight = pindexPrev->nHeight + 1; CCoinsViewCache viewNew(*pcoinsTip, true); CValidationState state; - if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true)) + if (!ConnectBlock(*pblock, state, &indexDummy, viewNew, true)) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); } @@ -4512,8 +4616,12 @@ void static BitcoinMiner(CWallet *pwallet) unsigned int nExtraNonce = 0; try { loop { - while (vNodes.empty()) - MilliSleep(1000); + if (Params().NetworkID() != CChainParams::REGTEST) { + // Busy-wait for the network to come online so we don't waste time mining + // on an obsolete chain. In regtest mode we expect to fly solo. + while (vNodes.empty()) + MilliSleep(1000); + } // // Create new block @@ -4575,6 +4683,12 @@ void static BitcoinMiner(CWallet *pwallet) SetThreadPriority(THREAD_PRIORITY_NORMAL); CheckWork(pblock, *pwalletMain, reservekey); SetThreadPriority(THREAD_PRIORITY_LOWEST); + + // In regression test mode, stop mining after a block is found. This + // allows developers to controllably generate a block on demand. + if (Params().NetworkID() == CChainParams::REGTEST) + throw boost::thread_interrupted(); + break; } } @@ -4610,7 +4724,7 @@ void static BitcoinMiner(CWallet *pwallet) // Check for stop or if block needs to be rebuilt boost::this_thread::interruption_point(); - if (vNodes.empty()) + if (vNodes.empty() && Params().NetworkID() != CChainParams::REGTEST) break; if (nBlockNonce >= 0xffff0000) break; @@ -4622,7 +4736,7 @@ void static BitcoinMiner(CWallet *pwallet) // Update nTime every few seconds UpdateTime(*pblock, pindexPrev); nBlockTime = ByteReverse(pblock->nTime); - if (fTestNet) + if (TestNet()) { // Changing pblock->nTime can change work required on testnet: nBlockBits = ByteReverse(pblock->nBits); @@ -4642,8 +4756,12 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet) static boost::thread_group* minerThreads = NULL; int nThreads = GetArg("-genproclimit", -1); - if (nThreads < 0) - nThreads = boost::thread::hardware_concurrency(); + if (nThreads < 0) { + if (Params().NetworkID() == CChainParams::REGTEST) + nThreads = 1; + else + nThreads = boost::thread::hardware_concurrency(); + } if (minerThreads != NULL) { @@ -4660,59 +4778,6 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet) minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); } -// Amount compression: -// * If the amount is 0, output 0 -// * first, divide the amount (in base units) by the largest power of 10 possible; call the exponent e (e is max 9) -// * if e<9, the last digit of the resulting number cannot be 0; store it as d, and drop it (divide by 10) -// * call the result n -// * output 1 + 10*(9*n + d - 1) + e -// * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9 -// (this is decodable, as d is in [1-9] and e is in [0-9]) - -uint64 CTxOutCompressor::CompressAmount(uint64 n) -{ - if (n == 0) - return 0; - int e = 0; - while (((n % 10) == 0) && e < 9) { - n /= 10; - e++; - } - if (e < 9) { - int d = (n % 10); - assert(d >= 1 && d <= 9); - n /= 10; - return 1 + (n*9 + d - 1)*10 + e; - } else { - return 1 + (n - 1)*10 + 9; - } -} - -uint64 CTxOutCompressor::DecompressAmount(uint64 x) -{ - // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9 - if (x == 0) - return 0; - x--; - // x = 10*(9*n + d - 1) + e - int e = x % 10; - x /= 10; - uint64 n = 0; - if (e < 9) { - // x = 9*n + d - 1 - int d = (x % 9) + 1; - x /= 9; - // x = n - n = x*10 + d; - } else { - n = x+1; - } - while (e) { - n *= 10; - e--; - } - return n; -} class CMainCleanup |