X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=78a7ec3d275cdcf01e04ee4eab143e52b55e9075;hb=27960a36de879634ee8c491d0f4fe8160e8a4f75;hp=8949b97be659fedc3a40b48d14551252589f8d06;hpb=e89b9f6a2abaa120ff0fc3cea9ae364e8cbd25e4;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 8949b97..78a7ec3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,13 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" +#include "checkpoints.h" #include "db.h" #include "net.h" #include "init.h" -#include "cryptopp/sha.h" +#include #include using namespace std; @@ -15,16 +17,19 @@ using namespace boost; // Global state // +CCriticalSection cs_setpwalletRegistered; +set setpwalletRegistered; + CCriticalSection cs_main; -map mapTransactions; +static map mapTransactions; CCriticalSection cs_mapTransactions; unsigned int nTransactionsUpdated = 0; map mapNextTx; map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); -CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); +static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; CBigNum bnBestChainWork = 0; @@ -33,31 +38,14 @@ uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; int64 nTimeBestReceived = 0; +CMedianFilter cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have + map mapOrphanBlocks; multimap mapOrphanBlocksByPrev; map mapOrphanTransactions; multimap mapOrphanTransactionsByPrev; -map mapWallet; -vector vWalletUpdated; -CCriticalSection cs_mapWallet; - -map, CPrivKey> mapKeys; -map > mapPubKeys; -CCriticalSection cs_mapKeys; -CKey keyUser; - -map mapRequestCount; -CCriticalSection cs_mapRequestCount; - -map mapAddressBook; -CCriticalSection cs_mapAddressBook; - -set setKeyPool; -CCriticalSection cs_setKeyPool; - -vector vchDefaultKey; double dHashesPerSec; int64 nHPSTimerStart; @@ -65,7 +53,6 @@ int64 nHPSTimerStart; // Settings int fGenerateBitcoins = false; int64 nTransactionFee = 0; -CAddress addrIncoming; int fLimitProcessors = false; int nLimitProcessors = 1; int fMinimizeToTray = true; @@ -77,6 +64,98 @@ int fUseUPnP = false; #endif +////////////////////////////////////////////////////////////////////////////// +// +// dispatching functions +// + +// These functions dispatch to one or all registered wallets + + +void RegisterWallet(CWallet* pwalletIn) +{ + CRITICAL_BLOCK(cs_setpwalletRegistered) + { + setpwalletRegistered.insert(pwalletIn); + } +} + +void UnregisterWallet(CWallet* pwalletIn) +{ + CRITICAL_BLOCK(cs_setpwalletRegistered) + { + setpwalletRegistered.erase(pwalletIn); + } +} + +// check whether the passed transaction is from us +bool static IsFromMe(CTransaction& tx) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->IsFromMe(tx)) + return true; + return false; +} + +// get the wallet transaction with the given hash (if it exists) +bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + if (pwallet->GetTransaction(hashTx,wtx)) + return true; + return false; +} + +// erases transaction with the given hash from all wallets +void static EraseFromWallets(uint256 hash) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->EraseFromWallet(hash); +} + +// make sure all wallets know about the given transaction, in the given block +void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); +} + +// notify wallets about a new best chain +void static SetBestChain(const CBlockLocator& loc) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->SetBestChain(loc); +} + +// notify wallets about an updated transaction +void static UpdatedTransaction(const uint256& hashTx) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->UpdatedTransaction(hashTx); +} + +// dump all wallets +void static PrintWallets(const CBlock& block) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->PrintWallet(block); +} + +// notify wallets about an incoming inventory (for request counts) +void static Inventory(const uint256& hash) +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->Inventory(hash); +} + +// ask wallets to resend their transactions +void static ResendWalletTransactions() +{ + BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) + pwallet->ResendWalletTransactions(); +} + + @@ -94,12 +173,13 @@ void AddOrphanTx(const CDataStream& vMsg) uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) return; + CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg); BOOST_FOREACH(const CTxIn& txin, tx.vin) mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); } -void EraseOrphanTx(uint256 hash) +void static EraseOrphanTx(uint256 hash) { if (!mapOrphanTransactions.count(hash)) return; @@ -121,6 +201,23 @@ void EraseOrphanTx(uint256 hash) mapOrphanTransactions.erase(hash); } +int LimitOrphanTxSize(int nMaxOrphans) +{ + int nEvicted = 0; + while (mapOrphanTransactions.size() > nMaxOrphans) + { + // Evict a random orphan: + std::vector randbytes(32); + RAND_bytes(&randbytes[0], 32); + uint256 randomhash(randbytes); + map::iterator it = mapOrphanTransactions.lower_bound(randomhash); + if (it == mapOrphanTransactions.end()) + it = mapOrphanTransactions.begin(); + EraseOrphanTx(it->first); + ++nEvicted; + } + return nEvicted; +} @@ -223,36 +320,46 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) bool CTransaction::CheckTransaction() const { // Basic checks that don't depend on any context - if (vin.empty() || vout.empty()) - return error("CTransaction::CheckTransaction() : vin or vout empty"); - + if (vin.empty()) + return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); + if (vout.empty()) + return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); // Size limits if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) - return error("CTransaction::CheckTransaction() : size limits failed"); + return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); // Check for negative or overflow output values int64 nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, vout) { if (txout.nValue < 0) - return error("CTransaction::CheckTransaction() : txout.nValue negative"); + return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); if (txout.nValue > MAX_MONEY) - return error("CTransaction::CheckTransaction() : txout.nValue too high"); + return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return error("CTransaction::CheckTransaction() : txout total out of range"); + return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); + } + + // Check for duplicate inputs + set vInOutPoints; + BOOST_FOREACH(const CTxIn& txin, vin) + { + if (vInOutPoints.count(txin.prevout)) + return false; + vInOutPoints.insert(txin.prevout); } if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return error("CTransaction::CheckTransaction() : coinbase script size"); + return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); } else { BOOST_FOREACH(const CTxIn& txin, vin) if (txin.prevout.IsNull()) - return error("CTransaction::CheckTransaction() : prevout is null"); + return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); } return true; @@ -268,7 +375,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi // Coinbase is only valid in a block, not as a loose transaction if (IsCoinBase()) - return error("AcceptToMemoryPool() : coinbase as individual tx"); + return DoS(100, error("AcceptToMemoryPool() : coinbase as individual tx")); // To help v0.1.5 clients who would see it as a negative number if ((int64)nLockTime > INT_MAX) @@ -281,7 +388,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi // 34 bytes because a TxOut is: // 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length if (GetSigOpCount() > nSize / 34 || nSize < 100) - return error("AcceptToMemoryPool() : nonstandard transaction"); + return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount"); // Rather not work on nonstandard transactions (unless -testnet) if (!fTestNet && !IsStandard()) @@ -329,10 +436,11 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi // Check against previous transactions map mapUnused; int64 nFees = 0; - if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false)) + bool fInvalid = false; + if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, 0, fInvalid)) { - if (pfMissingInputs) - *pfMissingInputs = true; + if (fInvalid) + return error("AcceptToMemoryPool() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } @@ -357,7 +465,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi nLastTime = nNow; // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB - if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe()) + if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(*this)) return error("AcceptToMemoryPool() : free transaction rejected by rate limiter"); if (fDebug) printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); @@ -380,7 +488,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi ///// are we sure this is ok when loading transactions or restoring block txes // If updated, erase old tx from wallet if (ptxOld) - EraseFromWallet(ptxOld->GetHash()); + EraseFromWallets(ptxOld->GetHash()); printf("AcceptToMemoryPool(): accepted %s\n", hash.ToString().substr(0,10).c_str()); return true; @@ -551,8 +659,7 @@ bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) return true; } - -uint256 GetOrphanRoot(const CBlock* pblock) +uint256 static GetOrphanRoot(const CBlock* pblock) { // Work back to the first block in the orphan chain while (mapOrphanBlocks.count(pblock->hashPrevBlock)) @@ -560,7 +667,7 @@ uint256 GetOrphanRoot(const CBlock* pblock) return pblock->GetHash(); } -int64 GetBlockValue(int nHeight, int64 nFees) +int64 static GetBlockValue(int nHeight, int64 nFees) { int64 nSubsidy = 50 * COIN; @@ -570,19 +677,65 @@ int64 GetBlockValue(int nHeight, int64 nFees) return nSubsidy + nFees; } -unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast) +static const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks +static const int64 nTargetSpacing = 10 * 60; +static const int64 nInterval = nTargetTimespan / nTargetSpacing; + +// +// minimum amount of work that could possibly be required nTime after +// minimum work required was nBase +// +unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) { - const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks - const int64 nTargetSpacing = 10 * 60; - const int64 nInterval = nTargetTimespan / nTargetSpacing; + // Testnet has min-difficulty blocks + // after nTargetSpacing*2 time between blocks: + if (fTestNet && nTime > nTargetSpacing*2) + return bnProofOfWorkLimit.GetCompact(); + + CBigNum bnResult; + bnResult.SetCompact(nBase); + while (nTime > 0 && bnResult < bnProofOfWorkLimit) + { + // Maximum 400% adjustment... + bnResult *= 4; + // ... in best-case exactly 4-times-normal target time + nTime -= nTargetTimespan*4; + } + if (bnResult > bnProofOfWorkLimit) + bnResult = bnProofOfWorkLimit; + return bnResult.GetCompact(); +} + +unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlock *pblock) +{ + unsigned int nProofOfWorkLimit = bnProofOfWorkLimit.GetCompact(); // Genesis block if (pindexLast == NULL) - return bnProofOfWorkLimit.GetCompact(); + return nProofOfWorkLimit; // Only change once per interval if ((pindexLast->nHeight+1) % nInterval != 0) + { + // Special rules for testnet after 15 Feb 2012: + if (fTestNet && pblock->nTime > 1329264000) + { + // 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) + return nProofOfWorkLimit; + else + { + // Return the last non-special-min-difficulty-rules-block + const CBlockIndex* pindex = pindexLast; + while (pindex->pprev && pindex->nHeight % nInterval != 0 && pindex->nBits == nProofOfWorkLimit) + pindex = pindex->pprev; + return pindex->nBits; + } + } + return pindexLast->nBits; + } // Go back by what we want to be 14 days worth of blocks const CBlockIndex* pindexFirst = pindexLast; @@ -632,9 +785,15 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits) return true; } +// Return maximum amount of blocks that other nodes claim to have +int GetNumBlocksOfPeers() +{ + return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate()); +} + bool IsInitialBlockDownload() { - if (pindexBest == NULL || (!fTestNet && nBestHeight < 118000)) + if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; @@ -647,7 +806,7 @@ bool IsInitialBlockDownload() pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); } -void InvalidChainFound(CBlockIndex* pindexNew) +void static InvalidChainFound(CBlockIndex* pindexNew) { if (pindexNew->bnChainWork > bnBestInvalidWork) { @@ -661,6 +820,15 @@ void InvalidChainFound(CBlockIndex* pindexNew) printf("InvalidChainFound: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); } +void CBlock::UpdateTime(const CBlockIndex* pindexPrev) +{ + nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + + // Updating time can change work required on testnet: + if (fTestNet) + nBits = GetNextWorkRequired(pindexPrev, this); +} + @@ -698,17 +866,29 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb) } // Remove transaction from index - if (!txdb.EraseTxIndex(*this)) - return error("DisconnectInputs() : EraseTxPos failed"); + // This can fail if a duplicate of this transaction was in a chain that got + // reorganized away. This is only possible if this transaction was completely + // spent, so erasing it would be a no-op anway. + txdb.EraseTxIndex(*this); return true; } bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, - CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) + CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee, + bool& fInvalid) { + // FetchInputs can return false either because we just haven't seen some inputs + // (in which case the transaction should be stored as an orphan) + // or because the transaction is malformed (in which case the transaction should + // be dropped). If tx is definitely invalid, fInvalid will be set to true. + fInvalid = false; + // Take over previous transactions' spent pointers + // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain + // fMiner is true when called from the internal bitcoin miner + // ... both are false when called from CTransaction::AcceptToMemoryPool if (!IsCoinBase()) { int64 nValueIn = 0; @@ -719,7 +899,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPoo // Read txindex CTxIndex txindex; bool fFound = true; - if (fMiner && mapTestPool.count(prevout.hash)) + if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) { // Get txindex from current proposed changes txindex = mapTestPool[prevout.hash]; @@ -754,7 +934,12 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPoo } if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) - return error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()); + { + // Revisit this if/when transaction replacement is implemented and allows + // adding inputs: + fInvalid = true; + return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); + } // If prev is coinbase, check that it's matured if (txPrev.IsCoinBase()) @@ -762,53 +947,53 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPoo if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); - // Verify signature - if (!VerifySignature(txPrev, *this, i)) - return error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); - - // Check for conflicts + // Skip ECDSA signature verification when connecting blocks (fBlock=true) + // before the last blockchain checkpoint. This is safe because block merkle hashes are + // still computed and checked, and any change will be caught at the next checkpoint. + if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate()))) + // Verify signature + if (!VerifySignature(txPrev, *this, i)) + return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + + // Check for conflicts (double-spend) + // This doesn't trigger the DoS code on purpose; if it did, it would make it easier + // for an attacker to attempt to split the network. if (!txindex.vSpent[prevout.n].IsNull()) return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); // Check for negative or overflow input values nValueIn += txPrev.vout[prevout.n].nValue; if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return error("ConnectInputs() : txin values out of range"); + return DoS(100, error("ConnectInputs() : txin values out of range")); // Mark outpoints as spent txindex.vSpent[prevout.n] = posThisTx; // Write back - if (fBlock) - { - if (!txdb.UpdateTxIndex(prevout.hash, txindex)) - return error("ConnectInputs() : UpdateTxIndex failed"); - } - else if (fMiner) + if (fBlock || fMiner) { mapTestPool[prevout.hash] = txindex; } } if (nValueIn < GetValueOut()) - return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()); + return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); // Tally transaction fees int64 nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) - return error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()); + return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); if (nTxFee < nMinFee) return false; nFees += nTxFee; if (!MoneyRange(nFees)) - return error("ConnectInputs() : nFees out of range"); + return DoS(100, error("ConnectInputs() : nFees out of range")); } if (fBlock) { - // Add transaction to disk index - if (!txdb.AddTxIndex(*this, posThisTx, pindexBlock->nHeight)) - return error("ConnectInputs() : AddTxPos failed"); + // Add transaction to changes + mapTestPool[GetHash()] = CTxIndex(posThisTx, vout.size()); } else if (fMiner) { @@ -894,19 +1079,46 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) if (!CheckBlock()) return false; + // Do not allow blocks that contain transactions which 'overwrite' older transactions, + // unless those are already completely spent. + // If such overwrites are allowed, coinbases and transactions depending upon those + // can be duplicated to remove the ability to spend the first instance -- even after + // being sent to another address. + // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. + // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool + // already refuses previously-known transaction id's entirely. + // This rule applies to all blocks whose timestamp is after March 15, 2012, 0:00 UTC. + // On testnet it is enabled as of februari 20, 2012, 0:00 UTC. + if (pindex->nTime > 1331769600 || (fTestNet && pindex->nTime > 1329696000)) + BOOST_FOREACH(CTransaction& tx, vtx) + { + CTxIndex txindexOld; + if (txdb.ReadTxIndex(tx.GetHash(), txindexOld)) + BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent) + if (pos.IsNull()) + return false; + } + //// issue here: it doesn't know the version unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); - map mapUnused; + map mapQueuedChanges; int64 nFees = 0; BOOST_FOREACH(CTransaction& tx, vtx) { CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); nTxPos += ::GetSerializeSize(tx, SER_DISK); - if (!tx.ConnectInputs(txdb, mapUnused, posThisTx, pindex, nFees, true, false)) + bool fInvalid; + if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false, 0, fInvalid)) return false; } + // Write queued txindex changes + for (map::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) + { + if (!txdb.UpdateTxIndex((*mi).first, (*mi).second)) + return error("ConnectBlock() : UpdateTxIndex failed"); + } if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) return false; @@ -923,12 +1135,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) // Watch for transactions paying to me BOOST_FOREACH(CTransaction& tx, vtx) - AddToWalletIfInvolvingMe(tx, this, true); + SyncWithWallets(tx, this, true); return true; } -bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) +bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) { printf("REORGANIZE\n"); @@ -1066,10 +1278,8 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) // Update best block in wallet (so we can detect restored wallets) if (!IsInitialBlockDownload()) { - CWalletDB walletdb; const CBlockLocator locator(pindexNew); - if (!walletdb.WriteBestBlock(locator)) - return error("SetBestChain() : WriteWalletBest failed"); + ::SetBestChain(locator); } // New best block @@ -1123,8 +1333,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) { // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; - CRITICAL_BLOCK(cs_mapWallet) - vWalletUpdated.push_back(hashPrevBestCoinBase); + UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = vtx[0].GetHash(); } @@ -1142,11 +1351,11 @@ bool CBlock::CheckBlock() const // Size limits if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) - return error("CheckBlock() : size limits failed"); + return DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount if (!CheckProofOfWork(GetHash(), nBits)) - return error("CheckBlock() : proof of work failed"); + return DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) @@ -1154,23 +1363,23 @@ bool CBlock::CheckBlock() const // First transaction must be coinbase, the rest must not be if (vtx.empty() || !vtx[0].IsCoinBase()) - return error("CheckBlock() : first tx is not coinbase"); + return DoS(100, error("CheckBlock() : first tx is not coinbase")); for (int i = 1; i < vtx.size(); i++) if (vtx[i].IsCoinBase()) - return error("CheckBlock() : more than one coinbase"); + return DoS(100, error("CheckBlock() : more than one coinbase")); // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.CheckTransaction()) - return error("CheckBlock() : CheckTransaction failed"); + return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); // Check that it's not full of nonstandard transactions if (GetSigOpCount() > MAX_BLOCK_SIGOPS) - return error("CheckBlock() : too many nonstandard transactions"); + return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkleroot if (hashMerkleRoot != BuildMerkleTree()) - return error("CheckBlock() : hashMerkleRoot mismatch"); + return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); return true; } @@ -1185,13 +1394,13 @@ bool CBlock::AcceptBlock() // Get prev block index map::iterator mi = mapBlockIndex.find(hashPrevBlock); if (mi == mapBlockIndex.end()) - return error("AcceptBlock() : prev block not found"); + return DoS(10, error("AcceptBlock() : prev block not found")); CBlockIndex* pindexPrev = (*mi).second; int nHeight = pindexPrev->nHeight+1; // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev)) - return error("AcceptBlock() : incorrect proof of work"); + if (nBits != GetNextWorkRequired(pindexPrev, this)) + return DoS(100, error("AcceptBlock() : incorrect proof of work")); // Check timestamp against prev if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) @@ -1200,18 +1409,11 @@ bool CBlock::AcceptBlock() // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.IsFinal(nHeight, GetBlockTime())) - return error("AcceptBlock() : contains a non-final transaction"); + return DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint - if (!fTestNet) - if ((nHeight == 11111 && hash != uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) || - (nHeight == 33333 && hash != uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) || - (nHeight == 68555 && hash != uint256("0x00000000001e1b4903550a0b96e9a9405c8a95f387162e4944e8d9fbe501cd6a")) || - (nHeight == 70567 && hash != uint256("0x00000000006a49b14bcf27462068f1264c961f11fa2e0eddd2be0791e1d4124a")) || - (nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) || - (nHeight == 105000 && hash != uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) || - (nHeight == 118000 && hash != uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553"))) - return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight); + if (!Checkpoints::CheckBlock(nHeight, hash)) + return DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight)); // Write block to history file if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK))) @@ -1227,7 +1429,7 @@ bool CBlock::AcceptBlock() if (hashBestChain == hash) CRITICAL_BLOCK(cs_vNodes) BOOST_FOREACH(CNode* pnode, vNodes) - if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 118000)) + if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 140700)) pnode->PushInventory(CInv(MSG_BLOCK, hash)); return true; @@ -1246,6 +1448,30 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) if (!pblock->CheckBlock()) return error("ProcessBlock() : CheckBlock FAILED"); + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && pblock->hashPrevBlock != hashBestChain) + { + // Extra checks to prevent "fill up memory by spamming with bogus blocks" + int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; + if (deltaTime < 0) + { + if (pfrom) + pfrom->Misbehaving(100); + return error("ProcessBlock() : block with timestamp before last checkpoint"); + } + CBigNum bnNewBlock; + bnNewBlock.SetCompact(pblock->nBits); + CBigNum bnRequired; + bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); + if (bnNewBlock > bnRequired) + { + if (pfrom) + pfrom->Misbehaving(100); + return error("ProcessBlock() : block with too little proof-of-work"); + } + } + + // If don't already have its previous block, shunt it off to holding area until we get it if (!mapBlockIndex.count(pblock->hashPrevBlock)) { @@ -1294,47 +1520,6 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) -template -bool ScanMessageStart(Stream& s) -{ - // Scan ahead to the next pchMessageStart, which should normally be immediately - // at the file pointer. Leaves file pointer at end of pchMessageStart. - s.clear(0); - short prevmask = s.exceptions(0); - const char* p = BEGIN(pchMessageStart); - try - { - loop - { - char c; - s.read(&c, 1); - if (s.fail()) - { - s.clear(0); - s.exceptions(prevmask); - return false; - } - if (*p != c) - p = BEGIN(pchMessageStart); - if (*p == c) - { - if (++p == END(pchMessageStart)) - { - s.clear(0); - s.exceptions(prevmask); - return true; - } - } - } - } - catch (...) - { - s.clear(0); - s.exceptions(prevmask); - return false; - } -} - bool CheckDiskSpace(uint64 nAdditionalBytes) { uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; @@ -1510,7 +1695,7 @@ void PrintBlockTree() for (int i = 0; i < nCol; i++) printf("| "); printf("|\n"); - } + } nPrevCol = nCol; // print columns @@ -1528,16 +1713,7 @@ void PrintBlockTree() DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), block.vtx.size()); - CRITICAL_BLOCK(cs_mapWallet) - { - if (mapWallet.count(block.vtx[0].GetHash())) - { - CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; - printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit()); - } - } - printf("\n"); - + PrintWallets(block); // put the main timechain first vector& vNext = mapNext[pindex]; @@ -1613,7 +1789,7 @@ string GetWarnings(string strFor) return strStatusBar; else if (strFor == "rpc") return strRPC; - assert(("GetWarnings() : invalid parameter", false)); + assert(!"GetWarnings() : invalid parameter"); return "error"; } @@ -1677,7 +1853,7 @@ bool CAlert::ProcessAlert() // -bool AlreadyHave(CTxDB& txdb, const CInv& inv) +bool static AlreadyHave(CTxDB& txdb, const CInv& inv) { switch (inv.type) { @@ -1694,16 +1870,17 @@ bool AlreadyHave(CTxDB& txdb, 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. -char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; +unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; -bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) +bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { static map > mapReuseKey; RandAddSeedPerfmon(); - if (fDebug) + if (fDebug) { printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); - printf("received: %s (%d bytes)\n", strCommand.c_str(), vRecv.size()); + printf("received: %s (%d bytes)\n", strCommand.c_str(), vRecv.size()); + } if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { printf("dropmessagestest DROPPING RECV MESSAGE\n"); @@ -1718,7 +1895,10 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // Each connection can only send one version message if (pfrom->nVersion != 0) + { + pfrom->Misbehaving(1); return false; + } int64 nTime; CAddress addrMe; @@ -1780,7 +1960,9 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Ask the first connected node for block updates static int nAskedForBlocks; - if (!pfrom->fClient && (nAskedForBlocks < 1 || vNodes.size() <= 1)) + if (!pfrom->fClient && + (pfrom->nVersion < 32000 || pfrom->nVersion >= 32400) && + (nAskedForBlocks < 1 || vNodes.size() <= 1)) { nAskedForBlocks++; pfrom->PushGetBlocks(pindexBest, uint256(0)); @@ -1794,12 +1976,15 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); + + cPeerBlockCounts.input(pfrom->nStartingHeight); } else if (pfrom->nVersion == 0) { // Must have a version message before anything else + pfrom->Misbehaving(1); return false; } @@ -1821,9 +2006,14 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000) return true; if (vAddr.size() > 1000) + { + pfrom->Misbehaving(20); return error("message addr size() = %d", vAddr.size()); + } // Store the new addresses + CAddrDB addrDB; + addrDB.TxnBegin(); int64 nNow = GetAdjustedTime(); int64 nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) @@ -1835,7 +2025,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) continue; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; - AddAddress(addr, 2 * 60 * 60); + AddAddress(addr, 2 * 60 * 60, &addrDB); pfrom->AddAddressKnown(addr); if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { @@ -1866,6 +2056,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } } } + addrDB.TxnCommit(); // Save addresses (it's ok if this fails) if (vAddr.size() < 1000) pfrom->fGetAddr = false; } @@ -1876,7 +2067,10 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vInv; vRecv >> vInv; if (vInv.size() > 50000) + { + pfrom->Misbehaving(20); return error("message inv size() = %d", vInv.size()); + } CTxDB txdb("r"); BOOST_FOREACH(const CInv& inv, vInv) @@ -1886,7 +2080,8 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(txdb, inv); - printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); + if (fDebug) + printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); if (!fAlreadyHave) pfrom->AskFor(inv); @@ -1894,12 +2089,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); // Track requests for our stuff - CRITICAL_BLOCK(cs_mapRequestCount) - { - map::iterator mi = mapRequestCount.find(inv.hash); - if (mi != mapRequestCount.end()) - (*mi).second++; - } + Inventory(inv.hash); } } @@ -1909,7 +2099,10 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vInv; vRecv >> vInv; if (vInv.size() > 50000) + { + pfrom->Misbehaving(20); return error("message getdata size() = %d", vInv.size()); + } BOOST_FOREACH(const CInv& inv, vInv) { @@ -1952,12 +2145,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } // Track requests for our stuff - CRITICAL_BLOCK(cs_mapRequestCount) - { - map::iterator mi = mapRequestCount.find(inv.hash); - if (mi != mapRequestCount.end()) - (*mi).second++; - } + Inventory(inv.hash); } } @@ -1975,20 +2163,24 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pindex) pindex = pindex->pnext; int nLimit = 500 + locator.GetDistanceBack(); + unsigned int nBytes = 0; printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); for (; pindex; pindex = pindex->pnext) { if (pindex->GetBlockHash() == hashStop) { - printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + printf(" getblocks stopping at %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); - if (--nLimit <= 0) + CBlock block; + block.ReadFromDisk(pindex, true); + nBytes += block.GetSerializeSize(SER_NETWORK); + if (--nLimit <= 0 || nBytes >= SendBufferSize()/2) { // When this block is requested, we'll send an inv that'll make them // getblocks the next batch of inventory. - printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); + printf(" getblocks stopping at limit %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes); pfrom->hashContinue = pindex->GetBlockHash(); break; } @@ -2045,7 +2237,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) bool fMissingInputs = false; if (tx.AcceptToMemoryPool(true, &fMissingInputs)) { - AddToWalletIfInvolvingMe(tx, NULL, true); + SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -2066,7 +2258,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (tx.AcceptToMemoryPool(true)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); - AddToWalletIfInvolvingMe(tx, NULL, true); + SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -2081,7 +2273,13 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); AddOrphanTx(vMsg); + + // DoS prevention: do not allow mapOrphanTransactions to grow unbounded + int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); + if (nEvicted > 0) + printf("mapOrphan overflow, removed %d tx\n", nEvicted); } + if (tx.nDoS) pfrom->Misbehaving(tx.nDoS); } @@ -2098,6 +2296,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (ProcessBlock(pfrom, &block)) mapAlreadyAskedFor.erase(inv); + if (block.nDoS) pfrom->Misbehaving(block.nDoS); } @@ -2143,7 +2342,7 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Keep giving the same key to the same ip until they use it if (!mapReuseKey.count(pfrom->addr.ip)) - mapReuseKey[pfrom->addr.ip] = GetKeyFromKeyPool(); + pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr.ip], true); // Send back approval of order and pubkey to use CScript scriptPubKey; @@ -2152,37 +2351,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } - else if (strCommand == "submitorder") - { - uint256 hashReply; - vRecv >> hashReply; - - if (!GetBoolArg("-allowreceivebyip")) - { - pfrom->PushMessage("reply", hashReply, (int)2); - return true; - } - - CWalletTx wtxNew; - vRecv >> wtxNew; - wtxNew.fFromMe = false; - - // Broadcast - if (!wtxNew.AcceptWalletTransaction()) - { - pfrom->PushMessage("reply", hashReply, (int)1); - return error("submitorder AcceptWalletTransaction() failed, returning error 1"); - } - wtxNew.fTimeReceivedIsTxTime = true; - AddToWallet(wtxNew); - wtxNew.RelayWalletTransaction(); - mapReuseKey.erase(pfrom->addr.ip); - - // Send back confirmation - pfrom->PushMessage("reply", hashReply, (int)0); - } - - else if (strCommand == "reply") { uint256 hashReply; @@ -2239,7 +2407,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; } - bool ProcessMessages(CNode* pfrom) { CDataStream& vRecv = pfrom->vRecv; @@ -2359,8 +2526,6 @@ bool ProcessMessages(CNode* pfrom) } - - bool SendMessages(CNode* pto, bool fSendTrickle) { CRITICAL_BLOCK(cs_main) @@ -2483,16 +2648,10 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // always trickle our own transactions if (!fTrickleWait) { - TRY_CRITICAL_BLOCK(cs_mapWallet) - { - map::iterator mi = mapWallet.find(inv.hash); - if (mi != mapWallet.end()) - { - CWalletTx& wtx = (*mi).second; - if (wtx.fFromMe) - fTrickleWait = true; - } - } + CWalletTx wtx; + if (GetTransaction(inv.hash, wtx)) + if (wtx.fFromMe) + fTrickleWait = true; } if (fTrickleWait) @@ -2538,6 +2697,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vGetData.clear(); } } + mapAlreadyAskedFor[inv] = nNow; pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) @@ -2565,7 +2725,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // BitcoinMiner // -int FormatHashBlocks(void* pbuffer, unsigned int len) +int static FormatHashBlocks(void* pbuffer, unsigned int len) { unsigned char* pdata = (unsigned char*)pbuffer; unsigned int blocks = 1 + ((len + 8) / 64); @@ -2580,15 +2740,25 @@ int FormatHashBlocks(void* pbuffer, unsigned int len) return blocks; } -using CryptoPP::ByteReverse; - static const unsigned int pSHA256InitState[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; -inline void SHA256Transform(void* pstate, void* pinput, const void* pinit) +void SHA256Transform(void* pstate, void* pinput, const void* pinit) { - memcpy(pstate, pinit, 32); - CryptoPP::SHA256::Transform((CryptoPP::word32*)pstate, (CryptoPP::word32*)pinput); + SHA256_CTX ctx; + unsigned char data[64]; + + SHA256_Init(&ctx); + + for (int i = 0; i < 16; i++) + ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]); + + for (int i = 0; i < 8; i++) + ctx.h[i] = ((uint32_t*)pinit)[i]; + + SHA256_Update(&ctx, data, sizeof(data)); + for (int i = 0; i < 8; i++) + ((uint32_t*)pstate)[i] = ctx.h[i]; } // @@ -2598,7 +2768,7 @@ inline void SHA256Transform(void* pstate, void* pinput, const void* pinit) // between calls, but periodically or if nNonce is 0xffff0000 or above, // the block is rebuilt and nNonce starts over at zero. // -unsigned int ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) +unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) { unsigned int& nNonce = *(unsigned int*)(pdata + 12); for (;;) @@ -2624,7 +2794,7 @@ unsigned int ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* } } - +// Some explaining would be appreciated class COrphan { public: @@ -2752,12 +2922,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Transaction fee required depends on block size bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); - int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, true); + int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree); // Connecting shouldn't fail due to dependency on other memory pool transactions // because we're already processing them in order of dependency map mapTestPoolTmp(mapTestPool); - if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee)) + bool fInvalid; + if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee, fInvalid)) continue; swap(mapTestPool, mapTestPoolTmp); @@ -2787,24 +2958,25 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nBits = GetNextWorkRequired(pindexPrev); + pblock->UpdateTime(pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); pblock->nNonce = 0; return pblock.release(); } -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime) +void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) { // Update nExtraNonce - int64 nNow = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - if (++nExtraNonce >= 0x7f && nNow > nPrevTime+1) + static uint256 hashPrevBlock; + if (hashPrevBlock != pblock->hashPrevBlock) { - nExtraNonce = 1; - nPrevTime = nNow; + nExtraNonce = 0; + hashPrevBlock = pblock->hashPrevBlock; } - pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nBits << CBigNum(nExtraNonce); + ++nExtraNonce; + pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nTime << CBigNum(nExtraNonce); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); } @@ -2855,7 +3027,7 @@ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash } -bool CheckWork(CBlock* pblock, CReserveKey& reservekey) +bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) { uint256 hash = pblock->GetHash(); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); @@ -2880,27 +3052,27 @@ bool CheckWork(CBlock* pblock, CReserveKey& reservekey) reservekey.KeepKey(); // Track how many getdata requests this block gets - CRITICAL_BLOCK(cs_mapRequestCount) - mapRequestCount[pblock->GetHash()] = 0; + CRITICAL_BLOCK(wallet.cs_wallet) + wallet.mapRequestCount[pblock->GetHash()] = 0; // Process this block the same as if we had received it from another node if (!ProcessBlock(NULL, pblock)) return error("BitcoinMiner : ProcessBlock, block not accepted"); } - Sleep(2000); return true; } -void BitcoinMiner() +void static ThreadBitcoinMiner(void* parg); + +void static BitcoinMiner(CWallet *pwallet) { printf("BitcoinMiner started\n"); SetThreadPriority(THREAD_PRIORITY_LOWEST); // Each thread has its own key and counter - CReserveKey reservekey; + CReserveKey reservekey(pwallet); unsigned int nExtraNonce = 0; - int64 nPrevTime = 0; while (fGenerateBitcoins) { @@ -2927,7 +3099,7 @@ void BitcoinMiner() auto_ptr pblock(CreateNewBlock(reservekey)); if (!pblock.get()) return; - IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce, nPrevTime); + IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size()); @@ -2942,6 +3114,7 @@ void BitcoinMiner() FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); + unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8); unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); @@ -2974,7 +3147,7 @@ void BitcoinMiner() assert(hash == pblock->GetHash()); SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckWork(pblock.get(), reservekey); + CheckWork(pblock.get(), *pwalletMain, reservekey); SetThreadPriority(THREAD_PRIORITY_LOWEST); break; } @@ -3029,18 +3202,25 @@ void BitcoinMiner() break; // Update nTime every few seconds - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->UpdateTime(pindexPrev); nBlockTime = ByteReverse(pblock->nTime); + if (fTestNet) + { + // Changing pblock->nTime can change work required on testnet: + nBlockBits = ByteReverse(pblock->nBits); + hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + } } } } -void ThreadBitcoinMiner(void* parg) +void static ThreadBitcoinMiner(void* parg) { + CWallet* pwallet = (CWallet*)parg; try { vnThreadsRunning[3]++; - BitcoinMiner(); + BitcoinMiner(pwallet); vnThreadsRunning[3]--; } catch (std::exception& e) { @@ -3058,12 +3238,12 @@ void ThreadBitcoinMiner(void* parg) } -void GenerateBitcoins(bool fGenerate) +void GenerateBitcoins(bool fGenerate, CWallet* pwallet) { if (fGenerateBitcoins != fGenerate) { fGenerateBitcoins = fGenerate; - CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins); + WriteSetting("fGenerateBitcoins", fGenerateBitcoins); MainFrameRepaint(); } if (fGenerateBitcoins) @@ -3078,7 +3258,7 @@ void GenerateBitcoins(bool fGenerate) printf("Starting %d BitcoinMiner threads\n", nAddThreads); for (int i = 0; i < nAddThreads; i++) { - if (!CreateThread(ThreadBitcoinMiner, NULL)) + if (!CreateThread(ThreadBitcoinMiner, pwallet)) printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); Sleep(10); }