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 bnPoWBase = CBigNum(uint256("0x00000000ffff0000000000000000000000000000000000000000000000000000")); // difficulty-1 target
CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 16);
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)
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
{
}
// select stake target limit according to hard-coded conditions
-CBigNum static GetProofOfStakeLimit(int nHeight, unsigned int nTime)
+CBigNum inline GetProofOfStakeLimit(int nHeight, unsigned int nTime)
{
if(fTestNet) // separate proof of stake target limit for testnet
return bnProofOfStakeLimit;
}
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);
}
// miner's coin stake reward based on nBits and coin age spent (coin-days)
-int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime)
+int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly)
{
- int64 nRewardCoinYear;
+ int64 nRewardCoinYear, nSubsidy;
if(fTestNet || nTime > STAKE_SWITCH_TIME)
{
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)
+ // NovaCoin: A reasonably continuous curve is used to avoid shock to market
+
+ CBigNum bnLowerBound = 1 * CENT, // Lower interest bound is 1% per year
+ bnUpperBound = bnRewardCoinYearLimit, // Upper interest bound is 100% per year
+ bnMidPart, bnRewardPart;
- 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)
+
+ if(!fTestNet && nTime < STAKECURVE_SWITCH_TIME)
+ {
+ //
+ // Until 20 Oct 2013: reward for coin-year is cut in half every 64x multiply of PoS difficulty
+ //
+ // (nRewardCoinYearLimit / nRewardCoinYear) ** 6 == bnProofOfStakeLimit / bnTarget
+ //
+ // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/6)
+ //
+
+ bnMidPart = bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit;
+ bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget;
+ }
+ else
+ {
+ //
+ // Since 20 Oct 2013: reward for coin-year is cut in half every 8x multiply of PoS difficulty
+ //
+ // (nRewardCoinYearLimit / nRewardCoinYear) ** 3 == bnProofOfStakeLimit / bnTarget
+ //
+ // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/3)
+ //
+
+ bnMidPart = bnMidValue * bnMidValue * bnMidValue * bnTargetLimit;
+ bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget;
+ }
+
+ if (bnMidPart > bnRewardPart)
bnUpperBound = bnMidValue;
else
bnLowerBound = bnMidValue;
nRewardCoinYear = 5 * CENT;
}
- int64 nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
+ if(bCoinYearOnly)
+ return nRewardCoinYear;
+
+ // Fix problem with proof-of-stake rewards calculation since 20 Sep 2013
+ if(nTime < CHAINCHECKS_SWITCH_TIME)
+ nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
+ else
+ nSubsidy = nCoinAge * nRewardCoinYear * 33 / (365 * 33 + 8);
+
if (fDebug && GetBoolArg("-printcreation"))
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
// get proof of work blocks max spacing according to hard-coded conditions
-int64 static GetTargetSpacingWorkMax(int nHeight, unsigned int nTime)
+int64 inline 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 (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)
CBigNum CBlockIndex::GetBlockTrust() const
{
CBigNum bnTarget;
+ bnTarget.SetCompact(nBits);
- // Old protocol
- if (!fTestNet && GetBlockTime() < CHAINCHECKS_SWITCH_TIME)
- {
- CBigNum bnTarget;
- bnTarget.SetCompact(nBits);
+ if (bnTarget <= 0)
+ return 0;
- if (bnTarget <= 0)
- return 0;
+ /* Old protocol, will be removed later */
+ if (!fTestNet && GetBlockTime() < CHAINCHECKS_SWITCH_TIME)
return (IsProofOfStake()? (CBigNum(1)<<256) / (bnTarget+1) : 1);
- }
- // New protocol
- if (pprev == NULL || pprev->nHeight < 10)
- return 1;
+ /* New protocol */
+
+ // Calculate work amount for block
+ CBigNum bnPoWTrust = bnPoWBase / (bnTarget+1);
+
+ // Set bnPowTrust to 1 if we are checking PoS block or PoW difficulty is too low
+ bnPoWTrust = (IsProofOfStake() || bnPoWTrust < 1) ? 1 : bnPoWTrust;
+
+ // Return bnPoWTrust for the first 12 blocks
+ if (pprev == NULL || pprev->nHeight < 12)
+ return bnPoWTrust;
const CBlockIndex* currentIndex = pprev;
if(IsProofOfStake())
{
- bnTarget.SetCompact(nBits);
- if (bnTarget <= 0)
- return 0;
-
+ // Return 1/3 of score if parent block is not the PoW block
if (!pprev->IsProofOfWork())
return (CBigNum(1)<<256) / (3 * (bnTarget+1));
}
else
{
+ // Return bnPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks
if (!(pprev->IsProofOfStake() && pprev->pprev->IsProofOfStake()))
- return 1 + (2 * (pprev->bnChainTrust - pprev->pprev->bnChainTrust) / 3);
+ return bnPoWTrust + (2 * (pprev->bnChainTrust - pprev->pprev->bnChainTrust) / 3);
int nPoSCount = 0;
currentIndex = currentIndex->pprev;
}
- // Return 2/3 of previous block score if less than 7 PoS blocks found
+ // Return bnPoWTrust + 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 bnPoWTrust + (2 * (pprev->bnChainTrust - pprev->pprev->bnChainTrust) / 3);
+
+ bnTarget.SetCompact(pprev->nBits);
- return (pprev->bnChainTrust - pprev->pprev->bnChainTrust);
+ if (bnTarget <= 0)
+ return 0;
+
+ // Return bnPoWTrust + full trust score for previous block nBits
+ return bnPoWTrust + (CBigNum(1)<<256) / (bnTarget+1);
}
}
CInv inv(MSG_TX, tx.GetHash());
pfrom->AddInventoryKnown(inv);
+ // Truncate messages to the size of the tx in them
+ unsigned int nSize = ::GetSerializeSize(tx,SER_NETWORK, PROTOCOL_VERSION);
+ if (nSize < vMsg.size()){
+ vMsg.resize(nSize);
+ }
+
bool fMissingInputs = false;
if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs))
{
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
CReserveKey reservekey(pwallet);
unsigned int nExtraNonce = 0;
- while (fGenerateBitcoins || fProofOfStake)
+ while (fProofOfStake)
{
if (fShutdown)
return;
Sleep(1000);
if (fShutdown)
return;
- if ((!fGenerateBitcoins) && !fProofOfStake)
+ if (!fProofOfStake)
return;
}
//
// Create new block
//
- unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
CBlockIndex* pindexPrev = pindexBest;
auto_ptr<CBlock> pblock(CreateNewBlock(pwallet, 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);
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);
- }
- }
-}