#include "alert.h"
#include "checkpoints.h"
#include "db.h"
+#include "txdb.h"
#include "net.h"
#include "init.h"
#include "ui_interface.h"
// Settings
int64 nTransactionFee = MIN_TX_FEE;
-int64 nMinimumInputValue = MIN_TX_FEE;
+int64 nMinimumInputValue = MIN_TXOUT_AMOUNT;
extern enum Checkpoints::CPMode CheckpointsMode;
}
// ask wallets to resend their transactions
-void ResendWalletTransactions()
+void ResendWalletTransactions(bool fForce)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
- pwallet->ResendWalletTransactions();
+ pwallet->ResendWalletTransactions(fForce);
}
CBlockIndex *CCoinsView::GetBestBlock() { return NULL; }
bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; }
bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; }
+bool CCoinsView::GetStats(CCoinsStats &stats) { return false; }
CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { }
bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); }
CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); }
bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
+bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); }
bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); }
}
CCoinsViewCache *pcoinsTip = NULL;
+CBlockTreeDB *pblocktree = NULL;
//////////////////////////////////////////////////////////////////////////////
//
bool CTransaction::IsStandard() const
{
- if (nVersion > CTransaction::CURRENT_VERSION)
+ if (nVersion > CTransaction::CURRENT_VERSION) {
return false;
+ }
BOOST_FOREACH(const CTxIn& txin, 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) {
+ return false;
+ }
+ if (!txin.scriptSig.IsPushOnly()) {
return false;
- if (!txin.scriptSig.IsPushOnly())
+ }
+ if (fEnforceCanonical && !txin.scriptSig.HasCanonicalPushes()) {
return false;
+ }
}
+
+ unsigned int nDataOut = 0;
+ txnouttype whichType;
BOOST_FOREACH(const CTxOut& txout, vout) {
- if (!::IsStandard(txout.scriptPubKey))
- return false;
- if (txout.nValue == 0)
+ if (!::IsStandard(txout.scriptPubKey, whichType)) {
return false;
+ }
+ if (whichType == TX_NULL_DATA)
+ nDataOut++;
+ else {
+ if (txout.nValue == 0) {
+ return false;
+ }
+ if (fEnforceCanonical && !txout.scriptPubKey.HasCanonicalPushes()) {
+ return false;
+ }
+ }
}
+
+ // only one OP_RETURN txout is permitted
+ if (nDataOut > 1) {
+ return false;
+ }
+
return true;
}
if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake())
return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
- // Enforce minimum output amount for user transactions until 1 May 2014 04:00:00 GMT
- if (!fTestNet && !IsCoinBase() && !txout.IsEmpty() && nTime < OUTPUT_SWITCH_TIME && txout.nValue < MIN_TXOUT_AMOUNT)
- return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum"));
-
+ if (txout.nValue < 0)
+ return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue is negative"));
if (txout.nValue > MAX_MONEY)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high"));
nValueOut += txout.nValue;
}
int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree,
- enum GetMinFee_mode mode, unsigned int nBytes) const
+ enum GetMinFee_mode mode, unsigned int nBytes, int64 nMinTxFee, int64 nMinRelayTxFee) const
{
- // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE
- int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE;
+
+ // Use new fees approach if we are on test network or
+ // switch date has been reached
+ bool fNewApproach = fTestNet || nTime > FEE_SWITCH_TIME;
+
+ if(!fNewApproach)
+ {
+ // Enforce 0.01 as minimum fee for old approach
+ nMinTxFee = CENT;
+ nMinRelayTxFee = CENT;
+ }
+
+ // Base fee is either nMinTxFee or nMinRelayTxFee
+ int64 nBaseFee = (mode == GMF_RELAY) ? nMinRelayTxFee : nMinTxFee;
unsigned int nNewBlockSize = nBlockSize + nBytes;
int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee;
- // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01
- if (nMinFee < nBaseFee)
+ if (fNewApproach)
{
+ if (fAllowFree)
+ {
+ if (nBlockSize == 1)
+ {
+ // Transactions under 1K are free
+ if (nBytes < 1000)
+ nMinFee = 0;
+ }
+ else
+ {
+ // Free transaction area
+ if (nNewBlockSize < 27000)
+ nMinFee = 0;
+ }
+ }
+
+ // To limit dust spam, require additional MIN_TX_FEE/MIN_RELAY_TX_FEE for
+ // each output which is less than 0.01
+ BOOST_FOREACH(const CTxOut& txout, vout)
+ if (txout.nValue < CENT)
+ nMinFee += nBaseFee;
+ }
+ else if (nMinFee < nBaseFee)
+ {
+ // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if
+ // any output is less than 0.01
BOOST_FOREACH(const CTxOut& txout, vout)
if (txout.nValue < CENT)
nMinFee = nBaseFee;
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// Don't accept it if it can't get into a block
- int64 txMinFee = tx.GetMinFee(1000, false, GMF_RELAY, nSize);
+ int64 txMinFee = tx.GetMinFee(1000, true, GMF_RELAY, nSize);
if (nFees < txMinFee)
return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d,
hash.ToString().c_str(),
vtxid.push_back((*mi).first);
}
-
-
-
+// Return depth of transaction in blockchain:
+// -1 : not in blockchain, and not in memory pool (conflicted transaction)
+// 0 : in memory pool, waiting to be included in a block
+// >=1 : this many blocks deep in the main chain
int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
{
- if (hashBlock == 0 || nIndex == -1)
- return 0;
+ bool fInMemPool = mempool.exists(GetHash());
+
+ if (hashBlock == 0 || nIndex == -1) {
+ return fInMemPool ? 0 : -1;
+ }
// Find the block it claims to be in
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
- if (mi == mapBlockIndex.end())
- return 0;
+ if (mi == mapBlockIndex.end()) {
+ return fInMemPool ? 0 : -1;
+ }
CBlockIndex* pindex = (*mi).second;
- if (!pindex || !pindex->IsInMainChain())
- return 0;
+ if (!pindex || !pindex->IsInMainChain()) {
+ return fInMemPool ? 0 : -1;
+ }
// Make sure the merkle branch connects to this block
if (!fMerkleVerified)
{
- if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot)
- return 0;
+ if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) {
+ return fInMemPool ? 0 : -1;
+ }
fMerkleVerified = true;
}
if (pindexNew->nChainTrust > nBestInvalidTrust)
{
nBestInvalidTrust = pindexNew->nChainTrust;
- CChainDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
+ pblocktree->WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
uiInterface.NotifyBlocksChanged();
}
void static InvalidBlockFound(CBlockIndex *pindex) {
pindex->nStatus |= BLOCK_FAILED_VALID;
- CChainDB().WriteBlockIndex(CDiskBlockIndex(pindex));
+ pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
setBlockIndexValid.erase(pindex);
InvalidChainFound(pindex);
if (pindex->pnext)
do {
if (pindexTest->nStatus & BLOCK_FAILED_MASK) {
// mark descendants failed
- CChainDB chaindb;
CBlockIndex *pindexFailed = pindexNewBest;
while (pindexTest != pindexFailed) {
pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
setBlockIndexValid.erase(pindexFailed);
- chaindb.WriteBlockIndex(CDiskBlockIndex(pindexFailed));
+ pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed));
pindexFailed = pindexFailed->pprev;
}
InvalidChainFound(pindexNewBest);
if (!GetCoinAge(nCoinAge))
return error("CheckInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
- int64 nStakeReward = GetValueOut() - nValueIn;
- int64 nCalculatedStakeReward = GetProofOfStakeReward(nCoinAge, pblock->nBits, nTime) - GetMinFee() + MIN_TX_FEE;
+ unsigned int nTxSize = (nTime > STAKEFEE_SWITCH_TIME || fTestNet) ? GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) : 0;
+ int64 nReward = GetValueOut() - nValueIn;
+ int64 nCalculatedReward = GetProofOfStakeReward(nCoinAge, pblock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize, CENT) + CENT;
- if (nStakeReward > nCalculatedStakeReward)
- return DoS(100, error("CheckInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nStakeReward, nCalculatedStakeReward));
+ if (nReward > nCalculatedReward)
+ return DoS(100, error("CheckInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nReward, nCalculatedReward));
}
else
{
nFees += nTxFee;
if (!MoneyRange(nFees))
return DoS(100, error("CheckInputs() : nFees out of range"));
-
- // enforce transaction fees for every block until 1 May 2014 04:00:00 GMT
- if (!fTestNet && nTxFee < GetMinFee() && nTime < OUTPUT_SWITCH_TIME)
- return pblock? DoS(100, error("CheckInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())) : false;
}
// The first loop above does all the inexpensive checks.
// Skip ECDSA signature verification when connecting blocks
// 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 (csmode == CS_ALWAYS ||
+ if (csmode == CS_ALWAYS ||
(csmode == CS_AFTER_CHECKPOINT && inputs.GetBestBlock()->nHeight >= Checkpoints::GetTotalBlocksEstimate())) {
for (unsigned int i = 0; i < vin.size(); i++) {
const COutPoint &prevout = vin[i].prevout;
return error("DisconnectBlock() : cannot restore coin inputs");
}
}
+
+ // clean up wallet after disconnecting coinstake
+ SyncWithWallets(vtx[i].GetHash(), vtx[i], this, false, false);
}
// move best block pointer to prevout block
return true;
}
-bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
{
// Write undo information to disk
if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS)
{
- CChainDB chaindb;
-
if (pindex->GetUndoPos().IsNull()) {
CDiskBlockPos pos;
- if (!FindUndoPos(chaindb, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8))
+ if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8))
return error("ConnectBlock() : FindUndoPos failed");
if (!blockundo.WriteToDisk(pos))
return error("ConnectBlock() : CBlockUndo::WriteToDisk failed");
pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS;
CDiskBlockIndex blockindex(pindex);
- if (!chaindb.WriteBlockIndex(blockindex))
+ if (!pblocktree->WriteBlockIndex(blockindex))
return error("ConnectBlock() : WriteBlockIndex failed");
}
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
CBlock block;
if (!block.ReadFromDisk(pindex))
- return error("SetBestBlock() : ReadFromDisk for disconnect failed");
+ return error("SetBestChain() : ReadFromDisk for disconnect failed");
CCoinsViewCache viewTemp(view, true);
if (!block.DisconnectBlock(pindex, viewTemp))
- return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ return error("SetBestChain() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
if (!viewTemp.Flush())
- return error("SetBestBlock() : Cache flush failed after disconnect");
+ return error("SetBestChain() : Cache flush failed after disconnect");
// Queue memory transactions to resurrect
BOOST_FOREACH(const CTransaction& tx, block.vtx)
BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
CBlock block;
if (!block.ReadFromDisk(pindex))
- return error("SetBestBlock() : ReadFromDisk for connect failed");
+ return error("SetBestChain() : ReadFromDisk for connect failed");
CCoinsViewCache viewTemp(view, true);
if (!block.ConnectBlock(pindex, viewTemp)) {
InvalidChainFound(pindexNew);
InvalidBlockFound(pindex);
- return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ return error("SetBestChain() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
}
if (!viewTemp.Flush())
- return error("SetBestBlock() : Cache flush failed after connect");
+ return error("SetBestChain() : Cache flush failed after connect");
// Queue memory transactions to delete
BOOST_FOREACH(const CTransaction& tx, block.vtx)
// Compute stake modifier
uint64 nStakeModifier = 0;
bool fGeneratedStakeModifier = false;
- if (!ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier))
+ if (!ComputeNextStakeModifier(pindexNew, nStakeModifier, fGeneratedStakeModifier))
return error("AddToBlockIndex() : ComputeNextStakeModifier() failed");
pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew);
setBlockIndexValid.insert(pindexNew);
- CChainDB chaindb;
- if (!chaindb.TxnBegin())
- return false;
- chaindb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
- if (!chaindb.TxnCommit())
- return false;
+ pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew));
// New best?
if (!ConnectBestBlock())
hashPrevBestCoinBase = GetTxHash(0);
}
+ pblocktree->Flush();
+
uiInterface.NotifyBlocksChanged();
return true;
}
-bool FindBlockPos(CChainDB &chaindb, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime)
+bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime)
{
bool fUpdatedLast = false;
fclose(file);
nLastBlockFile++;
infoLastBlockFile.SetNull();
- chaindb.ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine
+ pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine
fUpdatedLast = true;
}
fclose(file);
}
- if (!chaindb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+ if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return error("FindBlockPos() : cannot write updated block info");
if (fUpdatedLast)
- chaindb.WriteLastBlockFile(nLastBlockFile);
+ pblocktree->WriteLastBlockFile(nLastBlockFile);
return true;
}
-bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
+bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;
if (nFile == nLastBlockFile) {
pos.nPos = infoLastBlockFile.nUndoSize;
nNewSize = (infoLastBlockFile.nUndoSize += nAddSize);
- if (!chaindb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+ if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return error("FindUndoPos() : cannot write updated block info");
} else {
CBlockFileInfo info;
- if (!chaindb.ReadBlockFileInfo(nFile, info))
+ if (!pblocktree->ReadBlockFileInfo(nFile, info))
return error("FindUndoPos() : cannot read block info");
pos.nPos = info.nUndoSize;
nNewSize = (info.nUndoSize += nAddSize);
- if (!chaindb.WriteBlockFileInfo(nFile, info))
+ if (!pblocktree->WriteBlockFileInfo(nFile, info))
return error("FindUndoPos() : cannot write updated block info");
}
return error("AcceptBlock() : out of disk space");
CDiskBlockPos blockPos;
{
- CChainDB chaindb;
- if (!FindBlockPos(chaindb, blockPos, nBlockSize+8, nHeight, nTime))
+ if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime))
return error("AcceptBlock() : FindBlockPos failed");
}
if (!WriteToDisk(blockPos))
return false;
}
+// entropy bit for stake modifier if chosen by modifier
+unsigned int CBlock::GetStakeEntropyBit(unsigned int nTime) const
+{
+ // Protocol switch at novacoin block #9689
+ if (nTime >= ENTROPY_SWITCH_TIME || fTestNet)
+ {
+ // Take last bit of block hash as entropy bit
+ unsigned int nEntropyBit = ((GetHash().Get64()) & 1llu);
+ if (fDebug && GetBoolArg("-printstakemodifier"))
+ printf("GetStakeEntropyBit: nTime=%u hashBlock=%s nEntropyBit=%u\n", nTime, GetHash().ToString().c_str(), nEntropyBit);
+ return nEntropyBit;
+ }
+ // Before novacoin block #9689 - old protocol
+ uint160 hashSig = Hash160(vchBlockSig);
+ if (fDebug && GetBoolArg("-printstakemodifier"))
+ printf("GetStakeEntropyBit: hashSig=%s", hashSig.ToString().c_str());
+ hashSig >>= 159; // take the first bit of the hash
+ if (fDebug && GetBoolArg("-printstakemodifier"))
+ printf(" entropybit=%"PRI64d"\n", hashSig.Get64());
+ return hashSig.Get64();
+}
+
bool CheckDiskSpace(uint64 nAdditionalBytes)
{
uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available;
return OpenDiskFile(pos, "rev", fReadOnly);
}
+CBlockIndex * InsertBlockIndex(uint256 hash)
+{
+ if (hash == 0)
+ return NULL;
+
+ // Return existing
+ map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
+ if (mi != mapBlockIndex.end())
+ return (*mi).second;
+
+ // Create new
+ CBlockIndex* pindexNew = new CBlockIndex();
+ if (!pindexNew)
+ throw runtime_error("InsertBlockIndex() : new CBlockIndex failed");
+ mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
+ pindexNew->phashBlock = &((*mi).first);
+
+ return pindexNew;
+}
+
+bool static LoadBlockIndexDB()
+{
+ if (!pblocktree->LoadBlockIndexGuts())
+ return false;
+
+ if (fRequestShutdown)
+ return true;
+
+ // Calculate nChainTrust
+ vector<pair<int, CBlockIndex*> > vSortedByHeight;
+ vSortedByHeight.reserve(mapBlockIndex.size());
+ BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
+ {
+ CBlockIndex* pindex = item.second;
+ vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
+ }
+ sort(vSortedByHeight.begin(), vSortedByHeight.end());
+ BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
+ {
+ CBlockIndex* pindex = item.second;
+ pindex->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust();
+ pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
+ if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK))
+ setBlockIndexValid.insert(pindex);
+
+ // Calculate stake modifier checksum
+ pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
+ if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum))
+ return error("LoadBlockIndexDB() : Failed stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindex->nHeight, pindex->nStakeModifier);
+ }
+
+ // Load block file info
+ pblocktree->ReadLastBlockFile(nLastBlockFile);
+ printf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile);
+ if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+ printf("LoadBlockIndexDB(): last block file: %s\n", infoLastBlockFile.ToString().c_str());
+
+ // Load hashBestChain pointer to end of best chain
+ pindexBest = pcoinsTip->GetBestBlock();
+ if (pindexBest == NULL)
+ {
+ if (pindexGenesisBlock == NULL)
+ return true;
+ return error("LoadBlockIndexDB() : hashBestChain not loaded");
+ }
+ hashBestChain = pindexBest->GetBlockHash();
+ nBestHeight = pindexBest->nHeight;
+ nBestChainTrust = pindexBest->nChainTrust;
+
+ // set 'next' pointers in best chain
+ CBlockIndex *pindex = pindexBest;
+ while(pindex != NULL && pindex->pprev != NULL) {
+ CBlockIndex *pindexPrev = pindex->pprev;
+ pindexPrev->pnext = pindex;
+ pindex = pindexPrev;
+ }
+ printf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n",
+ hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
+ DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
+
+ // Load sync-checkpoint
+ if (!pblocktree->ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint))
+ return error("LoadBlockIndexDB() : hashSyncCheckpoint not loaded");
+ printf("LoadBlockIndexDB(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str());
+
+ // Load bnBestInvalidTrust, OK if it doesn't exist
+ CBigNum bnBestInvalidTrust;
+ pblocktree->ReadBestInvalidTrust(bnBestInvalidTrust);
+ nBestInvalidTrust = bnBestInvalidTrust.getuint256();
+
+ // Verify blocks in the best chain
+ int nCheckLevel = GetArg("-checklevel", 1);
+ int nCheckDepth = GetArg( "-checkblocks", 288);
+ if (nCheckDepth == 0)
+ nCheckDepth = 1000000000; // suffices until the year 19000
+ if (nCheckDepth > nBestHeight)
+ nCheckDepth = nBestHeight;
+ printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
+ CBlockIndex* pindexFork = NULL;
+ for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
+ {
+ if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
+ break;
+ CBlock block;
+ if (!block.ReadFromDisk(pindex))
+ return error("LoadBlockIndexDB() : block.ReadFromDisk failed");
+ // check level 1: verify block validity
+ if (nCheckLevel>0 && !block.CheckBlock())
+ {
+ printf("LoadBlockIndexDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
+ pindexFork = pindex->pprev;
+ }
+ // TODO: stronger verifications
+ }
+ if (pindexFork && !fRequestShutdown)
+ {
+ // TODO: reorg back
+ return error("LoadBlockIndexDB(): chain database corrupted");
+ }
+
+ return true;
+}
+
+
bool LoadBlockIndex(bool fAllowNew)
{
CBigNum bnTrustedModulus;
ZCParams = new libzerocoin::Params(bnTrustedModulus);
//
- // Load block index
+ // Load block index from databases
//
- CChainDB chaindb("cr");
- if (!LoadBlockIndex(chaindb))
+ if (!LoadBlockIndexDB())
return false;
- chaindb.Close();
//
// Init with genesis block
// Start new block file
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
- {
- CChainDB chaindb;
- if (!FindBlockPos(chaindb, blockPos, nBlockSize+8, 0, block.nTime))
- return error("AcceptBlock() : FindBlockPos failed");
- }
+ if (!FindBlockPos(blockPos, nBlockSize+8, 0, block.nTime))
+ return error("AcceptBlock() : FindBlockPos failed");
if (!block.WriteToDisk(blockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!block.AddToBlockIndex(blockPos))
// initialize synchronized checkpoint
if (!Checkpoints::WriteSyncCheckpoint((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)))
return error("LoadBlockIndex() : failed to init sync checkpoint");
+
+ // upgrade time set to zero if txdb initialized
+ if (!pblocktree->WriteModifierUpgradeTime(0))
+ return error("LoadBlockIndex() : failed to init upgrade info");
+ printf(" Upgrade Info: blocktreedb initialization\n");
}
string strPubKey = "";
+ // if checkpoint master key changed must reset sync-checkpoint
+ if (!pblocktree->ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
{
- CChainDB chaindb;
- // if checkpoint master key changed must reset sync-checkpoint
- if (!chaindb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
{
+ LOCK(Checkpoints::cs_hashSyncCheckpoint);
// write checkpoint master key to db
- chaindb.TxnBegin();
- if (!chaindb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
+ if (!pblocktree->WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
- if (!chaindb.TxnCommit())
- return error("LoadBlockIndex() : failed to commit new checkpoint master key to db");
- if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint())
- return error("LoadBlockIndex() : failed to reset sync-checkpoint");
}
+
+ if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint())
+ return error("LoadBlockIndex() : failed to reset sync-checkpoint");
+ }
+
+ // upgrade time set to zero if blocktreedb initialized
+ if (pblocktree->ReadModifierUpgradeTime(nModifierUpgradeTime))
+ {
+ if (nModifierUpgradeTime)
+ printf(" Upgrade Info: blocktreedb upgrade detected at timestamp %d\n", nModifierUpgradeTime);
+ else
+ printf(" Upgrade Info: no blocktreedb upgrade detected.\n");
+ }
+ else
+ {
+ nModifierUpgradeTime = GetTime();
+ printf(" Upgrade Info: upgrading blocktreedb at timestamp %u\n", nModifierUpgradeTime);
+ if (!pblocktree->WriteModifierUpgradeTime(nModifierUpgradeTime))
+ return error("LoadBlockIndex() : failed to write upgrade info");
}
return true;
strStatusBar = strMiscWarning;
}
- // * Should not enter safe mode for longer invalid chain
- // * If sync-checkpoint is too old do not enter safe mode
- // * Display warning only in the STRICT mode
- if (CheckpointsMode == Checkpoints::STRICT && Checkpoints::IsSyncCheckpointTooOld(60 * 60 * 24 * 10) &&
- !fTestNet && !IsInitialBlockDownload())
+ // if detected unmet upgrade requirement enter safe mode
+ // Note: Modifier upgrade requires blockchain redownload if past protocol switch
+ if (IsFixedModifierInterval(nModifierUpgradeTime + 60*60*24)) // 1 day margin
{
- nPriority = 100;
- strStatusBar = _("WARNING: Checkpoint is too old. Wait for block chain to download, or notify developers.");
+ nPriority = 5000;
+ strStatusBar = strRPC = "WARNING: Blockchain redownload required approaching or past v.0.4.4.7b6 upgrade deadline.";
}
// ppcoin: if detected invalid checkpoint enter safe mode
return true;
}
+ if (pfrom->nVersion < 60010)
+ {
+ printf("partner %s using a buggy client %d, disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion);
+ pfrom->fDisconnect = true;
+ return true;
+ }
+
// record my external IP reported by peer
if (addrFrom.IsRoutable() && addrMe.IsRoutable())
addrSeenByPeer = addrMe;