#include "init.h"
#include "ui_interface.h"
#include "kernel.h"
-#include "scrypt_mine.h"
+#include "zerocoin/Zerocoin.h"
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
+
using namespace std;
using namespace boost;
map<uint256, CBlockIndex*> mapBlockIndex;
set<pair<COutPoint, unsigned int> > setStakeSeen;
+libzerocoin::Params* ZCParams;
CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); // "standard" scrypt target limit for proof of work, results with 0,000244140625 proof-of-work difficulty
CBigNum bnProofOfStakeLegacyLimit(~uint256(0) >> 24); // proof of stake target limit from block #15000 and until 20 June 2013, results with 0,00390625 proof of stake difficulty
CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 16);
-unsigned int nStakeMinAge = 60 * 60 * 24 * 30; // 30 days as minimum age for coin age
-unsigned int nStakeMaxAge = 60 * 60 * 24 * 90; // 90 days as stake age of full weight
+unsigned int nStakeMinAge = 60 * 60 * 24 * 30; // 30 days as zero time weight
+unsigned int nStakeMaxAge = 60 * 60 * 24 * 90; // 90 days as full weight
unsigned int nStakeTargetSpacing = 10 * 60; // 10-minute stakes spacing
unsigned int nModifierInterval = 6 * 60 * 60; // time to elapse before new modifier is computed
set<pair<COutPoint, unsigned int> > setStakeSeenOrphan;
map<uint256, uint256> mapProofOfStake;
-map<uint256, CDataStream*> mapOrphanTransactions;
-map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
+map<uint256, CTransaction> mapOrphanTransactions;
+map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
// Settings
int64 nTransactionFee = MIN_TX_FEE;
-bool fStakeUsePooledKeys = false;
+int64 nMinimumInputValue = MIN_TX_FEE;
+
+extern enum Checkpoints::CPMode CheckpointsMode;
//////////////////////////////////////////////////////////////////////////////
//
}
+//////////////////////////////////////////////////////////////////////////////
+//
+// CCoinsView implementations
+//
+
+bool CCoinsView::GetCoins(uint256 txid, CCoins &coins) { return false; }
+bool CCoinsView::SetCoins(uint256 txid, const CCoins &coins) { return false; }
+bool CCoinsView::HaveCoins(uint256 txid) { return false; }
+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; }
+
+CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { }
+bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); }
+bool CCoinsViewBacked::SetCoins(uint256 txid, const CCoins &coins) { return base->SetCoins(txid, coins); }
+bool CCoinsViewBacked::HaveCoins(uint256 txid) { return base->HaveCoins(txid); }
+CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); }
+bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); }
+void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
+
+bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); }
+
+CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { }
+
+bool CCoinsViewCache::GetCoins(uint256 txid, CCoins &coins) {
+ if (cacheCoins.count(txid)) {
+ coins = cacheCoins[txid];
+ return true;
+ }
+ if (base->GetCoins(txid, coins)) {
+ cacheCoins[txid] = coins;
+ return true;
+ }
+ return false;
+}
+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);
+}
+CBlockIndex *CCoinsViewCache::GetBestBlock() {
+ if (pindexTip == NULL)
+ pindexTip = base->GetBestBlock();
+ return pindexTip;
+}
+bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) {
+ pindexTip = pindex;
+ return true;
+}
+
+bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) {
+ for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++)
+ cacheCoins[it->first] = it->second;
+ pindexTip = pindex;
+ return true;
+}
+
+bool CCoinsViewCache::Flush() {
+ bool fOk = base->BatchWrite(cacheCoins, pindexTip);
+ if (fOk)
+ cacheCoins.clear();
+ return fOk;
+}
+
+unsigned int CCoinsViewCache::GetCacheSize() {
+ return cacheCoins.size();
+}
+
+/** CCoinsView that brings transactions from a memorypool into view.
+ It does not check for spendings by memory pool transactions. */
+CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
+
+bool CCoinsViewMemPool::GetCoins(uint256 txid, CCoins &coins) {
+ if (base->GetCoins(txid, coins))
+ return true;
+ if (mempool.exists(txid)) {
+ const CTransaction &tx = mempool.lookup(txid);
+ coins = CCoins(tx, MEMPOOL_HEIGHT, -1);
+ return true;
+ }
+ return false;
+}
+
+bool CCoinsViewMemPool::HaveCoins(uint256 txid) {
+ return mempool.exists(txid) || base->HaveCoins(txid);
+}
+
+CCoinsViewCache *pcoinsTip = NULL;
//////////////////////////////////////////////////////////////////////////////
//
// mapOrphanTransactions
//
-bool AddOrphanTx(const CDataStream& vMsg)
+bool AddOrphanTx(const CTransaction& tx)
{
- CTransaction tx;
- CDataStream(vMsg) >> tx;
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
return false;
- CDataStream* pvMsg = new CDataStream(vMsg);
-
// Ignore big transactions, to avoid a
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
// large transaction with a missing parent then we assume
// have been mined or received.
// 10,000 orphans, each of which is at most 5,000 bytes big is
// at most 500 megabytes of orphans:
- if (pvMsg->size() > 5000)
+
+ size_t nSize = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
+
+ if (nSize > 5000)
{
- printf("ignoring large orphan tx (size: %"PRIszu", hash: %s)\n", pvMsg->size(), hash.ToString().substr(0,10).c_str());
- delete pvMsg;
+ printf("ignoring large orphan tx (size: %"PRIszu", hash: %s)\n", nSize, hash.ToString().substr(0,10).c_str());
return false;
}
- mapOrphanTransactions[hash] = pvMsg;
+ mapOrphanTransactions[hash] = tx;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
- mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg));
+ mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().substr(0,10).c_str(),
mapOrphanTransactions.size());
{
if (!mapOrphanTransactions.count(hash))
return;
- const CDataStream* pvMsg = mapOrphanTransactions[hash];
- CTransaction tx;
- CDataStream(*pvMsg) >> tx;
+ const CTransaction& tx = mapOrphanTransactions[hash];
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
mapOrphanTransactionsByPrev.erase(txin.prevout.hash);
}
- delete pvMsg;
mapOrphanTransactions.erase(hash);
}
{
// Evict a random orphan:
uint256 randomhash = GetRandHash();
- map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
+ map<uint256, CTransaction>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
EraseOrphanTx(it->first);
//////////////////////////////////////////////////////////////////////////////
//
-// CTransaction and CTxIndex
+// CTransaction
//
-bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet)
-{
- SetNull();
- if (!txdb.ReadTxIndex(prevout.hash, txindexRet))
- return false;
- if (!ReadFromDisk(txindexRet.pos))
- return false;
- if (prevout.n >= vout.size())
- {
- SetNull();
- return false;
- }
- return true;
-}
-
-bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout)
-{
- CTxIndex txindex;
- return ReadFromDisk(txdb, prevout, txindex);
-}
-
-bool CTransaction::ReadFromDisk(COutPoint prevout)
-{
- CTxDB txdb("r");
- CTxIndex txindex;
- return ReadFromDisk(txdb, prevout, txindex);
-}
-
bool CTransaction::IsStandard() const
{
if (nVersion > CTransaction::CURRENT_VERSION)
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
-bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const
+bool CTransaction::AreInputsStandard(CCoinsView& mapInputs) const
{
if (IsCoinBase())
return true; // Coinbases don't use vin normally
// beside "push data" in the scriptSig the
// IsStandard() call returns false
vector<vector<unsigned char> > stack;
- if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0))
+ if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0))
return false;
if (whichType == TX_SCRIPTHASH)
else
{
CBlock blockTmp;
- if (pblock == NULL)
- {
- // Load the block this tx is in
- CTxIndex txindex;
- if (!CTxDB("r").ReadTxIndex(GetHash(), txindex))
- return 0;
- if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos))
- return 0;
- pblock = &blockTmp;
+ if (pblock == NULL) {
+ CCoins coins;
+ if (pcoinsTip->GetCoins(GetHash(), coins)) {
+ CBlockIndex *pindex = FindBlockByHeight(coins.nHeight);
+ if (pindex) {
+ if (!blockTmp.ReadFromDisk(pindex))
+ return 0;
+ pblock = &blockTmp;
+ }
+ }
}
+ if (pblock) {
// Update the tx's hashBlock
hashBlock = pblock->GetHash();
// Fill in merkle branch
vMerkleBranch = pblock->GetMerkleBranch(nIndex);
+ }
}
// Is the tx in a block that's in the main chain
return pindexBest->nHeight - pindex->nHeight + 1;
}
-
-
-
-
-
-
bool CTransaction::CheckTransaction() const
{
// Basic checks that don't depend on any context
if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake())
return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
- // NovaCoin: enforce minimum output amount for user transactions
- // (and for all transactions until 20 Sep 2013)
- if ((!IsCoinBase() || nTime < CHAINCHECKS_SWITCH_TIME)
- && (!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT)
+ // 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 > MAX_MONEY)
}
int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree,
- enum GetMinFee_mode mode) const
+ enum GetMinFee_mode mode, unsigned int nBytes) 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;
- unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION);
unsigned int nNewBlockSize = nBlockSize + nBytes;
int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee;
return nMinFee;
}
+void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
+{
+ LOCK(cs);
+
+ std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0));
-bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
- bool* pfMissingInputs)
+ // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx
+ while (it != mapNextTx.end() && it->first.hash == hashTx) {
+ coins.Spend(it->first.n); // and remove those outputs from coins
+ it++;
+ }
+}
+
+bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs)
{
if (pfMissingInputs)
*pfMissingInputs = false;
if (tx.IsCoinBase())
return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx"));
- // ppcoin: coinstake is also only valid in a block, not as a loose transaction
+ // Coinstake is also only valid in a block, not as a loose transaction
if (tx.IsCoinStake())
return tx.DoS(100, error("CTxMemPool::accept() : coinstake as individual tx"));
if (!fTestNet && !tx.IsStandard())
return error("CTxMemPool::accept() : nonstandard transaction type");
- // Do we already have it?
+ // is it already in the memory pool?
uint256 hash = tx.GetHash();
{
LOCK(cs);
if (mapTx.count(hash))
return false;
}
- if (fCheckInputs)
- if (txdb.ContainsTx(hash))
- return false;
// Check for conflicts with in-memory transactions
CTransaction* ptxOld = NULL;
if (fCheckInputs)
{
- MapPrevTx mapInputs;
- map<uint256, CTxIndex> mapUnused;
- bool fInvalid = false;
- if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
- {
- if (fInvalid)
- return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str());
- if (pfMissingInputs)
- *pfMissingInputs = true;
+ CCoinsViewCache &view = *pcoinsTip;
+
+ // do we already have it?
+ if (view.HaveCoins(hash))
return false;
+
+ // do all inputs exist?
+ BOOST_FOREACH(const CTxIn txin, tx.vin) {
+ if (!view.HaveCoins(txin.prevout.hash)) {
+ if (pfMissingInputs)
+ *pfMissingInputs = true;
+ return false;
+ }
}
// Check for non-standard pay-to-script-hash in inputs
- if (!tx.AreInputsStandard(mapInputs) && !fTestNet)
+ if (!tx.AreInputsStandard(view) && !fTestNet)
return error("CTxMemPool::accept() : nonstandard transaction input");
// Note: if you modify this code to accept non-standard transactions, then
// you should add code here to check that the transaction does a
// reasonable number of ECDSA signature verifications.
- int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
+ int64 nFees = tx.GetValueIn(view)-tx.GetValueOut();
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);
+ int64 txMinFee = tx.GetMinFee(1000, false, GMF_RELAY, nSize);
if (nFees < txMinFee)
return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d,
hash.ToString().c_str(),
nFees, txMinFee);
+
// Continuously rate-limit free transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make others' transactions take longer to confirm.
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
+ if (!tx.CheckInputs(view, CS_ALWAYS, true, false))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
return true;
}
-bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs)
+bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs)
{
- return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs);
+ return mempool.accept(*this, fCheckInputs, pfMissingInputs);
}
bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
return pindexBest->nHeight - pindex->nHeight + 1;
}
-
int CMerkleTx::GetBlocksToMaturity() const
{
if (!(IsCoinBase() || IsCoinStake()))
}
-bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs)
+bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs)
{
if (fClient)
{
- if (!IsInMainChain() && !ClientConnectInputs())
+ if (!IsInMainChain() && !ClientCheckInputs())
return false;
- return CTransaction::AcceptToMemoryPool(txdb, false);
+ return CTransaction::AcceptToMemoryPool(false);
}
else
{
- return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs);
+ return CTransaction::AcceptToMemoryPool(fCheckInputs);
}
}
-bool CMerkleTx::AcceptToMemoryPool()
-{
- CTxDB txdb("r");
- return AcceptToMemoryPool(txdb);
-}
-
-
-
-bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs)
+bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs)
{
{
if (!(tx.IsCoinBase() || tx.IsCoinStake()))
{
uint256 hash = tx.GetHash();
- if (!mempool.exists(hash) && !txdb.ContainsTx(hash))
- tx.AcceptToMemoryPool(txdb, fCheckInputs);
+ if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash))
+ tx.AcceptToMemoryPool(fCheckInputs);
}
}
- return AcceptToMemoryPool(txdb, fCheckInputs);
+ return AcceptToMemoryPool(fCheckInputs);
}
return false;
}
-bool CWalletTx::AcceptWalletTransaction()
-{
- CTxDB txdb("r");
- return AcceptWalletTransaction(txdb);
-}
-
-int CTxIndex::GetDepthInMainChain() const
-{
- // Read block header
- CBlock block;
- if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false))
- return 0;
- // Find the block in the index
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash());
- if (mi == mapBlockIndex.end())
- return 0;
- CBlockIndex* pindex = (*mi).second;
- if (!pindex || !pindex->IsInMainChain())
- return 0;
- return 1 + nBestHeight - pindex->nHeight;
-}
-
// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock
-bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock)
+bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
{
+ CBlockIndex *pindexSlow = NULL;
{
LOCK(cs_main);
{
LOCK(mempool.cs);
if (mempool.exists(hash))
{
- tx = mempool.lookup(hash);
+ txOut = mempool.lookup(hash);
return true;
}
}
- CTxDB txdb("r");
- CTxIndex txindex;
- if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex))
- {
- CBlock block;
- if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
- hashBlock = block.GetHash();
- return true;
+
+ if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
+ int nHeight = -1;
+ {
+ CCoinsViewCache &view = *pcoinsTip;
+ CCoins coins;
+ if (view.GetCoins(hash, coins))
+ nHeight = coins.nHeight;
+ }
+ if (nHeight > 0)
+ pindexSlow = FindBlockByHeight(nHeight);
}
}
- return false;
-}
-
-
-
-
+ if (pindexSlow) {
+ CBlock block;
+ if (block.ReadFromDisk(pindexSlow)) {
+ BOOST_FOREACH(const CTransaction &tx, block.vtx) {
+ if (tx.GetHash() == hash) {
+ txOut = tx;
+ hashBlock = pindexSlow->GetBlockHash();
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
//////////////////////////////////////////////////////////////////////////////
*this = pindex->GetBlockHeader();
return true;
}
- if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions))
+ if (!ReadFromDisk(pindex->GetBlockPos(), fReadTransactions))
return false;
if (GetHash() != pindex->GetBlockHash())
return error("CBlock::ReadFromDisk() : GetHash() doesn't match index");
if(bCoinYearOnly)
return nRewardCoinYear;
- // Fix problem with proof-of-stake rewards calculation since 20 Sep 2013
- if(nTime < CHAINCHECKS_SWITCH_TIME)
- nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
- else
- nSubsidy = nCoinAge * nRewardCoinYear * 33 / (365 * 33 + 8);
+ nSubsidy = nCoinAge * nRewardCoinYear * 33 / (365 * 33 + 8);
// Set reasonable reward limit for large inputs since 20 Oct 2013
//
if (pindexNew->nChainTrust > nBestInvalidTrust)
{
nBestInvalidTrust = pindexNew->nChainTrust;
- CTxDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
+ CChainDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
uiInterface.NotifyBlocksChanged();
}
}
-
-
-
-
-
-
-
-
-
-bool CTransaction::DisconnectInputs(CTxDB& txdb)
-{
- // Relinquish previous transactions' spent pointers
- if (!IsCoinBase())
- {
- BOOST_FOREACH(const CTxIn& txin, vin)
- {
- COutPoint prevout = txin.prevout;
-
- // Get prev txindex from disk
- CTxIndex txindex;
- if (!txdb.ReadTxIndex(prevout.hash, txindex))
- return error("DisconnectInputs() : ReadTxIndex failed");
-
- if (prevout.n >= txindex.vSpent.size())
- return error("DisconnectInputs() : prevout.n out of range");
-
- // Mark outpoint as not spent
- txindex.vSpent[prevout.n].SetNull();
-
- // Write back
- if (!txdb.UpdateTxIndex(prevout.hash, txindex))
- return error("DisconnectInputs() : UpdateTxIndex failed");
- }
- }
-
- // Remove transaction from index
- // 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 anyway.
- txdb.EraseTxIndex(*this);
-
- return true;
-}
-
-
-bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
- bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid)
-{
- // FetchInputs can return false either because we just haven't seen some inputs
- // (in which case the transaction should be stored as an orphan)
- // or because the transaction is malformed (in which case the transaction should
- // be dropped). If tx is definitely invalid, fInvalid will be set to true.
- fInvalid = false;
-
- if (IsCoinBase())
- return true; // Coinbase transactions have no inputs to fetch.
-
- for (unsigned int i = 0; i < vin.size(); i++)
- {
- COutPoint prevout = vin[i].prevout;
- if (inputsRet.count(prevout.hash))
- continue; // Got it already
-
- // Read txindex
- CTxIndex& txindex = inputsRet[prevout.hash].first;
- bool fFound = true;
- if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
- {
- // Get txindex from current proposed changes
- txindex = mapTestPool.find(prevout.hash)->second;
- }
- else
- {
- // Read txindex from txdb
- fFound = txdb.ReadTxIndex(prevout.hash, txindex);
- }
- if (!fFound && (fBlock || fMiner))
- return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
-
- // Read txPrev
- CTransaction& txPrev = inputsRet[prevout.hash].second;
- if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
- {
- // Get prev tx from single transactions in memory
- {
- LOCK(mempool.cs);
- if (!mempool.exists(prevout.hash))
- return error("FetchInputs() : %s mempool Tx prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
- txPrev = mempool.lookup(prevout.hash);
- }
- if (!fFound)
- txindex.vSpent.resize(txPrev.vout.size());
- }
- else
- {
- // Get prev tx from disk
- if (!txPrev.ReadFromDisk(txindex.pos))
- return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
- }
- }
-
- // Make sure all prevout.n indexes are valid:
- for (unsigned int i = 0; i < vin.size(); i++)
- {
- const COutPoint prevout = vin[i].prevout;
- assert(inputsRet.count(prevout.hash) != 0);
- const CTxIndex& txindex = inputsRet[prevout.hash].first;
- const CTransaction& txPrev = inputsRet[prevout.hash].second;
- if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
- {
- // Revisit this if/when transaction replacement is implemented and allows
- // adding inputs:
- fInvalid = true;
- return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
- }
- }
-
- return true;
-}
-
-const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const
+CTxOut CTransaction::GetOutputFor(const CTxIn& input, CCoinsView& view)
{
- MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash);
- if (mi == inputs.end())
+ CCoins coins;
+ if (!view.GetCoins(input.prevout.hash, coins))
throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");
- const CTransaction& txPrev = (mi->second).second;
- if (input.prevout.n >= txPrev.vout.size())
- throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range");
+ 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 txPrev.vout[input.prevout.n];
+ return out;
}
-int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const
+int64 CTransaction::GetValueIn(CCoinsView& inputs) const
{
if (IsCoinBase())
return 0;
nResult += GetOutputFor(vin[i], inputs).nValue;
}
return nResult;
-
}
-unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const
+unsigned int CTransaction::GetP2SHSigOpCount(CCoinsView& inputs) const
{
if (IsCoinBase())
return 0;
unsigned int nSigOps = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
- const CTxOut& prevout = GetOutputFor(vin[i], inputs);
+ CTxOut prevout = GetOutputFor(vin[i], inputs);
if (prevout.scriptPubKey.IsPayToScriptHash())
nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
}
return nSigOps;
}
-bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
- map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
- const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash)
+bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight, unsigned int nTimeStamp) const
{
- // Take over previous transactions' spent pointers
- // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
- // fMiner is true when called from the internal bitcoin miner
- // ... both are false when called from CTransaction::AcceptToMemoryPool
- if (!IsCoinBase())
- {
- int64 nValueIn = 0;
- int64 nFees = 0;
- for (unsigned int i = 0; i < vin.size(); i++)
- {
- COutPoint prevout = vin[i].prevout;
- assert(inputs.count(prevout.hash) > 0);
- CTxIndex& txindex = inputs[prevout.hash].first;
- CTransaction& txPrev = inputs[prevout.hash].second;
+ uint256 hash = GetHash();
- if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
- return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
+ // 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");
- // If prev is coinbase or coinstake, check that it's matured
- if (txPrev.IsCoinBase() || txPrev.IsCoinStake())
- for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < nCoinbaseMaturity; pindex = pindex->pprev)
- if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
- return error("ConnectInputs() : tried to spend %s at depth %d", txPrev.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - pindex->nHeight);
+ if (coins.nTime > nTimeStamp)
+ return error("UpdateCoins() : timestamp violation");
- // ppcoin: check transaction timestamp
- if (txPrev.nTime > nTime)
- return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction"));
+ 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");
+ }
+ }
- // Check for negative or overflow input values
- nValueIn += txPrev.vout[prevout.n].nValue;
- if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
- return DoS(100, error("ConnectInputs() : txin values out of range"));
+ // add outputs
+ if (!inputs.SetCoins(hash, CCoins(*this, nHeight, nTimeStamp)))
+ return error("UpdateCoins() : cannot update output");
+ return true;
+}
+
+bool CTransaction::HaveInputs(CCoinsView &inputs) const
+{
+ 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;
+ if (!inputs.HaveCoins(prevout.hash))
+ return false;
}
- // The first loop above does all the inexpensive checks.
- // Only if ALL inputs pass do we perform expensive ECDSA signature checks.
- // Helps prevent CPU exhaustion attacks.
+
+ // 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);
+ 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
+{
+ if (!IsCoinBase())
+ {
+ int64 nValueIn = 0;
+ int64 nFees = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
- COutPoint prevout = vin[i].prevout;
- assert(inputs.count(prevout.hash) > 0);
- CTxIndex& txindex = inputs[prevout.hash].first;
- CTransaction& txPrev = inputs[prevout.hash].second;
+ 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 (!txindex.vSpent[prevout.n].IsNull())
- return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str());
+ if (!coins.IsAvailable(prevout.n))
+ return error("CheckInputs() : %s prev tx already used", GetHash().ToString().substr(0,10).c_str());
- // Skip ECDSA signature verification when connecting blocks (fBlock=true)
- // 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 (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
- {
- // Verify signature
- if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
- {
- // only during transition phase for P2SH: do not invoke anti-DoS code for
- // potentially old clients relaying bad P2SH transactions
- if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0))
- return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
-
- return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
- }
+ // 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);
}
- // Mark outpoints as spent
- txindex.vSpent[prevout.n] = posThisTx;
+ // Check transaction timestamp
+ if (coins.nTime > nTime)
+ return DoS(100, error("CheckInputs() : transaction timestamp earlier than input transaction"));
- // Write back
- if (fBlock || fMiner)
- {
- mapTestPool[prevout.hash] = txindex;
- }
+ // Check for negative or overflow input values
+ nValueIn += coins.vout[prevout.n].nValue;
+ if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
+ return DoS(100, error("CheckInputs() : txin values out of range"));
}
if (IsCoinStake())
{
- // ppcoin: coin stake tx earns reward instead of paying fee
+ if (!pblock)
+ return error("CheckInputs() : %s is a coinstake, but no block specified", GetHash().ToString().substr(0,10).c_str());
+
+ // Coin stake tx earns reward instead of paying fee
uint64 nCoinAge;
- if (!GetCoinAge(txdb, nCoinAge))
- return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
+ 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, pindexBlock->nBits, nTime) - GetMinFee() + MIN_TX_FEE;
+ int64 nCalculatedStakeReward = GetProofOfStakeReward(nCoinAge, pblock->nBits, nTime) - GetMinFee() + MIN_TX_FEE;
if (nStakeReward > nCalculatedStakeReward)
- return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nStakeReward, nCalculatedStakeReward));
+ return DoS(100, error("CheckInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nStakeReward, nCalculatedStakeReward));
}
else
{
if (nValueIn < GetValueOut())
- return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
+ return DoS(100, error("ChecktInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
// Tally transaction fees
int64 nTxFee = nValueIn - GetValueOut();
if (nTxFee < 0)
- return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
- // ppcoin: enforce transaction fees for every block
- if (nTxFee < GetMinFee())
- return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())) : false;
-
+ return DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
nFees += nTxFee;
if (!MoneyRange(nFees))
- return DoS(100, error("ConnectInputs() : nFees out of range"));
+ 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.
+ // Only if ALL inputs pass do we perform expensive ECDSA signature checks.
+ // Helps prevent CPU exhaustion attacks.
+
+ // 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 ||
+ (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);
+
+ // Verify signature
+ if (!VerifySignature(coins, *this, i, fStrictPayToScriptHash, fStrictEncodings, 0)) {
+ // only during transition phase for P2SH: do not invoke anti-DoS code for
+ // potentially old clients relaying bad P2SH transactions
+ if (fStrictPayToScriptHash && VerifySignature(coins, *this, i, false, fStrictEncodings, 0))
+ return error("CheckInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
+
+ return DoS(100,error("CheckInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
+ }
+ }
+ }
+
+
}
return true;
}
-bool CTransaction::ClientConnectInputs()
+bool CTransaction::ClientCheckInputs() const
{
if (IsCoinBase())
return false;
return false;
// Verify signature
- if (!VerifySignature(txPrev, *this, i, true, 0))
+ if (!VerifySignature(CCoins(txPrev, -1, -1), *this, i, true, false, 0))
return error("ConnectInputs() : VerifySignature failed");
///// this is redundant with the mempool.mapNextTx stuff,
return true;
}
+bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
+{
+ assert(pindex == view.GetBestBlock());
+ CBlockUndo blockUndo;
+ {
+ CDiskBlockPos pos = pindex->GetUndoPos();
+ if (pos.IsNull())
+ return error("DisconnectBlock() : no undo data available");
+ FILE *file = OpenUndoFile(pos, true);
+ if (file == NULL)
+ return error("DisconnectBlock() : undo file not available");
+ CAutoFile fileUndo(file, SER_DISK, CLIENT_VERSION);
+ fileUndo >> blockUndo;
+ }
+ assert(blockUndo.vtxundo.size() + 1 == vtx.size());
-bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex)
-{
- // Disconnect in reverse order
- for (int i = vtx.size()-1; i >= 0; i--)
- if (!vtx[i].DisconnectInputs(txdb))
- return false;
+ // undo transactions in reverse order
+ for (int i = vtx.size() - 1; i >= 0; i--) {
+ const CTransaction &tx = vtx[i];
+ uint256 hash = tx.GetHash();
- // Update block index on disk without changing it in memory.
- // The memory index structure will be changed after the db commits.
- if (pindex->pprev)
- {
- CDiskBlockIndex blockindexPrev(pindex->pprev);
- blockindexPrev.hashNext = 0;
- if (!txdb.WriteBlockIndex(blockindexPrev))
- return error("DisconnectBlock() : WriteBlockIndex failed");
+ // check that all outputs are available
+ CCoins outs;
+ if (!view.GetCoins(hash, outs))
+ return error("DisconnectBlock() : outputs still spent? database corrupted");
+
+ 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");
+
+ // restore inputs
+ if (i > 0) { // not coinbases
+ const CTxUndo &txundo = blockUndo.vtxundo[i-1];
+ assert(txundo.vprevout.size() == tx.vin.size());
+ for (unsigned int j = tx.vin.size(); j-- > 0;) {
+ const COutPoint &out = tx.vin[j].prevout;
+ const CTxInUndo &undo = txundo.vprevout[j];
+ CCoins coins;
+ view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent
+ if (coins.IsPruned()) {
+ if (undo.nHeight == 0)
+ return error("DisconnectBlock() : undo data doesn't contain tx metadata? database corrupted");
+ coins.fCoinBase = undo.fCoinBase;
+ coins.nHeight = undo.nHeight;
+ coins.nVersion = undo.nVersion;
+ } else {
+ if (undo.nHeight != 0)
+ return error("DisconnectBlock() : undo data contains unneeded tx metadata? database corrupted");
+ }
+ if (coins.IsAvailable(out.n))
+ return error("DisconnectBlock() : prevout output not spent? database corrupted");
+ if (coins.vout.size() < out.n+1)
+ coins.vout.resize(out.n+1);
+ coins.vout[out.n] = undo.txout;
+ if (!view.SetCoins(out.hash, coins))
+ return error("DisconnectBlock() : cannot restore coin inputs");
+ }
+ }
}
- // ppcoin: clean up wallet after disconnecting coinstake
- BOOST_FOREACH(CTransaction& tx, vtx)
- SyncWithWallets(tx, this, false, false);
+ // move best block pointer to prevout block
+ view.SetBestBlock(pindex->pprev);
return true;
}
-bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
+bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+
+bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck)
{
- // Check it again in case a previous version let a bad block in, but skip BlockSig checking
- if (!CheckBlock(!fJustCheck, !fJustCheck, false))
+ // Check it again in case a previous version let a bad block in
+ if (!CheckBlock(!fJustCheck, !fJustCheck))
return false;
+ // verify that the view's current state corresponds to the previous block
+ assert(pindex->pprev == view.GetBestBlock());
+
// 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
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
// two in the chain that violate it. This prevents exploiting the issue against nodes in their
// initial block download.
- bool fEnforceBIP30 = true; // Always active in NovaCoin
- bool fStrictPayToScriptHash = true; // Always active in NovaCoin
+ bool fEnforceBIP30 = true;
- //// issue here: it doesn't know the version
- unsigned int nTxPos;
- if (fJustCheck)
- // FetchInputs treats CDiskTxPos(1,1,1) as a special "refer to memorypool" indicator
- // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from
- nTxPos = 1;
- else
- nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size());
+ if (fEnforceBIP30) {
+ BOOST_FOREACH(CTransaction& tx, vtx) {
+ uint256 hash = tx.GetHash();
+ CCoins coins;
+ if (view.GetCoins(hash, coins) && !coins.IsPruned())
+ return error("ConnectBlock() : tried to overwrite transaction");
+ }
+ }
- map<uint256, CTxIndex> mapQueuedChanges;
- int64 nFees = 0;
- int64 nValueIn = 0;
- int64 nValueOut = 0;
+ // BIP16 always active
+ bool fStrictPayToScriptHash = true;
+
+ CBlockUndo blockundo;
+
+ int64 nFees = 0, nValueIn = 0, nValueOut = 0;
unsigned int nSigOps = 0;
BOOST_FOREACH(CTransaction& tx, vtx)
{
- uint256 hashTx = tx.GetHash();
-
- if (fEnforceBIP30) {
- CTxIndex txindexOld;
- if (txdb.ReadTxIndex(hashTx, txindexOld)) {
- BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent)
- if (pos.IsNull())
- return false;
- }
- }
-
nSigOps += tx.GetLegacySigOpCount();
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
- CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
- if (!fJustCheck)
- nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
-
- MapPrevTx mapInputs;
- if (tx.IsCoinBase())
- nValueOut += tx.GetValueOut();
- else
+ if (!tx.IsCoinBase())
{
- bool fInvalid;
- if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
- return false;
+ if (!tx.HaveInputs(view))
+ return DoS(100, error("ConnectBlock() : inputs missing/spent"));
if (fStrictPayToScriptHash)
{
// Add in sigops done by pay-to-script-hash inputs;
// this is to prevent a "rogue miner" from creating
// an incredibly-expensive-to-validate block.
- nSigOps += tx.GetP2SHSigOpCount(mapInputs);
+ nSigOps += tx.GetP2SHSigOpCount(view);
if (nSigOps > MAX_BLOCK_SIGOPS)
- return DoS(100, error("ConnectBlock() : too many sigops"));
+ return DoS(100, error("ConnectBlock() : too many sigops"));
}
- int64 nTxValueIn = tx.GetValueIn(mapInputs);
int64 nTxValueOut = tx.GetValueOut();
+ int64 nTxValueIn = tx.GetValueIn(view);
+
nValueIn += nTxValueIn;
nValueOut += nTxValueOut;
+
if (!tx.IsCoinStake())
nFees += nTxValueIn - nTxValueOut;
- if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
+ if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash, false, this))
return false;
}
+ else
+ {
+ nValueOut += tx.GetValueOut();
+ }
- mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
+ CTxUndo txundo;
+ if (!tx.UpdateCoins(view, txundo, pindex->nHeight, pindex->nTime))
+ return error("ConnectBlock() : UpdateInputs failed");
+ if (!tx.IsCoinBase())
+ blockundo.vtxundo.push_back(txundo);
}
- // ppcoin: track money supply and mint amount info
pindex->nMint = nValueOut - nValueIn + nFees;
pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn;
- if (!txdb.WriteBlockIndex(CDiskBlockIndex(pindex)))
- return error("Connect() : WriteBlockIndex for pindex failed");
-
- // ppcoin: fees are not collected by miners as in bitcoin
- // ppcoin: fees are destroyed to compensate the entire network
- if (fDebug && GetBoolArg("-printcreation"))
- printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees);
if (fJustCheck)
return true;
- // 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");
- }
+ CChainDB chaindb;
+ CDiskBlockPos pos;
- // Update block index on disk without changing it in memory.
- // The memory index structure will be changed after the db commits.
- if (pindex->pprev)
+ // Write undo information to disk
+ if (pindex->GetUndoPos().IsNull())
{
- CDiskBlockIndex blockindexPrev(pindex->pprev);
- blockindexPrev.hashNext = pindex->GetBlockHash();
- if (!txdb.WriteBlockIndex(blockindexPrev))
- return error("ConnectBlock() : WriteBlockIndex failed");
+ 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");
+
+ // update nUndoPos in block index
+ pindex->nUndoPos = pos.nPos + 1;
}
+ CDiskBlockIndex blockindex(pindex);
+ if (!chaindb.WriteBlockIndex(blockindex))
+ return error("ConnectBlock() : WriteBlockIndex failed");
+
+ // add this block to the view's blockchain
+ if (!view.SetBestBlock(pindex))
+ return false;
+
+ // fees are destroyed to compensate the entire network
+ if (fDebug && GetBoolArg("-printcreation"))
+ 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);
return true;
}
-bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
+bool CBlock::SetBestChain(CBlockIndex* pindexNew)
{
- printf("REORGANIZE\n");
+ CCoinsViewCache &view = *pcoinsTip;
- // Find the fork
- CBlockIndex* pfork = pindexBest;
+ // special case for attaching the genesis block
+ // note that no ConnectBlock is called, so its coinbase output is non-spendable
+ if (pindexGenesisBlock == NULL && pindexNew->GetBlockHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
+ {
+ view.SetBestBlock(pindexNew);
+ if (!view.Flush())
+ return false;
+ pindexGenesisBlock = pindexNew;
+ pindexBest = pindexNew;
+ hashBestChain = pindexNew->GetBlockHash();
+ nBestHeight = pindexBest->nHeight;
+ nBestChainTrust = pindexNew->nChainTrust;
+ return true;
+ }
+
+ // Find the fork (typically, there is none)
+ CBlockIndex* pfork = view.GetBestBlock();
CBlockIndex* plonger = pindexNew;
while (pfork != plonger)
{
while (plonger->nHeight > pfork->nHeight)
if (!(plonger = plonger->pprev))
- return error("Reorganize() : plonger->pprev is null");
+ return error("SetBestChain() : plonger->pprev is null");
if (pfork == plonger)
break;
if (!(pfork = pfork->pprev))
- return error("Reorganize() : pfork->pprev is null");
+ return error("SetBestChain() : pfork->pprev is null");
}
- // List of what to disconnect
+ // List of what to disconnect (typically nothing)
vector<CBlockIndex*> vDisconnect;
- for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev)
+ for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev)
vDisconnect.push_back(pindex);
- // List of what to connect
+ // List of what to connect (typically only pindexNew)
vector<CBlockIndex*> vConnect;
for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev)
vConnect.push_back(pindex);
reverse(vConnect.begin(), vConnect.end());
- printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
- printf("REORGANIZE: Connect %"PRIszu" blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
+ if (vDisconnect.size() > 0) {
+ printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
+ printf("REORGANIZE: Connect %"PRIszu" blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
+ }
// Disconnect shorter branch
vector<CTransaction> vResurrect;
- BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
- {
+ BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
CBlock block;
if (!block.ReadFromDisk(pindex))
- return error("Reorganize() : ReadFromDisk for disconnect failed");
- if (!block.DisconnectBlock(txdb, pindex))
- return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ return error("SetBestBlock() : 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());
+ if (!viewTemp.Flush())
+ return error("SetBestBlock() : Cache flush failed after disconnect");
// Queue memory transactions to resurrect
BOOST_FOREACH(const CTransaction& tx, block.vtx)
- if (!(tx.IsCoinBase() || tx.IsCoinStake()))
+ if (!tx.IsCoinBase())
vResurrect.push_back(tx);
}
// Connect longer branch
vector<CTransaction> vDelete;
- for (unsigned int i = 0; i < vConnect.size(); i++)
- {
- CBlockIndex* pindex = vConnect[i];
+ BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
CBlock block;
- if (!block.ReadFromDisk(pindex))
- return error("Reorganize() : ReadFromDisk for connect failed");
- if (!block.ConnectBlock(txdb, pindex))
- {
- // Invalid block
- return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ 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 = █
}
+ CCoinsViewCache viewTemp(view, true);
+ if (!pblock->ConnectBlock(pindex, viewTemp)) {
+ InvalidChainFound(pindexNew);
+ return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ }
+ if (!viewTemp.Flush())
+ return error("SetBestBlock() : Cache flush failed after connect");
// Queue memory transactions to delete
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ BOOST_FOREACH(const CTransaction& tx, pblock->vtx)
vDelete.push_back(tx);
}
- if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash()))
- return error("Reorganize() : WriteHashBestChain failed");
// Make sure it's successfully written to disk before changing memory structure
- if (!txdb.TxnCommit())
- return error("Reorganize() : TxnCommit failed");
+ bool fIsInitialDownload = IsInitialBlockDownload();
+ if (!fIsInitialDownload || view.GetCacheSize()>5000)
+ if (!view.Flush())
+ return false;
+
+ // At this point, all changes have been done to the database.
+ // Proceed by updating the memory structures.
// Disconnect shorter branch
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
// Resurrect memory transactions that were in the disconnected branch
BOOST_FOREACH(CTransaction& tx, vResurrect)
- tx.AcceptToMemoryPool(txdb, false);
+ tx.AcceptToMemoryPool(false);
// Delete redundant memory transactions that are in the connected branch
BOOST_FOREACH(CTransaction& tx, vDelete)
mempool.remove(tx);
- printf("REORGANIZE: done\n");
-
- return true;
-}
-
-
-// Called from inside SetBestChain: attaches a block to the new best chain being built
-bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
-{
- uint256 hash = GetHash();
-
- // Adding to current best branch
- if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
- {
- txdb.TxnAbort();
- InvalidChainFound(pindexNew);
- return false;
- }
- if (!txdb.TxnCommit())
- return error("SetBestChain() : TxnCommit failed");
-
- // Add to current best branch
- pindexNew->pprev->pnext = pindexNew;
-
- // Delete redundant memory transactions
- BOOST_FOREACH(CTransaction& tx, vtx)
- mempool.remove(tx);
-
- return true;
-}
-
-bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
-{
- uint256 hash = GetHash();
-
- if (!txdb.TxnBegin())
- return error("SetBestChain() : TxnBegin failed");
-
- if (pindexGenesisBlock == NULL && hash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
- {
- txdb.WriteHashBestChain(hash);
- if (!txdb.TxnCommit())
- return error("SetBestChain() : TxnCommit failed");
- pindexGenesisBlock = pindexNew;
- }
- else if (hashPrevBlock == hashBestChain)
- {
- if (!SetBestChainInner(txdb, pindexNew))
- return error("SetBestChain() : SetBestChainInner failed");
- }
- else
- {
- // the first block in the new chain that will cause it to become the new best chain
- CBlockIndex *pindexIntermediate = pindexNew;
-
- // list of blocks that need to be connected afterwards
- std::vector<CBlockIndex*> vpindexSecondary;
-
- // Reorganize is costly in terms of db load, as it works in a single db transaction.
- // Try to limit how much needs to be done inside
- while (pindexIntermediate->pprev && pindexIntermediate->pprev->nChainTrust > pindexBest->nChainTrust)
- {
- vpindexSecondary.push_back(pindexIntermediate);
- pindexIntermediate = pindexIntermediate->pprev;
- }
-
- if (!vpindexSecondary.empty())
- printf("Postponing %"PRIszu" reconnects\n", vpindexSecondary.size());
-
- // Switch to new best branch
- if (!Reorganize(txdb, pindexIntermediate))
- {
- txdb.TxnAbort();
- InvalidChainFound(pindexNew);
- return error("SetBestChain() : Reorganize failed");
- }
-
- // Connect further blocks
- BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary)
- {
- CBlock block;
- if (!block.ReadFromDisk(pindex))
- {
- printf("SetBestChain() : ReadFromDisk failed\n");
- break;
- }
- if (!txdb.TxnBegin()) {
- printf("SetBestChain() : TxnBegin 2 failed\n");
- break;
- }
- // errors now are not fatal, we still did a reorganisation to a new chain in a valid way
- if (!block.SetBestChainInner(txdb, pindex))
- break;
- }
- }
-
// Update best block in wallet (so we can detect restored wallets)
- bool fIsInitialDownload = IsInitialBlockDownload();
if (!fIsInitialDownload)
{
const CBlockLocator locator(pindexNew);
}
// New best block
- hashBestChain = hash;
+ hashBestChain = pindexNew->GetBlockHash();
pindexBest = pindexNew;
pblockindexFBBHLast = NULL;
nBestHeight = pindexBest->nHeight;
return true;
}
-// ppcoin: total coin age spent in transaction, in the unit of coin-days.
+// Total coin age spent in transaction, in the unit of coin-days.
// Only those coins meeting minimum age requirement counts. As those
// transactions not in main chain are not currently indexed so we
// might not find out about their coin age. Older transactions are
// guaranteed to be in main chain by sync-checkpoint. This rule is
// introduced to help nodes establish a consistent view of the coin
// age (trust score) of competing branches.
-bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const
+bool CTransaction::GetCoinAge(uint64& nCoinAge) const
{
+ CCoinsViewCache &inputs = *pcoinsTip;
+
CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds
nCoinAge = 0;
if (IsCoinBase())
return true;
- BOOST_FOREACH(const CTxIn& txin, vin)
+ for (unsigned int i = 0; i < vin.size(); i++)
{
- // First try finding the previous transaction in database
- CTransaction txPrev;
- CTxIndex txindex;
- if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
- continue; // previous transaction not in main chain
- if (nTime < txPrev.nTime)
- return false; // Transaction timestamp violation
+ const COutPoint &prevout = vin[i].prevout;
+ CCoins coins;
+ if (!inputs.GetCoins(prevout.hash, coins))
+ continue;
- // Read block header
- CBlock block;
- if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
- return false; // unable to read block of previous transaction
- if (block.GetBlockTime() + nStakeMinAge > nTime)
- continue; // only count coins meeting min age requirement
+ if (nTime < coins.nTime)
+ return false; // Transaction timestamp violation
- int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
- bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT;
+ // only count coins meeting min age requirement
+ if (coins.nBlockTime + nStakeMinAge > nTime)
+ continue;
- if (fDebug && GetBoolArg("-printcoinage"))
- printf("coin age nValueIn=%"PRI64d" nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString().c_str());
+ int64 nValueIn = coins.vout[vin[i].prevout.n].nValue;
+ bnCentSecond += CBigNum(nValueIn) * (nTime-coins.nTime) / CENT;
}
CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60);
return true;
}
-// ppcoin: total coin age spent in block, in the unit of coin-days.
+// Total coin age spent in block, in the unit of coin-days.
bool CBlock::GetCoinAge(uint64& nCoinAge) const
{
nCoinAge = 0;
- CTxDB txdb("r");
BOOST_FOREACH(const CTransaction& tx, vtx)
{
uint64 nTxCoinAge;
- if (tx.GetCoinAge(txdb, nTxCoinAge))
+ if (tx.GetCoinAge(nTxCoinAge))
nCoinAge += nTxCoinAge;
else
return false;
return true;
}
-bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
+bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
{
// Check for duplicate
uint256 hash = GetHash();
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);
+ CBlockIndex* pindexNew = new CBlockIndex(*this);
if (!pindexNew)
return error("AddToBlockIndex() : new CBlockIndex failed");
- pindexNew->phashBlock = &hash;
+ map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
+ pindexNew->phashBlock = &((*mi).first);
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
}
-
- // ppcoin: compute chain trust score
pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();
+ pindexNew->pos = pos;
+ pindexNew->nUndoPos = 0;
- // ppcoin: compute stake entropy bit for stake modifier
+ // Compute stake entropy bit for stake modifier
if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nTime)))
return error("AddToBlockIndex() : SetStakeEntropyBit() failed");
- // ppcoin: record proof-of-stake hash value
+ // Record proof-of-stake hash value
if (pindexNew->IsProofOfStake())
{
if (!mapProofOfStake.count(hash))
pindexNew->hashProofOfStake = mapProofOfStake[hash];
}
- // ppcoin: compute stake modifier
+ // Compute stake modifier
uint64 nStakeModifier = 0;
bool fGeneratedStakeModifier = false;
if (!ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier))
if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum))
return error("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindexNew->nHeight, nStakeModifier);
- // Add to mapBlockIndex
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
- if (pindexNew->IsProofOfStake())
- setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
- pindexNew->phashBlock = &((*mi).first);
-
- // Write to disk block index
- CTxDB txdb;
- if (!txdb.TxnBegin())
+ CChainDB chaindb;
+ if (!chaindb.TxnBegin())
return false;
- txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
- if (!txdb.TxnCommit())
+ chaindb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
+ if (!chaindb.TxnCommit())
return false;
// New best
- if (pindexNew->nChainTrust > nBestChainTrust)
- if (!SetBestChain(txdb, pindexNew))
- return false;
-
- txdb.Close();
+ if (!SetBestChain(pindexNew))
+ return false;
if (pindexNew == pindexBest)
{
return true;
}
+bool FindBlockPos(CChainDB &chaindb, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime)
+{
+ bool fUpdatedLast = false;
+
+ LOCK(cs_LastBlockFile);
+
+ while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
+ printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str());
+ FILE *file = OpenBlockFile(pos);
+ FileCommit(file);
+ fclose(file);
+ file = OpenUndoFile(pos);
+ FileCommit(file);
+ fclose(file);
+ nLastBlockFile++;
+ infoLastBlockFile.SetNull();
+ chaindb.ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine
+ fUpdatedLast = true;
+ }
+
+ pos.nFile = nLastBlockFile;
+ pos.nPos = infoLastBlockFile.nSize;
+ infoLastBlockFile.nSize += nAddSize;
+ infoLastBlockFile.AddBlock(nHeight, nTime);
+
+ unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
+ unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
+ if (nNewChunks > nOldChunks) {
+ FILE *file = OpenBlockFile(pos);
+ if (file) {
+ printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile);
+ AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos);
+ }
+ fclose(file);
+ }
+
+ if (!chaindb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+ return error("FindBlockPos() : cannot write updated block info");
+ if (fUpdatedLast)
+ chaindb.WriteLastBlockFile(nLastBlockFile);
+
+ return true;
+}
+
+bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
+{
+ pos.nFile = nFile;
+
+ LOCK(cs_LastBlockFile);
+ unsigned int nNewSize;
+ if (nFile == nLastBlockFile) {
+ pos.nPos = infoLastBlockFile.nUndoSize;
+ nNewSize = (infoLastBlockFile.nUndoSize += nAddSize);
+ if (!chaindb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+ return error("FindUndoPos() : cannot write updated block info");
+ } else {
+ CBlockFileInfo info;
+ if (!chaindb.ReadBlockFileInfo(nFile, info))
+ return error("FindUndoPos() : cannot read block info");
+ pos.nPos = info.nUndoSize;
+ nNewSize = (info.nUndoSize += nAddSize);
+ if (!chaindb.WriteBlockFileInfo(nFile, info))
+ return error("FindUndoPos() : cannot write updated block info");
+ }
+ unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
+ unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
+ if (nNewChunks > nOldChunks) {
+ FILE *file = OpenUndoFile(pos);
+ if (file) {
+ printf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile);
+ AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos);
+ }
+ fclose(file);
+ }
+
+ return true;
+}
bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) const
{
return DoS(50, error("CheckBlock() : proof of work failed"));
// Check timestamp
- if (GetBlockTime() > GetAdjustedTime() + nMaxClockDrift)
+ 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 DoS(100, error("CheckBlock() : more than one coinbase"));
// Check coinbase timestamp
- if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift)
+ if (GetBlockTime() > FutureDrift((int64)vtx[0].nTime))
return DoS(50, error("CheckBlock() : coinbase timestamp is too early"));
if (IsProofOfStake())
}
else
{
- // Coinbase fee paid until 20 Sep 2013
- int64 nFee = GetBlockTime() < CHAINCHECKS_SWITCH_TIME ? vtx[0].GetMinFee() - MIN_TX_FEE : 0;
-
+ int64 nReward = GetProofOfWorkReward(nBits);
// Check coinbase reward
- if (vtx[0].GetValueOut() > (GetProofOfWorkReward(nBits) - nFee))
+ if (vtx[0].GetValueOut() > nReward)
return DoS(50, error("CheckBlock() : coinbase reward exceeded (actual=%"PRI64d" vs calculated=%"PRI64d")",
vtx[0].GetValueOut(),
- GetProofOfWorkReward(nBits) - nFee));
+ nReward));
// Should we check proof-of-work block signature or not?
//
if(!fTestNet && fCheckSig)
{
- bool isAfterCheckpoint = (GetBlockTime() > Checkpoints::GetLastCheckpointTime());
bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME);
- bool checkPoWSig = (isAfterCheckpoint && GetBlockTime() < CHAINCHECKS_SWITCH_TIME);
// NovaCoin: check proof-of-work block signature
- if ((checkEntropySig || checkPoWSig) && !CheckBlockSignature(false))
+ if (checkEntropySig && !CheckBlockSignature(false))
return DoS(100, error("CheckBlock() : bad proof-of-work block signature"));
}
}
+
// Check transactions
BOOST_FOREACH(const CTransaction& tx, vtx)
{
if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
- // ppcoin: check transaction timestamp
+ // check transaction timestamp
if (GetBlockTime() < (int64)tx.nTime)
return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp"));
}
if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree())
return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
-
return true;
}
+
bool CBlock::AcceptBlock()
{
// Check for duplicate
return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake"));
// Check timestamp against prev
- if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + nMaxClockDrift < pindexPrev->GetBlockTime())
+ if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime())
return error("AcceptBlock() : block's timestamp is too early");
// Check that all transactions are finalized
if (!Checkpoints::CheckHardened(nHeight, hash))
return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lock-in at %d", nHeight));
+ bool cpSatisfies = Checkpoints::CheckSync(hash, pindexPrev);
+
// Check that the block satisfies synchronized checkpoint
- if (!GetBoolArg("-nosynccheckpoints", false) && !Checkpoints::CheckSync(hash, pindexPrev))
+ if (CheckpointsMode == Checkpoints::STRICT && !cpSatisfies)
return error("AcceptBlock() : rejected by synchronized checkpoint");
+ if (CheckpointsMode == Checkpoints::ADVISORY && !cpSatisfies)
+ strMiscWarning = _("WARNING: syncronized checkpoint violation detected, but skipped!");
+
// Enforce rule that the coinbase starts with serialized block height
CScript expect = CScript() << nHeight;
- if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin()))
+ if (vtx[0].vin[0].scriptSig.size() < expect.size() ||
+ !std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin()))
return DoS(100, error("AcceptBlock() : block height mismatch in coinbase"));
// Write block to history file
+ unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION);
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION)))
return error("AcceptBlock() : out of disk space");
- unsigned int nFile = -1;
- unsigned int nBlockPos = 0;
- if (!WriteToDisk(nFile, nBlockPos))
+ CDiskBlockPos blockPos;
+ {
+ CChainDB chaindb;
+ if (!FindBlockPos(chaindb, blockPos, nBlockSize+8, nHeight, nTime))
+ return error("AcceptBlock() : FindBlockPos failed");
+ }
+ if (!WriteToDisk(blockPos))
return error("AcceptBlock() : WriteToDisk failed");
- if (!AddToBlockIndex(nFile, nBlockPos))
+ if (!AddToBlockIndex(blockPos))
return error("AcceptBlock() : AddToBlockIndex failed");
// Relay inventory, but don't relay old inventory during initial block download
pnode->PushInventory(CInv(MSG_BLOCK, hash));
}
- // ppcoin: check pending sync-checkpoint
+ // Check pending sync-checkpoint
Checkpoints::AcceptPendingSyncCheckpoint();
return true;
if (bnTarget <= 0)
return 0;
- /* Old protocol, will be removed later */
+ /* Old protocol */
if (!fTestNet && GetBlockTime() < CHAINCHECKS_SWITCH_TIME)
return (IsProofOfStake()? ((CBigNum(1)<<256) / (bnTarget+1)).getuint256() : 1);
return true;
}
-// ppcoin: sign block
-bool CBlock::SignBlock(const CKeyStore& keystore)
+// novacoin: attempt to generate suitable proof-of-stake
+bool CBlock::SignBlock(CWallet& wallet)
{
- vector<valtype> vSolutions;
- txnouttype whichType;
+ // if we are trying to sign
+ // something except proof-of-stake block template
+ if (!vtx[0].vout[0].IsEmpty())
+ return false;
- if(!IsProofOfStake())
- {
- for(unsigned int i = 0; i < vtx[0].vout.size(); i++)
- {
- const CTxOut& txout = vtx[0].vout[i];
+ // if we are trying to sign
+ // a complete proof-of-stake block
+ if (IsProofOfStake())
+ return true;
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
- continue;
+ static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp
- if (whichType == TX_PUBKEY)
- {
- // Sign
- valtype& vchPubKey = vSolutions[0];
- CKey key;
+ CKey key;
+ CTransaction txCoinStake;
+ int64 nSearchTime = txCoinStake.nTime; // search to current time
- if (!keystore.GetKey(Hash160(vchPubKey), key))
- continue;
- if (key.GetPubKey() != vchPubKey)
- continue;
- if(!key.Sign(GetHash(), vchBlockSig))
- continue;
-
- return true;
- }
- }
- }
- else
+ if (nSearchTime > nLastCoinStakeSearchTime)
{
- const CTxOut& txout = vtx[1].vout[1];
-
- if (!Solver(txout.scriptPubKey, whichType, vSolutions))
- return false;
-
- if (whichType == TX_PUBKEY)
+ if (wallet.CreateCoinStake(wallet, nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake, key))
{
- // Sign
- valtype& vchPubKey = vSolutions[0];
- CKey key;
-
- if (!keystore.GetKey(Hash160(vchPubKey), key))
- return false;
- if (key.GetPubKey() != vchPubKey)
- return false;
-
- return key.Sign(GetHash(), vchBlockSig);
+ if (txCoinStake.nTime >= max(pindexBest->GetMedianTimePast()+1, PastDrift(pindexBest->GetBlockTime())))
+ {
+ // make sure coinstake would meet timestamp protocol
+ // as it would be the same as the block timestamp
+ vtx[0].nTime = nTime = txCoinStake.nTime;
+ nTime = max(pindexBest->GetMedianTimePast()+1, GetMaxTransactionTime());
+ nTime = max(GetBlockTime(), PastDrift(pindexBest->GetBlockTime()));
+
+ // we have to make sure that we have no future timestamps in
+ // our transactions set
+ for (vector<CTransaction>::iterator it = vtx.begin(); it != vtx.end();)
+ if (it->nTime > nTime) { it = vtx.erase(it); } else { ++it; }
+
+ vtx.insert(vtx.begin() + 1, txCoinStake);
+ hashMerkleRoot = BuildMerkleTree();
+
+ // append a signature to our block
+ return key.Sign(GetHash(), vchBlockSig);
+ }
}
+ nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime;
+ nLastCoinStakeSearchTime = nSearchTime;
}
- printf("Sign failed\n");
return false;
}
return true;
}
-static filesystem::path BlockFilePath(unsigned int nFile)
-{
- string strBlockFn = strprintf("blk%04u.dat", nFile);
- return GetDataDir() / strBlockFn;
-}
-FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode)
+CCriticalSection cs_LastBlockFile;
+CBlockFileInfo infoLastBlockFile;
+int nLastBlockFile = 0;
+
+FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{
- if ((nFile < 1) || (nFile == (unsigned int) -1))
+ if (pos.IsNull())
return NULL;
- FILE* file = fopen(BlockFilePath(nFile).string().c_str(), pszMode);
- if (!file)
+ boost::filesystem::path path = GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
+ boost::filesystem::create_directories(path.parent_path());
+ FILE* file = fopen(path.string().c_str(), "rb+");
+ if (!file && !fReadOnly)
+ file = fopen(path.string().c_str(), "wb+");
+ if (!file) {
+ printf("Unable to open file %s\n", path.string().c_str());
return NULL;
- if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w'))
- {
- if (fseek(file, nBlockPos, SEEK_SET) != 0)
- {
+ }
+ if (pos.nPos) {
+ if (fseek(file, pos.nPos, SEEK_SET)) {
+ printf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str());
fclose(file);
return NULL;
}
return file;
}
-static unsigned int nCurrentBlockFile = 1;
+FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) {
+ return OpenDiskFile(pos, "blk", fReadOnly);
+}
-FILE* AppendBlockFile(unsigned int& nFileRet)
-{
- nFileRet = 0;
- loop
- {
- FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab");
- if (!file)
- return NULL;
- if (fseek(file, 0, SEEK_END) != 0)
- return NULL;
- // FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB
- if (ftell(file) < (long)(0x7F000000 - MAX_SIZE))
- {
- nFileRet = nCurrentBlockFile;
- return file;
- }
- fclose(file);
- nCurrentBlockFile++;
- }
+FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
+ return OpenDiskFile(pos, "rev", fReadOnly);
}
bool LoadBlockIndex(bool fAllowNew)
{
+ CBigNum bnTrustedModulus;
+
if (fTestNet)
{
pchMessageStart[0] = 0xcd;
pchMessageStart[2] = 0xc0;
pchMessageStart[3] = 0xef;
+ bnTrustedModulus.SetHex("f0d14cf72623dacfe738d0892b599be0f31052239cddd95a3f25101c801dc990453b38c9434efe3f372db39a32c2bb44cbaea72d62c8931fa785b0ec44531308df3e46069be5573e49bb29f4d479bfc3d162f57a5965db03810be7636da265bfced9c01a6b0296c77910ebdc8016f70174f0f18a57b3b971ac43a934c6aedbc5c866764a3622b5b7e3f9832b8b3f133c849dbcc0396588abcd1e41048555746e4823fb8aba5b3d23692c6857fccce733d6bb6ec1d5ea0afafecea14a0f6f798b6b27f77dc989c557795cc39a0940ef6bb29a7fc84135193a55bcfc2f01dd73efad1b69f45a55198bd0e6bef4d338e452f6a420f1ae2b1167b923f76633ab6e55");
bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; // 16 bits PoW target limit for testnet
nStakeMinAge = 2 * 60 * 60; // test net min age is 2 hours
nModifierInterval = 20 * 60; // test modifier interval is 20 minutes
nCoinbaseMaturity = 10; // test maturity is 10 blocks
nStakeTargetSpacing = 5 * 60; // test block spacing is 5 minutes
}
+ else
+ {
+ bnTrustedModulus.SetHex("d01f952e1090a5a72a3eda261083256596ccc192935ae1454c2bafd03b09e6ed11811be9f3a69f5783bbbced8c6a0c56621f42c2d19087416facf2f13cc7ed7159d1c5253119612b8449f0c7f54248e382d30ecab1928dbf075c5425dcaee1a819aa13550e0f3227b8c685b14e0eae094d65d8a610a6f49fff8145259d1187e4c6a472fa5868b2b67f957cb74b787f4311dbc13c97a2ca13acdb876ff506ebecbb904548c267d68868e07a32cd9ed461fbc2f920e9940e7788fed2e4817f274df5839c2196c80abe5c486df39795186d7bc86314ae1e8342f3c884b158b4b05b4302754bf351477d35370bad6639b2195d30006b77bf3dbb28b848fd9ecff5662bf39dde0c974e83af51b0d3d642d43834827b8c3b189065514636b8f2a59c42ba9b4fc4975d4827a5d89617a3873e4b377b4d559ad165748632bd928439cfbc5a8ef49bc2220e0b15fb0aa302367d5e99e379a961c1bc8cf89825da5525e3c8f14d7d8acca2fa9c133a2176ae69874d8b1d38b26b9c694e211018005a97b40848681b9dd38feb2de141626fb82591aad20dc629b2b6421cef1227809551a0e4e943ab99841939877f18f2d9c0addc93cf672e26b02ed94da3e6d329e8ac8f3736eebbf37bb1a21e5aadf04ee8e3b542f876aa88b2adf2608bd86329b7f7a56fd0dc1c40b48188731d11082aea360c62a0840c2db3dad7178fd7e359317ae081");
+ }
+
+ // Set up the Zerocoin Params object
+ ZCParams = new libzerocoin::Params(bnTrustedModulus);
//
// Load block index
//
- CTxDB txdb("cr");
- if (!txdb.LoadBlockIndex())
+ CChainDB chaindb("cr");
+ if (!LoadBlockIndex(chaindb))
return false;
- txdb.Close();
+ chaindb.Close();
//
// Init with genesis block
block.nNonce = !fTestNet ? 1575379 : 46534;
//// debug print
+ uint256 hash = block.GetHash();
+ printf("%s\n", hash.ToString().c_str());
assert(block.hashMerkleRoot == uint256("0x4cb33b3b6a861dcbc685d3e614a9cafb945738d6833f182855679f2fad02057b"));
block.print();
- assert(block.GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
+ assert(hash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet));
assert(block.CheckBlock());
// Start new block file
- unsigned int nFile;
- unsigned int nBlockPos;
- if (!block.WriteToDisk(nFile, nBlockPos))
+ 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 (!block.WriteToDisk(blockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
- if (!block.AddToBlockIndex(nFile, nBlockPos))
+ if (!block.AddToBlockIndex(blockPos))
return error("LoadBlockIndex() : genesis block not accepted");
- // ppcoin: initialize synchronized checkpoint
+ // initialize synchronized checkpoint
if (!Checkpoints::WriteSyncCheckpoint((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)))
return error("LoadBlockIndex() : failed to init sync checkpoint");
}
- // ppcoin: if checkpoint master key changed must reset sync-checkpoint
+ string strPubKey = "";
{
- CTxDB txdb;
- string strPubKey = "";
- if (!txdb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
+ CChainDB chaindb;
+ // if checkpoint master key changed must reset sync-checkpoint
+ if (!chaindb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
{
// write checkpoint master key to db
- txdb.TxnBegin();
- if (!txdb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
+ chaindb.TxnBegin();
+ if (!chaindb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
- if (!txdb.TxnCommit())
+ 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");
}
- txdb.Close();
}
return true;
}
-
-
void PrintBlockTree()
{
// pre-compute tree structure
// print item
CBlock block;
block.ReadFromDisk(pindex);
- printf("%d (%u,%u) %s %08x %s mint %7s tx %"PRIszu"",
+ printf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"",
pindex->nHeight,
- pindex->nFile,
- pindex->nBlockPos,
- block.GetHash().ToString().c_str(),
- block.nBits,
+ pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos,
DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
- FormatMoney(pindex->nMint).c_str(),
block.vtx.size());
PrintWallets(block);
return nLoaded > 0;
}
-
-
-
-
-
-
-
-
//////////////////////////////////////////////////////////////////////////////
//
// CAlert
extern map<uint256, CAlert> mapAlerts;
extern CCriticalSection cs_mapAlerts;
-static string strMintMessage = "Info: Minting suspended due to locked wallet.";
-static string strMintWarning;
+extern string strMintMessage;
+extern string strMintWarning;
string GetWarnings(string strFor)
{
// * Should not enter safe mode for longer invalid chain
// * If sync-checkpoint is too old do not enter safe mode
- // * Do not display warning if -nosynccheckpoints specified
- if (!GetBoolArg("-nosynccheckpoints", false) && Checkpoints::IsSyncCheckpointTooOld(60 * 60 * 24 * 10) && !fTestNet && !IsInitialBlockDownload())
+ // * Display warning only in the STRICT mode
+ if (CheckpointsMode == Checkpoints::STRICT && Checkpoints::IsSyncCheckpointTooOld(60 * 60 * 24 * 10) &&
+ !fTestNet && !IsInitialBlockDownload())
{
nPriority = 100;
- strStatusBar = "WARNING: Checkpoint is too old. Wait for block chain to download, or notify developers.";
+ strStatusBar = _("WARNING: Checkpoint is too old. Wait for block chain to download, or notify developers.");
}
// ppcoin: if detected invalid checkpoint enter safe mode
if (Checkpoints::hashInvalidCheckpoint != 0)
{
nPriority = 3000;
- strStatusBar = strRPC = "WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or notify developers.";
+ strStatusBar = strRPC = _("WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or notify developers.");
}
// Alerts
//
-bool static AlreadyHave(CTxDB& txdb, const CInv& inv)
+bool static AlreadyHave(const CInv& inv)
{
switch (inv.type)
{
case MSG_TX:
{
- bool txInMap = false;
+ bool txInMap = false;
{
- LOCK(mempool.cs);
- txInMap = (mempool.exists(inv.hash));
+ LOCK(mempool.cs);
+ txInMap = mempool.exists(inv.hash);
}
- return txInMap ||
- mapOrphanTransactions.count(inv.hash) ||
- txdb.ContainsTx(inv.hash);
+ return txInMap || mapOrphanTransactions.count(inv.hash) ||
+ pcoinsTip->HaveCoins(inv.hash);
}
-
case MSG_BLOCK:
return mapBlockIndex.count(inv.hash) ||
mapOrphanBlocks.count(inv.hash);
return true;
}
-
-
-
-
if (strCommand == "version")
{
// Each connection can only send one version message
return true;
}
- // ppcoin: record my external IP reported by peer
+ // record my external IP reported by peer
if (addrFrom.IsRoutable() && addrMe.IsRoutable())
addrSeenByPeer = addrMe;
item.second.RelayTo(pfrom);
}
- // ppcoin: relay sync-checkpoint
+ // Relay sync-checkpoint
{
LOCK(Checkpoints::cs_hashSyncCheckpoint);
if (!Checkpoints::checkpointMessage.IsNull())
pfrom->fDisconnect = true;
}
-
else if (strCommand == "inv")
{
vector<CInv> vInv;
break;
}
}
- CTxDB txdb("r");
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
{
const CInv &inv = vInv[nInv];
return true;
pfrom->AddInventoryKnown(inv);
- bool fAlreadyHave = AlreadyHave(txdb, inv);
+ bool fAlreadyHave = AlreadyHave(inv);
if (fDebug)
printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
vector<uint256> vWorkQueue;
vector<uint256> vEraseQueue;
CDataStream vMsg(vRecv);
- CTxDB txdb("r");
CTransaction tx;
vRecv >> tx;
CInv inv(MSG_TX, tx.GetHash());
pfrom->AddInventoryKnown(inv);
- // Truncate messages to the size of the tx in them
- unsigned int nSize = ::GetSerializeSize(tx,SER_NETWORK, PROTOCOL_VERSION);
- if (nSize < vMsg.size()){
- vMsg.resize(nSize);
- }
-
bool fMissingInputs = false;
- if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs))
+ if (tx.AcceptToMemoryPool(true, &fMissingInputs))
{
SyncWithWallets(tx, NULL, true);
- RelayMessage(inv, vMsg);
+ RelayTransaction(tx, inv.hash);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
vEraseQueue.push_back(inv.hash);
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
- for (map<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
+ for (set<uint256>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
mi != mapOrphanTransactionsByPrev[hashPrev].end();
++mi)
{
- const CDataStream& vMsg = *((*mi).second);
- CTransaction tx;
- CDataStream(vMsg) >> tx;
- CInv inv(MSG_TX, tx.GetHash());
+ const uint256& orphanTxHash = *mi;
+ CTransaction& orphanTx = mapOrphanTransactions[orphanTxHash];
bool fMissingInputs2 = false;
- if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs2))
+ if (orphanTx.AcceptToMemoryPool(true, &fMissingInputs2))
{
- printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
+ printf(" accepted orphan tx %s\n", orphanTxHash.ToString().substr(0,10).c_str());
SyncWithWallets(tx, NULL, true);
- RelayMessage(inv, vMsg);
- mapAlreadyAskedFor.erase(inv);
- vWorkQueue.push_back(inv.hash);
- vEraseQueue.push_back(inv.hash);
+ RelayTransaction(orphanTx, orphanTxHash);
+ mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanTxHash));
+ vWorkQueue.push_back(orphanTxHash);
+ vEraseQueue.push_back(orphanTxHash);
}
else if (!fMissingInputs2)
{
// invalid orphan
- vEraseQueue.push_back(inv.hash);
- printf(" removed invalid orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
+ vEraseQueue.push_back(orphanTxHash);
+ printf(" removed invalid orphan tx %s\n", orphanTxHash.ToString().substr(0,10).c_str());
}
}
}
}
else if (fMissingInputs)
{
- AddOrphanTx(vMsg);
+ AddOrphanTx(tx);
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
// (x) data
//
- loop
+ while (true)
{
// Don't bother if send buffer is too full to respond anyway
if (pfrom->vSend.size() >= SendBufferSize())
//
vector<CInv> vGetData;
int64 nNow = GetTime() * 1000000;
- CTxDB txdb("r");
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{
const CInv& inv = (*pto->mapAskFor.begin()).second;
- if (!AlreadyHave(txdb, inv))
+ if (!AlreadyHave(inv))
{
if (fDebugNet)
printf("sending getdata: %s\n", inv.ToString().c_str());
return true;
}
+// Amount compression:
+// * If the amount is 0, output 0
+// * first, divide the amount (in base units) by the largest power of 10 possible; call the exponent e (e is max 9)
+// * if e<9, the last digit of the resulting number cannot be 0; store it as d, and drop it (divide by 10)
+// * call the result n
+// * output 1 + 10*(9*n + d - 1) + e
+// * if e==9, we only know the resulting number is not zero, so output 1 + 10*(n - 1) + 9
+// (this is decodable, as d is in [1-9] and e is in [0-9])
-
-
-
-
-
-
-
-
-
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// BitcoinMiner
-//
-
-int static FormatHashBlocks(void* pbuffer, unsigned int len)
+uint64 CTxOutCompressor::CompressAmount(uint64 n)
{
- unsigned char* pdata = (unsigned char*)pbuffer;
- unsigned int blocks = 1 + ((len + 8) / 64);
- unsigned char* pend = pdata + 64 * blocks;
- memset(pdata + len, 0, 64 * blocks - len);
- pdata[len] = 0x80;
- unsigned int bits = len * 8;
- pend[-1] = (bits >> 0) & 0xff;
- pend[-2] = (bits >> 8) & 0xff;
- pend[-3] = (bits >> 16) & 0xff;
- pend[-4] = (bits >> 24) & 0xff;
- return blocks;
-}
-
-static const unsigned int pSHA256InitState[8] =
-{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
-
-void SHA256Transform(void* pstate, void* pinput, const void* pinit)
-{
- SHA256_CTX ctx;
- unsigned char data[64];
-
- SHA256_Init(&ctx);
-
- for (int i = 0; i < 16; i++)
- ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]);
-
- for (int i = 0; i < 8; i++)
- ctx.h[i] = ((uint32_t*)pinit)[i];
-
- SHA256_Update(&ctx, data, sizeof(data));
- for (int i = 0; i < 8; i++)
- ((uint32_t*)pstate)[i] = ctx.h[i];
-}
-
-// Some explaining would be appreciated
-class COrphan
-{
-public:
- CTransaction* ptx;
- set<uint256> setDependsOn;
- double dPriority;
- double dFeePerKb;
-
- COrphan(CTransaction* ptxIn)
- {
- ptx = ptxIn;
- dPriority = dFeePerKb = 0;
- }
-
- void print() const
- {
- printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n",
- ptx->GetHash().ToString().substr(0,10).c_str(), dPriority, dFeePerKb);
- BOOST_FOREACH(uint256 hash, setDependsOn)
- printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str());
- }
-};
-
-
-uint64 nLastBlockTx = 0;
-uint64 nLastBlockSize = 0;
-int64 nLastCoinStakeSearchInterval = 0;
-
-// We want to sort transactions by priority and fee, so:
-typedef boost::tuple<double, double, CTransaction*> TxPriority;
-class TxPriorityCompare
-{
- bool byFee;
-public:
- TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
- bool operator()(const TxPriority& a, const TxPriority& b)
- {
- if (byFee)
- {
- if (a.get<1>() == b.get<1>())
- return a.get<0>() < b.get<0>();
- return a.get<1>() < b.get<1>();
- }
- else
- {
- if (a.get<0>() == b.get<0>())
- return a.get<1>() < b.get<1>();
- return a.get<0>() < b.get<0>();
- }
- }
-};
-
-// CreateNewBlock:
-// fProofOfStake: try (best effort) to make a proof-of-stake block
-CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake)
-{
- // Create new block
- auto_ptr<CBlock> pblock(new CBlock());
- if (!pblock.get())
- return NULL;
-
- // Create coinbase tx
- CTransaction txNew;
- txNew.vin.resize(1);
- txNew.vin[0].prevout.SetNull();
- txNew.vout.resize(1);
-
- if (!fProofOfStake)
- {
- CReserveKey reservekey(pwallet);
- txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG;
- }
- else
- txNew.vout[0].SetEmpty();
-
- // Add our coinbase tx as first transaction
- pblock->vtx.push_back(txNew);
-
- // Largest block you're willing to create:
- unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2);
- // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
- nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize));
-
- // How much of the block should be dedicated to high-priority transactions,
- // included regardless of the fees they pay
- unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000);
- nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
-
- // Minimum block size you want to create; block will be filled with free transactions
- // until there are no more or the block reaches this size:
- unsigned int nBlockMinSize = GetArg("-blockminsize", 0);
- nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
-
- // Fee-per-kilobyte amount considered the same as "free"
- // Be careful setting this: if you set it to zero then
- // a transaction spammer can cheaply fill blocks using
- // 1-satoshi-fee transactions. It should be set above the real
- // cost to you of processing a transaction.
- int64 nMinTxFee = MIN_TX_FEE;
- if (mapArgs.count("-mintxfee"))
- ParseMoney(mapArgs["-mintxfee"], nMinTxFee);
-
- // ppcoin: if coinstake available add coinstake tx
- static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup
- CBlockIndex* pindexPrev = pindexBest;
-
- if (fProofOfStake) // attempt to find a coinstake
- {
- pblock->nBits = GetNextTargetRequired(pindexPrev, true);
- CTransaction txCoinStake;
- int64 nSearchTime = txCoinStake.nTime; // search to current time
- if (nSearchTime > nLastCoinStakeSearchTime)
- {
- if (pwallet->CreateCoinStake(*pwallet, pblock->nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake))
- {
- if (txCoinStake.nTime >= max(pindexPrev->GetMedianTimePast()+1, pindexPrev->GetBlockTime() - nMaxClockDrift))
- { // make sure coinstake would meet timestamp protocol
- // as it would be the same as the block timestamp
- pblock->vtx[0].nTime = txCoinStake.nTime;
- pblock->vtx.push_back(txCoinStake);
- }
- }
- nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime;
- nLastCoinStakeSearchTime = nSearchTime;
- }
- }
-
- pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake());
-
- // Collect memory pool transactions into the block
- int64 nFees = 0;
- {
- LOCK2(cs_main, mempool.cs);
- CBlockIndex* pindexPrev = pindexBest;
- CTxDB txdb("r");
-
- // Priority order to process transactions
- list<COrphan> vOrphan; // list memory doesn't move
- map<uint256, vector<COrphan*> > mapDependers;
-
- // This vector will be sorted into a priority queue:
- vector<TxPriority> vecPriority;
- vecPriority.reserve(mempool.mapTx.size());
- for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
- {
- CTransaction& tx = (*mi).second;
- if (tx.IsCoinBase() || tx.IsCoinStake() || !tx.IsFinal())
- continue;
-
- COrphan* porphan = NULL;
- double dPriority = 0;
- int64 nTotalIn = 0;
- bool fMissingInputs = false;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
- {
- // Read prev transaction
- CTransaction txPrev;
- CTxIndex txindex;
- if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
- {
- // This should never happen; all transactions in the memory
- // pool should connect to either transactions in the chain
- // or other transactions in the memory pool.
- if (!mempool.mapTx.count(txin.prevout.hash))
- {
- printf("ERROR: mempool transaction missing input\n");
- if (fDebug) assert("mempool transaction missing input" == 0);
- fMissingInputs = true;
- if (porphan)
- vOrphan.pop_back();
- break;
- }
-
- // Has to wait for dependencies
- if (!porphan)
- {
- // Use list for automatic deletion
- vOrphan.push_back(COrphan(&tx));
- porphan = &vOrphan.back();
- }
- mapDependers[txin.prevout.hash].push_back(porphan);
- porphan->setDependsOn.insert(txin.prevout.hash);
- nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue;
- continue;
- }
- int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
- nTotalIn += nValueIn;
-
- int nConf = txindex.GetDepthInMainChain();
- dPriority += (double)nValueIn * nConf;
- }
- if (fMissingInputs) continue;
-
- // Priority is sum(valuein * age) / txsize
- unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
- dPriority /= nTxSize;
-
- // This is a more accurate fee-per-kilobyte than is used by the client code, because the
- // client code rounds up the size to the nearest 1K. That's good, because it gives an
- // incentive to create smaller transactions.
- double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0);
-
- if (porphan)
- {
- porphan->dPriority = dPriority;
- porphan->dFeePerKb = dFeePerKb;
- }
- else
- vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second));
- }
-
- // Collect transactions into block
- map<uint256, CTxIndex> mapTestPool;
- uint64 nBlockSize = 1000;
- uint64 nBlockTx = 0;
- int nBlockSigOps = 100;
- bool fSortedByFee = (nBlockPrioritySize <= 0);
-
- TxPriorityCompare comparer(fSortedByFee);
- std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
-
- while (!vecPriority.empty())
- {
- // Take highest priority transaction off the priority queue:
- double dPriority = vecPriority.front().get<0>();
- double dFeePerKb = vecPriority.front().get<1>();
- CTransaction& tx = *(vecPriority.front().get<2>());
-
- std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
- vecPriority.pop_back();
-
- // Size limits
- unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
- if (nBlockSize + nTxSize >= nBlockMaxSize)
- continue;
-
- // Legacy limits on sigOps:
- unsigned int nTxSigOps = tx.GetLegacySigOpCount();
- if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
- continue;
-
- // Timestamp limit
- if (tx.nTime > GetAdjustedTime() || (pblock->IsProofOfStake() && tx.nTime > pblock->vtx[1].nTime))
- continue;
-
- // ppcoin: simplify transaction fee - allow free = false
- int64 nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK);
-
- // Skip free transactions if we're past the minimum block size:
- if (fSortedByFee && (dFeePerKb < nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
- continue;
-
- // Prioritize by fee once past the priority size or we run out of high-priority
- // transactions:
- if (!fSortedByFee &&
- ((nBlockSize + nTxSize >= nBlockPrioritySize) || (dPriority < COIN * 144 / 250)))
- {
- fSortedByFee = true;
- comparer = TxPriorityCompare(fSortedByFee);
- std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
- }
-
- // Connecting shouldn't fail due to dependency on other memory pool transactions
- // because we're already processing them in order of dependency
- map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
- MapPrevTx mapInputs;
- bool fInvalid;
- if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid))
- continue;
-
- int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
- if (nTxFees < nMinFee)
- continue;
-
- nTxSigOps += tx.GetP2SHSigOpCount(mapInputs);
- if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
- continue;
-
- if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
- continue;
- mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size());
- swap(mapTestPool, mapTestPoolTmp);
-
- // Added
- pblock->vtx.push_back(tx);
- nBlockSize += nTxSize;
- ++nBlockTx;
- nBlockSigOps += nTxSigOps;
- nFees += nTxFees;
-
- if (fDebug && GetBoolArg("-printpriority"))
- {
- printf("priority %.1f feeperkb %.1f txid %s\n",
- dPriority, dFeePerKb, tx.GetHash().ToString().c_str());
- }
-
- // Add transactions that depend on this one to the priority queue
- uint256 hash = tx.GetHash();
- if (mapDependers.count(hash))
- {
- BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
- {
- if (!porphan->setDependsOn.empty())
- {
- porphan->setDependsOn.erase(hash);
- if (porphan->setDependsOn.empty())
- {
- vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx));
- std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
- }
- }
- }
- }
- }
-
- nLastBlockTx = nBlockTx;
- nLastBlockSize = nBlockSize;
-
- if (fDebug && GetBoolArg("-printpriority"))
- printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize);
-
- if (pblock->IsProofOfWork())
- pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits);
-
- // Fill in header
- pblock->hashPrevBlock = pindexPrev->GetBlockHash();
- if (pblock->IsProofOfStake())
- pblock->nTime = pblock->vtx[1].nTime; //same as coinstake timestamp
- pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime());
- pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
- if (pblock->IsProofOfWork())
- pblock->UpdateTime(pindexPrev);
- pblock->nNonce = 0;
+ if (n == 0)
+ return 0;
+ int e = 0;
+ while (((n % 10) == 0) && e < 9) {
+ n /= 10;
+ e++;
}
-
- return pblock.release();
-}
-
-
-void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
-{
- // Update nExtraNonce
- static uint256 hashPrevBlock;
- if (hashPrevBlock != pblock->hashPrevBlock)
- {
- nExtraNonce = 0;
- hashPrevBlock = pblock->hashPrevBlock;
+ if (e < 9) {
+ int d = (n % 10);
+ assert(d >= 1 && d <= 9);
+ n /= 10;
+ return 1 + (n*9 + d - 1)*10 + e;
+ } else {
+ return 1 + (n - 1)*10 + 9;
}
- ++nExtraNonce;
- unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
- pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS;
- assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100);
-
- pblock->hashMerkleRoot = pblock->BuildMerkleTree();
}
-
-void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1)
+uint64 CTxOutCompressor::DecompressAmount(uint64 x)
{
- //
- // Pre-build hash buffers
- //
- struct
- {
- struct unnamed2
- {
- int nVersion;
- uint256 hashPrevBlock;
- uint256 hashMerkleRoot;
- unsigned int nTime;
- unsigned int nBits;
- unsigned int nNonce;
- }
- block;
- unsigned char pchPadding0[64];
- uint256 hash1;
- unsigned char pchPadding1[64];
- }
- tmp;
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.block.nVersion = pblock->nVersion;
- tmp.block.hashPrevBlock = pblock->hashPrevBlock;
- tmp.block.hashMerkleRoot = pblock->hashMerkleRoot;
- tmp.block.nTime = pblock->nTime;
- tmp.block.nBits = pblock->nBits;
- tmp.block.nNonce = pblock->nNonce;
-
- FormatHashBlocks(&tmp.block, sizeof(tmp.block));
- FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
-
- // Byte swap all the input buffer
- for (unsigned int i = 0; i < sizeof(tmp)/4; i++)
- ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
-
- // Precalc the first half of the first hash, which stays constant
- SHA256Transform(pmidstate, &tmp.block, pSHA256InitState);
-
- memcpy(pdata, &tmp.block, 128);
- memcpy(phash1, &tmp.hash1, 64);
-}
-
-
-bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
-{
- uint256 hashBlock = pblock->GetHash();
- uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
-
- if(!pblock->IsProofOfWork())
- return error("CheckWork() : %s is not a proof-of-work block", hashBlock.GetHex().c_str());
-
- if (hashBlock > hashTarget)
- return error("CheckWork() : proof-of-work not meeting target");
-
- //// debug print
- printf("CheckWork() : new proof-of-stake block found \n hash: %s \ntarget: %s\n", hashBlock.GetHex().c_str(), hashTarget.GetHex().c_str());
- pblock->print();
- printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
-
- // Found a solution
- {
- LOCK(cs_main);
- if (pblock->hashPrevBlock != hashBestChain)
- return error("CheckWork() : generated block is stale");
-
- // Remove key from key pool
- reservekey.KeepKey();
-
- // Track how many getdata requests this block gets
- {
- LOCK(wallet.cs_wallet);
- wallet.mapRequestCount[hashBlock] = 0;
- }
-
- // Process this block the same as if we had received it from another node
- if (!ProcessBlock(NULL, pblock))
- return error("CheckWork() : ProcessBlock, block not accepted");
- }
-
- return true;
-}
-
-bool CheckStake(CBlock* pblock, CWallet& wallet)
-{
- uint256 proofHash = 0, hashTarget = 0;
- uint256 hashBlock = pblock->GetHash();
-
- if(!pblock->IsProofOfStake())
- return error("CheckStake() : %s is not a proof-of-stake block", hashBlock.GetHex().c_str());
-
- // verify hash target and signature of coinstake tx
- if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, proofHash, hashTarget))
- return error("CheckStake() : proof-of-stake checking failed");
-
- //// debug print
- printf("CheckStake() : new proof-of-stake block found \n hash: %s \nproofhash: %s \ntarget: %s\n", hashBlock.GetHex().c_str(), proofHash.GetHex().c_str(), hashTarget.GetHex().c_str());
- pblock->print();
- printf("out %s\n", FormatMoney(pblock->vtx[1].GetValueOut()).c_str());
-
- // Found a solution
- {
- LOCK(cs_main);
- if (pblock->hashPrevBlock != hashBestChain)
- return error("CheckStake() : generated block is stale");
-
- // Track how many getdata requests this block gets
- {
- LOCK(wallet.cs_wallet);
- wallet.mapRequestCount[hashBlock] = 0;
- }
-
- // Process this block the same as if we had received it from another node
- if (!ProcessBlock(NULL, pblock))
- return error("CheckStake() : ProcessBlock, block not accepted");
- }
-
- return true;
-}
-
-void StakeMiner(CWallet *pwallet)
-{
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
-
- // Make this thread recognisable as the mining thread
- RenameThread("novacoin-miner");
-
- // Each thread has its own counter
- unsigned int nExtraNonce = 0;
-
- while (true)
- {
- if (fShutdown)
- return;
- while (vNodes.empty() || IsInitialBlockDownload())
- {
- Sleep(1000);
- if (fShutdown)
- return;
- }
-
- while (pwallet->IsLocked())
- {
- strMintWarning = strMintMessage;
- Sleep(1000);
- }
- strMintWarning = "";
-
- //
- // Create new block
- //
- CBlockIndex* pindexPrev = pindexBest;
-
- auto_ptr<CBlock> pblock(CreateNewBlock(pwallet, true));
- if (!pblock.get())
- return;
- IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);
-
- if(pblock->IsProofOfStake())
- {
- // Trying to sign a block
- if (!pblock->SignBlock(*pwalletMain))
- {
- strMintWarning = strMintMessage;
- continue;
- }
-
- strMintWarning = "";
- SetThreadPriority(THREAD_PRIORITY_NORMAL);
- CheckStake(pblock.get(), *pwalletMain);
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
- }
-
- Sleep(500);
- continue;
- }
+ // x = 0 OR x = 1+10*(9*n + d - 1) + e OR x = 1+10*(n - 1) + 9
+ if (x == 0)
+ return 0;
+ x--;
+ // x = 10*(9*n + d - 1) + e
+ int e = x % 10;
+ x /= 10;
+ uint64 n = 0;
+ if (e < 9) {
+ // x = 9*n + d - 1
+ int d = (x % 9) + 1;
+ x /= 9;
+ // x = n
+ n = x*10 + d;
+ } else {
+ n = x+1;
+ }
+ while (e) {
+ n *= 10;
+ e--;
+ }
+ return n;
}
-