map<uint256, CBlockIndex*> mapBlockIndex;
set<pair<COutPoint, unsigned int> > setStakeSeen;
uint256 hashGenesisBlock = hashGenesisBlockOfficial;
-static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
+CBigNum bnProofOfWorkLimit(~uint256(0) >> 20);
+CBigNum bnInitialHashTarget(~uint256(0) >> 20);
+unsigned int nStakeMinAge = STAKE_MIN_AGE;
+int nCoinbaseMaturity = COINBASE_MATURITY_PPC;
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
-uint64 nBestChainTrust = 0;
-uint64 nBestInvalidTrust = 0;
+CBigNum bnBestChainTrust = 0;
+CBigNum bnBestInvalidTrust = 0;
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
-const string strMessageMagic = "PPCoin Signed Message:\n";
+const string strMessageMagic = "NovaCoin Signed Message:\n";
double dHashesPerSec;
int64 nHPSTimerStart;
const CTxOut& txout = vout[i];
if (txout.IsEmpty() && (!IsCoinBase()) && (!IsCoinStake()))
return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
- if (txout.nValue < 0)
- return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative"));
+ // ppcoin: enforce minimum output amount
+ if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT)
+ return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum"));
if (txout.nValue > MAX_MONEY)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high"));
nValueOut += txout.nValue;
{
if (!(IsCoinBase() || IsCoinStake()))
return 0;
- return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain());
+ return max(0, (nCoinbaseMaturity+20) - GetDepthInMainChain());
}
return pblockOrphan->hashPrevBlock;
}
-int64 static GetProofOfWorkReward(unsigned int nBits)
+int64 GetProofOfWorkReward(unsigned int nBits)
{
- CBigNum bnSubsidyLimit = 9999 * COIN; // subsidy amount for difficulty 1
+ CBigNum bnSubsidyLimit = MAX_MINT_PROOF_OF_WORK;
CBigNum bnTarget;
bnTarget.SetCompact(nBits);
CBigNum bnTargetLimit = bnProofOfWorkLimit;
bnTargetLimit.SetCompact(bnTargetLimit.GetCompact());
- // ppcoin: subsidy is cut in half every 16x multiply of difficulty
+ // ppcoin: subsidy is cut in half every 64x multiply of difficulty
// A reasonably continuous curve is used to avoid shock to market
- // (nSubsidyLimit / nSubsidy) ** 4 == bnProofOfWorkLimit / bnTarget
+ // (nSubsidyLimit / nSubsidy) ** 6 == bnProofOfWorkLimit / bnTarget
+ //
+ // Human readable form:
+ //
+ // nSubsidy = 100 / (diff ^ 1/6)
CBigNum bnLowerBound = CENT;
CBigNum bnUpperBound = bnSubsidyLimit;
while (bnLowerBound + CENT <= bnUpperBound)
CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2;
if (fDebug && GetBoolArg("-printcreation"))
printf("GetProofOfWorkReward() : lower=%"PRI64d" upper=%"PRI64d" mid=%"PRI64d"\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64());
- if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget)
+ if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget)
bnUpperBound = bnMidValue;
else
bnLowerBound = bnMidValue;
if (fDebug && GetBoolArg("-printcreation"))
printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy);
- return nSubsidy;
+ return min(nSubsidy, MAX_MINT_PROOF_OF_WORK);
}
// ppcoin: miner's coin stake is rewarded based on coin age spent (coin-days)
int64 GetProofOfStakeReward(int64 nCoinAge)
{
- static int64 nRewardCoinYear = CENT; // creation amount per coin-year
+ static int64 nRewardCoinYear = 5 * CENT; // creation amount per coin-year
int64 nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
if (fDebug && GetBoolArg("-printcreation"))
printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nCoinAge);
static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week
static const int64 nTargetSpacingStake = 10 * 60; // ten minutes
static const int64 nTargetSpacingWorkMax = 2 * 60 * 60; // two hours
-static const int64 nMaxClockDrift = 2 * 60 * 60; // two hours
//
// minimum amount of work that could possibly be required nTime after
// ppcoin: find last block index up to pindex
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake)
{
- while (pindex && (pindex->IsProofOfStake() != fProofOfStake))
+ while (pindex && pindex->pprev && (pindex->IsProofOfStake() != fProofOfStake))
pindex = pindex->pprev;
return pindex;
}
unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
{
- // Genesis block and first block
- if (pindexLast == NULL || pindexLast->pprev == NULL)
- return bnProofOfWorkLimit.GetCompact();
+ if (pindexLast == NULL)
+ return bnProofOfWorkLimit.GetCompact(); // genesis block
const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
- if (pindexPrev == NULL)
- return bnProofOfWorkLimit.GetCompact();
+ if (pindexPrev->pprev == NULL)
+ return bnInitialHashTarget.GetCompact(); // first block
const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
- if (pindexPrevPrev == NULL)
- return bnProofOfWorkLimit.GetCompact();
+ if (pindexPrevPrev->pprev == NULL)
+ return bnInitialHashTarget.GetCompact(); // second block
+
int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();
// ppcoin: target change every block
void static InvalidChainFound(CBlockIndex* pindexNew)
{
- if (pindexNew->nChainTrust > nBestInvalidTrust)
+ if (pindexNew->bnChainTrust > bnBestInvalidTrust)
{
- nBestInvalidTrust = pindexNew->nChainTrust;
- CTxDB().WriteBestInvalidTrust(nBestInvalidTrust);
+ bnBestInvalidTrust = pindexNew->bnChainTrust;
+ CTxDB().WriteBestInvalidTrust(bnBestInvalidTrust);
MainFrameRepaint();
}
- printf("InvalidChainFound: invalid block=%s height=%d trust=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, CBigNum(pindexNew->nChainTrust).ToString().c_str());
- printf("InvalidChainFound: current best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
- if (pindexBest && nBestInvalidTrust > nBestChainTrust + pindexBest->GetBlockTrust() * 6)
- printf("InvalidChainFound: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n");
+ printf("InvalidChainFound: invalid block=%s height=%d trust=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, CBigNum(pindexNew->bnChainTrust).ToString().c_str());
+ printf("InvalidChainFound: current best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(bnBestChainTrust).ToString().c_str());
+ // ppcoin: should not enter safe mode for longer invalid chain
}
void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
// If prev is coinbase/coinstake, check that it's matured
if (txPrev.IsCoinBase() || txPrev.IsCoinStake())
- for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev)
+ 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 coinbase/coinstake at depth %d", pindexBlock->nHeight - pindex->nHeight);
if (!GetCoinAge(txdb, nCoinAge))
return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
int64 nStakeReward = GetValueOut() - nValueIn;
- if (nStakeReward > GetProofOfStakeReward(nCoinAge))
+ if (nStakeReward > GetProofOfStakeReward(nCoinAge) - GetMinFee() + MIN_TX_FEE)
return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str()));
}
else
// already refuses previously-known transaction id's entirely.
// This rule applies to all blocks whose timestamp is after March 15, 2012, 0:00 UTC.
// On testnet it is enabled as of februari 20, 2012, 0:00 UTC.
- if (pindex->nTime > 1331769600 || (fTestNet && pindex->nTime > 1329696000))
+
+ BOOST_FOREACH(CTransaction& tx, vtx)
{
- BOOST_FOREACH(CTransaction& tx, vtx)
+ CTxIndex txindexOld;
+ if (txdb.ReadTxIndex(tx.GetHash(), txindexOld))
{
- CTxIndex txindexOld;
- if (txdb.ReadTxIndex(tx.GetHash(), txindexOld))
- {
- BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent)
- if (pos.IsNull())
- return false;
- }
+ BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent)
+ if (pos.IsNull())
+ return false;
}
}
map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0;
+ int64 nValueIn = 0;
+ int64 nValueOut = 0;
unsigned int nSigOps = 0;
BOOST_FOREACH(CTransaction& tx, vtx)
{
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
MapPrevTx mapInputs;
- if (!tx.IsCoinBase())
+ if (tx.IsCoinBase())
+ nValueOut += tx.GetValueOut();
+ else
{
bool fInvalid;
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
return DoS(100, error("ConnectBlock() : too many sigops"));
}
+ int64 nTxValueIn = tx.GetValueIn(mapInputs);
+ int64 nTxValueOut = tx.GetValueOut();
+ nValueIn += nTxValueIn;
+ nValueOut += nTxValueOut;
if (!tx.IsCoinStake())
- nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut();
+ nFees += nTxValueIn - nTxValueOut;
if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
return false;
mapQueuedChanges[tx.GetHash()] = CTxIndex(posThisTx, tx.vout.size());
}
+ // 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");
+
// Write queued txindex changes
for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
{
// ppcoin: fees are not collected by miners as in bitcoin
// ppcoin: fees are destroyed to compensate the entire network
- if (IsProofOfWork() && vtx[0].GetValueOut() > GetProofOfWorkReward(nBits))
- return false;
if (fDebug && GetBoolArg("-printcreation"))
printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees);
CDiskBlockIndex blockindexPrev(pindex->pprev);
blockindexPrev.hashNext = pindex->GetBlockHash();
if (!txdb.WriteBlockIndex(blockindexPrev))
- return error("ConnectBlock() : WriteBlockIndex failed");
+ return error("ConnectBlock() : WriteBlockIndex for blockindexPrev failed");
}
// Watch for transactions paying to me
// 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)
+ while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainTrust > pindexBest->bnChainTrust)
{
vpindexSecondary.push_back(pindexIntermediate);
pindexIntermediate = pindexIntermediate->pprev;
hashBestChain = hash;
pindexBest = pindexNew;
nBestHeight = pindexBest->nHeight;
- nBestChainTrust = pindexNew->nChainTrust;
+ bnBestChainTrust = pindexNew->bnChainTrust;
nTimeBestReceived = GetTime();
nTransactionsUpdated++;
- printf("SetBestChain: new best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
+ printf("SetBestChain: new best=%s height=%d trust=%s moneysupply=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainTrust.ToString().c_str(), FormatMoney(pindexBest->nMoneySupply).c_str());
std::string strCmd = GetArg("-blocknotify", "");
// ppcoin: coinstake must meet hash target according to the protocol:
-// input 0 must meet the formula
+// kernel (input 0) must meet the formula
// hash(nBits + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDay
// this ensures that the chance of getting a coinstake is proportional to the
// amount of coin age one owns.
if (!IsCoinStake())
return true;
- // Input 0 must match the stake hash target per coin age (nBits)
+ // Kernel (input 0) must match the stake hash target per coin age (nBits)
const CTxIn& txin = vin[0];
// First try finding the previous transaction in database
CBlock block;
if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
return false; // unable to read block of previous transaction
- if (block.GetBlockTime() + STAKE_MIN_AGE > nTime)
+ if (block.GetBlockTime() + nStakeMinAge > nTime)
return false; // only count coins meeting min age requirement
int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
- CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60);
+ CBigNum bnCoinDay = CBigNum(nValueIn) * min(nTime-txPrev.nTime, (unsigned int)STAKE_MAX_AGE) / COIN / (24 * 60 * 60);
// Calculate hash
CDataStream ss(SER_GETHASH, 0);
ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << txPrev.nTime << txin.prevout.n << nTime;
CBlock block;
if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
return false; // unable to read block of previous transaction
- if (block.GetBlockTime() + STAKE_MIN_AGE > nTime)
+ if (block.GetBlockTime() + nStakeMinAge > nTime)
continue; // only count coins meeting min age requirement
int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
}
// ppcoin: compute chain trust score
- uint64 nCoinAge;
- if (!GetCoinAge(nCoinAge))
- return error("AddToBlockIndex() : invalid transaction in block");
- pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + nCoinAge;
+ pindexNew->bnChainTrust = (pindexNew->pprev ? pindexNew->pprev->bnChainTrust : 0) + pindexNew->GetBlockTrust();
CTxDB txdb;
if (!txdb.TxnBegin())
return false;
// New best
- if (pindexNew->nChainTrust > nBestChainTrust)
+ if (pindexNew->bnChainTrust > bnBestChainTrust)
if (!SetBestChain(txdb, pindexNew))
return false;
return DoS(100, error("CheckBlock() : coinstake in wrong position"));
// ppcoin: coinbase output should be empty if proof-of-stake block
- if (IsProofOfStake() && !vtx[0].vout[0].IsEmpty())
+ if (IsProofOfStake() && (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty()))
return error("CheckBlock() : coinbase output not empty for proof-of-stake block");
// Check coinbase timestamp
if (IsProofOfStake() && GetBlockTime() > (int64)vtx[1].nTime + nMaxClockDrift)
return DoS(50, error("CheckBlock() : coinstake timestamp is too early"));
+ // Check coinbase reward
+ if (vtx[0].GetValueOut() > (IsProofOfWork()? (GetProofOfWorkReward(nBits) - vtx[0].GetMinFee() + MIN_TX_FEE) : 0))
+ return DoS(50, error("CheckBlock() : coinbase reward exceeded %s > %s",
+ FormatMoney(vtx[0].GetValueOut()).c_str(),
+ FormatMoney(IsProofOfWork()? GetProofOfWorkReward(nBits) : 0).c_str()));
+
// Check transactions
BOOST_FOREACH(const CTransaction& tx, vtx)
{
if (!Checkpoints::CheckSync(hash, pindexPrev))
return error("AcceptBlock() : rejected by synchronized checkpoint");
+ if(nHeight > 0)
+ {
+ CScript expect = CScript() << nHeight;
+
+ if (!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
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION)))
return error("AcceptBlock() : out of disk space");
// ppcoin: verify hash target and signature of coinstake tx
if (pblock->IsProofOfStake() && !pblock->vtx[1].CheckProofOfStake(pblock->nBits))
- return error("ProcessBlock() : check proof-of-stake failed for block %s", hash.ToString().c_str());
+ {
+ printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str());
+ return false; // do not error here as we expect this during initial block download
+ }
CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint();
if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2));
// ppcoin: getblocks may not obtain the ancestor block rejected
// earlier by duplicate-stake check so we ask for it again directly
- pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2)));
+ if (!IsInitialBlockDownload())
+ pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2)));
}
return true;
}
string strMessage = _("Warning: Disk space is low");
strMiscWarning = strMessage;
printf("*** %s\n", strMessage.c_str());
- ThreadSafeMessageBox(strMessage, "PPCoin", wxOK | wxICON_EXCLAMATION | wxMODAL);
+ ThreadSafeMessageBox(strMessage, "NovaCoin", wxOK | wxICON_EXCLAMATION | wxMODAL);
StartShutdown();
return false;
}
if (fTestNet)
{
hashGenesisBlock = hashGenesisBlockTestNet;
- bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28);
- pchMessageStart[0] = 0xfa;
- pchMessageStart[1] = 0xbf;
- pchMessageStart[2] = 0xb5;
- pchMessageStart[3] = 0xda;
+ nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day
+ nCoinbaseMaturity = 60;
}
+ printf("%s Network: genesis=0x%s nBitsLimit=0x%08x nBitsInitial=0x%08x nStakeMinAge=%d nCoinbaseMaturity=%d\n",
+ fTestNet? "Test" : "NovaCoin", hashGenesisBlock.ToString().substr(0, 20).c_str(), bnProofOfWorkLimit.GetCompact(), bnInitialHashTarget.GetCompact(), nStakeMinAge, nCoinbaseMaturity);
+
//
// Load block index
//
// vMerkleTree: 4a5e1e
// Genesis block
- const char* pszTimestamp = "MarketWatch 07/Nov/2011 Gold tops $1,790 to end at over six-week high";
+ const char* pszTimestamp = "https://bitcointalk.org/index.php?topic=134179.msg1502196#msg1502196";
CTransaction txNew;
- txNew.nTime = 1339538219;
+ txNew.nTime = 1360105017;
txNew.vin.resize(1);
txNew.vout.resize(1);
- txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
+ txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout[0].SetEmpty();
CBlock block;
block.vtx.push_back(txNew);
block.hashPrevBlock = 0;
block.hashMerkleRoot = block.BuildMerkleTree();
block.nVersion = 1;
- block.nTime = 1339540307;
+ block.nTime = 1360105017;
block.nBits = bnProofOfWorkLimit.GetCompact();
- block.nNonce = 1281822831;
-
- if (fTestNet)
- {
- block.nTime = 1296688602;
- block.nBits = 0x1d07fff8;
- block.nNonce = 384568319;
- }
+ block.nNonce = 1575379;
//// debug print
printf("%s\n", block.GetHash().ToString().c_str());
printf("%s\n", hashGenesisBlock.ToString().c_str());
printf("%s\n", block.hashMerkleRoot.ToString().c_str());
- assert(block.hashMerkleRoot == uint256("0x1557f46a17fcf8843dbe4c0c0edfd1d17eeff2c3c48d73a59d11f5d176e4b54d"));
+ assert(block.hashMerkleRoot == uint256("0x4cb33b3b6a861dcbc685d3e614a9cafb945738d6833f182855679f2fad02057b"));
block.print();
assert(block.GetHash() == hashGenesisBlock);
assert(block.CheckBlock());
return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
if (!txdb.TxnCommit())
return error("LoadBlockIndex() : failed to commit new checkpoint master key to db");
- if (!Checkpoints::ResetSyncCheckpoint())
+ if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint())
return error("LoadBlockIndex() : failed to reset sync-checkpoint");
}
txdb.Close();
// print item
CBlock block;
block.ReadFromDisk(pindex);
- printf("%d (%u,%u) %s %08lx %s tx %d",
+ printf("%d (%u,%u) %s %08lx %s mint %7s tx %d",
pindex->nHeight,
pindex->nFile,
pindex->nBlockPos,
- block.GetHash().ToString().substr(0,20).c_str(),
+ block.GetHash().ToString().c_str(),
block.nBits,
DateTimeStrFormat(block.GetBlockTime()).c_str(),
+ FormatMoney(pindex->nMint).c_str(),
block.vtx.size());
PrintWallets(block);
map<uint256, CAlert> mapAlerts;
CCriticalSection cs_mapAlerts;
+static string strMintMessage = _("Info: Minting suspended due to locked wallet.");
+static string strMintWarning;
+
string GetWarnings(string strFor)
{
int nPriority = 0;
if (GetBoolArg("-testsafemode"))
strRPC = "test";
+ // ppcoin: wallet lock warning for minting
+ if (strMintWarning != "")
+ {
+ nPriority = 0;
+ strStatusBar = strMintWarning;
+ }
+
// Misc warnings like out of disk space and clock is wrong
if (strMiscWarning != "")
{
strStatusBar = strMiscWarning;
}
- // Longer invalid proof-of-work chain
- if (pindexBest && nBestInvalidTrust > nBestChainTrust + pindexBest->GetBlockTrust() * 6)
+ // ppcoin: should not enter safe mode for longer invalid chain
+ // ppcoin: if sync-checkpoint too old enter safe mode
+ if (Checkpoints::IsMatureSyncCheckpoint() && !fTestNet)
{
nPriority = 2000;
- strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.";
+ strStatusBar = strRPC = "WARNING: Checkpoint is too old. Wait for block chain 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 other nodes may need to upgrade.";
+ strStatusBar = strRPC = "WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or notify developers.";
}
// Alerts
{
nPriority = alert.nPriority;
strStatusBar = alert.strStatusBar;
+ if (nPriority > 1000)
+ strRPC = strStatusBar; // ppcoin: safe mode for high alert
}
}
}
-// The message start string is designed to be unlikely to occur in normal data.
-// The characters are rarely used upper ascii, not valid as UTF-8, and produce
-// a large 4-byte int at any alignment.
-unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 };
-
-
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{
static map<CService, vector<unsigned char> > mapReuseKey;
if (pindex->GetBlockHash() == hashStop)
{
printf(" getblocks stopping at %d %s (%u bytes)\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str(), nBytes);
+ // ppcoin: tell downloading node about the latest block if it's
+ // without risk being rejected due to stake connection check
+ if (hashStop != hashBestChain && pindex->GetBlockTime() + nStakeMinAge > pindexBest->GetBlockTime())
+ pfrom->PushInventory(CInv(MSG_BLOCK, hashBestChain));
break;
}
pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash()));
// (x) data
//
+ unsigned char pchMessageStart[4];
+ GetMessageStart(pchMessageStart);
+ static int64 nTimeLastPrintMessageStart = 0;
+ if (fDebug && GetBoolArg("-printmessagestart") && nTimeLastPrintMessageStart + 30 < GetAdjustedTime())
+ {
+ string strMessageStart((const char *)pchMessageStart, sizeof(pchMessageStart));
+ vector<unsigned char> vchMessageStart(strMessageStart.begin(), strMessageStart.end());
+ printf("ProcessMessages : AdjustedTime=%"PRI64d" MessageStart=%s\n", GetAdjustedTime(), HexStr(vchMessageStart).c_str());
+ nTimeLastPrintMessageStart = GetAdjustedTime();
+ }
+
loop
{
// Scan for message start
((uint32_t*)pstate)[i] = ctx.h[i];
}
-//
-// ScanHash scans nonces looking for a hash with at least some zero bits.
-// It operates on big endian data. Caller does the byte reversing.
-// All input buffers are 16-byte aligned. nNonce is usually preserved
-// between calls, but periodically or if nNonce is 0xffff0000 or above,
-// the block is rebuilt and nNonce starts over at zero.
-//
-unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone)
-{
- unsigned int& nNonce = *(unsigned int*)(pdata + 12);
- for (;;)
- {
- // Crypto++ SHA-256
- // Hash pdata using pmidstate as the starting state into
- // preformatted buffer phash1, then hash phash1 into phash
- nNonce++;
- SHA256Transform(phash1, pdata, pmidstate);
- SHA256Transform(phash, phash1, pSHA256InitState);
-
- // Return the nonce if the hash has at least some zero bits,
- // caller will check if it has enough to reach the target
- if (((unsigned short*)phash)[14] == 0)
- return nNonce;
-
- // If nothing found after trying for a while, return -1
- if ((nNonce & 0xffff) == 0)
- {
- nHashesDone = 0xffff+1;
- return (unsigned int) -1;
- }
- }
-}
-
// Some explaining would be appreciated
class COrphan
{
uint64 nLastBlockTx = 0;
uint64 nLastBlockSize = 0;
+int64 nLastCoinStakeSearchInterval = 0;
-CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfWorkOnly)
+// CreateNewBlock:
+// fProofOfStake: try (best effort) to make a proof-of-stake block
+CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake)
{
CReserveKey reservekey(pwallet);
pblock->vtx.push_back(txNew);
// ppcoin: if coinstake available add coinstake tx
- static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift + 60; // only initialized at startup
+ static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup
CBlockIndex* pindexPrev = pindexBest;
- if (!fProofOfWorkOnly)
+ if (fProofOfStake) // attemp to find a coinstake
{
- while (nLastCoinStakeCheckTime < GetAdjustedTime())
+ pblock->nBits = GetNextTargetRequired(pindexPrev, true);
+ CTransaction txCoinStake;
+ int64 nSearchTime = GetAdjustedTime();
+ if (nSearchTime > nLastCoinStakeSearchTime)
{
- pindexPrev = pindexBest; // get best block again to avoid getting stale
- pblock->nBits = GetNextTargetRequired(pindexPrev, true);
- CTransaction txCoinStake;
- {
- static CCriticalSection cs;
- LOCK(cs);
- // mining may have been suspended for a while so
- // need to take max to satisfy the timestamp protocol
- nLastCoinStakeCheckTime++;
- nLastCoinStakeCheckTime = max(nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift + 60));
- txCoinStake.nTime = nLastCoinStakeCheckTime;
- }
- if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake))
+ if (pwallet->CreateCoinStake(*pwallet, pblock->nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake))
{
pblock->vtx.push_back(txCoinStake);
pblock->vtx[0].vout[0].SetEmpty();
- break;
}
+ nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime;
+ nLastCoinStakeSearchTime = nSearchTime;
}
}
nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize;
- printf("CreateNewBlock(): total size %lu\n", nBlockSize);
+ if (fDebug && GetBoolArg("-printpriority"))
+ printf("CreateNewBlock(): total size %lu\n", nBlockSize);
}
if (pblock->IsProofOfWork())
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
- pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
- pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime());
+ pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime());
pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
pblock->UpdateTime(pindexPrev);
pblock->nNonce = 0;
hashPrevBlock = pblock->hashPrevBlock;
}
++nExtraNonce;
- pblock->vtx[0].vin[0].scriptSig = (CScript() << pblock->nTime << CBigNum(nExtraNonce)) + COINBASE_FLAGS;
+
+ 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 static ThreadBitcoinMiner(void* parg);
-static bool fGenerateBitcoins = false;
-static bool fLimitProcessors = false;
-static int nLimitProcessors = -1;
+bool fGenerateBitcoins = false;
+bool fLimitProcessors = false;
+int nLimitProcessors = -1;
-void static BitcoinMiner(CWallet *pwallet)
+void BitcoinMiner(CWallet *pwallet, bool fProofOfStake)
{
- printf("BitcoinMiner started\n");
+ void *scratchbuf = scrypt_buffer_alloc();
+
+ printf("CPUMiner started for proof-of-%s\n", fProofOfStake? "stake" : "work");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
// Each thread has its own key and counter
CReserveKey reservekey(pwallet);
unsigned int nExtraNonce = 0;
- while (fGenerateBitcoins)
+ while (fGenerateBitcoins || fProofOfStake)
{
if (fShutdown)
return;
Sleep(1000);
if (fShutdown)
return;
- if (!fGenerateBitcoins)
+ if ((!fGenerateBitcoins) && !fProofOfStake)
return;
}
+ while (pwallet->IsLocked())
+ {
+ strMintWarning = strMintMessage;
+ Sleep(1000);
+ }
+ strMintWarning = "";
//
// Create new block
unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
CBlockIndex* pindexPrev = pindexBest;
- auto_ptr<CBlock> pblock(CreateNewBlock(pwallet));
+ auto_ptr<CBlock> pblock(CreateNewBlock(pwallet, fProofOfStake));
if (!pblock.get())
return;
IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);
- // ppcoin: if proof-of-stake block found then process block
- if (pblock->IsProofOfStake())
+ if (fProofOfStake)
{
- if (!pblock->SignBlock(*pwalletMain))
+ // ppcoin: if proof-of-stake block found then process block
+ if (pblock->IsProofOfStake())
{
- error("BitcoinMiner: Unable to sign new proof-of-stake block");
- return;
+ if (!pblock->SignBlock(*pwalletMain))
+ {
+ strMintWarning = strMintMessage;
+ continue;
+ }
+ strMintWarning = "";
+ printf("CPUMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str());
+ SetThreadPriority(THREAD_PRIORITY_NORMAL);
+ CheckWork(pblock.get(), *pwalletMain, reservekey);
+ SetThreadPriority(THREAD_PRIORITY_LOWEST);
}
- printf("BitcoinMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str());
- SetThreadPriority(THREAD_PRIORITY_NORMAL);
- CheckWork(pblock.get(), *pwalletMain, reservekey);
- SetThreadPriority(THREAD_PRIORITY_LOWEST);
+ Sleep(500);
continue;
}
//
int64 nStart = GetTime();
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
- uint256 hashbuf[2];
- uint256& hash = *alignup<16>(hashbuf);
+
+ unsigned int max_nonce = 0xffff0000;
+ block_header res_header;
+ uint256 result;
+
loop
{
unsigned int nHashesDone = 0;
unsigned int nNonceFound;
- // Crypto++ SHA-256
- nNonceFound = ScanHash_CryptoPP(pmidstate, pdata + 64, phash1,
- (char*)&hash, nHashesDone);
+ nNonceFound = scanhash_scrypt(
+ (block_header *)&pblock->nVersion,
+ scratchbuf,
+ max_nonce,
+ nHashesDone,
+ UBEGIN(result),
+ &res_header
+ );
// Check if something found
if (nNonceFound != (unsigned int) -1)
{
- for (unsigned int i = 0; i < sizeof(hash)/4; i++)
- ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]);
-
- if (hash <= hashTarget)
+ if (result <= hashTarget)
{
// Found a solution
- pblock->nNonce = ByteReverse(nNonceFound);
- assert(hash == pblock->GetHash());
+ pblock->nNonce = nNonceFound;
+ assert(result == pblock->GetHash());
if (!pblock->SignBlock(*pwalletMain))
{
- error("BitcoinMiner: Unable to sign new proof-of-work block");
- return;
+ strMintWarning = strMintMessage;
+ break;
}
-
+ strMintWarning = "";
SetThreadPriority(THREAD_PRIORITY_NORMAL);
CheckWork(pblock.get(), *pwalletMain, reservekey);
SetThreadPriority(THREAD_PRIORITY_LOWEST);
break;
// Update nTime every few seconds
- pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
- pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime());
+ pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime());
pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
pblock->UpdateTime(pindexPrev);
nBlockTime = ByteReverse(pblock->nTime);
break; // need to update coinbase timestamp
}
}
+
+ scrypt_buffer_free(scratchbuf);
}
void static ThreadBitcoinMiner(void* parg)
try
{
vnThreadsRunning[THREAD_MINER]++;
- BitcoinMiner(pwallet);
+ BitcoinMiner(pwallet, false);
vnThreadsRunning[THREAD_MINER]--;
}
catch (std::exception& e) {