X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=01d99ebaf2c65efbd500e2b968722b43c008050b;hb=726b753c71c1ec24fd0f55a7badef4ef5a00769a;hp=18d85f295d762dfac83046088f7a0b95268bb31f;hpb=84a4a7763f386934da90e2bd1e355b70023fa9ca;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 18d85f2..01d99eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,14 +32,19 @@ unsigned int nTransactionsUpdated = 0; map mapBlockIndex; set > setStakeSeen; -uint256 hashGenesisBlock = hashGenesisBlockOfficial; -static CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); -static CBigNum bnProofOfStakeLimit(~uint256(0) >> 24); -static CBigNum bnProofOfStakeHardLimit(~uint256(0) >> 30); -static CBigNum bnInitialHashTarget(~uint256(0) >> 20); -unsigned int nStakeMinAge = 60 * 60 * 24 * 30; // minimum age for coin age -unsigned int nStakeMaxAge = 60 * 60 * 24 * 90; // stake age of full weight -unsigned int nStakeTargetSpacing = 10 * 60; // 10-minute block spacing + +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 bnProofOfStakeLimit(~uint256(0) >> 27); // proof of stake target limit since 20 June 2013, equal to 0.03125 proof of stake difficulty +CBigNum bnProofOfStakeHardLimit(~uint256(0) >> 30); // disabled temporarily, will be used in the future to fix minimal proof of stake difficulty at 0.25 + +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 nStakeTargetSpacing = 10 * 60; // 10-minute stakes spacing +unsigned int nModifierInterval = 6 * 60 * 60; // time to elapse before new modifier is computed + int nCoinbaseMaturity = 500; CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; @@ -470,9 +475,13 @@ bool CTransaction::CheckTransaction() const const CTxOut& txout = vout[i]; if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake()) return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction")); - // ppcoin: enforce minimum output amount - if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT) + + // 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) 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; @@ -492,7 +501,7 @@ bool CTransaction::CheckTransaction() const if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); + return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size is invalid")); } else { @@ -514,23 +523,6 @@ int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, unsigned int nNewBlockSize = nBlockSize + nBytes; int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; - if (fAllowFree) - { - if (nBlockSize == 1) - { - // Transactions under 10K are free - // (about 4500 BTC if made of 50 BTC inputs) - if (nBytes < 10000) - nMinFee = 0; - } - else - { - // Free transaction area - if (nNewBlockSize < 27000) - nMinFee = 0; - } - } - // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 if (nMinFee < nBaseFee) { @@ -946,15 +938,32 @@ uint256 WantedByOrphan(const CBlock* pblockOrphan) return pblockOrphan->hashPrevBlock; } +// select stake target limit according to hard-coded conditions +CBigNum static GetProofOfStakeLimit(int nHeight, unsigned int nTime) +{ + if(fTestNet) // separate proof of stake target limit for testnet + return bnProofOfStakeLimit; + if(nTime > TARGETS_SWITCH_TIME) // 27 bits since 20 July 2013 + return bnProofOfStakeLimit; + if(nHeight + 1 > 15000) // 24 bits since block 15000 + return bnProofOfStakeLegacyLimit; + if(nHeight + 1 > 14060) // 31 bits since block 14060 until 15000 + return bnProofOfStakeHardLimit; + + return bnProofOfWorkLimit; // return bnProofOfWorkLimit of none matched +} + +// miner's coin base reward based on nBits int64 GetProofOfWorkReward(unsigned int nBits) { 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 64x multiply of difficulty + // NovaCoin: subsidy is cut in half every 64x multiply of PoW difficulty // A reasonably continuous curve is used to avoid shock to market // (nSubsidyLimit / nSubsidy) ** 6 == bnProofOfWorkLimit / bnTarget // @@ -975,6 +984,7 @@ int64 GetProofOfWorkReward(unsigned int nBits) } int64 nSubsidy = bnUpperBound.getuint64(); + nSubsidy = (nSubsidy / CENT) * CENT; if (fDebug && GetBoolArg("-printcreation")) printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy); @@ -982,18 +992,70 @@ int64 GetProofOfWorkReward(unsigned int nBits) 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) +// miner's coin stake reward based on nBits and coin age spent (coin-days) +int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime) { - static int64 nRewardCoinYear = 5 * CENT; // creation amount per coin-year + int64 nRewardCoinYear; + + if(fTestNet || nTime > STAKE_SWITCH_TIME) + { + // Stage 2 of emission process is PoS-based. It will be active on mainNet since 20 Jun 2013. + + CBigNum bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE; // Base stake mint rate, 100% year interest + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + CBigNum bnTargetLimit = GetProofOfStakeLimit(0, nTime); + bnTargetLimit.SetCompact(bnTargetLimit.GetCompact()); + + // NovaCoin: reward for coin-year is cut in half every 64x multiply of PoS difficulty + // A reasonably continuous curve is used to avoid shock to market + // (nRewardCoinYearLimit / nRewardCoinYear) ** 6 == bnProofOfStakeLimit / bnTarget + // + // Human readable form: + // + // nRewardCoinYear = 1 / (posdiff ^ 1/6) + + CBigNum bnLowerBound = 1 * CENT; // Lower interest bound is 1% per year + CBigNum bnUpperBound = bnRewardCoinYearLimit; + while (bnLowerBound + CENT <= bnUpperBound) + { + CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; + if (fDebug && GetBoolArg("-printcreation")) + printf("GetProofOfStakeReward() : lower=%"PRI64d" upper=%"PRI64d" mid=%"PRI64d"\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); + if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget) + bnUpperBound = bnMidValue; + else + bnLowerBound = bnMidValue; + } + + nRewardCoinYear = bnUpperBound.getuint64(); + nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, MAX_MINT_PROOF_OF_STAKE); + } + else + { + // Old creation amount per coin-year, 5% fixed stake mint rate + nRewardCoinYear = 5 * CENT; + } + int64 nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear; if (fDebug && GetBoolArg("-printcreation")) - printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nCoinAge); + printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d" nBits=%d\n", FormatMoney(nSubsidy).c_str(), nCoinAge, nBits); return nSubsidy; } static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week -static const int64 nTargetSpacingWorkMax = 12 * nStakeTargetSpacing; // 2-hour + +// get proof of work blocks max spacing according to hard-coded conditions +int64 static GetTargetSpacingWorkMax(int nHeight, unsigned int nTime) +{ + if(nTime > TARGETS_SWITCH_TIME) + return 3 * nStakeTargetSpacing; // 30 minutes on mainNet since 20 Jul 2013 00:00:00 + + if(fTestNet) + return 3 * nStakeTargetSpacing; // 15 minutes on testNet + + return 12 * nStakeTargetSpacing; // 2 hours otherwise +} // // minimum amount of work that could possibly be required nTime after @@ -1025,33 +1087,19 @@ const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfSta return pindex; } -unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) +unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) { - CBigNum bnTargetLimit = bnProofOfWorkLimit; - - if(fProofOfStake) - { - // Proof-of-Stake blocks has own target limit since nVersion=3 supermajority on mainNet and always on testNet - if(fTestNet) - bnTargetLimit = bnProofOfStakeHardLimit; - else - { - if(fTestNet || (pindexLast->nHeight + 1 > 15000)) - bnTargetLimit = bnProofOfStakeLimit; - else if(pindexLast->nHeight + 1 > 14060) - bnTargetLimit = bnProofOfStakeHardLimit; - } - } + CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : GetProofOfStakeLimit(pindexLast->nHeight, pindexLast->nTime); if (pindexLast == NULL) return bnTargetLimit.GetCompact(); // genesis block const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake); if (pindexPrev->pprev == NULL) - return bnInitialHashTarget.GetCompact(); // first block + return bnTargetLimit.GetCompact(); // first block const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake); if (pindexPrevPrev->pprev == NULL) - return bnInitialHashTarget.GetCompact(); // second block + return bnTargetLimit.GetCompact(); // second block int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); @@ -1059,7 +1107,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? nStakeTargetSpacing : min(nTargetSpacingWorkMax, (int64) nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight)); + int64 nTargetSpacing = fProofOfStake? nStakeTargetSpacing : min(GetTargetSpacingWorkMax(pindexLast->nHeight, pindexLast->nTime), (int64) nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight)); int64 nInterval = nTargetTimespan / nTargetSpacing; bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); bnNew /= ((nInterval + 1) * nTargetSpacing); @@ -1116,15 +1164,21 @@ void static InvalidChainFound(CBlockIndex* pindexNew) uiInterface.NotifyBlocksChanged(); } - printf("InvalidChainFound: invalid block=%s height=%d trust=%s date=%s\n", + CBigNum bnBestInvalidBlockTrust = pindexNew->bnChainTrust - pindexNew->pprev->bnChainTrust; + CBigNum bnBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->bnChainTrust - pindexBest->pprev->bnChainTrust) : pindexBest->bnChainTrust; + + printf("InvalidChainFound: invalid block=%s height=%d trust=%s blocktrust=%s date=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, - pindexNew->bnChainTrust.ToString().c_str(), DateTimeStrFormat("%x %H:%M:%S", - pindexNew->GetBlockTime()).c_str()); - printf("InvalidChainFound: current best=%s height=%d trust=%s date=%s\n", - hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainTrust.ToString().c_str(), + pindexNew->bnChainTrust.ToString().c_str(), bnBestInvalidBlockTrust.ToString().c_str(), + DateTimeStrFormat("%x %H:%M:%S", pindexNew->GetBlockTime()).c_str()); + printf("InvalidChainFound: current best=%s height=%d trust=%s blocktrust=%s date=%s\n", + hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, + bnBestChainTrust.ToString().c_str(), + bnBestBlockTrust.ToString().c_str(), DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); } + void CBlock::UpdateTime(const CBlockIndex* pindexPrev) { nTime = max(GetBlockTime(), GetAdjustedTime()); @@ -1381,7 +1435,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) - GetMinFee() + MIN_TX_FEE) + if (nStakeReward > GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee() + MIN_TX_FEE) return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str())); } else @@ -1736,7 +1790,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) if (!txdb.TxnBegin()) return error("SetBestChain() : TxnBegin failed"); - if (pindexGenesisBlock == NULL && hash == hashGenesisBlock) + if (pindexGenesisBlock == NULL && hash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) { txdb.WriteHashBestChain(hash); if (!txdb.TxnCommit()) @@ -1810,8 +1864,12 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) bnBestChainTrust = pindexNew->bnChainTrust; nTimeBestReceived = GetTime(); nTransactionsUpdated++; - printf("SetBestChain: new best=%s height=%d trust=%s date=%s\n", + + CBigNum bnBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->bnChainTrust - pindexBest->pprev->bnChainTrust) : pindexBest->bnChainTrust; + + printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%s date=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainTrust.ToString().c_str(), + bnBestBlockTrust.ToString().c_str(), DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); // Check the version of the last 100 blocks to see if we need to upgrade: @@ -2000,6 +2058,24 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CheckBlock() : size limits failed")); + // Special short-term limits to avoid 10,000 BDB lock limit: + if (GetBlockTime() < LOCKS_SWITCH_TIME) + { + // Rule is: #unique txids referenced <= 4,500 + // ... to prevent 10,000 BDB lock exhaustion on old clients + set setTxIn; + for (size_t i = 0; i < vtx.size(); i++) + { + setTxIn.insert(vtx[i].GetHash()); + if (i == 0) continue; // skip coinbase txin + BOOST_FOREACH(const CTxIn& txin, vtx[i].vin) + setTxIn.insert(txin.prevout.hash); + } + size_t nTxids = setTxIn.size(); + if (nTxids > 4500) + return error("CheckBlock() : maxlocks violation"); + } + // Check proof of work matches claimed amount if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits)) return DoS(50, error("CheckBlock() : proof of work failed")); @@ -2015,28 +2091,38 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const if (vtx[i].IsCoinBase()) return DoS(100, error("CheckBlock() : more than one coinbase")); - // ppcoin: only the second transaction can be the optional coinstake - for (unsigned int i = 2; i < vtx.size(); i++) - if (vtx[i].IsCoinStake()) - 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.size() != 1 || !vtx[0].vout[0].IsEmpty())) - return error("CheckBlock() : coinbase output not empty for proof-of-stake block"); - // Check coinbase timestamp if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift) return DoS(50, error("CheckBlock() : coinbase timestamp is too early")); - // Check coinstake timestamp - if (IsProofOfStake() && !CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime)) - return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%"PRI64d" nTimeTx=%u", GetBlockTime(), vtx[1].nTime)); + if (IsProofOfStake()) + { + // ppcoin: coinbase output should be empty if proof-of-stake block + if (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty()) + return error("CheckBlock() : coinbase output not empty for proof-of-stake block"); + + // Second transaction must be coinstake, the rest must not be + if (vtx.empty() || !vtx[1].IsCoinStake()) + return DoS(100, error("CheckBlock() : second tx is not coinstake")); + for (unsigned int i = 2; i < vtx.size(); i++) + if (vtx[i].IsCoinStake()) + return DoS(100, error("CheckBlock() : more than one coinstake")); + + // Check coinstake timestamp + if (!CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime)) + return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%"PRI64d" nTimeTx=%u", GetBlockTime(), vtx[1].nTime)); + } + else + { + // Coinbase fee paid until 20 Sep 2013 + int64 nFee = GetBlockTime() < CHAINCHECKS_SWITCH_TIME ? vtx[0].GetMinFee() - MIN_TX_FEE : 0; - // 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", + // Check coinbase reward + if (vtx[0].GetValueOut() > (GetProofOfWorkReward(nBits) - nFee)) + return DoS(50, error("CheckBlock() : coinbase reward exceeded %s > %s", FormatMoney(vtx[0].GetValueOut()).c_str(), - FormatMoney(IsProofOfWork()? GetProofOfWorkReward(nBits) : 0).c_str())); + FormatMoney(GetProofOfWorkReward(nBits) - nFee).c_str())); + } // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) @@ -2071,9 +2157,12 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); - // ppcoin: check block signature - if (!CheckBlockSignature()) - return DoS(100, error("CheckBlock() : bad block signature")); + // NovaCoin: check proof-of-stake block signature + if (IsProofOfStake() || (!fTestNet && GetBlockTime() < CHAINCHECKS_SWITCH_TIME)) + { + if (!CheckBlockSignature()) + return DoS(100, error("CheckBlock() : bad block signature")); + } return true; } @@ -2111,7 +2200,16 @@ bool CBlock::AcceptBlock() // ppcoin: check that the block satisfies synchronized checkpoint if (!Checkpoints::CheckSync(hash, pindexPrev)) - return error("AcceptBlock() : rejected by synchronized checkpoint"); + { + if(!GetBoolArg("-nosynccheckpoints", false)) + { + return error("AcceptBlock() : rejected by synchronized checkpoint"); + } + else + { + strMiscWarning = _("WARNING: syncronized checkpoint violation detected, but skipped!"); + } + } // Reject block.nVersion < 3 blocks since 95% threshold on mainNet and always on testNet: if (nVersion < 3 && ((!fTestNet && nHeight > 14060) || (fTestNet && nHeight > 0))) @@ -2148,6 +2246,75 @@ bool CBlock::AcceptBlock() return true; } +CBigNum CBlockIndex::GetBlockTrust() const +{ + CBigNum bnTarget; + + // Old protocol + if (!fTestNet && GetBlockTime() < CHAINCHECKS_SWITCH_TIME) + { + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + + if (bnTarget <= 0) + return 0; + return (IsProofOfStake()? (CBigNum(1)<<256) / (bnTarget+1) : 1); + } + + // New protocol + if (pprev == NULL || pprev->nHeight < 10) + return 1; + + const CBlockIndex* currentIndex = pprev; + + if(IsProofOfStake()) + { + bnTarget.SetCompact(nBits); + if (bnTarget <= 0) + return 0; + + if (!pprev->IsProofOfWork()) + return (CBigNum(1)<<256) / (3 * (bnTarget+1)); + + int nPoWCount = 0; + + // Check last 12 blocks type + while (pprev->nHeight - currentIndex->nHeight < 12) + { + if (currentIndex->IsProofOfWork()) + nPoWCount++; + currentIndex = currentIndex->pprev; + } + + // Return 1/3 of score if less than 3 PoW blocks found + if (nPoWCount < 3) + return (CBigNum(1)<<256) / (3 * (bnTarget+1)); + + return (CBigNum(1)<<256) / (bnTarget+1); + } + else + { + if (!(pprev->IsProofOfStake() && pprev->pprev->IsProofOfStake())) + return 1 + (2 * (pprev->bnChainTrust - pprev->pprev->bnChainTrust) / 3); + + int nPoSCount = 0; + + // Check last 12 blocks type + while (pprev->nHeight - currentIndex->nHeight < 12) + { + if (currentIndex->IsProofOfStake()) + nPoSCount++; + currentIndex = currentIndex->pprev; + } + + // Return 2/3 of previous block score if less than 7 PoS blocks found + if (nPoSCount < 7) + return 1 + (2 * (pprev->bnChainTrust - pprev->pprev->bnChainTrust) / 3); + + return (pprev->bnChainTrust - pprev->pprev->bnChainTrust); + } +} + bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck) { unsigned int nFound = 0; @@ -2337,7 +2504,7 @@ bool CBlock::SignBlock(const CKeyStore& keystore) // ppcoin: check block signature bool CBlock::CheckBlockSignature() const { - if (GetHash() == hashGenesisBlock) + if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) return vchBlockSig.empty(); vector vSolutions; @@ -2462,10 +2629,11 @@ bool LoadBlockIndex(bool fAllowNew) pchMessageStart[2] = 0xc0; pchMessageStart[3] = 0xef; - - hashGenesisBlock = hashGenesisBlockTestNet; - nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day - nCoinbaseMaturity = 60; + 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 } // @@ -2484,14 +2652,24 @@ 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 + + // MainNet: + + //CBlock(hash=00000a060336cbb72fe969666d337b87198b1add2abaa59cca226820b32933a4, ver=1, hashPrevBlock=0000000000000000000000000000000000000000000000000000000000000000, hashMerkleRoot=4cb33b3b6a861dcbc685d3e614a9cafb945738d6833f182855679f2fad02057b, nTime=1360105017, nBits=1e0fffff, nNonce=1575379, vtx=1, vchBlockSig=) + // Coinbase(hash=4cb33b3b6a, nTime=1360105017, ver=1, vin.size=1, vout.size=1, nLockTime=0) + // CTxIn(COutPoint(0000000000, 4294967295), coinbase 04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936) + // CTxOut(empty) + // vMerkleTree: 4cb33b3b6a + + // TestNet: + + //CBlock(hash=0000c763e402f2436da9ed36c7286f62c3f6e5dbafce9ff289bd43d7459327eb, ver=1, hashPrevBlock=0000000000000000000000000000000000000000000000000000000000000000, hashMerkleRoot=4cb33b3b6a861dcbc685d3e614a9cafb945738d6833f182855679f2fad02057b, nTime=1360105017, nBits=1f00ffff, nNonce=46534, vtx=1, vchBlockSig=) + // Coinbase(hash=4cb33b3b6a, nTime=1360105017, ver=1, vin.size=1, vout.size=1, nLockTime=0) + // CTxIn(COutPoint(0000000000, 4294967295), coinbase 04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936) + // CTxOut(empty) + // vMerkleTree: 4cb33b3b6a + const char* pszTimestamp = "https://bitcointalk.org/index.php?topic=134179.msg1502196#msg1502196"; CTransaction txNew; txNew.nTime = 1360105017; @@ -2506,12 +2684,12 @@ bool LoadBlockIndex(bool fAllowNew) block.nVersion = 1; block.nTime = 1360105017; block.nBits = bnProofOfWorkLimit.GetCompact(); - block.nNonce = 1575379; + block.nNonce = !fTestNet ? 1575379 : 46534; //// debug print assert(block.hashMerkleRoot == uint256("0x4cb33b3b6a861dcbc685d3e614a9cafb945738d6833f182855679f2fad02057b")); block.print(); - assert(block.GetHash() == hashGenesisBlock); + assert(block.GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)); assert(block.CheckBlock()); // Start new block file @@ -2523,7 +2701,7 @@ bool LoadBlockIndex(bool fAllowNew) return error("LoadBlockIndex() : genesis block not accepted"); // ppcoin: initialize synchronized checkpoint - if (!Checkpoints::WriteSyncCheckpoint(hashGenesisBlock)) + if (!Checkpoints::WriteSyncCheckpoint((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))) return error("LoadBlockIndex() : failed to init sync checkpoint"); } @@ -2711,14 +2889,13 @@ string GetWarnings(string strFor) 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 != "") @@ -3879,13 +4056,15 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) // 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)); + // Special compatibility rule before 20 Aug: limit size to 500,000 bytes: + if (GetAdjustedTime() < LOCKS_SWITCH_TIME) + nBlockMaxSize = std::min(nBlockMaxSize, (unsigned int)(MAX_BLOCK_SIZE_GEN)); + // 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); @@ -3909,7 +4088,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup CBlockIndex* pindexPrev = pindexBest; - if (fProofOfStake) // attemp to find a coinstake + if (fProofOfStake) // attempt to find a coinstake { pblock->nBits = GetNextTargetRequired(pindexPrev, true); CTransaction txCoinStake; @@ -4121,7 +4300,9 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; - printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); + + if (fDebug && GetBoolArg("-printpriority")) + printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); if (pblock->IsProofOfWork()) pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits); @@ -4242,17 +4423,8 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) return true; } -void static ThreadBitcoinMiner(void* parg); - -static bool fGenerateBitcoins = false; -static bool fLimitProcessors = false; -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); // Make this thread recognisable as the mining thread @@ -4262,7 +4434,7 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) CReserveKey reservekey(pwallet); unsigned int nExtraNonce = 0; - while (fGenerateBitcoins || fProofOfStake) + while (fProofOfStake) { if (fShutdown) return; @@ -4271,7 +4443,7 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) Sleep(1000); if (fShutdown) return; - if ((!fGenerateBitcoins) && !fProofOfStake) + if (!fProofOfStake) return; } @@ -4285,7 +4457,6 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) // // Create new block // - unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; CBlockIndex* pindexPrev = pindexBest; auto_ptr pblock(CreateNewBlock(pwallet, fProofOfStake)); @@ -4304,7 +4475,7 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) continue; } strMintWarning = ""; - printf("CPUMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str()); + printf("StakeMiner : 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); @@ -4312,174 +4483,6 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) Sleep(500); continue; } - - printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), - ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); - - // - // Pre-build hash buffers - // - char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf); - char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); - char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); - - FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); - - unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); - unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); - - - // - // Search - // - int64 nStart = GetTime(); - uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - - unsigned int max_nonce = 0xffff0000; - block_header res_header; - uint256 result; - - loop - { - unsigned int nHashesDone = 0; - unsigned int nNonceFound; - - nNonceFound = scanhash_scrypt( - (block_header *)&pblock->nVersion, - scratchbuf, - max_nonce, - nHashesDone, - UBEGIN(result), - &res_header - ); - - // Check if something found - if (nNonceFound != (unsigned int) -1) - { - if (result <= hashTarget) - { - // Found a solution - pblock->nNonce = nNonceFound; - assert(result == pblock->GetHash()); - if (!pblock->SignBlock(*pwalletMain)) - { -// strMintWarning = strMintMessage; - break; - } - strMintWarning = ""; - - SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckWork(pblock.get(), *pwalletMain, reservekey); - SetThreadPriority(THREAD_PRIORITY_LOWEST); - break; - } - } - - // Meter hashes/sec - static int64 nHashCounter; - if (nHPSTimerStart == 0) - { - nHPSTimerStart = GetTimeMillis(); - nHashCounter = 0; - } - else - nHashCounter += nHashesDone; - if (GetTimeMillis() - nHPSTimerStart > 4000) - { - static CCriticalSection cs; - { - LOCK(cs); - if (GetTimeMillis() - nHPSTimerStart > 4000) - { - dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); - nHPSTimerStart = GetTimeMillis(); - nHashCounter = 0; - static int64 nLogTime; - if (GetTime() - nLogTime > 30 * 60) - { - nLogTime = GetTime(); - printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[THREAD_MINER], dHashesPerSec/1000.0); - } - } - } - } - - // Check for stop or if block needs to be rebuilt - if (fShutdown) - return; - if (!fGenerateBitcoins) - return; - if (fLimitProcessors && vnThreadsRunning[THREAD_MINER] > nLimitProcessors) - return; - if (vNodes.empty()) - break; - if (nBlockNonce >= 0xffff0000) - break; - if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) - break; - if (pindexPrev != pindexBest) - break; - - // Update nTime every few seconds - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); - pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift); - pblock->UpdateTime(pindexPrev); - nBlockTime = ByteReverse(pblock->nTime); - - if (pblock->GetBlockTime() >= (int64)pblock->vtx[0].nTime + nMaxClockDrift) - break; // need to update coinbase timestamp - } } - - scrypt_buffer_free(scratchbuf); -} - -void static ThreadBitcoinMiner(void* parg) -{ - CWallet* pwallet = (CWallet*)parg; - try - { - vnThreadsRunning[THREAD_MINER]++; - BitcoinMiner(pwallet, false); - vnThreadsRunning[THREAD_MINER]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_MINER]--; - PrintException(&e, "ThreadBitcoinMiner()"); - } catch (...) { - vnThreadsRunning[THREAD_MINER]--; - PrintException(NULL, "ThreadBitcoinMiner()"); - } - nHPSTimerStart = 0; - if (vnThreadsRunning[THREAD_MINER] == 0) - dHashesPerSec = 0; - printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[THREAD_MINER]); } - -void GenerateBitcoins(bool fGenerate, CWallet* pwallet) -{ - fGenerateBitcoins = fGenerate; - nLimitProcessors = GetArg("-genproclimit", -1); - if (nLimitProcessors == 0) - fGenerateBitcoins = false; - fLimitProcessors = (nLimitProcessors != -1); - - if (fGenerate) - { - int nProcessors = boost::thread::hardware_concurrency(); - printf("%d processors\n", nProcessors); - if (nProcessors < 1) - nProcessors = 1; - if (fLimitProcessors && nProcessors > nLimitProcessors) - nProcessors = nLimitProcessors; - int nAddThreads = nProcessors - vnThreadsRunning[THREAD_MINER]; - printf("Starting %d BitcoinMiner threads\n", nAddThreads); - for (int i = 0; i < nAddThreads; i++) - { - if (!NewThread(ThreadBitcoinMiner, pwallet)) - printf("Error: NewThread(ThreadBitcoinMiner) failed\n"); - Sleep(10); - } - } -}