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
+uint256 nPoWBase = uint256("0x00000000ffff0000000000000000000000000000000000000000000000000000"); // difficulty-1 target
CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 16);
int nCoinbaseMaturity = 500;
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
-CBigNum bnBestChainTrust = 0;
-CBigNum bnBestInvalidTrust = 0;
+
+uint256 nBestChainTrust = 0;
+uint256 nBestInvalidTrust = 0;
+
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
}
// 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;
}
// 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, nSubsidyLimit = 10 * COIN;
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;
+ bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit;
+ }
+ 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;
+ bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit;
+ }
+
+ if (bnMidPart * bnTargetLimit > bnRewardPart * bnTarget)
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);
+
+ // Set reasonable reward limit for large inputs since 20 Oct 2013
+ //
+ // This will stimulate large holders to use smaller inputs, that's good for the network protection
+ if(fTestNet || STAKECURVE_SWITCH_TIME < nTime)
+ {
+ if (fDebug && GetBoolArg("-printcreation") && nSubsidyLimit < nSubsidy)
+ printf("GetProofOfStakeReward(): %s is greater than %s, coinstake reward will be truncated\n", FormatMoney(nSubsidy).c_str(), FormatMoney(nSubsidyLimit).c_str());
+
+ nSubsidy = min(nSubsidy, nSubsidyLimit);
+ }
+
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
void static InvalidChainFound(CBlockIndex* pindexNew)
{
- if (pindexNew->bnChainTrust > bnBestInvalidTrust)
+ if (pindexNew->nChainTrust > nBestInvalidTrust)
{
- bnBestInvalidTrust = pindexNew->bnChainTrust;
- CTxDB().WriteBestInvalidTrust(bnBestInvalidTrust);
+ nBestInvalidTrust = pindexNew->nChainTrust;
+ CTxDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
uiInterface.NotifyBlocksChanged();
}
- CBigNum bnBestInvalidBlockTrust = pindexNew->bnChainTrust - pindexNew->pprev->bnChainTrust;
- CBigNum bnBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->bnChainTrust - pindexBest->pprev->bnChainTrust) : pindexBest->bnChainTrust;
+ uint256 nBestInvalidBlockTrust = pindexNew->nChainTrust - pindexNew->pprev->nChainTrust;
+ uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
- printf("InvalidChainFound: invalid block=%s height=%d trust=%s blocktrust=%s date=%s\n",
+ printf("InvalidChainFound: invalid block=%s height=%d trust=%s blocktrust=%"PRI64d" date=%s\n",
pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight,
- pindexNew->bnChainTrust.ToString().c_str(), bnBestInvalidBlockTrust.ToString().c_str(),
+ CBigNum(pindexNew->nChainTrust).ToString().c_str(), nBestInvalidBlockTrust.Get64(),
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(),
+ printf("InvalidChainFound: current best=%s height=%d trust=%s blocktrust=%"PRI64d" date=%s\n",
+ hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
+ CBigNum(pindexBest->nChainTrust).ToString().c_str(),
+ nBestBlockTrust.Get64(),
DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
}
uint64 nCoinAge;
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, pindexBlock->nBits, nTime) - GetMinFee() + MIN_TX_FEE)
- return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str()));
+ int64 nCalculatedStakeReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee() + MIN_TX_FEE;
+
+ if (nStakeReward > nCalculatedStakeReward)
+ return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nStakeReward, nCalculatedStakeReward));
}
else
{
bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
{
- // Check it again in case a previous version let a bad block in
- if (!CheckBlock(!fJustCheck, !fJustCheck))
+ // Check it again in case a previous version let a bad block in, but skip BlockSig checking
+ if (!CheckBlock(!fJustCheck, !fJustCheck, false))
return false;
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// 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->bnChainTrust > pindexBest->bnChainTrust)
+ while (pindexIntermediate->pprev && pindexIntermediate->pprev->nChainTrust > pindexBest->nChainTrust)
{
vpindexSecondary.push_back(pindexIntermediate);
pindexIntermediate = pindexIntermediate->pprev;
pindexBest = pindexNew;
pblockindexFBBHLast = NULL;
nBestHeight = pindexBest->nHeight;
- bnBestChainTrust = pindexNew->bnChainTrust;
+ nBestChainTrust = pindexNew->nChainTrust;
nTimeBestReceived = GetTime();
nTransactionsUpdated++;
- CBigNum bnBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->bnChainTrust - pindexBest->pprev->bnChainTrust) : pindexBest->bnChainTrust;
+ uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
- 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(),
+ printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%"PRI64d" date=%s\n",
+ hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
+ CBigNum(nBestChainTrust).ToString().c_str(),
+ nBestBlockTrust.Get64(),
DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
// Check the version of the last 100 blocks to see if we need to upgrade:
}
// ppcoin: compute chain trust score
- pindexNew->bnChainTrust = (pindexNew->pprev ? pindexNew->pprev->bnChainTrust : 0) + pindexNew->GetBlockTrust();
+ pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();
// ppcoin: compute stake entropy bit for stake modifier
- if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nHeight)))
+ if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nTime)))
return error("AddToBlockIndex() : SetStakeEntropyBit() failed");
// ppcoin: record proof-of-stake hash value
return false;
// New best
- if (pindexNew->bnChainTrust > bnBestChainTrust)
+ if (pindexNew->nChainTrust > nBestChainTrust)
if (!SetBestChain(txdb, pindexNew))
return false;
-bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const
+bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) const
{
// These are checks that are independent of context
// that can be verified before saving an orphan block.
if (IsProofOfStake())
{
- // ppcoin: coinbase output should be empty if proof-of-stake block
+ // 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");
+ return DoS(100, 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())
// 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));
+
+ // NovaCoin: check proof-of-stake block signature
+ if (fCheckSig && !CheckBlockSignature(true))
+ return DoS(100, error("CheckBlock() : bad proof-of-stake block signature"));
}
else
{
// 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(GetProofOfWorkReward(nBits) - nFee).c_str()));
+ return DoS(50, error("CheckBlock() : coinbase reward exceeded (actual=%"PRI64d" vs calculated=%"PRI64d")",
+ vtx[0].GetValueOut(),
+ GetProofOfWorkReward(nBits) - nFee));
+
+ // Should we check proof-of-work block signature or not?
+ //
+ // * Always skip on TestNet
+ // * Perform checking for the first 9689 blocks
+ // * Perform checking since last checkpoint until 20 Sep 2013 (will be removed after)
+
+ if(!fTestNet && fCheckSig)
+ {
+ bool isAfterCheckpoint = (GetBlockTime() > Checkpoints::GetLastCheckpointTime());
+ bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME);
+ bool checkPoWSig = (isAfterCheckpoint && GetBlockTime() < CHAINCHECKS_SWITCH_TIME);
+
+ // NovaCoin: check proof-of-work block signature
+ if ((checkEntropySig || checkPoWSig) && !CheckBlockSignature(false))
+ return DoS(100, error("CheckBlock() : bad proof-of-work block signature"));
+ }
}
// Check transactions
if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree())
return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
- // 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;
}
}
}
- // Reject block.nVersion < 3 blocks since 95% threshold on mainNet and always on testNet:
- if (nVersion < 3 && ((!fTestNet && nHeight > 14060) || (fTestNet && nHeight > 0)))
- return error("CheckBlock() : rejected nVersion < 3 block");
-
// Enforce rule that the coinbase starts with serialized block height
CScript expect = CScript() << nHeight;
if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin()))
return true;
}
-CBigNum CBlockIndex::GetBlockTrust() const
+uint256 CBlockIndex::GetBlockTrust() const
{
CBigNum bnTarget;
+ bnTarget.SetCompact(nBits);
- // Old protocol
+ if (bnTarget <= 0)
+ return 0;
+
+ /* Old protocol, will be removed later */
if (!fTestNet && GetBlockTime() < CHAINCHECKS_SWITCH_TIME)
- {
- CBigNum bnTarget;
- bnTarget.SetCompact(nBits);
+ return (IsProofOfStake()? ((CBigNum(1)<<256) / (bnTarget+1)).getuint256() : 1);
- if (bnTarget <= 0)
- return 0;
- return (IsProofOfStake()? (CBigNum(1)<<256) / (bnTarget+1) : 1);
- }
+ /* New protocol */
+
+ // Calculate work amount for block
+ uint256 nPoWTrust = (CBigNum(nPoWBase) / (bnTarget+1)).getuint256();
- // New protocol
+ // Set nPowTrust to 1 if we are checking PoS block or PoW difficulty is too low
+ nPoWTrust = (IsProofOfStake() || nPoWTrust < 1) ? 1 : nPoWTrust;
+
+ // Return nPoWTrust for the first 12 blocks
if (pprev == NULL || pprev->nHeight < 12)
- return 1;
+ return nPoWTrust;
const CBlockIndex* currentIndex = pprev;
if(IsProofOfStake())
{
- bnTarget.SetCompact(nBits);
- if (bnTarget <= 0)
- return 0;
+ CBigNum bnNewTrust = (CBigNum(1)<<256) / (bnTarget+1);
+ // Return 1/3 of score if parent block is not the PoW block
if (!pprev->IsProofOfWork())
- return (CBigNum(1)<<256) / (3 * (bnTarget+1));
+ return (bnNewTrust / 3).getuint256();
int nPoWCount = 0;
// Return 1/3 of score if less than 3 PoW blocks found
if (nPoWCount < 3)
- return (CBigNum(1)<<256) / (3 * (bnTarget+1));
+ return (bnNewTrust / 3).getuint256();
- return (CBigNum(1)<<256) / (bnTarget+1);
+ return bnNewTrust.getuint256();
}
else
{
+ CBigNum bnLastBlockTrust = CBigNum(pprev->nChainTrust - pprev->pprev->nChainTrust);
+
+ // Return nPoWTrust + 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 nPoWTrust + (2 * bnLastBlockTrust / 3).getuint256();
int nPoSCount = 0;
currentIndex = currentIndex->pprev;
}
- // Return 2/3 of previous block score if less than 7 PoS blocks found
+ // Return nPoWTrust + 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 nPoWTrust + (2 * bnLastBlockTrust / 3).getuint256();
- return (pprev->bnChainTrust - pprev->pprev->bnChainTrust);
+ bnTarget.SetCompact(pprev->nBits);
+
+ if (bnTarget <= 0)
+ return 0;
+
+ CBigNum bnNewTrust = (CBigNum(1)<<256) / (bnTarget+1);
+
+ // Return nPoWTrust + full trust score for previous block nBits
+ return nPoWTrust + bnNewTrust.getuint256();
}
}
}
// ppcoin: check block signature
-bool CBlock::CheckBlockSignature() const
+bool CBlock::CheckBlockSignature(bool fProofOfStake) const
{
if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
return vchBlockSig.empty();
vector<valtype> vSolutions;
txnouttype whichType;
- if(IsProofOfStake())
+ if(fProofOfStake)
{
const CTxOut& txout = vtx[1].vout[1];
else if (strCommand == "getaddr")
{
+ // Don't return addresses older than nCutOff timestamp
+ int64 nCutOff = GetTime() - (nNodeLifespan * 24 * 60 * 60);
pfrom->vAddrToSend.clear();
vector<CAddress> vAddr = addrman.GetAddr();
BOOST_FOREACH(const CAddress &addr, vAddr)
- pfrom->PushAddress(addr);
+ if(addr.nTime > nCutOff)
+ pfrom->PushAddress(addr);
}