return error("AcceptTransaction() : CheckTransaction failed");
// To help v0.1.5 clients who would see it as a negative number
- if (nLockTime > INT_MAX)
- return error("AcceptTransaction() : not accepting nLockTime beyond 2038");
+ if ((int64)nLockTime > INT_MAX)
+ return error("AcceptTransaction() : not accepting nLockTime beyond 2038 yet");
// Do we already have it?
uint256 hash = GetHash();
COutPoint outpoint = vin[i].prevout;
if (mapNextTx.count(outpoint))
{
+ // Disable replacement feature for now
+ return false;
+
// Allow replacing with a newer version of the same transaction
if (i != 0)
return false;
{
if (ptxOld)
{
- printf("mapTransaction.erase(%s) replacing with new version\n", ptxOld->GetHash().ToString().c_str());
- mapTransactions.erase(ptxOld->GetHash());
+ printf("AcceptTransaction() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
+ ptxOld->RemoveFromMemoryPool();
}
AddToMemoryPool();
}
if (fFirst)
return;
+ // Only do it if there's been a new block since last time
+ static int64 nLastTime;
+ if (nTimeBestReceived < nLastTime)
+ return;
+ nLastTime = GetTime();
+
// Rebroadcast any of our txes that aren't in a block yet
printf("ResendWalletTransactions()\n");
CTxDB txdb("r");
// CBlock and CBlockIndex
//
-bool CBlock::ReadFromDisk(const CBlockIndex* pblockindex, bool fReadTransactions)
+bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions)
{
- return ReadFromDisk(pblockindex->nFile, pblockindex->nBlockPos, fReadTransactions);
+ if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions))
+ return false;
+ if (GetHash() != pindex->GetBlockHash())
+ return error("CBlock::ReadFromDisk() : GetHash() doesn't match index");
+ return true;
}
uint256 GetOrphanRoot(const CBlock* pblock)
assert(pindexFirst);
// Limit adjustment step
- int64 nActualTimespan = (int64)pindexLast->nTime - (int64)pindexFirst->nTime;
+ int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime();
printf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan);
if (nActualTimespan < nTargetTimespan/4)
nActualTimespan = nTargetTimespan/4;
return bnNew.GetCompact();
}
+bool CheckProofOfWork(uint256 hash, unsigned int nBits)
+{
+ CBigNum bnTarget;
+ bnTarget.SetCompact(nBits);
+
+ // Check range
+ if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit)
+ return error("CheckProofOfWork() : nBits below minimum work");
+
+ // Check proof of work matches claimed amount
+ if (hash > bnTarget.getuint256())
+ return error("CheckProofOfWork() : hash doesn't match nBits");
+
+ return true;
+}
+
bool IsInitialBlockDownload()
{
if (pindexBest == NULL)
nLastUpdate = GetTime();
}
return (GetTime() - nLastUpdate < 10 &&
- pindexBest->nTime < GetTime() - 24 * 60 * 60);
+ pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60);
}
bool IsLockdown()
CTxDB().WriteBestInvalidWork(bnBestInvalidWork);
MainFrameRepaint();
}
- printf("Lockdown: invalid block=%s height=%d work=%s\n", pindexNew->GetBlockHash().ToString().substr(0,22).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str());
- printf("Lockdown: current best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,22).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
+ printf("Lockdown: invalid block=%s height=%d work=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str());
+ printf("Lockdown: current best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
printf("Lockdown: IsLockdown()=%d\n", (IsLockdown() ? 1 : 0));
if (IsLockdown())
printf("Lockdown: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n");
nValueIn += txPrev.vout[prevout.n].nValue;
// Check for negative or overflow input values
- if (txPrev.vout[prevout.n].nValue < 0)
- return error("ConnectInputs() : txin.nValue negative");
- if (txPrev.vout[prevout.n].nValue > MAX_MONEY)
- return error("ConnectInputs() : txin.nValue too high");
- if (nValueIn > MAX_MONEY)
- return error("ConnectInputs() : txin total too high");
+ if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
+ return error("ConnectInputs() : txin values out of range");
}
+ if (nValueIn < GetValueOut())
+ return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,6).c_str());
+
// Tally transaction fees
int64 nTxFee = nValueIn - GetValueOut();
if (nTxFee < 0)
foreach(CBlockIndex* pindex, vDisconnect)
{
CBlock block;
- if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos))
+ if (!block.ReadFromDisk(pindex))
return error("Reorganize() : ReadFromDisk for disconnect failed");
if (!block.DisconnectBlock(txdb, pindex))
return error("Reorganize() : DisconnectBlock failed");
{
CBlockIndex* pindex = vConnect[i];
CBlock block;
- if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos))
+ if (!block.ReadFromDisk(pindex))
return error("Reorganize() : ReadFromDisk for connect failed");
if (!block.ConnectBlock(txdb, pindex))
{
bnBestChainWork = pindexNew->bnChainWork;
nTimeBestReceived = GetTime();
nTransactionsUpdated++;
- printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,22).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
+ printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
return true;
}
// Check for duplicate
uint256 hash = GetHash();
if (mapBlockIndex.count(hash))
- return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,16).c_str());
+ return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str());
// Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this);
return error("CheckBlock() : size limits failed");
// Check timestamp
- if (nTime > GetAdjustedTime() + 2 * 60 * 60)
+ if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
return error("CheckBlock() : block timestamp too far in the future");
// First transaction must be coinbase, the rest must not be
return error("CheckBlock() : CheckTransaction failed");
// Check proof of work matches claimed amount
- if (CBigNum().SetCompact(nBits) > bnProofOfWorkLimit)
- return error("CheckBlock() : nBits below minimum work");
- if (GetHash() > CBigNum().SetCompact(nBits).getuint256())
- return error("CheckBlock() : hash doesn't match nBits");
+ if (!CheckProofOfWork(GetHash(), nBits))
+ return error("CheckBlock() : proof of work failed");
// Check merkleroot
if (hashMerkleRoot != BuildMerkleTree())
CBlockIndex* pindexPrev = (*mi).second;
// Check timestamp against prev
- if (nTime <= pindexPrev->GetMedianTimePast())
+ if (GetBlockTime() <= pindexPrev->GetMedianTimePast())
return error("AcceptBlock() : block's timestamp is too early");
// Check that all transactions are finalized
foreach(const CTransaction& tx, vtx)
- if (!tx.IsFinal(pindexPrev->nHeight+1, nTime))
+ if (!tx.IsFinal(pindexPrev->nHeight+1, GetBlockTime()))
return error("AcceptBlock() : contains a non-final transaction");
// Check proof of work
// Check for duplicate
uint256 hash = pblock->GetHash();
if (mapBlockIndex.count(hash))
- return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,16).c_str());
+ return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,20).c_str());
if (mapOrphanBlocks.count(hash))
- return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,16).c_str());
+ return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
// Preliminary checks
if (!pblock->CheckBlock())
// If don't already have its previous block, shunt it off to holding area until we get it
if (!mapBlockIndex.count(pblock->hashPrevBlock))
{
- printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,16).c_str());
+ printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
mapOrphanBlocks.insert(make_pair(hash, pblock));
mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock));
pindex->nHeight,
pindex->nFile,
pindex->nBlockPos,
- block.GetHash().ToString().substr(0,16).c_str(),
- DateTimeStrFormat("%x %H:%M:%S", block.nTime).c_str(),
+ block.GetHash().ToString().substr(0,20).c_str(),
+ DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
block.vtx.size());
CRITICAL_BLOCK(cs_mapWallet)
if (pindex)
pindex = pindex->pnext;
int nLimit = 500 + locator.GetDistanceBack();
- printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,16).c_str(), nLimit);
+ 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,16).c_str());
+ printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str());
break;
}
pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
{
// 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,16).c_str());
+ printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str());
pfrom->hashContinue = pindex->GetBlockHash();
break;
}
vRecv >> *pblock;
//// debug print
- printf("received block %s\n", pblock->GetHash().ToString().substr(0,16).c_str());
+ printf("received block %s\n", pblock->GetHash().ToString().substr(0,20).c_str());
// pblock->print();
CInv inv(MSG_BLOCK, pblock->GetHash());
static const int64 COIN = 100000000;
static const int64 CENT = 1000000;
static const int64 MAX_MONEY = 21000000 * COIN;
+inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
static const int COINBASE_MATURITY = 100;
-
static const CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
void GenerateBitcoins(bool fGenerate);
void ThreadBitcoinMiner(void* parg);
void BitcoinMiner();
+bool CheckProofOfWork(uint256 hash, unsigned int nBits);
bool IsInitialBlockDownload();
bool IsLockdown();
int64 GetCredit() const
{
+ if (!MoneyRange(nValue))
+ throw runtime_error("CTxOut::GetCredit() : value out of range");
if (IsMine())
return nValue;
return 0;
nBlockHeight = nBestHeight;
if (nBlockTime == 0)
nBlockTime = GetAdjustedTime();
- if (nLockTime < (nLockTime < 500000000 ? nBlockHeight : nBlockTime))
+ if ((int64)nLockTime < (nLockTime < 500000000 ? (int64)nBlockHeight : nBlockTime))
return true;
foreach(const CTxIn& txin, vin)
if (!txin.IsFinal())
if (txout.nValue > MAX_MONEY)
return error("CTransaction::CheckTransaction() : txout.nValue too high");
nValueOut += txout.nValue;
- if (nValueOut > MAX_MONEY)
- return error("CTransaction::CheckTransaction() : txout total too high");
+ if (!MoneyRange(nValueOut))
+ return error("CTransaction::CheckTransaction() : txout total out of range");
}
if (IsCoinBase())
{
int64 nDebit = 0;
foreach(const CTxIn& txin, vin)
+ {
nDebit += txin.GetDebit();
+ if (!MoneyRange(nDebit))
+ throw runtime_error("CTransaction::GetDebit() : value out of range");
+ }
return nDebit;
}
{
int64 nCredit = 0;
foreach(const CTxOut& txout, vout)
+ {
nCredit += txout.GetCredit();
+ if (!MoneyRange(nCredit))
+ throw runtime_error("CTransaction::GetCredit() : value out of range");
+ }
return nCredit;
}
int64 nValueOut = 0;
foreach(const CTxOut& txout, vout)
{
- if (txout.nValue < 0)
- throw runtime_error("CTransaction::GetValueOut() : negative value");
nValueOut += txout.nValue;
+ if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut))
+ throw runtime_error("CTransaction::GetValueOut() : value out of range");
}
return nValueOut;
}
return Hash(BEGIN(nVersion), END(nNonce));
}
+ int64 GetBlockTime() const
+ {
+ return (int64)nTime;
+ }
+
uint256 BuildMerkleTree() const
{
filein >> *this;
// Check the header
- if (CBigNum().SetCompact(nBits) > bnProofOfWorkLimit)
- return error("CBlock::ReadFromDisk() : nBits errors in block header");
- if (GetHash() > CBigNum().SetCompact(nBits).getuint256())
- return error("CBlock::ReadFromDisk() : GetHash() errors in block header");
+ if (!CheckProofOfWork(GetHash(), nBits))
+ return error("CBlock::ReadFromDisk() : errors in block header");
return true;
}
void print() const
{
printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n",
- GetHash().ToString().substr(0,16).c_str(),
+ GetHash().ToString().substr(0,20).c_str(),
nVersion,
- hashPrevBlock.ToString().substr(0,16).c_str(),
+ hashPrevBlock.ToString().substr(0,20).c_str(),
hashMerkleRoot.ToString().substr(0,6).c_str(),
nTime, nBits, nNonce,
vtx.size());
int64 GetBlockValue(int nHeight, int64 nFees) const;
bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex);
bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex);
- bool ReadFromDisk(const CBlockIndex* blockindex, bool fReadTransactions=true);
+ bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true);
bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew);
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos);
bool CheckBlock() const;
return *phashBlock;
}
+ int64 GetBlockTime() const
+ {
+ return (int64)nTime;
+ }
+
CBigNum GetBlockWork() const
{
+ if (CBigNum().SetCompact(nBits) <= 0)
+ return 0;
return (CBigNum(1)<<256) / (CBigNum().SetCompact(nBits)+1);
}
return (pnext || this == pindexBest);
}
+ bool CheckIndex() const
+ {
+ return CheckProofOfWork(GetBlockHash(), nBits);
+ }
+
bool EraseBlockFromDisk()
{
// Open history file
int64 GetMedianTimePast() const
{
- unsigned int pmedian[nMedianTimeSpan];
- unsigned int* pbegin = &pmedian[nMedianTimeSpan];
- unsigned int* pend = &pmedian[nMedianTimeSpan];
+ int64 pmedian[nMedianTimeSpan];
+ int64* pbegin = &pmedian[nMedianTimeSpan];
+ int64* pend = &pmedian[nMedianTimeSpan];
const CBlockIndex* pindex = this;
for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev)
- *(--pbegin) = pindex->nTime;
+ *(--pbegin) = pindex->GetBlockTime();
sort(pbegin, pend);
return pbegin[(pend - pbegin)/2];
for (int i = 0; i < nMedianTimeSpan/2; i++)
{
if (!pindex->pnext)
- return nTime;
+ return GetBlockTime();
pindex = pindex->pnext;
}
return pindex->GetMedianTimePast();
return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)",
pprev, pnext, nFile, nBlockPos, nHeight,
hashMerkleRoot.ToString().substr(0,6).c_str(),
- GetBlockHash().ToString().substr(0,16).c_str());
+ GetBlockHash().ToString().substr(0,20).c_str());
}
void print() const
str += CBlockIndex::ToString();
str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)",
GetBlockHash().ToString().c_str(),
- hashPrev.ToString().substr(0,16).c_str(),
- hashNext.ToString().substr(0,16).c_str());
+ hashPrev.ToString().substr(0,20).c_str(),
+ hashNext.ToString().substr(0,20).c_str());
return str;
}