From 10bbfffb589a3dd32da631411d6fe6a45dd702a2 Mon Sep 17 00:00:00 2001 From: celbalrai <80897309+celbalrai@users.noreply.github.com> Date: Tue, 27 Apr 2021 07:17:42 +0200 Subject: [PATCH] Introduce block time protocol V2 (15 second windows) - Port and adapt PIVX commits on time protocol V2 (PR #1002) - Port PIVX commits on nTime offset warnings (PR #1138) - Adjust chain parameters - Add several copyright notices --- src/chain.cpp | 32 +++++++++++ src/chain.h | 17 ++++-- src/chainparams.cpp | 42 +++++++++++---- src/consensus/params.h | 10 +++- src/miner.cpp | 13 ++++- src/miner.h | 2 +- src/net.cpp | 26 +++++++++ src/net.h | 2 + src/net_processing.cpp | 7 +++ src/pos/kernel.cpp | 28 +++++----- src/pos/kernel.h | 14 +++-- src/pos/staker.cpp | 2 +- src/pos/staking-manager.cpp | 103 ++++++++++++++++++++---------------- src/pos/staking-manager.h | 6 +-- src/pow.cpp | 20 ++++--- src/timedata.cpp | 5 -- src/timedata.h | 1 + src/validation.cpp | 26 ++++++++- src/validation.h | 4 ++ 19 files changed, 262 insertions(+), 98 deletions(-) diff --git a/src/chain.cpp b/src/chain.cpp index 7be3f77417bab..59b08186635f4 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -124,6 +124,38 @@ unsigned int CBlockIndex::GetStakeEntropyBit() const return ((UintToArith256(GetBlockHash()).GetLow64()) & 1); } +int CBlockIndex::FutureBlockTimeDrift(const int nHeight, const Consensus::Params& params) const +{ + if (params.IsTimeProtocolV2(nHeight)) + // PoS (TimeV2): 14 seconds + return params.nTimeSlotLength - 1; + + // PoS (TimeV1): 3 minutes + // PoW: 2 hours + return (nHeight >= params.nPosStartHeight) ? MAX_FUTURE_BLOCK_TIME_POS : MAX_FUTURE_BLOCK_TIME_POW; +} + +int64_t CBlockIndex::MaxFutureBlockTime(int64_t nAdjustedTime, const Consensus::Params& params) const +{ + return nAdjustedTime + FutureBlockTimeDrift(nHeight+1, params); +} + +int64_t CBlockIndex::MinPastBlockTime(const Consensus::Params& params) const +{ + // Time Protocol v1: pindexPrev->MedianTimePast + 1 + if (!params.IsTimeProtocolV2(nHeight+1)) + return GetMedianTimePast(); + + // on the transition from Time Protocol v1 to v2 + // pindexPrev->nTime might be in the future (up to the allowed drift) + // so we allow the nBlockTimeProtocolV2 to be at most (180-14) seconds earlier than previous block + if (nHeight + 1 == params.nBlockTimeProtocolV2) + return GetBlockTime() - FutureBlockTimeDrift(nHeight, params) + FutureBlockTimeDrift(nHeight + 1, params); + + // Time Protocol v2: pindexPrev->nTime + return GetBlockTime(); +} + arith_uint256 GetBlockProof(const CBlockIndex& block) { arith_uint256 bnTarget; diff --git a/src/chain.h b/src/chain.h index 714f7b6d6df7f..b6a2207e6b8c6 100644 --- a/src/chain.h +++ b/src/chain.h @@ -1,5 +1,9 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2011-2013 The PPCoin developers +// Copyright (c) 2013-2014 The NovaCoin Developers +// Copyright (c) 2014-2018 The BlackCoin Developers +// Copyright (c) 2015-2019 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -18,15 +22,16 @@ * Maximum amount of time that a block timestamp is allowed to exceed the * current network-adjusted time before the block will be accepted. */ -static const int64_t MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60; +static const int64_t MAX_FUTURE_BLOCK_TIME_POW = 2 * 60 * 60; +static const int64_t MAX_FUTURE_BLOCK_TIME_POS = 3 * 60; /** * Timestamp window used as a grace period by code that compares external * timestamps (such as timestamps passed to RPCs, or wallet key creation times) * to block timestamps. This should be set at least as high as - * MAX_FUTURE_BLOCK_TIME. + * MAX_FUTURE_BLOCK_TIME_POW. */ -static const int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME; +static const int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME_POW; class CBlockFileInfo { @@ -362,6 +367,12 @@ class CBlockIndex return pbegin[(pend - pbegin)/2]; } + int FutureBlockTimeDrift(const int nHeight, const Consensus::Params& params) const; + + int64_t MaxFutureBlockTime(int64_t nAdjustedTime, const Consensus::Params& params) const; + + int64_t MinPastBlockTime(const Consensus::Params& params) const; + bool IsProofOfWork() const { return !(nFlags & BLOCK_PROOF_OF_STAKE); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 36f757efb9b16..a38bdeea487f7 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -360,7 +360,7 @@ class CMainParams : public CChainParams { consensus.nGovernanceMinQuorum = 10; consensus.nGovernanceFilterElements = 20000; consensus.nMasternodeMinimumConfirmations = 15; - consensus.V16DeploymentHeight = 1600000; + consensus.V16DeploymentHeight = std::numeric_limits::max(); consensus.BIP34Height = 1; consensus.BIP34Hash = uint256S("000001364c4ed20f1b240810b5aa91fee23ae9b64b6e746b594b611cf6d8c87b"); consensus.BIP65Height = consensus.V16DeploymentHeight; @@ -379,14 +379,19 @@ class CMainParams : public CChainParams { // Wagerr specific parameters // Proof of Stake parameters consensus.nPosStartHeight = 201; - consensus.nPivxProtocolV2StartHeight = std::numeric_limits::max(); + consensus.nBlockTimeProtocolV2 = consensus.V16DeploymentHeight; consensus.posLimit = uint256S("000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 24 + consensus.posLimit_V2 = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20 + consensus.nTimeSlotLength = 15; consensus.nPosTargetSpacing = 1 * 60; // 1 minute + consensus.nPosTargetTimespan = 40 * 60; // 40 minutes + consensus.nPosTargetTimespan_V2 = 2 * consensus.nTimeSlotLength * 60; // 30 minutes consensus.nStakeMinDepth = 600; + consensus.nStakeMinAge = 60 * 60; // 1 hour consensus.nBlockStakeModifierV1A = 1000; - consensus.nBlockStakeModifierV2 = std::numeric_limits::max(); + consensus.nBlockStakeModifierV2 = consensus.V16DeploymentHeight; // ATP parameters - consensus.ATPStartHeight = std::numeric_limits::max(); + consensus.ATPStartHeight = consensus.V16DeploymentHeight; consensus.WagerrAddrPrefix = "wagerr"; consensus.strTokenManagementKey = "sYG1qGUtbTdNRYtFsKvnY3GvuauF3eVwhT"; consensus.nOpGroupNewRequiredConfirmations = 1; @@ -548,7 +553,7 @@ class CTestNetParams : public CChainParams { consensus.nGovernanceMinQuorum = 1; consensus.nGovernanceFilterElements = 500; consensus.nMasternodeMinimumConfirmations = 1; - consensus.V16DeploymentHeight = 1600000; + consensus.V16DeploymentHeight = std::numeric_limits::max(); consensus.BIP34Height = 1; consensus.BIP34Hash = uint256S("0000065432f43b3efb23bd0f63fe33d00d02a5f36233fe1b982c08274d58ef12"); consensus.BIP65Height = consensus.V16DeploymentHeight; @@ -567,10 +572,15 @@ class CTestNetParams : public CChainParams { // Wagerr specific parameters // Proof of Stake parameters consensus.nPosStartHeight = 201; - consensus.nPivxProtocolV2StartHeight = std::numeric_limits::max(); + consensus.nBlockTimeProtocolV2 = consensus.V16DeploymentHeight; consensus.posLimit = uint256S("000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 24 + consensus.posLimit_V2 = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20 + consensus.nTimeSlotLength = 15; consensus.nPosTargetSpacing = 1 * 60; // 1 minute + consensus.nPosTargetTimespan = 40 * 60; // 40 minutes + consensus.nPosTargetTimespan_V2 = 2 * consensus.nTimeSlotLength * 60; // 30 minutes consensus.nStakeMinDepth = 100; + consensus.nStakeMinAge = 60 * 60; // 1 hour consensus.nBlockStakeModifierV1A = 51197; consensus.nBlockStakeModifierV2 = std::numeric_limits::max(); // ATP parameters @@ -739,12 +749,17 @@ class CDevNetParams : public CChainParams { // Wagerr specific parameters // Proof of Stake parameters consensus.nPosStartHeight = 201; - consensus.nPivxProtocolV2StartHeight = 2000; + consensus.nBlockTimeProtocolV2 = consensus.V16DeploymentHeight; consensus.posLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1 + consensus.posLimit_V2 = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1 + consensus.nTimeSlotLength = 15; consensus.nPosTargetSpacing = 1 * 60; // 1 minute + consensus.nPosTargetTimespan = 40 * 60; // 40 minutes + consensus.nPosTargetTimespan_V2 = 2 * consensus.nTimeSlotLength * 60; // 30 minutes consensus.nStakeMinDepth = 100; + consensus.nStakeMinAge = 60 * 60; // 1 hour consensus.nBlockStakeModifierV1A = 1000; - consensus.nBlockStakeModifierV2 = consensus.nPivxProtocolV2StartHeight; + consensus.nBlockStakeModifierV2 = consensus.V16DeploymentHeight; // ATP parameters consensus.ATPStartHeight = std::numeric_limits::max(); consensus.WagerrAddrPrefix = "wagerrtest"; @@ -889,7 +904,7 @@ class CRegTestParams : public CChainParams { consensus.nGovernanceMinQuorum = 1; consensus.nGovernanceFilterElements = 100; consensus.nMasternodeMinimumConfirmations = 1; - consensus.V16DeploymentHeight = 1600000; + consensus.V16DeploymentHeight = 300; consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests) consensus.BIP34Hash = uint256(); consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests) @@ -908,12 +923,17 @@ class CRegTestParams : public CChainParams { // Wagerr specific parameters // Proof of Stake parameters consensus.nPosStartHeight = 201; - consensus.nPivxProtocolV2StartHeight = 2000; + consensus.nBlockTimeProtocolV2 = consensus.V16DeploymentHeight; consensus.posLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1 + consensus.posLimit_V2 = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1 + consensus.nTimeSlotLength = 15; consensus.nPosTargetSpacing = 1 * 60; // 1 minute + consensus.nPosTargetTimespan = 40 * 60; // 40 minutes + consensus.nPosTargetTimespan_V2 = 2 * consensus.nTimeSlotLength * 60; // 30 minutes consensus.nStakeMinDepth = 100; + consensus.nStakeMinAge = 0; consensus.nBlockStakeModifierV1A = 1000; - consensus.nBlockStakeModifierV2 = consensus.nPivxProtocolV2StartHeight; + consensus.nBlockStakeModifierV2 = consensus.V16DeploymentHeight; // ATP parameters consensus.ATPStartHeight = std::numeric_limits::max(); consensus.WagerrAddrPrefix = "wagerrreg"; diff --git a/src/consensus/params.h b/src/consensus/params.h index e935a447d2ed5..83fe389ac31f6 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -190,13 +190,21 @@ struct Params { /** Proof of stake parameters */ int64_t nPosStartHeight; - int64_t nPivxProtocolV2StartHeight; uint256 posLimit; + uint256 posLimit_V2; int64_t nPosTargetSpacing; + int64_t nPosTargetTimespan; + int64_t nPosTargetTimespan_V2; int32_t nStakeMinDepth; + int32_t nStakeMinAge; int64_t nBlockStakeModifierV1A; int64_t nBlockStakeModifierV2; + /** Time Protocol V2 **/ + int nBlockTimeProtocolV2; + bool IsTimeProtocolV2(const int nHeight) const { return nHeight >= nBlockTimeProtocolV2; } + int nTimeSlotLength; + /** ATP parameters */ int64_t ATPStartHeight; std::string WagerrAddrPrefix; diff --git a/src/miner.cpp b/src/miner.cpp index dd8ddd42f79ae..010e7ec6dfb89 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -1,5 +1,9 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2011-2013 The PPCoin developers +// Copyright (c) 2013-2014 The NovaCoin Developers +// Copyright (c) 2014-2018 The BlackCoin Developers +// Copyright (c) 2015-2019 The PIVX developers // Copyright (c) 2014-2021 The Dash Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -61,7 +65,12 @@ uint64_t nLastBlockSize = 0; int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { int64_t nOldTime = pblock->nTime; - int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + int64_t nNewTime; + if (consensusParams.IsTimeProtocolV2(pindexPrev->nHeight + 1)) { + nNewTime = GetTimeSlot(GetAdjustedTime()); + } else { + nNewTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + } if (nOldTime < nNewTime) pblock->nTime = nNewTime; @@ -139,7 +148,7 @@ bool BlockAssembler::SplitCoinstakeVouts(std::shared_ptr co } std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, - std::shared_ptr pCoinstakeTx, std::shared_ptr coinstakeInput, unsigned int nTxNewTime) + std::shared_ptr pCoinstakeTx, std::shared_ptr coinstakeInput, uint64_t nTxNewTime) { CBasicKeyStore tempKeystore; #ifdef ENABLE_WALLET diff --git a/src/miner.h b/src/miner.h index dcd5c3e2fe468..3e56f157b39bf 100644 --- a/src/miner.h +++ b/src/miner.h @@ -162,7 +162,7 @@ class BlockAssembler /** Construct a new block template with coinbase to scriptPubKeyIn */ std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn, - std::shared_ptr pCoinstakeTx = nullptr, std::shared_ptr coinstakeInput = nullptr, unsigned int nTxNewTime = 0); + std::shared_ptr pCoinstakeTx = nullptr, std::shared_ptr coinstakeInput = nullptr, uint64_t nTxNewTime = 0); private: // utility functions diff --git a/src/net.cpp b/src/net.cpp index f3281bb15d95c..250e9f59e1676 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2338,6 +2338,32 @@ int CConnman::GetExtraOutboundCount() return std::max(nOutbound - nMaxOutbound, 0); } +void CConnman::CheckOffsetDisconnectedPeers(const CNetAddr& ip) +{ + int nConnections = 0; + { + LOCK(cs_vNodes); + for (CNode* pnode : vNodes) { + if (pnode->fSuccessfullyConnected) + nConnections++; + if (nConnections == 2) + return; + } + } + + // Not enough connections. Insert peer. + static std::set setOffsetDisconnectedPeers; + setOffsetDisconnectedPeers.insert(ip); + if (setOffsetDisconnectedPeers.size() >= 16) { + // clear the set + setOffsetDisconnectedPeers.clear(); + // Trigger the warning + std::string strMessage = _("Warning: Peers are being disconnected due time differences. Please check that your computer's date and time are correct! If your clock is wrong Wagerr Core will not work properly."); + LogPrintf("*** %s\n", strMessage); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); + } +} + void CConnman::ThreadOpenConnections(const std::vector connect) { // Connect to specific addresses diff --git a/src/net.h b/src/net.h index 68f3e9572380e..e33b03c793827 100644 --- a/src/net.h +++ b/src/net.h @@ -414,6 +414,8 @@ friend class CNode; // not yet fully disconnected. int GetExtraOutboundCount(); + void CheckOffsetDisconnectedPeers(const CNetAddr& ip); + bool AddNode(const std::string& node); bool RemoveAddedNode(const std::string& node); std::vector GetAddedNodeInfo(); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 994ba944bfd1a..3fa89125952ee 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2319,6 +2319,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr int64_t nTimeOffset = nTime - GetTime(); pfrom->nTimeOffset = nTimeOffset; + const int nTimeSlotLength = Params().GetConsensus().nTimeSlotLength; + if (abs64(nTimeOffset) > 2 * nTimeSlotLength) { + LogPrintf("timeOffset (%d seconds) too large. Disconnecting node %s\n", + nTimeOffset, pfrom->addr.ToString().c_str()); + pfrom->fDisconnect = true; + connman->CheckOffsetDisconnectedPeers(pfrom->addr); + } AddTimeData(pfrom->addr, nTimeOffset); // Feeler connections exist only to verify if address is online. diff --git a/src/pos/kernel.cpp b/src/pos/kernel.cpp index 263815dbde5c8..4b23221988722 100644 --- a/src/pos/kernel.cpp +++ b/src/pos/kernel.cpp @@ -1,5 +1,7 @@ -// Copyright (c) 2012-2013 The PPCoin developers -// Copyright (c) 2015-2018 The PIVX developers +// Copyright (c) 2011-2013 The PPCoin developers +// Copyright (c) 2013-2014 The NovaCoin Developers +// Copyright (c) 2014-2018 The BlackCoin Developers +// Copyright (c) 2015-2019 The PIVX developers // Copyright (c) 2018-2019 The Ion developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -229,7 +231,7 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeMod // The stake modifier used to hash for a stake kernel is chosen as the stake // modifier about a selection interval later than the coin generating the kernel -bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake) +bool GetKernelStakeModifier(const uint256& hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake) { nStakeModifier = 0; if (!mapBlockIndex.count(hashBlockFrom)) @@ -316,7 +318,7 @@ bool HasStakeMinAgeOrDepth(const int contextHeight, const uint32_t contextTime, { // before stake modifier V2, the age required was 60 * 60 (1 hour) / not required on regtest if (contextHeight < Params().GetConsensus().nBlockStakeModifierV2) - return (Params().NetworkIDString() == CBaseChainParams::REGTEST || (utxoFromBlockTime + 3600 <= contextTime)); + return (Params().NetworkIDString() == CBaseChainParams::REGTEST || (utxoFromBlockTime + Params().GetConsensus().nStakeMinAge <= contextTime)); // after stake modifier V2, we require the utxo to be nStakeMinDepth deep in the chain return (contextHeight - utxoFromBlockHeight >= Params().GetConsensus().nStakeMinDepth); @@ -349,7 +351,7 @@ bool ContextualCheckZerocoinStake(int nPreviousBlockHeight, CStakeInput* stake) } // Check kernel hash target and coinstake signature -bool initStakeInput(const CBlock block, std::unique_ptr& ionStake, std::unique_ptr& zStake, int nPreviousBlockHeight) { +bool initStakeInput(const CBlock& block, std::unique_ptr& ionStake, std::unique_ptr& zStake, int nPreviousBlockHeight) { const CTransaction tx = *block.vtx[1]; if (!tx.IsCoinStake()) return error("%s : called on non-coinstake %s", __func__, tx.GetHash().GetHex()); @@ -390,7 +392,7 @@ bool initStakeInput(const CBlock block, std::unique_ptr& ionStake, std:: } // Check kernel hash target and coinstake signature -bool CheckProofOfStake(const CBlock block, uint256& hashProofOfStake, const CBlockIndex* pindex) +bool CheckProofOfStake(const CBlock& block, uint256& hashProofOfStake, const CBlockIndex* pindex) { std::unique_ptr ionStake; std::unique_ptr zStake; @@ -432,13 +434,6 @@ bool CheckProofOfStake(const CBlock block, uint256& hashProofOfStake, const CBlo return true; } -// Check whether the coinstake timestamp meets protocol -bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx) -{ - // v0.3 protocol - return (nTimeBlock == nTimeTx); -} - // Get stake modifier checksum unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex) { @@ -465,6 +460,13 @@ bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierCheck return true; } +// Timestamp for time protocol V2: slot duration 15 seconds +int64_t GetTimeSlot(const int64_t nTime) +{ + const int slotLen = Params().GetConsensus().nTimeSlotLength; + return (nTime / slotLen) * slotLen; +} + bool SetPOSParameters(const CBlock& block, CValidationState& state, CBlockIndex* pindexNew) { AssertLockHeld(cs_main); diff --git a/src/pos/kernel.h b/src/pos/kernel.h index a573f4c7ffa15..32a5ee7442285 100644 --- a/src/pos/kernel.h +++ b/src/pos/kernel.h @@ -1,5 +1,7 @@ -// Copyright (c) 2012-2013 The PPCoin developers -// Copyright (c) 2015-2018 The PIVX developers +// Copyright (c) 2011-2013 The PPCoin developers +// Copyright (c) 2013-2014 The NovaCoin Developers +// Copyright (c) 2014-2018 The BlackCoin Developers +// Copyright (c) 2015-2019 The PIVX developers // Copyright (c) 2018-2019 The Ion developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -18,16 +20,16 @@ static const unsigned int MODIFIER_INTERVAL = 60; static const int MODIFIER_INTERVAL_RATIO = 3; // Compute the hash modifier for proof-of-stake -bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake); +bool GetKernelStakeModifier(const uint256& hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake); bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier); bool ComputeStakeModifierV2(CBlockIndex* pindex, const uint256& kernel); // Initialize the stake input object -bool initStakeInput(const CBlock block, std::unique_ptr& stake, int nPreviousBlockHeight); +bool initStakeInput(const CBlock& block, std::unique_ptr& stake, int nPreviousBlockHeight); // Check kernel hash target and coinstake signature // Sets hashProofOfStake on success return -bool CheckProofOfStake(const CBlock block, uint256& hashProofOfStake, const CBlockIndex* pindex); +bool CheckProofOfStake(const CBlock& block, uint256& hashProofOfStake, const CBlockIndex* pindex); bool CheckStakeKernelHash(const CBlockIndex* pindexPrev, const unsigned int nBits, CStakeInput* stake, const unsigned int nTimeTx, uint256& hashProofOfStake, const bool fVerify = false); // Returns the proof of stake hash bool GetHashProofOfStake(const CBlockIndex* pindexPrev, CStakeInput* stake, const unsigned int nTimeTx, const bool fVerify, uint256& hashProofOfStakeRet); @@ -45,6 +47,8 @@ bool GetKernelStakeModifierPreDGW(uint256 hashBlockFrom, uint64_t& nStakeModifie bool ContextualCheckZerocoinStake(int nPreviousBlockHeight, CStakeInput* stake); +int64_t GetTimeSlot(const int64_t nTime); + bool SetPOSParameters(const CBlock& block, CValidationState& state,CBlockIndex* pindexNew); #endif // BITCOIN_KERNEL_H diff --git a/src/pos/staker.cpp b/src/pos/staker.cpp index 9ebe14277d340..25a4fb196180d 100644 --- a/src/pos/staker.cpp +++ b/src/pos/staker.cpp @@ -52,7 +52,7 @@ UniValue generateHybridBlocks(std::shared_ptr coinbaseKey, int nGen // If nHeight > POS start, wallet should be enabled. std::unique_ptr pblocktemplate = nullptr; - unsigned int nCoinStakeTime; + int64_t nCoinStakeTime; if (fPosPhase) { std::shared_ptr coinstakeTxPtr = std::shared_ptr(new CMutableTransaction); std::shared_ptr coinstakeInputPtr = std::shared_ptr(new CStake); diff --git a/src/pos/staking-manager.cpp b/src/pos/staking-manager.cpp index dd75d30898f40..386cb66921949 100644 --- a/src/pos/staking-manager.cpp +++ b/src/pos/staking-manager.cpp @@ -87,9 +87,10 @@ bool CStakingManager::SelectStakeCoins(std::list >& return true; } -bool CStakingManager::Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInput, unsigned int nBits, unsigned int& nTimeTx, uint256& hashProofOfStake) +bool CStakingManager::Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInput, unsigned int nBits, int64_t& nTimeTx, uint256& hashProofOfStake) { - int prevHeight = pindexPrev->nHeight; + const int prevHeight = pindexPrev->nHeight; + const int nHeight = pindexPrev->nHeight + 1; // get stake input pindex CBlockIndex* pindexFrom = stakeInput->GetIndexFrom(); @@ -98,36 +99,51 @@ bool CStakingManager::Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInp const uint32_t nTimeBlockFrom = pindexFrom->nTime; const int nHeightBlockFrom = pindexFrom->nHeight; - // check for maturity (min age/depth) requirements - if (!HasStakeMinAgeOrDepth(prevHeight + 1, nTimeTx, nHeightBlockFrom, nTimeBlockFrom)) - return error("%s : min age violation - height=%d - nTimeTx=%d, nTimeBlockFrom=%d, nHeightBlockFrom=%d", - __func__, prevHeight + 1, nTimeTx, nTimeBlockFrom, nHeightBlockFrom); - - // iterate the hashing bool fSuccess = false; - const unsigned int nHashDrift = 60; - const unsigned int nFutureTimeDriftPoS = 180; - unsigned int nTryTime = nTimeTx - 1; - // iterate from nTimeTx up to nTimeTx + nHashDrift - // but not after the max allowed future blocktime drift (3 minutes for PoS) - const unsigned int maxTime = std::min(nTimeTx + nHashDrift, (uint32_t)GetAdjustedTime() + nFutureTimeDriftPoS); - - while (nTryTime < maxTime) - { - //new block came in, move on - if (chainActive.Height() != prevHeight) - break; - ++nTryTime; + const Consensus::Params& params = Params().GetConsensus(); + if (params.IsTimeProtocolV2(nHeight)) { + if (nHeight < nHeightBlockFrom + params.nStakeMinDepth) + return error("%s : min depth violation, nHeight=%d, nHeightBlockFrom=%d", __func__, nHeight, nHeightBlockFrom); - // if stake hash does not meet the target then continue to next iteration - if (!CheckStakeKernelHash(pindexPrev, nBits, stakeInput, nTryTime, hashProofOfStake)) - continue; + nTimeTx = GetTimeSlot(GetAdjustedTime()); + // double check that we are not on the same slot as prev block + if (nTimeTx <= pindexPrev->nTime && Params().NetworkIDString() != CBaseChainParams::REGTEST) + return false; + + // check stake kernel + fSuccess = CheckStakeKernelHash(pindexPrev, nBits, stakeInput, nTimeTx, hashProofOfStake); + } else { + // iterate from maxTime down to pindexPrev->nTime (or min time due to maturity, 60 min after blockFrom) + const unsigned int prevBlockTime = pindexPrev->nTime; + const unsigned int maxTime = pindexPrev->MaxFutureBlockTime(GetAdjustedTime(), params); + unsigned int minTime = std::max(prevBlockTime, nTimeBlockFrom + 3600); + if (Params().NetworkIDString() == CBaseChainParams::REGTEST) + minTime = prevBlockTime; + unsigned int nTryTime = maxTime; + + if (maxTime <= minTime) { + // too early to stake + return false; + } + + while (nTryTime > minTime) + { + //new block came in, move on + if (chainActive.Height() != prevHeight) + break; + + --nTryTime; - // if we made it this far, then we have successfully found a valid kernel hash - fSuccess = true; - nTimeTx = nTryTime; - break; + // if stake hash does not meet the target then continue to next iteration + if (!CheckStakeKernelHash(pindexPrev, nBits, stakeInput, nTryTime, hashProofOfStake)) + continue; + + // if we made it this far, then we have successfully found a valid kernel hash + fSuccess = true; + nTimeTx = nTryTime; + break; + } } mapHashedBlocks.clear(); @@ -135,7 +151,7 @@ bool CStakingManager::Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInp return fSuccess; } -bool CStakingManager::CreateCoinStake(const CBlockIndex* pindexPrev, std::shared_ptr& coinstakeTx, std::shared_ptr& coinstakeInput, unsigned int& nTxNewTime) { +bool CStakingManager::CreateCoinStake(const CBlockIndex* pindexPrev, std::shared_ptr& coinstakeTx, std::shared_ptr& coinstakeInput, int64_t& nTxNewTime) { if (pwallet == nullptr || pindexPrev == nullptr) return false; @@ -160,9 +176,9 @@ bool CStakingManager::CreateCoinStake(const CBlockIndex* pindexPrev, std::shared return false; } - if (GetAdjustedTime() - chainActive.Tip()->GetBlockTime() < 60) { + if (GetAdjustedTime() - pindexPrev->GetBlockTime() < 60) { if (Params().NetworkIDString() == CBaseChainParams::REGTEST) { -// MilliSleep(1000); + MilliSleep(100); } } @@ -170,13 +186,6 @@ bool CStakingManager::CreateCoinStake(const CBlockIndex* pindexPrev, std::shared bool fKernelFound = false; int nAttempts = 0; - // Block time. - nTxNewTime = GetAdjustedTime(); - // If the block time is in the future, then starts there. - if (pindexPrev->nTime > nTxNewTime) { - nTxNewTime = pindexPrev->nTime; - } - for (std::unique_ptr& stakeInput : listInputs) { // Make sure the wallet is unlocked and shutdown hasn't been requested if (pwallet->IsLocked(true) || ShutdownRequested()) @@ -185,7 +194,6 @@ bool CStakingManager::CreateCoinStake(const CBlockIndex* pindexPrev, std::shared boost::this_thread::interruption_point(); CBlockHeader dummyBlockHeader; - dummyBlockHeader.nTime = nTxNewTime; unsigned int stakeNBits = GetNextWorkRequired(pindexPrev, &dummyBlockHeader, Params().GetConsensus()); uint256 hashProofOfStake = uint256(); nAttempts++; @@ -272,12 +280,17 @@ void CStakingManager::DoMaintenance(CConnman& connman) return; } + const bool fTimeV2 = Params().GetConsensus().IsTimeProtocolV2(chainActive.Height()+1); //search our map of hashed blocks, see if bestblock has been hashed yet - if (mapHashedBlocks.count(chainActive.Tip()->nHeight) && !fLastLoopOrphan) { - // wait max 5 seconds if recently hashed - int nTimePast = GetTime() - mapHashedBlocks[chainActive.Tip()->nHeight]; - if (nTimePast < nHashInterval && nTimePast >= 0) { - MilliSleep(std::min(nHashInterval - nTimePast, (unsigned int)5) * 1000); + const int chainHeight = chainActive.Height(); + if (mapHashedBlocks.count(chainHeight) && !fLastLoopOrphan) + { + int64_t nTime = GetAdjustedTime(); + int64_t tipHashTime = mapHashedBlocks[chainHeight]; + if ( (!fTimeV2 && nTime < tipHashTime + 22) || + (fTimeV2 && GetTimeSlot(nTime) <= tipHashTime) ) + { + MilliSleep(std::min(nHashInterval - (nTime - tipHashTime), (int64_t)5) * 1000); return; } } @@ -304,7 +317,7 @@ void CStakingManager::DoMaintenance(CConnman& connman) std::shared_ptr coinstakeTxPtr = std::shared_ptr(new CMutableTransaction); std::shared_ptr coinstakeInputPtr = nullptr; std::unique_ptr pblocktemplate = nullptr; - unsigned int nCoinStakeTime; + int64_t nCoinStakeTime; if (CreateCoinStake(chainActive.Tip(), coinstakeTxPtr, coinstakeInputPtr, nCoinStakeTime)) { // Coinstake found. Extract signing key from coinstake try { diff --git a/src/pos/staking-manager.h b/src/pos/staking-manager.h index 8b50b1006d5d5..fb172f386e6be 100644 --- a/src/pos/staking-manager.h +++ b/src/pos/staking-manager.h @@ -38,7 +38,7 @@ class CStakingManager int64_t nLastCoinStakeSearchInterval; int64_t nLastCoinStakeSearchTime; unsigned int nExtraNonce; - const unsigned int nHashInterval; + const int64_t nHashInterval; public: CStakingManager(std::shared_ptr pwalletIn = nullptr); @@ -49,8 +49,8 @@ class CStakingManager bool MintableCoins(); bool SelectStakeCoins(std::list >& listInputs, CAmount nTargetAmount, int blockHeight); - bool CreateCoinStake(const CBlockIndex* pindexPrev, std::shared_ptr& coinstakeTx, std::shared_ptr& coinstakeInput, unsigned int& nTxNewTime); - bool Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInput, unsigned int nBits, unsigned int& nTimeTx, uint256& hashProofOfStake); + bool CreateCoinStake(const CBlockIndex* pindexPrev, std::shared_ptr& coinstakeTx, std::shared_ptr& coinstakeInput, int64_t& nTxNewTime); + bool Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInput, unsigned int nBits, int64_t& nTimeTx, uint256& hashProofOfStake); bool IsStaking(); void UpdatedBlockTip(const CBlockIndex* pindex); diff --git a/src/pow.cpp b/src/pow.cpp index 65960f6a7939f..6bbf6e0be371b 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2015-2017 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -33,11 +34,12 @@ unsigned int static GetNextWorkRequiredOrig(const CBlockIndex* pindexLast, const return UintToArith256(params.powLimit).GetCompact(); } - arith_uint256 bnTargetLimit = fProofOfStake ? UintToArith256(params.posLimit) : UintToArith256(params.powLimit); // Off-by-one if (pindexLast->nHeight >= params.nPosStartHeight) { - int64_t nTargetSpacing = 60; - int64_t nTargetTimespan = 60 * 40; + const bool fTimeV2 = params.IsTimeProtocolV2(pindexLast->nHeight+1); + const arith_uint256 bnTargetLimit = fTimeV2 ? UintToArith256(params.posLimit_V2) : UintToArith256(params.posLimit); + const int64_t nTargetSpacing = params.nPosTargetSpacing; + const int64_t nTargetTimespan = fTimeV2 ? params.nPosTargetTimespan_V2 : params.nPosTargetTimespan; int64_t nActualSpacing = 0; if (pindexLast->nHeight != 0) @@ -45,12 +47,18 @@ unsigned int static GetNextWorkRequiredOrig(const CBlockIndex* pindexLast, const if (nActualSpacing < 0) nActualSpacing = 1; + if (fTimeV2 && nActualSpacing > nTargetSpacing*10) + nActualSpacing = nTargetSpacing*10; // ppcoin: target change every block // ppcoin: retarget with exponential moving toward target spacing arith_uint256 bnNew; bnNew.SetCompact(pindexLast->nBits); + // on first block with V2 time protocol, reduce the difficulty by a factor 16 + if (pindexLast->nHeight+1 == params.nBlockTimeProtocolV2) + bnNew <<= 4; + int64_t nInterval = nTargetTimespan / nTargetSpacing; bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); bnNew /= ((nInterval + 1) * nTargetSpacing); @@ -61,6 +69,9 @@ unsigned int static GetNextWorkRequiredOrig(const CBlockIndex* pindexLast, const return bnNew.GetCompact(); } + // Proof of work + const arith_uint256 bnTargetLimit = UintToArith256(params.powLimit); + for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) { if (PastBlocksMax > 0 && i > PastBlocksMax) { break; @@ -135,9 +146,6 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead } bool fProofOfStake = IsProofOfStakeHeight(pindexLast->nHeight + 1, params); - if (pindexLast->nHeight + 1 < params.nPivxProtocolV2StartHeight) { - return GetNextWorkRequiredOrig(pindexLast, params, fProofOfStake); - } return GetNextWorkRequiredOrig(pindexLast, params, fProofOfStake); } diff --git a/src/timedata.cpp b/src/timedata.cpp index f3c38b72276a3..424da63e588db 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -37,11 +37,6 @@ int64_t GetAdjustedTime() return GetTime() + GetTimeOffset(); } -static int64_t abs64(int64_t n) -{ - return (n >= 0 ? n : -n); -} - #define BITCOIN_TIMEDATA_MAX_SAMPLES 200 void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) diff --git a/src/timedata.h b/src/timedata.h index bc281942a22f6..95128dd6ec421 100644 --- a/src/timedata.h +++ b/src/timedata.h @@ -71,6 +71,7 @@ class CMedianFilter }; /** Functions to keep track of adjusted P2P time */ +inline int64_t abs64(int64_t n) { return (n >= 0 ? n : -n); } int64_t GetTimeOffset(); int64_t GetAdjustedTime(); void AddTimeData(const CNetAddr& ip, int64_t nTime); diff --git a/src/validation.cpp b/src/validation.cpp index f547c76e1c88c..92a86912006c4 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1,6 +1,11 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2011-2013 The PPCoin developers +// Copyright (c) 2013-2014 The NovaCoin Developers +// Copyright (c) 2014-2018 The BlackCoin Developers +// Copyright (c) 2015-2019 The PIVX developers // Copyright (c) 2014-2021 The Dash Core developers + // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -3807,6 +3812,17 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P return true; } +static bool IsValidBlockTimeStamp(const int64_t nTime, const int nHeight, const Consensus::Params& params) +{ + // Before time protocol V2, blocks can have arbitrary timestamps + if (!params.IsTimeProtocolV2(nHeight)) + return true; + + // Time protocol v2 requires time in slots + return (nTime % params.nTimeSlotLength) == 0; +} + + /** Context-dependent validity checks. * By "context", we mean only the previous block headers, but not the UTXO * set; UTXO-related validity checks are done in ConnectBlock(). @@ -3837,13 +3853,19 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta } // Check timestamp against prev - if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) +// if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) + if (block.GetBlockTime() <= pindexPrev->MinPastBlockTime(consensusParams)) return state.Invalid(false, REJECT_INVALID, "time-too-old", strprintf("block's timestamp is too early %d %d", block.GetBlockTime(), pindexPrev->GetMedianTimePast())); // Check timestamp - if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME) +// if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME_POW) + if (block.GetBlockTime() > pindexPrev->MaxFutureBlockTime(nAdjustedTime, consensusParams)) return state.Invalid(false, REJECT_INVALID, "time-too-new", strprintf("block timestamp too far in the future %d %d", block.GetBlockTime(), nAdjustedTime + 2 * 60 * 60)); + // Check blocktime mask + if (!IsValidBlockTimeStamp(block.GetBlockTime(), nHeight, consensusParams)) + return state.DoS(100, error("%s : block timestamp mask not valid", __func__), REJECT_INVALID, "invalid-time-mask"); + // check for version 2, 3 and 4 upgrades if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) || (block.nVersion < 2 && nHeight >= consensusParams.BIP66Height) || diff --git a/src/validation.h b/src/validation.h index ce0e05adcfb02..7b3a3d48a74c5 100644 --- a/src/validation.h +++ b/src/validation.h @@ -1,5 +1,9 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2011-2013 The PPCoin developers +// Copyright (c) 2013-2014 The NovaCoin Developers +// Copyright (c) 2014-2018 The BlackCoin Developers +// Copyright (c) 2015-2019 The PIVX developers // Copyright (c) 2014-2021 The Dash Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php.