X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=63e326e3960e7f49155357ea9d743e89e37a6770;hb=3176e0f244d929669aa3e1d81e0787d82d9150d3;hp=70c761d1b80227abda47d1026c79ccc27902468c;hpb=03aa39a354a0aa7331df82e8d401b29e5cb7082c;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 70c761d..63e326e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers -// Copyright (c) 2011-2012 The PPCoin developers +// Copyright (c) 2011-2013 The PPCoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,6 +9,8 @@ #include "net.h" #include "init.h" #include "ui_interface.h" +#include "kernel.h" +#include "scrypt_mine.h" #include #include #include @@ -31,12 +33,14 @@ unsigned int nTransactionsUpdated = 0; map mapBlockIndex; set > setStakeSeen; uint256 hashGenesisBlock = hashGenesisBlockOfficial; -static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); -static CBigNum bnInitialHashTarget(~uint256(0) >> 36); +static CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); +static 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; @@ -46,6 +50,7 @@ CMedianFilter cPeerBlockCounts(5, 0); // Amount of blocks that other nodes map mapOrphanBlocks; multimap mapOrphanBlocksByPrev; set > setStakeSeenOrphan; +map mapProofOfStake; map mapOrphanTransactions; map > mapOrphanTransactionsByPrev; @@ -53,7 +58,7 @@ map > mapOrphanTransactionsByPrev; // 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; @@ -709,7 +714,7 @@ int CMerkleTx::GetBlocksToMaturity() const { if (!(IsCoinBase() || IsCoinStake())) return 0; - return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain()); + return max(0, (nCoinbaseMaturity+20) - GetDepthInMainChain()); } @@ -822,7 +827,7 @@ uint256 WantedByOrphan(const CBlock* pblockOrphan) return pblockOrphan->hashPrevBlock; } -int64 static GetProofOfWorkReward(unsigned int nBits) +int64 GetProofOfWorkReward(unsigned int nBits) { CBigNum bnSubsidyLimit = MAX_MINT_PROOF_OF_WORK; CBigNum bnTarget; @@ -830,9 +835,13 @@ int64 static GetProofOfWorkReward(unsigned int 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) @@ -840,7 +849,7 @@ int64 static GetProofOfWorkReward(unsigned int nBits) 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; @@ -851,13 +860,13 @@ int64 static GetProofOfWorkReward(unsigned int nBits) 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); @@ -865,9 +874,7 @@ int64 GetProofOfStakeReward(int64 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 +static const int64 nTargetSpacingWorkMax = 12 * STAKE_TARGET_SPACING; // 2-hour // // minimum amount of work that could possibly be required nTime after @@ -915,7 +922,7 @@ unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fP // ppcoin: retarget with exponential moving toward target spacing CBigNum bnNew; bnNew.SetCompact(pindexPrev->nBits); - int64 nTargetSpacing = fProofOfStake? nTargetSpacingStake : min(nTargetSpacingWorkMax, nTargetSpacingStake * (1 + pindexLast->nHeight - pindexPrev->nHeight)); + int64 nTargetSpacing = fProofOfStake? STAKE_TARGET_SPACING : min(nTargetSpacingWorkMax, (int64) STAKE_TARGET_SPACING * (1 + pindexLast->nHeight - pindexPrev->nHeight)); int64 nInterval = nTargetTimespan / nTargetSpacing; bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); bnNew /= ((nInterval + 1) * nTargetSpacing); @@ -965,14 +972,14 @@ bool IsInitialBlockDownload() 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()); + 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 } @@ -1168,7 +1175,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, // 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); @@ -1232,7 +1239,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, 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 @@ -1369,6 +1376,8 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) map mapQueuedChanges; int64 nFees = 0; + int64 nValueIn = 0; + int64 nValueOut = 0; unsigned int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { @@ -1380,7 +1389,9 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) 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)) @@ -1396,8 +1407,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) 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; @@ -1406,6 +1421,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) 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::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) { @@ -1415,8 +1436,6 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) // 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); @@ -1427,7 +1446,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) 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 @@ -1597,7 +1616,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) // 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; @@ -1645,10 +1664,10 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) 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", ""); @@ -1662,69 +1681,6 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) } -// ppcoin: coinstake must meet hash target according to the protocol: -// 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. -// The reason this hash is chosen is the following: -// nBits: encodes all past block timestamps, making computing hash in advance -// more difficult -// txPrev.block.nTime: prevent nodes from guessing a good timestamp to -// generate transaction for future advantage -// txPrev.offset: offset of txPrev inside block, to reduce the chance of -// nodes generating coinstake at the same time -// txPrev.nTime: reduce the chance of nodes generating coinstake at the same -// time -// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes -// generating coinstake at the same time -// block/tx hash should not be used here as they can be generated in vast -// quantities so as to generate blocks faster, degrading the system back into -// a proof-of-work situation. -// -bool CTransaction::CheckProofOfStake(unsigned int nBits) const -{ - CBigNum bnTargetPerCoinDay; - bnTargetPerCoinDay.SetCompact(nBits); - - if (!IsCoinStake()) - return true; - - // 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 - CTxDB txdb("r"); - CTransaction txPrev; - CTxIndex txindex; - if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) - return false; // previous transaction not in main chain - txdb.Close(); - if (nTime < txPrev.nTime) - return false; // Transaction timestamp violation - - // Verify signature - if (!VerifySignature(txPrev, *this, 0, true, 0)) - return DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", GetHash().ToString().c_str())); - - // 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() + STAKE_MIN_AGE > 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); - // Calculate hash - CDataStream ss(SER_GETHASH, 0); - ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << txPrev.nTime << txin.prevout.n << nTime; - if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay) - return true; - else - return DoS(100, error("CheckProofOfStake() : check target failed on coinstake %s", GetHash().ToString().c_str())); -} - // ppcoin: 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 @@ -1754,7 +1710,7 @@ bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const 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; @@ -1805,11 +1761,8 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this); if (!pindexNew) return error("AddToBlockIndex() : new CBlockIndex failed"); - map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - if (pindexNew->fProofOfStake) - setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); - pindexNew->phashBlock = &((*mi).first); + pindexNew->phashBlock = &hash; map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); if (miPrev != mapBlockIndex.end()) { @@ -1818,11 +1771,37 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) } // 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(); + + // ppcoin: compute stake entropy bit for stake modifier + if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit())) + return error("AddToBlockIndex() : SetStakeEntropyBit() failed"); + + // ppcoin: record proof-of-stake hash value + if (pindexNew->IsProofOfStake()) + { + if (!mapProofOfStake.count(hash)) + return error("AddToBlockIndex() : hashProofOfStake not found in map"); + pindexNew->hashProofOfStake = mapProofOfStake[hash]; + } + + // ppcoin: compute stake modifier + uint64 nStakeModifier = 0; + bool fGeneratedStakeModifier = false; + if (!ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier)) + return error("AddToBlockIndex() : ComputeNextStakeModifier() failed"); + pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); + pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew); + if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) + return error("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindexNew->nHeight, nStakeModifier); + + // Add to mapBlockIndex + map::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()) return false; @@ -1831,7 +1810,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) return false; // New best - if (pindexNew->nChainTrust > nBestChainTrust) + if (pindexNew->bnChainTrust > bnBestChainTrust) if (!SetBestChain(txdb, pindexNew)) return false; @@ -1882,7 +1861,7 @@ bool CBlock::CheckBlock() const 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 @@ -1890,8 +1869,14 @@ bool CBlock::CheckBlock() const return DoS(50, error("CheckBlock() : coinbase timestamp is too early")); // Check coinstake timestamp - if (IsProofOfStake() && GetBlockTime() > (int64)vtx[1].nTime + nMaxClockDrift) - return DoS(50, error("CheckBlock() : coinstake timestamp is too early")); + if (IsProofOfStake() && !CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime)) + return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%u nTimeTx=%u", GetBlockTime(), vtx[1].nTime)); + + // 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) @@ -1967,6 +1952,14 @@ bool CBlock::AcceptBlock() 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"); @@ -2013,10 +2006,16 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) return error("ProcessBlock() : CheckBlock FAILED"); // ppcoin: verify hash target and signature of coinstake tx - if (pblock->IsProofOfStake() && !pblock->vtx[1].CheckProofOfStake(pblock->nBits)) + if (pblock->IsProofOfStake()) { - 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 + uint256 hashProofOfStake = 0; + if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, hashProofOfStake)) + { + printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str()); + return false; // do not error here as we expect this during initial block download + } + if (!mapProofOfStake.count(hash)) // add to mapProofOfStake + mapProofOfStake.insert(make_pair(hash, hashProofOfStake)); } CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint(); @@ -2109,21 +2108,57 @@ bool CBlock::SignBlock(const CKeyStore& keystore) { vector vSolutions; txnouttype whichType; - const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0]; + int nVouts = nTime < 1361664000 ? 1 : vtx[0].vout.size(); - if (!Solver(txout.scriptPubKey, whichType, vSolutions)) - return false; - if (whichType == TX_PUBKEY) + if(!IsProofOfStake()) { - // Sign - const valtype& vchPubKey = vSolutions[0]; - CKey key; - if (!keystore.GetKey(Hash160(vchPubKey), key)) - return false; - if (key.GetPubKey() != vchPubKey) + for(int i = 0; i < nVouts; i++) + { + const CTxOut& txout = vtx[0].vout[i]; + + if (!Solver(txout.scriptPubKey, whichType, vSolutions)) + continue; + + if (whichType == TX_PUBKEY) + { + // Sign + valtype& vchPubKey = vSolutions[0]; + CKey key; + + if (!keystore.GetKey(Hash160(vchPubKey), key)) + continue; + if (key.GetPubKey() != vchPubKey) + continue; + if(!key.Sign(GetHash(), vchBlockSig)) + continue; + + return true; + } + } + } + else + { + const CTxOut& txout = vtx[1].vout[1]; + + if (!Solver(txout.scriptPubKey, whichType, vSolutions)) return false; - return key.Sign(GetHash(), vchBlockSig); + + if (whichType == TX_PUBKEY) + { + // 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); + } } + + printf("Sign failed\n"); return false; } @@ -2135,19 +2170,49 @@ bool CBlock::CheckBlockSignature() const vector vSolutions; txnouttype whichType; - const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0]; + int nVouts = nTime < 1361664000 ? 1 : vtx[0].vout.size(); - if (!Solver(txout.scriptPubKey, whichType, vSolutions)) - return false; - if (whichType == TX_PUBKEY) + if(IsProofOfStake()) { - const valtype& vchPubKey = vSolutions[0]; - CKey key; - if (!key.SetPubKey(vchPubKey)) - return false; - if (vchBlockSig.empty()) + const CTxOut& txout = vtx[1].vout[1]; + + if (!Solver(txout.scriptPubKey, whichType, vSolutions)) return false; - return key.Verify(GetHash(), vchBlockSig); + if (whichType == TX_PUBKEY) + { + valtype& vchPubKey = vSolutions[0]; + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + if (vchBlockSig.empty()) + return false; + return key.Verify(GetHash(), vchBlockSig); + } + } + else + { + for(int i = 0; i < nVouts; i++) + { + const CTxOut& txout = vtx[0].vout[i]; + + if (!Solver(txout.scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_PUBKEY) + { + // Verify + valtype& vchPubKey = vSolutions[0]; + CKey key; + if (!key.SetPubKey(vchPubKey)) + continue; + if (vchBlockSig.empty()) + continue; + if(!key.Verify(GetHash(), vchBlockSig)) + continue; + + return true; + } + } } return false; } @@ -2168,7 +2233,7 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) 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; } @@ -2221,13 +2286,13 @@ bool LoadBlockIndex(bool fAllowNew) 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 nModifierInterval=%d\n", + fTestNet? "Test" : "NovaCoin", hashGenesisBlock.ToString().substr(0, 20).c_str(), bnProofOfWorkLimit.GetCompact(), bnInitialHashTarget.GetCompact(), nStakeMinAge, nCoinbaseMaturity, nModifierInterval); + // // Load block index // @@ -2244,42 +2309,25 @@ bool LoadBlockIndex(bool fAllowNew) if (!fAllowNew) return false; - // Genesis Block: - // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) - // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) - // CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) - // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) - // 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((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector((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()); @@ -2309,7 +2357,7 @@ bool LoadBlockIndex(bool fAllowNew) 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(); @@ -2365,13 +2413,14 @@ void PrintBlockTree() // 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); @@ -2436,18 +2485,18 @@ string GetWarnings(string strFor) } // ppcoin: should not enter safe mode for longer invalid chain - // ppcoin: if sync-checkpoint too old enter safe mode - if (Checkpoints::IsMatureSyncCheckpoint()) + // ppcoin: if sync-checkpoint is too old do not enter safe mode + if (Checkpoints::IsSyncCheckpointTooOld(60 * 60 * 24 * 10) && !fTestNet) { - nPriority = 2000; - strStatusBar = strRPC = "WARNING: Checkpoint is too old. Wait for block chain to download, or notify developers of the issue."; + nPriority = 100; + 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 of the issue."; + strStatusBar = strRPC = "WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or notify developers."; } // Alerts @@ -2561,12 +2610,6 @@ bool static AlreadyHave(CTxDB& txdb, const CInv& inv) -// 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 > mapReuseKey; @@ -2913,7 +2956,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) 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() + STAKE_MIN_AGE > pindexBest->GetBlockTime()) + if (hashStop != hashBestChain && pindex->GetBlockTime() + nStakeMinAge > pindexBest->GetBlockTime()) pfrom->PushInventory(CInv(MSG_BLOCK, hashBestChain)); break; } @@ -3197,6 +3240,17 @@ bool ProcessMessages(CNode* pfrom) // (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 vchMessageStart(strMessageStart.begin(), strMessageStart.end()); + printf("ProcessMessages : AdjustedTime=%"PRI64d" MessageStart=%s\n", GetAdjustedTime(), HexStr(vchMessageStart).c_str()); + nTimeLastPrintMessageStart = GetAdjustedTime(); + } + loop { // Scan for message start @@ -3511,39 +3565,6 @@ void SHA256Transform(void* pstate, void* pinput, const void* pinit) ((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 { @@ -3569,6 +3590,7 @@ public: uint64 nLastBlockTx = 0; uint64 nLastBlockSize = 0; +int64 nLastCoinStakeSearchInterval = 0; // CreateNewBlock: // fProofOfStake: try (best effort) to make a proof-of-stake block @@ -3592,31 +3614,28 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) pblock->vtx.push_back(txNew); // ppcoin: if coinstake available add coinstake tx - static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift / 2; // only initialized at startup + static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup CBlockIndex* pindexPrev = pindexBest; if (fProofOfStake) // attemp to find a coinstake { - while (nLastCoinStakeCheckTime < GetAdjustedTime()) + pblock->nBits = GetNextTargetRequired(pindexPrev, true); + CTransaction txCoinStake; + int64 nSearchTime = txCoinStake.nTime; // search to current time + 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 / 2)); - 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; + 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].vout[0].SetEmpty(); + pblock->vtx[0].nTime = txCoinStake.nTime; + pblock->vtx.push_back(txCoinStake); + } } + nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; + nLastCoinStakeSearchTime = nSearchTime; } } @@ -3708,7 +3727,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) continue; // Timestamp limit - if (tx.nTime > GetAdjustedTime()) + if (tx.nTime > GetAdjustedTime() || (pblock->IsProofOfStake() && tx.nTime > pblock->vtx[1].nTime)) continue; // ppcoin: simplify transaction fee - allow free = false @@ -3770,9 +3789,12 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + 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); - pblock->UpdateTime(pindexPrev); + if (pblock->IsProofOfWork()) + pblock->UpdateTime(pindexPrev); pblock->nNonce = 0; return pblock.release(); @@ -3789,7 +3811,10 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& 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(); @@ -3888,6 +3913,8 @@ static int nLimitProcessors = -1; void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) { + void *scratchbuf = scrypt_buffer_alloc(); + printf("CPUMiner started for proof-of-%s\n", fProofOfStake? "stake" : "work"); SetThreadPriority(THREAD_PRIORITY_LOWEST); @@ -3927,9 +3954,9 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); - // ppcoin: if proof-of-stake block found then process block if (fProofOfStake) { + // ppcoin: if proof-of-stake block found then process block if (pblock->IsProofOfStake()) { if (!pblock->SignBlock(*pwalletMain)) @@ -3968,28 +3995,33 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) // 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)) { strMintWarning = strMintMessage; @@ -4058,6 +4090,8 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) break; // need to update coinbase timestamp } } + + scrypt_buffer_free(scratchbuf); } void static ThreadBitcoinMiner(void* parg)