#include "alert.h"
#include "checkpoints.h"
#include "db.h"
+#include "txdb.h"
#include "net.h"
#include "init.h"
#include "ui_interface.h"
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
+set<CBlockIndex*, CBlockIndexTrustComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed
CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
// Settings
int64 nTransactionFee = MIN_TX_FEE;
-int64 nMinimumInputValue = MIN_TX_FEE;
+int64 nMinimumInputValue = MIN_TXOUT_AMOUNT;
extern enum Checkpoints::CPMode CheckpointsMode;
}
// make sure all wallets know about the given transaction, in the given block
-void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect)
+void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect)
{
if (!fConnect)
{
}
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
- pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
+ pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate);
}
// notify wallets about a new best chain
}
// 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); }
return false;
}
+// Select coins from read-only cache or database
+bool CCoinsViewCache::GetCoinsReadOnly(uint256 txid, CCoins &coins) {
+ if (cacheCoins.count(txid)) {
+ coins = cacheCoins[txid]; // get from cache
+ return true;
+ }
+ if (cacheCoinsReadOnly.count(txid)) {
+ coins = cacheCoinsReadOnly[txid]; // get from read-only cache
+ return true;
+ }
+ if (base->GetCoins(txid, coins)) {
+ cacheCoinsReadOnly[txid] = coins; // save to read-only cache
+ return true;
+ }
+ return false;
+}
+
+std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(uint256 txid) {
+ std::map<uint256,CCoins>::iterator it = cacheCoins.find(txid);
+ if (it != cacheCoins.end())
+ return it;
+ CCoins tmp;
+ if (!base->GetCoins(txid,tmp))
+ return it;
+ std::pair<std::map<uint256,CCoins>::iterator,bool> ret = cacheCoins.insert(std::make_pair(txid, tmp));
+ return ret.first;
+}
+
+CCoins &CCoinsViewCache::GetCoins(uint256 txid) {
+ std::map<uint256,CCoins>::iterator it = FetchCoins(txid);
+ assert(it != cacheCoins.end());
+ return it->second;
+}
+
bool CCoinsViewCache::SetCoins(uint256 txid, const CCoins &coins) {
cacheCoins[txid] = coins;
return true;
}
bool CCoinsViewCache::HaveCoins(uint256 txid) {
- return cacheCoins.count(txid) || base->HaveCoins(txid);
+ return FetchCoins(txid) != cacheCoins.end();
}
CBlockIndex *CCoinsViewCache::GetBestBlock() {
}
bool CCoinsViewCache::Flush() {
+ cacheCoinsReadOnly.clear(); // purge read-only cache
+
bool fOk = base->BatchWrite(cacheCoins, pindexTip);
if (fOk)
cacheCoins.clear();
}
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;
}
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
-bool CTransaction::AreInputsStandard(CCoinsView& mapInputs) const
+bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const
{
if (IsCoinBase())
return true; // Coinbases don't use vin normally
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;
}
}
+ if (!tx.HaveInputs(view))
+ return error("CTxMemPool::accept() : inputs already spent");
+
// Check for non-standard pay-to-script-hash in inputs
if (!tx.AreInputsStandard(view) && !fTestNet)
return error("CTxMemPool::accept() : nonstandard transaction input");
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();
}
DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
}
+void static InvalidBlockFound(CBlockIndex *pindex) {
+ pindex->nStatus |= BLOCK_FAILED_VALID;
+ pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
+ setBlockIndexValid.erase(pindex);
+ InvalidChainFound(pindex);
+ if (pindex->pnext)
+ ConnectBestBlock(); // reorganise away from the failed block
+}
+
+bool ConnectBestBlock() {
+ do {
+ CBlockIndex *pindexNewBest;
+
+ {
+ std::set<CBlockIndex*,CBlockIndexTrustComparator>::reverse_iterator it = setBlockIndexValid.rbegin();
+ if (it == setBlockIndexValid.rend())
+ return true;
+ pindexNewBest = *it;
+ }
+
+ if (pindexNewBest == pindexBest)
+ return true; // nothing to do
+
+ // check ancestry
+ CBlockIndex *pindexTest = pindexNewBest;
+ std::vector<CBlockIndex*> vAttach;
+ do {
+ if (pindexTest->nStatus & BLOCK_FAILED_MASK) {
+ // mark descendants failed
+ CBlockIndex *pindexFailed = pindexNewBest;
+ while (pindexTest != pindexFailed) {
+ pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
+ setBlockIndexValid.erase(pindexFailed);
+ pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed));
+ pindexFailed = pindexFailed->pprev;
+ }
+ InvalidChainFound(pindexNewBest);
+ break;
+ }
+
+ if (pindexBest == NULL || pindexTest->nChainTrust > pindexBest->nChainTrust)
+ vAttach.push_back(pindexTest);
+
+ if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) {
+ reverse(vAttach.begin(), vAttach.end());
+ BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach)
+ if (!SetBestChain(pindexSwitch))
+ return false;
+ return true;
+ }
+ pindexTest = pindexTest->pprev;
+ } while(true);
+ } while(true);
+}
void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
{
nTime = max(GetBlockTime(), GetAdjustedTime());
}
-
-CTxOut CTransaction::GetOutputFor(const CTxIn& input, CCoinsView& view)
+const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view)
{
- CCoins coins;
- if (!view.GetCoins(input.prevout.hash, coins))
- throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");
-
- if (input.prevout.n >= coins.vout.size())
- throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range or already spent");
-
- const CTxOut &out = coins.vout[input.prevout.n];
- if (out.IsNull())
- throw std::runtime_error("CTransaction::GetOutputFor() : already spent");
-
- return out;
+ const CCoins &coins = view.GetCoins(input.prevout.hash);
+ assert(coins.IsAvailable(input.prevout.n));
+ return coins.vout[input.prevout.n];
}
-int64 CTransaction::GetValueIn(CCoinsView& inputs) const
+int64 CTransaction::GetValueIn(CCoinsViewCache& inputs) const
{
if (IsCoinBase())
return 0;
int64 nResult = 0;
for (unsigned int i = 0; i < vin.size(); i++)
- {
nResult += GetOutputFor(vin[i], inputs).nValue;
- }
+
return nResult;
}
-unsigned int CTransaction::GetP2SHSigOpCount(CCoinsView& inputs) const
+unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const
{
if (IsCoinBase())
return 0;
unsigned int nSigOps = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
- CTxOut prevout = GetOutputFor(vin[i], inputs);
+ const CTxOut &prevout = GetOutputFor(vin[i], inputs);
if (prevout.scriptPubKey.IsPayToScriptHash())
nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
}
return nSigOps;
}
-bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight, unsigned int nTimeStamp) const
+bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, unsigned int nTimeStamp, const uint256 &txhash) const
{
- uint256 hash = GetHash();
-
// mark inputs spent
if (!IsCoinBase()) {
BOOST_FOREACH(const CTxIn &txin, vin) {
- CCoins coins;
- if (!inputs.GetCoins(txin.prevout.hash, coins))
- return error("UpdateCoins() : cannot find prevtx");
-
+ CCoins &coins = inputs.GetCoins(txin.prevout.hash);
if (coins.nTime > nTimeStamp)
return error("UpdateCoins() : timestamp violation");
-
CTxInUndo undo;
if (!coins.Spend(txin.prevout, undo))
return error("UpdateCoins() : cannot spend input");
txundo.vprevout.push_back(undo);
- if (!inputs.SetCoins(txin.prevout.hash, coins))
- return error("UpdateCoins() : cannot update input");
}
}
// add outputs
- if (!inputs.SetCoins(hash, CCoins(*this, nHeight, nTimeStamp)))
+ if (!inputs.SetCoins(txhash, CCoins(*this, nHeight, nTimeStamp)))
return error("UpdateCoins() : cannot update output");
return true;
}
-bool CTransaction::HaveInputs(CCoinsView &inputs) const
+bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const
{
- if (!IsCoinBase()) {
+ if (!IsCoinBase()) {
// first check whether information about the prevout hash is available
for (unsigned int i = 0; i < vin.size(); i++) {
const COutPoint &prevout = vin[i].prevout;
// then check whether the actual outputs are available
for (unsigned int i = 0; i < vin.size(); i++) {
const COutPoint &prevout = vin[i].prevout;
- CCoins coins;
- inputs.GetCoins(prevout.hash, coins);
+ const CCoins &coins = inputs.GetCoins(prevout.hash);
if (!coins.IsAvailable(prevout.n))
return false;
}
return true;
}
-bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings, CBlock *pblock) const
+bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings, CBlock *pblock) const
{
if (!IsCoinBase())
{
+ // 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 (!HaveInputs(inputs))
+ return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str());
+
+ CBlockIndex *pindexBlock = inputs.GetBestBlock();
int64 nValueIn = 0;
int64 nFees = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
const COutPoint &prevout = vin[i].prevout;
- CCoins coins;
- if (!inputs.GetCoins(prevout.hash, coins))
- return error("CheckInputs() : cannot find prevout tx");
-
- // 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 (!coins.IsAvailable(prevout.n))
- return error("CheckInputs() : %s prev tx already used", GetHash().ToString().substr(0,10).c_str());
+ const CCoins &coins = inputs.GetCoins(prevout.hash);
// If prev is coinbase or coinstake, check that it's matured
if (coins.IsCoinBase() || coins.IsCoinStake()) {
- CBlockIndex *pindexBlock = inputs.GetBestBlock();
if (pindexBlock->nHeight - coins.nHeight < nCoinbaseMaturity)
return error("CheckInputs() : tried to spend %s at depth %d", coins.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - coins.nHeight);
}
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;
- CCoins coins;
- inputs.GetCoins(prevout.hash, coins);
+ const CCoins &coins = inputs.GetCoins(prevout.hash);
// Verify signature
if (!VerifySignature(coins, *this, i, fStrictPayToScriptHash, fStrictEncodings, 0)) {
}
}
}
-
-
}
return true;
return true;
}
-bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
+bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view)
{
assert(pindex == view.GetBestBlock());
const CTransaction &tx = vtx[i];
uint256 hash = tx.GetHash();
+ // don't check coinbase coins for proof-of-stake block
+ if(IsProofOfStake() && tx.IsCoinBase())
+ continue;
+
// check that all outputs are available
- CCoins outs;
- if (!view.GetCoins(hash, outs))
+ if (!view.HaveCoins(hash))
return error("DisconnectBlock() : outputs still spent? database corrupted");
+ CCoins &outs = view.GetCoins(hash);
CCoins outsBlock = CCoins(tx, pindex->nHeight, pindex->nTime);
if (outs != outsBlock)
return error("DisconnectBlock() : added transaction mismatch? database corrupted");
// remove outputs
- if (!view.SetCoins(hash, CCoins()))
- return error("DisconnectBlock() : cannot delete coin outputs");
+ outs = CCoins();
// restore inputs
if (i > 0) { // not coinbases
if (undo.nHeight == 0)
return error("DisconnectBlock() : undo data doesn't contain tx metadata? database corrupted");
coins.fCoinBase = undo.fCoinBase;
+ coins.fCoinStake = undo.fCoinStake;
coins.nHeight = undo.nHeight;
+ coins.nTime = undo.nTime;
+ coins.nBlockTime = undo.nBlockTime;
coins.nVersion = undo.nVersion;
} else {
if (undo.nHeight != 0)
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, CCoinsView &view, bool fJustCheck)
+bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
{
// Check it again in case a previous version let a bad block in
if (!CheckBlock(!fJustCheck, !fJustCheck))
bool fEnforceBIP30 = true;
if (fEnforceBIP30) {
- BOOST_FOREACH(CTransaction& tx, vtx) {
- uint256 hash = tx.GetHash();
- CCoins coins;
- if (view.GetCoins(hash, coins) && !coins.IsPruned())
+ for (unsigned int i=0; i<vtx.size(); i++) {
+ uint256 hash = GetTxHash(i);
+ if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
return error("ConnectBlock() : tried to overwrite transaction");
}
}
int64 nFees = 0, nValueIn = 0, nValueOut = 0;
unsigned int nSigOps = 0;
- BOOST_FOREACH(CTransaction& tx, vtx)
+ for (unsigned int i=0; i<vtx.size(); i++)
{
+ const CTransaction &tx = vtx[i];
nSigOps += tx.GetLegacySigOpCount();
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
nValueOut += tx.GetValueOut();
}
+ // don't create coinbase coins for proof-of-stake block
+ if(IsProofOfStake() && tx.IsCoinBase())
+ continue;
+
CTxUndo txundo;
- if (!tx.UpdateCoins(view, txundo, pindex->nHeight, pindex->nTime))
+ if (!tx.UpdateCoins(view, txundo, pindex->nHeight, pindex->nTime, GetTxHash(i)))
return error("ConnectBlock() : UpdateInputs failed");
if (!tx.IsCoinBase())
blockundo.vtxundo.push_back(txundo);
if (fJustCheck)
return true;
- CChainDB chaindb;
- CDiskBlockPos pos;
-
// Write undo information to disk
- if (pindex->GetUndoPos().IsNull())
+ if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS)
{
- if (!FindUndoPos(chaindb, pindex->pos.nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8))
- return error("ConnectBlock() : FindUndoPos failed");
- if (!blockundo.WriteToDisk(pos))
- return error("ConnectBlock() : CBlockUndo::WriteToDisk failed");
+ if (pindex->GetUndoPos().IsNull()) {
+ CDiskBlockPos pos;
+ 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");
+
+ // update nUndoPos in block index
+ pindex->nUndoPos = pos.nPos;
+ pindex->nStatus |= BLOCK_HAVE_UNDO;
+ }
- // update nUndoPos in block index
- pindex->nUndoPos = pos.nPos + 1;
- }
+ pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS;
- CDiskBlockIndex blockindex(pindex);
- if (!chaindb.WriteBlockIndex(blockindex))
- return error("ConnectBlock() : WriteBlockIndex failed");
+ CDiskBlockIndex blockindex(pindex);
+ if (!pblocktree->WriteBlockIndex(blockindex))
+ return error("ConnectBlock() : WriteBlockIndex failed");
+ }
// add this block to the view's blockchain
if (!view.SetBestBlock(pindex))
printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees);
// Watch for transactions paying to me
- BOOST_FOREACH(CTransaction& tx, vtx)
- SyncWithWallets(tx, this, true);
+ for (unsigned int i=0; i<vtx.size(); i++)
+ SyncWithWallets(GetTxHash(i), vtx[i], this, true);
return true;
}
-bool CBlock::SetBestChain(CBlockIndex* pindexNew)
+bool SetBestChain(CBlockIndex* pindexNew)
{
CCoinsViewCache &view = *pcoinsTip;
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)
- if (!tx.IsCoinBase())
+ if (!tx.IsCoinBase() && !tx.IsCoinStake())
vResurrect.push_back(tx);
}
vector<CTransaction> vDelete;
BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
CBlock block;
- CBlock *pblock;
- if (pindex == pindexNew) // connecting *this block
- pblock = this;
- else { // other block; read it from disk
- if (!block.ReadFromDisk(pindex))
- return error("SetBestBlock() : ReadFromDisk for connect failed");
- pblock = █
- }
+ if (!block.ReadFromDisk(pindex))
+ return error("SetBestChain() : ReadFromDisk for connect failed");
CCoinsViewCache viewTemp(view, true);
- if (!pblock->ConnectBlock(pindex, viewTemp)) {
+ if (!block.ConnectBlock(pindex, viewTemp)) {
InvalidChainFound(pindexNew);
- return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ InvalidBlockFound(pindex);
+ 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, pblock->vtx)
+ BOOST_FOREACH(const CTransaction& tx, block.vtx)
vDelete.push_back(tx);
}
uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
- printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%"PRI64d" date=%s\n",
- hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
- CBigNum(nBestChainTrust).ToString().c_str(),
- nBestBlockTrust.Get64(),
+ printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%s tx=%lu date=%s\n",
+ hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str(), CBigNum(nBestBlockTrust).ToString().c_str(), (unsigned long)pindexNew->nChainTx,
DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
// Check the version of the last 100 blocks to see if we need to upgrade:
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
}
+ pindexNew->nTx = vtx.size();
pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();
- pindexNew->pos = pos;
+ pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx;
+ pindexNew->nFile = pos.nFile;
+ pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
+ pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA;
// Compute stake entropy bit for stake modifier
if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nTime)))
// 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);
if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum))
return error("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindexNew->nHeight, nStakeModifier);
- CChainDB chaindb;
- if (!chaindb.TxnBegin())
- return false;
- chaindb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
- if (!chaindb.TxnCommit())
- return false;
+ setBlockIndexValid.insert(pindexNew);
+
+ pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew));
- // New best
- if (!SetBestChain(pindexNew))
+ // New best?
+ if (!ConnectBestBlock())
return false;
if (pindexNew == pindexBest)
// Notify UI to display prev block's coinbase if it was ours
static uint256 hashPrevBestCoinBase;
UpdatedTransaction(hashPrevBestCoinBase);
- hashPrevBestCoinBase = vtx[0].GetHash();
+ 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");
}
// Check coinstake timestamp
if (!CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime))
return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%"PRI64d" nTimeTx=%u", GetBlockTime(), vtx[1].nTime));
-
- // NovaCoin: check proof-of-stake block signature
- if (fCheckSig && !CheckBlockSignature(true))
- return DoS(100, error("CheckBlock() : bad proof-of-stake block signature"));
}
else
{
{
bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME);
- // NovaCoin: check proof-of-work block signature
- if (checkEntropySig && !CheckBlockSignature(false))
+ // check legacy proof-of-work block signature
+ if (checkEntropySig && !CheckLegacySignature())
return DoS(100, error("CheckBlock() : bad proof-of-work block signature"));
}
}
// Check for duplicate txids. This is caught by ConnectInputs(),
// but catching it earlier avoids a potential DoS attack:
+ BuildMerkleTree();
set<uint256> uniqueTx;
- BOOST_FOREACH(const CTransaction& tx, vtx)
- {
- uniqueTx.insert(tx.GetHash());
+ for (unsigned int i=0; i<vtx.size(); i++) {
+ uniqueTx.insert(GetTxHash(i));
}
if (uniqueTx.size() != vtx.size())
return DoS(100, error("CheckBlock() : duplicate transaction"));
// Check proof-of-work or proof-of-stake
if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
- return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake"));
+ return DoS(100, error("AcceptBlock() : incorrect proof-of-%s amount", IsProofOfWork() ? "work" : "stake"));
// Check timestamp against prev
if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime())
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))
if (mapOrphanBlocks.count(hash))
return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
- // ppcoin: check proof-of-stake
- // Limited duplicity on stake: prevents block flood attack
- // Duplicate stake allowed only when there is orphan child block
- if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
- return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
-
// Preliminary checks
if (!pblock->CheckBlock())
return error("ProcessBlock() : CheckBlock FAILED");
- // ppcoin: verify hash target and signature of coinstake tx
if (pblock->IsProofOfStake())
{
- uint256 hashProofOfStake = 0, targetProofOfStake = 0;
- if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, hashProofOfStake, targetProofOfStake))
+ // Limited duplicity on stake: prevents block flood attack
+ // Duplicate stake allowed only when there is orphan child block
+ if (setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+ return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
+
+ bool fFatal = false;
+ uint256 hashProofOfStake;
+
+ // Verify proof-of-stake script, hash target and signature
+ if (!pblock->CheckSignature(fFatal, hashProofOfStake))
{
- printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str());
- return false; // do not error here as we expect this during initial block download
+ if (fFatal)
+ {
+ // Invalid coinstake script, blockhash signature or no generator defined, nothing to do here
+ // This also may occur when supplied proof-of-stake doesn't satisfy required target
+ if (pfrom)
+ pfrom->Misbehaving(100);
+ return error("ProcessBlock() : invalid signatures found in proof-of-stake block %s", hash.ToString().c_str());
+ }
+ else
+ {
+ // Blockhash and coinstake signatures are OK but target checkings failed
+ // This may occur during initial block download
+
+ if (pfrom)
+ pfrom->Misbehaving(1); // Small DoS penalty
+
+ printf("WARNING: ProcessBlock(): proof-of-stake target checkings failed for block %s, we'll try again later\n", hash.ToString().c_str());
+ return false;
+ }
}
+
if (!mapProofOfStake.count(hash)) // add to mapProofOfStake
mapProofOfStake.insert(make_pair(hash, hashProofOfStake));
}
{
if (pfrom)
pfrom->Misbehaving(100);
- return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work");
+ return error("ProcessBlock() : block with too little proof-of-%s", pblock->IsProofOfStake() ? "stake" : "work");
}
}
- // ppcoin: ask for pending sync-checkpoint if any
+ // Ask for pending sync-checkpoint if any
if (!IsInitialBlockDownload())
Checkpoints::AskForPendingSyncCheckpoint(pfrom);
{
printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
CBlock* pblock2 = new CBlock(*pblock);
- // ppcoin: check proof-of-stake
+
if (pblock2->IsProofOfStake())
{
// Limited duplicity on stake: prevents block flood attack
else
setStakeSeenOrphan.insert(pblock2->GetProofOfStake());
}
+
mapOrphanBlocks.insert(make_pair(hash, pblock2));
mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
return true;
}
-// novacoin: attempt to generate suitable proof-of-stake
+// attempt to generate suitable proof-of-stake
bool CBlock::SignBlock(CWallet& wallet)
{
// if we are trying to sign
return false;
}
-// ppcoin: check block signature
-bool CBlock::CheckBlockSignature(bool fProofOfStake) const
+// get generation key
+bool CBlock::GetGenerator(CKey& GeneratorKey) const
{
- if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
- return vchBlockSig.empty();
+ if(!IsProofOfStake())
+ return false;
vector<valtype> vSolutions;
txnouttype whichType;
- if(fProofOfStake)
+ const CTxOut& txout = vtx[1].vout[1];
+
+ if (!Solver(txout.scriptPubKey, whichType, vSolutions))
+ return false;
+ if (whichType == TX_PUBKEY)
{
- const CTxOut& txout = vtx[1].vout[1];
+ valtype& vchPubKey = vSolutions[0];
+ CKey key;
+ return GeneratorKey.SetPubKey(vchPubKey);
+ }
+
+ return false;
+}
+
+// verify proof-of-stake signatures
+bool CBlock::CheckSignature(bool& fFatal, uint256& hashProofOfStake) const
+{
+ CKey key;
+
+ // no generator or invalid hash signature means fatal error
+ fFatal = !GetGenerator(key) || !key.Verify(GetHash(), vchBlockSig);
+
+ if (fFatal)
+ return false;
+
+ uint256 hashTarget = 0;
+ if (!CheckProofOfStake(vtx[1], nBits, hashProofOfStake, hashTarget, fFatal))
+ return false; // hash target mismatch or invalid coinstake signature
+
+ return true;
+}
+
+// verify legacy proof-of-work signature
+bool CBlock::CheckLegacySignature() const
+{
+ if (IsProofOfStake())
+ return false;
+
+ vector<valtype> vSolutions;
+ txnouttype whichType;
+
+ for(unsigned int i = 0; i < vtx[0].vout.size(); i++)
+ {
+ const CTxOut& txout = vtx[0].vout[i];
if (!Solver(txout.scriptPubKey, whichType, vSolutions))
return false;
+
if (whichType == TX_PUBKEY)
{
+ // Verify
valtype& vchPubKey = vSolutions[0];
CKey key;
if (!key.SetPubKey(vchPubKey))
- return false;
+ continue;
if (vchBlockSig.empty())
- return false;
- return key.Verify(GetHash(), vchBlockSig);
+ continue;
+ if(!key.Verify(GetHash(), vchBlockSig))
+ continue;
+ return true;
}
}
- else
- {
- for(unsigned int i = 0; i < vtx[0].vout.size(); i++)
- {
- const CTxOut& txout = vtx[0].vout[i];
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
- return false;
-
- if (whichType == TX_PUBKEY)
- {
- // Verify
- valtype& vchPubKey = vSolutions[0];
- CKey key;
- if (!key.SetPubKey(vchPubKey))
- continue;
- if (vchBlockSig.empty())
- continue;
- if(!key.Verify(GetHash(), vchBlockSig))
- continue;
-
- return true;
- }
- }
- }
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;
bool fMissingInputs = false;
if (tx.AcceptToMemoryPool(true, &fMissingInputs))
{
- SyncWithWallets(tx, NULL, true);
+ SyncWithWallets(inv.hash, tx, NULL, true);
RelayTransaction(tx, inv.hash);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
if (orphanTx.AcceptToMemoryPool(true, &fMissingInputs2))
{
printf(" accepted orphan tx %s\n", orphanTxHash.ToString().substr(0,10).c_str());
- SyncWithWallets(tx, NULL, true);
+ SyncWithWallets(inv.hash, tx, NULL, true);
RelayTransaction(orphanTx, orphanTxHash);
mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanTxHash));
vWorkQueue.push_back(orphanTxHash);