diff --git a/src/clientversion.h b/src/clientversion.h index 42b43d1..4e237a1 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -8,7 +8,7 @@ // These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 1 #define CLIENT_VERSION_MINOR 0 -#define CLIENT_VERSION_REVISION 3 +#define CLIENT_VERSION_REVISION 4 #define CLIENT_VERSION_BUILD 0 // Set to true for release, false for prerelease or test build diff --git a/src/kernel.cpp b/src/kernel.cpp index 03e2a48..3206c66 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -7,17 +7,20 @@ #include "kernel.h" #include "txdb.h" +#include "main.h" using namespace std; // Get time weight -int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd) +int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd, int nHeight) { // Kernel hash weight starts from 0 at the min age // this change increases active coins participating the hash and helps // to secure the network when proof-of-stake difficulty is low - - return nIntervalEnd - nIntervalBeginning - nStakeMinAge; + + //return nIntervalEnd - nIntervalBeginning - nStakeMinAge; + return nIntervalEnd - nIntervalBeginning - GetStakeMinAge(nHeight); + } // Get the last stake modifier and its generation time from a given block @@ -204,7 +207,7 @@ static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifi { if (!pindex->pnext) { // reached best block; may happen if node is behind on block chain - if (fPrintProofOfStake || (pindex->GetBlockTime() + nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime())) + if (fPrintProofOfStake || (pindex->GetBlockTime() + GetStakeMinAge(nStakeModifierHeight) - nStakeModifierSelectionInterval > GetAdjustedTime())) return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s", pindex->GetBlockHash().ToString(), pindex->nHeight, hashBlockFrom.ToString()); else @@ -242,13 +245,13 @@ static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifi // quantities so as to generate blocks faster, degrading the system back into // a proof-of-work situation. // -static bool CheckStakeKernelHashV1(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake) +static bool CheckStakeKernelHashV1(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake, int nHeight) { if (nTimeTx < txPrev.nTime) // Transaction timestamp violation return error("CheckStakeKernelHash() : nTime violation"); unsigned int nTimeBlockFrom = blockFrom.GetBlockTime(); - if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement + if (nTimeBlockFrom + GetStakeMinAge(nHeight) > nTimeTx) // Min age requirement return error("CheckStakeKernelHash() : min age violation"); CBigNum bnTargetPerCoinDay; @@ -257,7 +260,7 @@ static bool CheckStakeKernelHashV1(unsigned int nBits, const CBlock& blockFrom, uint256 hashBlockFrom = blockFrom.GetHash(); - CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)txPrev.nTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60); + CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)txPrev.nTime, (int64_t)nTimeTx, nHeight) / COIN / (24 * 60 * 60); targetProofOfStake = (bnCoinDayWeight * bnTargetPerCoinDay).getuint256(); // Calculate hash @@ -324,12 +327,12 @@ static bool CheckStakeKernelHashV1(unsigned int nBits, const CBlock& blockFrom, // quantities so as to generate blocks faster, degrading the system back into // a proof-of-work situation. // -static bool CheckStakeKernelHashV2(CBlockIndex* pindexPrev, unsigned int nBits, unsigned int nTimeBlockFrom, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake) +static bool CheckStakeKernelHashV2(CBlockIndex* pindexPrev, unsigned int nBits, unsigned int nTimeBlockFrom, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake, int nHeight) { if (nTimeTx < txPrev.nTime) // Transaction timestamp violation return error("CheckStakeKernelHash() : nTime violation"); - if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement + if (nTimeBlockFrom + GetStakeMinAge(nHeight) > nTimeTx) // Min age requirement return error("CheckStakeKernelHash() : min age violation"); // Base target @@ -386,9 +389,9 @@ static bool CheckStakeKernelHashV2(CBlockIndex* pindexPrev, unsigned int nBits, bool CheckStakeKernelHash(CBlockIndex* pindexPrev, unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake) { if (IsProtocolV2(pindexPrev->nHeight+1)) - return CheckStakeKernelHashV2(pindexPrev, nBits, blockFrom.GetBlockTime(), txPrev, prevout, nTimeTx, hashProofOfStake, targetProofOfStake, fPrintProofOfStake); + return CheckStakeKernelHashV2(pindexPrev, nBits, blockFrom.GetBlockTime(), txPrev, prevout, nTimeTx, hashProofOfStake, targetProofOfStake, fPrintProofOfStake, pindexPrev->nHeight+1); else - return CheckStakeKernelHashV1(nBits, blockFrom, nTxPrevOffset, txPrev, prevout, nTimeTx, hashProofOfStake, targetProofOfStake, fPrintProofOfStake); + return CheckStakeKernelHashV1(nBits, blockFrom, nTxPrevOffset, txPrev, prevout, nTimeTx, hashProofOfStake, targetProofOfStake, fPrintProofOfStake, pindexPrev->nHeight+1); } // Check kernel hash target and coinstake signature @@ -446,7 +449,7 @@ bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, int64_t nTime, con if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) return false; - if (block.GetBlockTime() + nStakeMinAge > nTime) + if (block.GetBlockTime() + GetStakeMinAge(pindexPrev->nHeight+1) > nTime) return false; // only count coins meeting min age requirement if (pBlockTime) diff --git a/src/kernel.h b/src/kernel.h index 8418579..ac717f6 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -32,7 +32,7 @@ bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransaction& tx, unsigned bool CheckCoinStakeTimestamp(int nHeight, int64_t nTimeBlock, int64_t nTimeTx); // Get time weight using supplied timestamps -int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd); +int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd, int nHeight); // Wrapper around CheckStakeKernelHash() // Also checks existence of kernel input and min age diff --git a/src/main.cpp b/src/main.cpp index d304195..9687089 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,8 +43,31 @@ set > setStakeSeen; CBigNum bnProofOfStakeLimit(~uint256(0) >> 20); CBigNum bnProofOfStakeLimitV2(~uint256(0) >> 34); -unsigned int nStakeMinAge = 60 * 60; // 1 hours + +static unsigned int nStakeMinAgeV1 = 60 * 60; // 1 hours +static unsigned int nStakeMinAgeV2 = 8 * 60 * 60; // 8 hours after block 11,000 + unsigned int nModifierInterval = 10 * 60; // time to elapse before new modifier is computed +const int targetReadjustment_forkBlockHeight = 11000; //retargeting since 11,000 block + +bool IsProtocolMaturityV2(int nHeight) +{ + return(nHeight >= targetReadjustment_forkBlockHeight); +} + +unsigned int GetStakeMinAge(int nHeight) +{ + if(IsProtocolMaturityV2(nHeight)) + return nStakeMinAgeV2; + else + return nStakeMinAgeV1; +} + +int GetMinPeerProtoVersion(int nHeight) +{ + return(IsProtocolMaturityV2(nHeight)? NEW_PROTOCOL_VERSION : MIN_PEER_PROTO_VERSION); +} + int nCoinbaseMaturity = 15; //15 CBlockIndex* pindexGenesisBlock = NULL; @@ -1226,10 +1249,63 @@ const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfSta return pindex; } + + int nTargetSpacing = 60; //60s +unsigned int GetNextTargetRequiredV2(const CBlockIndex* pindexLast, bool fProofOfStake) +{ + CBigNum bnTargetLimit = fProofOfStake ? GetProofOfStakeLimit(pindexLast->nHeight) : Params().ProofOfWorkLimit(); + + 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(); + + if (nActualSpacing < 0) { + LogPrintf("Warning: spacing between blocks is negative. Resetting to target spacing.\n"); + nActualSpacing = nTargetSpacing; + } else if (nActualSpacing < 1 && pindexLast->nHeight >= targetReadjustment_forkBlockHeight) { + // For a smooth transition to the new readjustment algorithm set actual spacing close to TARGET_SPACING for 100 blocks. + nActualSpacing = nTargetSpacing - 10; + } else if (nActualSpacing < 1 && pindexLast->nHeight >= (targetReadjustment_forkBlockHeight + 100)) { + nActualSpacing = 1; + } + + // ppcoin: target change every block + // ppcoin: retarget with exponential moving toward target spacing + CBigNum bnNew; + bnNew.SetCompact(pindexPrev->nBits); + int64_t nInterval = 5; // Average over 5 blocks + + if (pindexLast->nHeight < targetReadjustment_forkBlockHeight) { + // Allow different difficulty in old blocks before the fork. + nInterval = 1; + } + + bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); + bnNew /= ((nInterval + 1) * nTargetSpacing); + + if (bnNew <= 0 || bnNew > bnTargetLimit) { + LogPrintf("Warning: new difficulty target out of range. Resetting to minimum difficulty.\n"); + bnNew = bnTargetLimit; + } + + return bnNew.GetCompact(); +} + unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) { + if(IsProtocolMaturityV2(pindexLast->nHeight+1)) + return GetNextTargetRequiredV2(pindexLast,fProofOfStake); + CBigNum bnTargetLimit = fProofOfStake ? GetProofOfStakeLimit(pindexLast->nHeight) : Params().ProofOfWorkLimit(); if (pindexLast == NULL) @@ -1260,6 +1336,7 @@ unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfS return bnNew.GetCompact(); } + bool CheckProofOfWork(uint256 hash, unsigned int nBits) { CBigNum bnTarget; @@ -2200,7 +2277,13 @@ bool CTransaction::GetCoinAge(CTxDB& txdb, uint64_t& 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() + nStakeMinAge > nTime) + + CBlockLocator locator(block.GetHash()); + int nHeight = locator.GetHeight(); + + assert(nHeight); + + if (block.GetBlockTime() + GetStakeMinAge(nHeight) > nTime) continue; // only count coins meeting min age requirement int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue; @@ -2873,7 +2956,7 @@ bool LoadBlockIndex(bool fAllowNew) if (TestNet()) { - nStakeMinAge = 1 * 60 * 60; // test net min age is 1 hour + nStakeMinAgeV1 = 1 * 60 * 60; // test net min age is 1 hour nCoinbaseMaturity = 10; // test maturity is 10 blocks } @@ -3332,7 +3415,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CAddress addrFrom; uint64_t nNonce = 1; vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; - if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) + if (pfrom->nVersion < GetMinPeerProtoVersion(nBestHeight)) { // disconnect from peers older than this proto version LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString(), pfrom->nVersion); diff --git a/src/main.h b/src/main.h index 7ba62c4..b1ea4b9 100644 --- a/src/main.h +++ b/src/main.h @@ -96,6 +96,9 @@ static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 inline bool IsProtocolV1RetargetingFixed(int nHeight) { return TestNet() || nHeight > 0; } inline bool IsProtocolV2(int nHeight) { return TestNet() || nHeight > 0; } +bool IsProtocolMaturityV2(int nHeight); +unsigned int GetStakeMinAge(int nHeight); + inline int64_t FutureDriftV1(int64_t nTime) { return nTime + 10 * 60; } inline int64_t FutureDriftV2(int64_t nTime) { return nTime + 10 * 60; } inline int64_t FutureDrift(int64_t nTime, int nHeight) { return IsProtocolV2(nHeight) ? FutureDriftV2(nTime) : FutureDriftV1(nTime); } diff --git a/src/net.cpp b/src/net.cpp index 730408b..7249a0f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -31,6 +31,7 @@ using namespace std; using namespace boost; static const int MAX_OUTBOUND_CONNECTIONS = 76; +int GetMinPeerProtoVersion(int nHeight); bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); @@ -445,8 +446,8 @@ void CNode::PushVersion() CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); CAddress addrMe = GetLocalAddress(&addr); RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), addr.ToString()); - PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, + LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", GetMinPeerProtoVersion(nBestHeight), nBestHeight, addrMe.ToString(), addrYou.ToString(), addr.ToString()); + PushMessage("version", GetMinPeerProtoVersion(nBestHeight), nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector()), nBestHeight); } diff --git a/src/version.h b/src/version.h index 3dc7862..21024d3 100644 --- a/src/version.h +++ b/src/version.h @@ -30,13 +30,14 @@ static const int DATABASE_VERSION = 71500; // network protocol versioning // -static const int PROTOCOL_VERSION = 69110; +static const int NEW_PROTOCOL_VERSION = 69120; //new version with modified maturity time and retargeting +static const int PROTOCOL_VERSION = 69110; //new version with modified maturity time and retargeting // intial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; // disconnect from peers older than this proto version -static const int MIN_PEER_PROTO_VERSION = 69100; +static const int MIN_PEER_PROTO_VERSION = 69110; static const int MIN_INSTANTX_PROTO_VERSION = 69100; diff --git a/src/wallet.cpp b/src/wallet.cpp index 6adc77a..fc7eaa5 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1541,7 +1541,7 @@ void CWallet::AvailableCoinsForStaking(vector& vCoins, unsigned int nSp const CWalletTx* pcoin = &(*it).second; // Filtering by tx timestamp instead of block timestamp may give false positives but never false negatives - if (pcoin->nTime + nStakeMinAge > nSpendTime) + if (pcoin->nTime + GetStakeMinAge(nBestHeight) > nSpendTime) continue; if (pcoin->GetBlocksToMaturity() > 0) @@ -3346,7 +3346,7 @@ uint64_t CWallet::GetStakeWeight() const if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) continue; - if (nCurrentTime - pcoin.first->nTime > nStakeMinAge) + if (nCurrentTime - pcoin.first->nTime > GetStakeMinAge(nBestHeight)) nWeight += pcoin.first->vout[pcoin.second].nValue; } @@ -3474,7 +3474,7 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int if (txNew.vout.size() == 2 && ((pcoin.first->vout[pcoin.second].scriptPubKey == scriptPubKeyKernel || pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey)) && pcoin.first->GetHash() != txNew.vin[0].prevout.hash) { - int64_t nTimeWeight = GetWeight((int64_t)pcoin.first->nTime, (int64_t)txNew.nTime); + int64_t nTimeWeight = GetWeight((int64_t)pcoin.first->nTime, (int64_t)txNew.nTime, nBestHeight); // Stop adding more inputs if already too many inputs if (txNew.vin.size() >= 100) @@ -3489,7 +3489,7 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int if (pcoin.first->vout[pcoin.second].nValue >= GetStakeCombineThreshold()) continue; // Do not add input that is still too young - if (nTimeWeight < nStakeMinAge) + if (nTimeWeight < GetStakeMinAge(nBestHeight)) continue; txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));