// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2011 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"
CCriticalSection cs_main;
-map<uint256, CTransaction> mapTransactions;
+static map<uint256, CTransaction> mapTransactions;
CCriticalSection cs_mapTransactions;
unsigned int nTransactionsUpdated = 0;
map<COutPoint, CInPoint> mapNextTx;
map<uint256, CBlockIndex*> mapBlockIndex;
uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
-CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
-const int nTotalBlocksEstimate = 134444; // Conservative estimate of total nr of blocks on main chain
+static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
+const int nTotalBlocksEstimate = 140700; // Conservative estimate of total nr of blocks on main chain
const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download"
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
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 error("CTransaction::CheckTransaction() : vin empty");
+ if (vout.empty())
+ return error("CTransaction::CheckTransaction() : vout empty");
// Size limits
if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE)
return error("CTransaction::CheckTransaction() : size limits failed");
return error("CTransaction::CheckTransaction() : txout total out of range");
}
+ // Check for duplicate inputs
+ set<COutPoint> 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)
}
// 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;
}
// 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];
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 (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)
{
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<uint256, CTxIndex> mapUnused;
+ map<uint256, CTxIndex> 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))
+ if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false))
return false;
}
+ // Write queued txindex changes
+ for (map<uint256, CTxIndex>::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;
(nHeight == 74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) ||
(nHeight == 105000 && hash != uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) ||
(nHeight == 118000 && hash != uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553")) ||
- (nHeight == 134444 && hash != uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")))
+ (nHeight == 134444 && hash != uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")) ||
+ (nHeight == 140700 && hash != uint256("0x000000000000033b512028abb90e1626d8b346fd0ed598ac0a3c371138dce2bd")))
return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight);
// Write block to history file
if (hashBestChain == hash)
CRITICAL_BLOCK(cs_vNodes)
BOOST_FOREACH(CNode* pnode, vNodes)
- if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 134444))
+ if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 140700))
pnode->PushInventory(CInv(MSG_BLOCK, hash));
return true;
-template<typename Stream>
-bool static 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;
// 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 static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{
static map<unsigned int, vector<unsigned char> > 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");
// 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));
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);
// Keep giving the same key to the same ip until they use it
if (!mapReuseKey.count(pfrom->addr.ip))
- mapReuseKey[pfrom->addr.ip] = pwalletMain->GetOrReuseKeyFromPool();
+ pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr.ip], true);
// Send back approval of order and pubkey to use
CScript scriptPubKey;
vGetData.clear();
}
}
+ mapAlreadyAskedFor[inv] = nNow;
pto->mapAskFor.erase(pto->mapAskFor.begin());
}
if (!vGetData.empty())
}
}
-
+// Some explaining would be appreciated
class COrphan
{
public:
}
-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();
}
reservekey.KeepKey();
// Track how many getdata requests this block gets
- CRITICAL_BLOCK(wallet.cs_mapRequestCount)
+ CRITICAL_BLOCK(wallet.cs_wallet)
wallet.mapRequestCount[pblock->GetHash()] = 0;
// Process this block the same as if we had received it from another node
// Each thread has its own key and counter
CReserveKey reservekey(pwallet);
unsigned int nExtraNonce = 0;
- int64 nPrevTime = 0;
while (fGenerateBitcoins)
{
auto_ptr<CBlock> 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());