diff --git a/src/kernel.cpp b/src/kernel.cpp index 96a0b09..20dfdc9 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -22,7 +22,6 @@ static std::map mapStakeModifierCheckpoints = ; -// Get time weight int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd) { // Kernel hash weight starts from 0 at the 10-day min age @@ -31,11 +30,10 @@ int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd) // // Maximum TimeWeight is 30 days. - // Tranz We are going to want to change this to fix the max weight. Requires a hard fork - // New Code: - // return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64_t)nStakeMaxAge); - return min(nIntervalEnd - nIntervalBeginning, (int64_t)nStakeMaxAge) - nStakeMinAge; - + if ( nIntervalEnd > VERSION1_5_SWITCH_TIME ) + return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64_t)nStakeMaxAge); + else + return min(nIntervalEnd - nIntervalBeginning, (int64_t)nStakeMaxAge) - nStakeMinAge; } // Get the last stake modifier and its generation time from a given block @@ -282,19 +280,12 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned CBigNum bnTargetPerCoinDay; bnTargetPerCoinDay.SetCompact(nBits); int64_t nValueIn = txPrev.vout[prevout.n].nValue; + uint256 hashBlockFrom = blockFrom.GetHash(); - // v0.3 protocol kernel hash weight starts from 0 at the 10-day min age - // this change increases active coins participating the hash and helps - // to secure the network when proof-of-stake difficulty is low - // Tranz We are going to want to change this to fix the max weight. Requires a hard fork - // New Code: - // int64_t nTimeWeight = min((int64_t)nTimeTx - txPrev.nTime - nStakeMinAge, (int64_t)nStakeMaxAge); - int64_t nTimeWeight = min((int64_t)nTimeTx - txPrev.nTime, (int64_t)nStakeMaxAge) - nStakeMinAge; - CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60); + CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)txPrev.nTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60); targetProofOfStake = CBigNum(bnCoinDayWeight * bnTargetPerCoinDay).getuint256(); - // Calculate hash CDataStream ss(SER_GETHASH, 0); uint64_t nStakeModifier = 0; diff --git a/src/main.cpp b/src/main.cpp index 4c0e499..63a4e55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1084,6 +1084,18 @@ int64_t GetProofOfWorkReward() // miner's coin stake reward based on nBits and coin age spent (coin-days) int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly) +{ + int64_t nSubsidy = 0; + + if ( nTime > VERSION1_5_SWITCH_TIME ) + nSubsidy = GetProofOfStakeRewardV2(nCoinAge, nBits, nTime, bCoinYearOnly); + else + nSubsidy = GetProofOfStakeRewardV1(nCoinAge, nBits, nTime, bCoinYearOnly); + + return nSubsidy; +} + +int64_t GetProofOfStakeRewardV1(int64_t nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly) { int64_t nRewardCoinYear; @@ -1175,6 +1187,54 @@ int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, unsigned int return nSubsidy; } + +int64_t GetProofOfStakeRewardV2(int64_t nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly) +{ + CBigNum bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE_FIX2; // Base stake mint rate, 100% year interest + int64_t nRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE_FIX2; + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + CBigNum bnTargetLimit = bnProofOfStakeLimit; + bnTargetLimit.SetCompact(bnTargetLimit.GetCompact()); + int64_t nSubsidyLimit = 250 * COIN; + + // HoboNickels: 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 + // (bnRewardCoinYearLimit / nRewardCoinYear) ** 4 == bnProofOfStakeLimit / bnTarget + // + // Human readable form: + // + // nRewardCoinYear = 1 / (posdiff ^ 1/4) + + CBigNum bnLowerBound = 10 * CENT; // Lower interest bound is 10% per year + CBigNum bnUpperBound = bnRewardCoinYearLimit; + while (bnLowerBound + CENT <= bnUpperBound) + { + CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; + + LogPrint("creation", "GetProofOfStakeReward() : lower=%d upper=%d mid=%d\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); + + if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget) + bnUpperBound = bnMidValue; + else + bnLowerBound = bnMidValue; + } + + int64_t nRewardCoinYear = bnUpperBound.getuint64(); + nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, nRewardCoinYearLimit); + + if(bCoinYearOnly) + return nRewardCoinYear; + + int64_t nSubsidy = (nCoinAge * 33 * nRewardCoinYear) / (365 * 33 + 8); + + LogPrint("creation","GetProofOfStakeReward(): create=%s nCoinAge=%d nBits=%d, Amount Truncated %s\n", FormatMoney(nSubsidy), nCoinAge, nBits, nSubsidyLimit < nSubsidy ? FormatMoney(nSubsidy - nSubsidyLimit) : FormatMoney(0)); + + nSubsidy = min(nSubsidy, nSubsidyLimit); + + return nSubsidy; +} + static const int64_t nTargetTimespan = 0.16 * 24 * 60 * 60; // 4-hour static const int64_t nTargetSpacingWorkMax = 12 * nStakeTargetSpacing; // 2-hour @@ -1223,6 +1283,14 @@ const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfSta } unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) +{ + if (pindexLast->nHeight + 1 > VERSION1_5_SWITCH_BLOCK) + return GetNextTargetRequiredV2(pindexLast, fProofOfStake); + else + return GetNextTargetRequiredV1(pindexLast, fProofOfStake); +} + +unsigned int GetNextTargetRequiredV1(const CBlockIndex* pindexLast, bool fProofOfStake) { CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : bnProofOfStakeLimit; @@ -1252,10 +1320,6 @@ unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfS int64_t nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); - // Tranz saved for later. - // int nHeight = pindexPrev->nHeight+1; - // if (nHeight >= ???? & nActualSpacing < 0) nActualSpacing = 0; //Sanity Check on nActualSpacing, corrects negative block values - // ppcoin: target change every block // ppcoin: retarget with exponential moving toward target spacing CBigNum bnNew; @@ -1271,6 +1335,39 @@ unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfS return bnNew.GetCompact(); } +unsigned int GetNextTargetRequiredV2(const CBlockIndex* pindexLast, bool fProofOfStake) +{ + CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : bnProofOfStakeLimit; + + if (pindexLast == NULL) + return bnTargetLimit.GetCompact(); // genesis block + + const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake); + if (pindexPrev->pprev == NULL) + return bnTargetLimit.GetCompact(); // first block + const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake); + if (pindexPrevPrev->pprev == NULL) + return bnTargetLimit.GetCompact(); // second block + + int64_t nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); + int64_t nTargetSpacing = fProofOfStake? nStakeTargetSpacing : min(nTargetSpacingWorkMax, (int64_t) nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight)); + + if (nActualSpacing < 0) + nActualSpacing = nTargetSpacing; + + CBigNum bnNew; + bnNew.SetCompact(pindexPrev->nBits); + + int64_t nInterval = nTargetTimespan / nTargetSpacing; + bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); + bnNew /= ((nInterval + 1) * nTargetSpacing); + + if (bnNew > bnTargetLimit) + bnNew = bnTargetLimit; + + return bnNew.GetCompact(); +} + bool CheckProofOfWork(uint256 hash, unsigned int nBits) { CBigNum bnTarget; @@ -1287,6 +1384,7 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits) return true; } + // Return maximum amount of blocks that other nodes claim to have int GetNumBlocksOfPeers() { diff --git a/src/main.h b/src/main.h index 523a424..935d437 100644 --- a/src/main.h +++ b/src/main.h @@ -60,12 +60,14 @@ static const int64_t MAX_SPLIT_AMOUNT = 20 * COIN; static const int64_t MAX_COMBINE_AMOUNT = MAX_SPLIT_AMOUNT * 2; -/** Hard Fork Change Times */ +/** Hard Fork Change Times/Block */ static const unsigned int PROTOCOL_SWITCH_TIME = 1371686400; // 20 Jun 2013 00:00:00 static const unsigned int REWARD_SWITCH_TIME = 1369432800; // 25 May 2013 00:00:00 static const unsigned int POS_REWARD_SWITCH_TIME = 1378684800; // 9 SEP 2013 00:00:00 static const unsigned int POS_REWARD_FIX_TIME = 1383177600; // 31 OCT 2013 00:00:00 static const unsigned int POS_REWARD_FIX_TIME2 = 1383606000; // 04 Nov 2013 23:00:00 +static const unsigned int VERSION1_5_SWITCH_TIME = 1421489410; // Sat, 17 Jan 2015 10:10:10 GMT +static const unsigned int VERSION1_5_SWITCH_BLOCK = 1600000; // Block 1.6 million, approx same time inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } @@ -75,8 +77,21 @@ static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 static const uint256 hashGenesisBlockOfficial("0x000009ea5ef5019446b315e7e581fc2ea184315ed46c9ddeadc8aa9442deedc9"); static const uint256 hashGenesisBlockTestNet("0x0000f9e0292f278190e4d58cd1e1e9a32b7466c8092bd2371ffc80b06f8eca4a"); -inline int64_t PastDrift(int64_t nTime) { return nTime - 2 * 60 * 60; } // up to 2 hours from the past -inline int64_t FutureDrift(int64_t nTime) { return nTime + 2 * 60 * 60; } // up to 2 hours from the future +inline int64_t PastDrift(int64_t nTime) +{ + if (nTime > VERSION1_5_SWITCH_TIME) + return nTime - 5 * 60; // up to 5 minutes from the past + else + return nTime - 2 * 60 * 60; // up to 120 minutes from the past +} + +inline int64_t FutureDrift(int64_t nTime) +{ + if (nTime > VERSION1_5_SWITCH_TIME) + return nTime + 5 * 60; // up to 5 minutes from the future + else + return nTime + 2 * 60 * 60; // up to 120 minutes from the future +} extern CScript COINBASE_FLAGS; @@ -151,8 +166,13 @@ bool LoadExternalBlockFile(FILE* fileIn); void GenerateBitcoins(bool fGenerate, CWallet* pwallet); bool CheckProofOfWork(uint256 hash, unsigned int nBits); unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake); +unsigned int GetNextTargetRequiredV1(const CBlockIndex* pindexLast, bool fProofOfStake); +unsigned int GetNextTargetRequiredV2(const CBlockIndex* pindexLast, bool fProofOfStake); + int64_t GetProofOfWorkReward(); int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, unsigned int nTime ,bool bCoinYearOnly=false); +int64_t GetProofOfStakeRewardV1(int64_t nCoinAge, unsigned int nBits, unsigned int nTime ,bool bCoinYearOnly=false); +int64_t GetProofOfStakeRewardV2(int64_t nCoinAge, unsigned int nBits, unsigned int nTime ,bool bCoinYearOnly=false); unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime); unsigned int ComputeMinStake(unsigned int nBase, int64_t nTime, unsigned int nBlockTime); diff --git a/src/wallet.cpp b/src/wallet.cpp index f8054d7..ae370b1 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1568,14 +1568,13 @@ bool CWallet::CreateTransaction(const vector >& vecSend, bool CWallet::GetStakeWeightFromValue(const int64_t& nTime, const int64_t& nValue, uint64_t& nWeight) { - - // This is a negative value when there is no weight. But set it to zero // so the user is not confused. Used in reporting in Coin Control. // Descisions based on this function should be used with care. int64_t nTimeWeight = GetWeight(nTime, (int64_t)GetTime()); + if (nTimeWeight < 0 ) - nTimeWeight=0; + nTimeWeight=0; CBigNum bnCoinDayWeight = CBigNum(nValue) * nTimeWeight / COIN / (24 * 60 * 60); nWeight = bnCoinDayWeight.getuint64();