// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2011 The Bitcoin developers
+// 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"
// Name of client reported in the 'version' message. Report the same name
// for both bitcoind and bitcoin-qt, to make it harder for attackers to
// target servers or GUI users specifically.
-const std::string CLIENT_NAME("bitcoin-qt");
+const std::string CLIENT_NAME("Satoshi");
CCriticalSection cs_setpwalletRegistered;
set<CWallet*> setpwalletRegistered;
const string strMessageMagic = "Bitcoin Signed Message:\n";
-
double dHashesPerSec;
int64 nHPSTimerStart;
// Settings
-int fGenerateBitcoins = false;
int64 nTransactionFee = 0;
-int fLimitProcessors = false;
-int nLimitProcessors = 1;
-int fMinimizeToTray = true;
-int fMinimizeOnClose = true;
-#if USE_UPNP
-int fUseUPnP = true;
-#else
-int fUseUPnP = false;
-#endif
+
//////////////////////////////////////////////////////////////////////////////
// mapOrphanTransactions
//
-void static AddOrphanTx(const CDataStream& vMsg)
+void AddOrphanTx(const CDataStream& vMsg)
{
CTransaction tx;
CDataStream(vMsg) >> tx;
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));
mapOrphanTransactions.erase(hash);
}
+int LimitOrphanTxSize(int nMaxOrphans)
+{
+ int nEvicted = 0;
+ while (mapOrphanTransactions.size() > nMaxOrphans)
+ {
+ // Evict a random orphan:
+ std::vector<unsigned char> randbytes(32);
+ RAND_bytes(&randbytes[0], 32);
+ uint256 randomhash(randbytes);
+ map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
+ if (it == mapOrphanTransactions.end())
+ it = mapOrphanTransactions.begin();
+ EraseOrphanTx(it->first);
+ ++nEvicted;
+ }
+ return nEvicted;
+}
// Remove transaction from memory pool
CRITICAL_BLOCK(cs_mapTransactions)
{
- BOOST_FOREACH(const CTxIn& txin, vin)
- mapNextTx.erase(txin.prevout);
- mapTransactions.erase(GetHash());
- nTransactionsUpdated++;
- --nPooledTx;
+ uint256 hash = GetHash();
+ if (mapTransactions.count(hash))
+ {
+ BOOST_FOREACH(const CTxIn& txin, vin)
+ mapNextTx.erase(txin.prevout);
+ mapTransactions.erase(hash);
+ nTransactionsUpdated++;
+ --nPooledTx;
+ }
}
return true;
}
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);
+}
+
}
// 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;
}
{
// Verify signature
if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
+ {
+ // only during transition phase for P2SH: do not invoke anti-DoS code for
+ // potentially old clients relaying bad P2SH transactions
+ if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0))
+ return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
+
return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
+ }
}
// Mark outpoints as spent
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;
+ }
+
// To avoid being on the short end of a block-chain split,
// don't do secondary validation of pay-to-script-hash transactions
// until blocks with timestamps after paytoscripthashtime (see init.cpp for default).
vConnect.push_back(pindex);
reverse(vConnect.begin(), vConnect.end());
+ printf("REORGANIZE: Disconnect %i blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
+ printf("REORGANIZE: Connect %i blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
+
// Disconnect shorter branch
vector<CTransaction> vResurrect;
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
if (!block.ReadFromDisk(pindex))
return error("Reorganize() : ReadFromDisk for disconnect failed");
if (!block.DisconnectBlock(txdb, pindex))
- return error("Reorganize() : DisconnectBlock failed");
+ return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
// Queue memory transactions to resurrect
BOOST_FOREACH(const CTransaction& tx, block.vtx)
{
// Invalid block
txdb.TxnAbort();
- return error("Reorganize() : ConnectBlock failed");
+ return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
}
// Queue memory transactions to delete
BOOST_FOREACH(CTransaction& tx, vDelete)
tx.RemoveFromMemoryPool();
+ printf("REORGANIZE: done\n");
+
return true;
}
printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr);
}
+// Called from inside SetBestChain: attaches a block to the new best chain being built
+bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
+{
+ uint256 hash = GetHash();
+
+ // Adding to current best branch
+ if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
+ {
+ txdb.TxnAbort();
+ InvalidChainFound(pindexNew);
+ return false;
+ }
+ if (!txdb.TxnCommit())
+ return error("SetBestChain() : TxnCommit failed");
+
+ // Add to current best branch
+ pindexNew->pprev->pnext = pindexNew;
+
+ // Delete redundant memory transactions
+ BOOST_FOREACH(CTransaction& tx, vtx)
+ tx.RemoveFromMemoryPool();
+
+ return true;
+}
+
bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
{
uint256 hash = GetHash();
}
else if (hashPrevBlock == hashBestChain)
{
- // Adding to current best branch
- if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
+ if (!SetBestChainInner(txdb, pindexNew))
+ return error("SetBestChain() : SetBestChainInner failed");
+ }
+ else
+ {
+ // the first block in the new chain that will cause it to become the new best chain
+ CBlockIndex *pindexIntermediate = pindexNew;
+
+ // list of blocks that need to be connected afterwards
+ std::vector<CBlockIndex*> vpindexSecondary;
+
+ // Reorganize is costly in terms of db load, as it works in a single db transaction.
+ // Try to limit how much needs to be done inside
+ while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork)
{
- txdb.TxnAbort();
- InvalidChainFound(pindexNew);
- return error("SetBestChain() : ConnectBlock failed");
+ vpindexSecondary.push_back(pindexIntermediate);
+ pindexIntermediate = pindexIntermediate->pprev;
}
- if (!txdb.TxnCommit())
- return error("SetBestChain() : TxnCommit failed");
- // Add to current best branch
- pindexNew->pprev->pnext = pindexNew;
+ if (!vpindexSecondary.empty())
+ printf("Postponing %i reconnects\n", vpindexSecondary.size());
- // Delete redundant memory transactions
- BOOST_FOREACH(CTransaction& tx, vtx)
- tx.RemoveFromMemoryPool();
- }
- else
- {
- // New best branch
- if (!Reorganize(txdb, pindexNew))
+ // Switch to new best branch
+ if (!Reorganize(txdb, pindexIntermediate))
{
txdb.TxnAbort();
InvalidChainFound(pindexNew);
return error("SetBestChain() : Reorganize failed");
}
+
+ // Connect futher blocks
+ BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary)
+ {
+ CBlock block;
+ if (!block.ReadFromDisk(pindex))
+ {
+ printf("SetBestChain() : ReadFromDisk failed\n");
+ break;
+ }
+ txdb.TxnBegin();
+ // errors now are not fatal, we still did a reorganisation to a new chain in a valid way
+ if (!block.SetBestChainInner(txdb, pindex))
+ break;
+ }
}
// Update best block in wallet (so we can detect restored wallets)
return error("AcceptBlock() : AddToBlockIndex failed");
// Relay inventory, but don't relay old inventory during initial block download
+ int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate();
if (hashBestChain == hash)
CRITICAL_BLOCK(cs_vNodes)
BOOST_FOREACH(CNode* pnode, vNodes)
- if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 140700))
+ if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
pnode->PushInventory(CInv(MSG_BLOCK, hash));
return true;
int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
if (deltaTime < 0)
{
- pfrom->Misbehaving(100);
+ if (pfrom)
+ pfrom->Misbehaving(100);
return error("ProcessBlock() : block with timestamp before last checkpoint");
}
CBigNum bnNewBlock;
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
if (bnNewBlock > bnRequired)
{
- pfrom->Misbehaving(100);
+ if (pfrom)
+ pfrom->Misbehaving(100);
return error("ProcessBlock() : block with too little proof-of-work");
}
}
CAddress addrFrom;
uint64 nNonce = 1;
vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
+ if (pfrom->nVersion < 209)
+ {
+ // Since February 20, 2012, the protocol is initiated at version 209,
+ // and earlier versions are no longer supported
+ printf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion);
+ pfrom->fDisconnect = true;
+ return false;
+ }
+
if (pfrom->nVersion == 10300)
pfrom->nVersion = 300;
- if (pfrom->nVersion >= 106 && !vRecv.empty())
+ if (!vRecv.empty())
vRecv >> addrFrom >> nNonce;
- if (pfrom->nVersion >= 106 && !vRecv.empty())
+ if (!vRecv.empty())
vRecv >> pfrom->strSubVer;
- if (pfrom->nVersion >= 209 && !vRecv.empty())
+ if (!vRecv.empty())
vRecv >> pfrom->nStartingHeight;
- if (pfrom->nVersion == 0)
- return false;
-
// Disconnect if we connected to ourself
if (nNonce == nLocalHostNonce && nNonce > 1)
{
AddTimeData(pfrom->addr, nTime);
// Change version
- if (pfrom->nVersion >= 209)
- pfrom->PushMessage("verack");
+ pfrom->PushMessage("verack");
pfrom->vSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
- if (pfrom->nVersion < 209)
- pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
if (!pfrom->fInbound)
{
vRecv >> vAddr;
// Don't want addr from older versions unless seeding
- if (pfrom->nVersion < 209)
- return true;
if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000)
return true;
if (vAddr.size() > 1000)
}
CTxDB txdb("r");
- BOOST_FOREACH(const CInv& inv, vInv)
+ for (int nInv = 0; nInv < vInv.size(); nInv++)
{
+ const CInv &inv = vInv[nInv];
+
if (fShutdown)
return true;
pfrom->AddInventoryKnown(inv);
if (fDebug)
printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
- if (!fAlreadyHave)
+ // Always request the last block in an inv bundle (even if we already have it), as it is the
+ // trigger for the other side to send further invs. If we are stuck on a (very long) side chain,
+ // this is necessary to connect earlier received orphan blocks to the chain again.
+ if (!fAlreadyHave || (inv.type == MSG_BLOCK && nInv==vInv.size()-1))
pfrom->AskFor(inv);
- else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash))
+ if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash))
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash]));
// Track requests for our stuff
{
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);
}
}
// Checksum
- if (vRecv.GetVersion() >= 209)
+ uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
+ unsigned int nChecksum = 0;
+ memcpy(&nChecksum, &hash, sizeof(nChecksum));
+ if (nChecksum != hdr.nChecksum)
{
- uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
- unsigned int nChecksum = 0;
- memcpy(&nChecksum, &hash, sizeof(nChecksum));
- if (nChecksum != hdr.nChecksum)
- {
- printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n",
- strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum);
- continue;
- }
+ printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n",
+ strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum);
+ continue;
}
// Copy message to its own buffer
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
- pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+ pblock->UpdateTime(pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get());
pblock->nNonce = 0;
void static ThreadBitcoinMiner(void* parg);
+static bool fGenerateBitcoins = false;
+static bool fLimitProcessors = false;
+static int nLimitProcessors = -1;
+
void static BitcoinMiner(CWallet *pwallet)
{
printf("BitcoinMiner started\n");
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);
{
nLogTime = GetTime();
printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str());
- printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[3], dHashesPerSec/1000.0);
+ printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[THREAD_MINER], dHashesPerSec/1000.0);
}
}
}
return;
if (!fGenerateBitcoins)
return;
- if (fLimitProcessors && vnThreadsRunning[3] > nLimitProcessors)
+ if (fLimitProcessors && vnThreadsRunning[THREAD_MINER] > nLimitProcessors)
return;
if (vNodes.empty())
break;
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();
+ }
}
}
}
CWallet* pwallet = (CWallet*)parg;
try
{
- vnThreadsRunning[3]++;
+ vnThreadsRunning[THREAD_MINER]++;
BitcoinMiner(pwallet);
- vnThreadsRunning[3]--;
+ vnThreadsRunning[THREAD_MINER]--;
}
catch (std::exception& e) {
- vnThreadsRunning[3]--;
+ vnThreadsRunning[THREAD_MINER]--;
PrintException(&e, "ThreadBitcoinMiner()");
} catch (...) {
- vnThreadsRunning[3]--;
+ vnThreadsRunning[THREAD_MINER]--;
PrintException(NULL, "ThreadBitcoinMiner()");
}
UIThreadCall(boost::bind(CalledSetStatusBar, "", 0));
nHPSTimerStart = 0;
- if (vnThreadsRunning[3] == 0)
+ if (vnThreadsRunning[THREAD_MINER] == 0)
dHashesPerSec = 0;
- printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]);
+ printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[THREAD_MINER]);
}
void GenerateBitcoins(bool fGenerate, CWallet* pwallet)
{
- if (fGenerateBitcoins != fGenerate)
- {
- fGenerateBitcoins = fGenerate;
- WriteSetting("fGenerateBitcoins", fGenerateBitcoins);
- MainFrameRepaint();
- }
- if (fGenerateBitcoins)
+ fGenerateBitcoins = fGenerate;
+ nLimitProcessors = GetArg("-genproclimit", -1);
+ if (nLimitProcessors == 0)
+ fGenerateBitcoins = false;
+ fLimitProcessors = (nLimitProcessors != -1);
+
+ if (fGenerate)
{
int nProcessors = boost::thread::hardware_concurrency();
printf("%d processors\n", nProcessors);
nProcessors = 1;
if (fLimitProcessors && nProcessors > nLimitProcessors)
nProcessors = nLimitProcessors;
- int nAddThreads = nProcessors - vnThreadsRunning[3];
+ int nAddThreads = nProcessors - vnThreadsRunning[THREAD_MINER];
printf("Starting %d BitcoinMiner threads\n", nAddThreads);
for (int i = 0; i < nAddThreads; i++)
{