From 7fe0dae12f747e4b157a72b5f1123cd61c2c95ba Mon Sep 17 00:00:00 2001 From: Yura Date: Mon, 18 Nov 2019 18:29:49 +0200 Subject: [PATCH] New diff algo (#147) * Added new difficulty algorithm based on exponential difficulty retargeting * Added hardfork conditions. Fixed issue with signmessage and tposcontract cleanup * Reverted difficulty changes. Implementing PoSV3 stake modifiers * Fixes to hardfork transition * Reverted testing changes. Changed where pos block is checked * Changed upgrage height. Reverted some old changes * Updated hf details * Updated version * Changed hf payments * Changed hardfork date --- configure.ac | 4 +- src/chain.cpp | 2 +- src/chain.h | 24 +++++++++-- src/chainparams.cpp | 1 + src/consensus/params.h | 1 + src/kernel.cpp | 74 +++++++++++++++++++++++----------- src/kernel.h | 16 +++++--- src/masternode-payments.cpp | 71 ++++++++++++++++++++++++++++++++- src/pow.cpp | 10 +++-- src/rpc/merchantnode.cpp | 2 + src/txdb.cpp | 1 + src/validation.cpp | 79 ++++++++++++++++++++++--------------- src/wallet/rpcwallet.cpp | 8 ++-- src/wallet/wallet.cpp | 21 +++++----- src/wallet/wallet.h | 4 +- 15 files changed, 227 insertions(+), 91 deletions(-) diff --git a/configure.ac b/configure.ac index 5dccd0bba..0472e2ecf 100644 --- a/configure.ac +++ b/configure.ac @@ -2,10 +2,10 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 1) define(_CLIENT_VERSION_MINOR, 0) -define(_CLIENT_VERSION_REVISION, 20) +define(_CLIENT_VERSION_REVISION, 21) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) -define(_COPYRIGHT_YEAR, 2018) +define(_COPYRIGHT_YEAR, 2019) define(_COPYRIGHT_HOLDERS,[The %s developers]) define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[XSN Core]]) AC_INIT([XSN Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[https://github.com/xsn/xsn/issues],[xsn],[https://xsncore.org/]) diff --git a/src/chain.cpp b/src/chain.cpp index b42ac34b3..ecef27a9d 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -193,7 +193,7 @@ arith_uint256 CBlockIndex::GetBlockTrust() const if (bnTarget <= 0) return 0; - if (IsProofOfStake()) { + if (IsProofOfStake(false)) { // Return trust score as usual return (arith_uint256(1) << 256) / (bnTarget + 1); } else { diff --git a/src/chain.h b/src/chain.h index 326a68280..77abef586 100644 --- a/src/chain.h +++ b/src/chain.h @@ -215,10 +215,12 @@ class CBlockIndex BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier + BLOCK_PROOF_OF_STAKE_V3 = (1 << 3), }; // proof-of-stake specific fields arith_uint256 GetBlockTrust() const; + uint256 hashStakeModifierV3; // hash modifier for PoS v3. uint64_t nStakeModifier; // hash modifier for proof-of-stake unsigned int nStakeModifierChecksum; // checksum of index; in-memeory only COutPoint prevoutStake; @@ -259,6 +261,7 @@ class CBlockIndex nMint = 0; nMoneySupply = 0; nFlags = 0; + hashStakeModifierV3.SetNull(); nStakeModifier = 0; nStakeModifierChecksum = 0; prevoutStake.SetNull(); @@ -296,7 +299,7 @@ class CBlockIndex hashProofOfStake = uint256(); if (block.IsProofOfStake()) { - SetProofOfStake(); + SetProofOfStake(false); prevoutStake = block.vtx[1]->vin[0].prevout; nStakeTime = block.nTime; } else { @@ -372,14 +375,22 @@ class CBlockIndex return !(nFlags & BLOCK_PROOF_OF_STAKE); } - bool IsProofOfStake() const + bool IsProofOfStake(bool isProofOfStakeV3) const { + if(isProofOfStakeV3) { + return (nFlags & BLOCK_PROOF_OF_STAKE_V3); + } + return (nFlags & BLOCK_PROOF_OF_STAKE); } - void SetProofOfStake() + void SetProofOfStake(bool isProofOfStakeV3) { nFlags |= BLOCK_PROOF_OF_STAKE; + if(isProofOfStakeV3) + { + nFlags |= BLOCK_PROOF_OF_STAKE_V3; + } } unsigned int GetStakeEntropyBit() const; @@ -484,10 +495,15 @@ class CDiskBlockIndex : public CBlockIndex READWRITE(nMoneySupply); READWRITE(nFlags); READWRITE(nStakeModifier); - if (IsProofOfStake()) { + if (IsProofOfStake(false)) { READWRITE(prevoutStake); READWRITE(nStakeTime); READWRITE(hashProofOfStake); + + if(IsProofOfStake(true)) { + READWRITE(hashStakeModifierV3); + } + } else { const_cast(this)->prevoutStake.SetNull(); const_cast(this)->nStakeTime = 0; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 9f322d9df..1d238d738 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -98,6 +98,7 @@ class CMainParams : public CChainParams { consensus.nPowTargetSpacing = 1 * 60; // XSN: 1 minutes consensus.nPosTargetSpacing = 1 * 60; // XSN: 1 minutes consensus.nPosTargetTimespan = 60 * 40; + consensus.nPoSUpdgradeHFHeight = 898488; // 1st of January 2019 consensus.nMerchantnodeMinimumConfirmations = 1; consensus.nMasternodeMinimumConfirmations = 15; consensus.nStakeMinAge = 60 * 60; diff --git a/src/consensus/params.h b/src/consensus/params.h index c9700a45c..7ce05beda 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -100,6 +100,7 @@ struct Params { int nCoinbaseMaturity; int nTPoSContractSignatureDeploymentTime; int nMaxBlockSpacingFixDeploymentHeight; + int nPoSUpdgradeHFHeight; }; } // namespace Consensus diff --git a/src/kernel.cpp b/src/kernel.cpp index c4e41a629..c30aea9ea 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -101,14 +101,14 @@ static bool SelectBlockFromCandidates( continue; // compute the selection hash by hashing its proof-hash and the // previous proof-of-stake modifier - uint256 hashProof = pindex->IsProofOfStake()? pindex->hashProofOfStake : pindex->GetBlockHash(); + uint256 hashProof = pindex->IsProofOfStake(false) ? pindex->hashProofOfStake : pindex->GetBlockHash(); CDataStream ss(SER_GETHASH, 0); ss << hashProof << nStakeModifierPrev; arith_uint256 hashSelection = UintToArith256(Hash(ss.begin(), ss.end())); // the selection hash is divided by 2**32 so that proof-of-stake block // is always favored over proof-of-work block. this is to preserve // the energy efficiency property - if (pindex->IsProofOfStake()) + if (pindex->IsProofOfStake(false)) hashSelection >>= 32; if (fSelected && hashSelection < hashBest) { @@ -127,6 +127,22 @@ static bool SelectBlockFromCandidates( return fSelected; } +// Stake Modifier (hash modifier of proof-of-stake): +// The purpose of stake modifier is to prevent a txout (coin) owner from +// computing future proof-of-stake generated by this txout at the time +// of transaction confirmation. To meet kernel protocol, the txout +// must hash with a future stake modifier to generate the proof. +uint256 ComputeStakeModifierV3(const CBlockIndex* pindexPrev, const uint256& kernel) +{ + if (!pindexPrev) + return uint256(); // genesis block's modifier is 0 + + CDataStream ss(SER_GETHASH, 0); + ss << kernel << pindexPrev->hashStakeModifierV3; + return Hash(ss.begin(), ss.end()); +} + + // Stake Modifier (hash modifier of proof-of-stake): // The purpose of stake modifier is to prevent a txout (coin) owner from // computing future proof-of-stake generated by this txout at the time @@ -217,7 +233,7 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStake while (pindex && pindex->nHeight >= nHeightFirstCandidate) { // '=' indicates proof-of-stake blocks not selected - if (pindex->IsProofOfStake()) + if (pindex->IsProofOfStake(false)) strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "="); pindex = pindex->pprev; } @@ -225,7 +241,7 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStake { // 'S' indicates selected proof-of-stake blocks // 'W' indicates selected proof-of-work blocks - strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W"); + strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake(false) ? "S" : "W"); } LogPrint(BCLog::KERNEL, "%s : selection height [%d, %d] map %s\n", __func__, nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap.c_str()); } @@ -368,16 +384,18 @@ uint256 stakeHash(unsigned int nTimeTx, CDataStream ss, unsigned int prevoutInde // quantities so as to generate blocks faster, degrading the system back into // a proof-of-work situation. // -bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake) +bool CheckStakeKernelHash(const CBlockIndex* pindexPrev, unsigned int nBits, uint256 hashBlockFrom, int64_t blockFromTime, + const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, + uint256& hashProofOfStake, bool fPoSV3, bool fPrintProofOfStake) { - nTxPrevOffset = 336; - auto txPrevTime = blockFrom.GetBlockTime(); + auto nTxPrevOffset = 336; + auto txPrevTime = blockFromTime; + unsigned int nTimeBlockFrom = blockFromTime; if (nTimeTx < txPrevTime) // Transaction timestamp violation return error("CheckStakeKernelHash() : nTime violation"); auto nStakeMinAge = Params().GetConsensus().nStakeMinAge; auto nStakeMaxAge = Params().GetConsensus().nStakeMaxAge; - unsigned int nTimeBlockFrom = blockFrom.GetBlockTime(); if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement return error("CheckStakeKernelHash() : min age violation"); @@ -396,10 +414,16 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nStakeModifierHeight = 0; int64_t nStakeModifierTime = 0; - if (!GetKernelStakeModifier(blockFrom.GetHash(), nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake)) - return error("Failed to get kernel stake modifier"); + if(fPoSV3) { + ss << pindexPrev->hashStakeModifierV3; + } + else { + + if (!GetKernelStakeModifier(hashBlockFrom, nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake)) + return error("Failed to get kernel stake modifier"); - ss << nStakeModifier; + ss << nStakeModifier; + } ss << nTimeBlockFrom << nTxPrevOffset << txPrevTime << prevout.n << nTimeTx; hashProofOfStake = Hash(ss.begin(), ss.end()); @@ -409,14 +433,14 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned __func__, nStakeModifier, nStakeModifierHeight, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nStakeModifierTime).c_str(), - mapBlockIndex[blockFrom.GetHash()]->nHeight, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", blockFrom.GetBlockTime()).c_str()); + mapBlockIndex[hashBlockFrom]->nHeight, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", blockFromTime).c_str()); LogPrint(BCLog::KERNEL, "%s : check protocol=%s modifier=0x%016" PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", __func__, "0.5", nStakeModifier, - nTimeBlockFrom, nTxPrevOffset, + blockFromTime, nTxPrevOffset, txPrevTime, prevout.n, nTimeTx, hashProofOfStake.ToString().c_str()); } @@ -431,13 +455,14 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned __func__, nStakeModifier, nStakeModifierHeight, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nStakeModifierTime).c_str(), - mapBlockIndex[blockFrom.GetHash()]->nHeight, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", blockFrom.GetBlockTime()).c_str()); + mapBlockIndex[hashBlockFrom]->nHeight, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", blockFromTime).c_str()); + LogPrint(BCLog::KERNEL, "%s : Generated pass protocol=%s modifier=0x%016" PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", __func__, "0.5", nStakeModifier, - nTimeBlockFrom, nTxPrevOffset, txPrevTime, prevout.n, nTimeTx, + blockFromTime, nTxPrevOffset, txPrevTime, prevout.n, nTimeTx, hashProofOfStake.ToString().c_str()); } return true; @@ -470,7 +495,7 @@ bool CheckKernelScript(CScript scriptVin, CScript scriptVout) } // Check kernel hash target and coinstake signature -bool CheckProofOfStake(const CBlock &block, uint256& hashProofOfStake) +bool CheckProofOfStake(const CBlockIndex *pindexPrev, const CBlock &block, uint256& hashProofOfStake, const Consensus::Params ¶ms) { const CTransactionRef &tx = block.vtx[1]; if (!tx->IsCoinStake()) @@ -483,9 +508,7 @@ bool CheckProofOfStake(const CBlock &block, uint256& hashProofOfStake) uint256 hashBlock; CTransactionRef txPrev; - const auto &cons = Params().GetConsensus(); - - if (!GetTransaction(txin.prevout.hash, txPrev, cons, hashBlock, true)) + if (!GetTransaction(txin.prevout.hash, txPrev, params, hashBlock, true)) return error("CheckProofOfStake() : INFO: read txPrev failed"); CTxOut prevTxOut = txPrev->vout[txin.prevout.n]; @@ -498,7 +521,7 @@ bool CheckProofOfStake(const CBlock &block, uint256& hashProofOfStake) return error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx->GetHash().ToString().c_str()); } - CBlockIndex* pindex = NULL; + CBlockIndex* pindex = nullptr; BlockMap::iterator it = mapBlockIndex.find(hashBlock); if (it != mapBlockIndex.end()) pindex = it->second; @@ -507,14 +530,16 @@ bool CheckProofOfStake(const CBlock &block, uint256& hashProofOfStake) // Read block header CBlock blockprev; - if (!ReadBlockFromDisk(blockprev, pindex->GetBlockPos(), cons)) + if (!ReadBlockFromDisk(blockprev, pindex->GetBlockPos(), params)) return error("CheckProofOfStake(): INFO: failed to find block"); if(!CheckKernelScript(prevTxOut.scriptPubKey, tx->vout[1].scriptPubKey)) return error("CheckProofOfStake() : INFO: check kernel script failed on coinstake %s, hashProof=%s \n", tx->GetHash().ToString().c_str(), hashProofOfStake.ToString().c_str()); + bool isProofOfStakeV3 = params.nPoSUpdgradeHFHeight < pindexPrev->nHeight; + unsigned int nTime = block.nTime; - if (!CheckStakeKernelHash(block.nBits, blockprev, /*postx->nTxOffset + */sizeof(CBlock), txPrev, txin.prevout, nTime, hashProofOfStake, true)) + if (!CheckStakeKernelHash(pindexPrev, block.nBits, blockprev.GetHash(), blockprev.GetBlockTime(), txPrev, txin.prevout, nTime, hashProofOfStake, isProofOfStakeV3, true)) return error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s \n", tx->GetHash().ToString().c_str(), hashProofOfStake.ToString().c_str()); // may occur during initial download or if behind on block chain sync return true; @@ -547,3 +572,4 @@ bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierCheck return nStakeModifierChecksum == mapStakeModifierCheckpoints[nHeight]; return true; } + diff --git a/src/kernel.h b/src/kernel.h index 90b3be164..97fd9e5ad 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -9,6 +9,10 @@ #include #include +namespace Consensus { +struct Params; +} + class CBlock; class CWallet; class COutPoint; @@ -24,18 +28,18 @@ extern unsigned int getIntervalVersion(bool fTestNet); // ratio of group interval length between the last group and the first group static const int MODIFIER_INTERVAL_RATIO = 3; -// Compute the hash modifier for proof-of-stake -bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier); +uint256 ComputeStakeModifierV3(const CBlockIndex* pindexPrev, const uint256& kernel); +bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier); // Check whether stake kernel meets hash target // Sets hashProofOfStake on success return -bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned int nTxPrevOffset, - const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, - uint256& hashProofOfStake, bool fPrintProofOfStake = false); +bool CheckStakeKernelHash(const CBlockIndex *pindexPrev, unsigned int nBits, uint256 hashBlockFrom, int64_t blockFromTime, const CTransactionRef& txPrev, + const COutPoint& prevout, unsigned int nTimeTx, + uint256& hashProofOfStake, bool fPoSV3, bool fPrintProofOfStake); // Check kernel hash target and coinstake signature // Sets hashProofOfStake on success return -bool CheckProofOfStake(const CBlock &block, uint256& hashProofOfStake); +bool CheckProofOfStake(const CBlockIndex *pindexPrev, const CBlock &block, uint256& hashProofOfStake, const Consensus::Params ¶ms); // Check whether the coinstake timestamp meets protocol bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx); diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index dbf37501a..7400b3fc8 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -23,7 +23,12 @@ CMasternodePayments mnpayments; CCriticalSection cs_vecPayees; CCriticalSection cs_mapMasternodeBlocks; -CCriticalSection cs_mapMasternodePaymentVotes; +static CCriticalSection cs_mapMasternodePaymentVotes; + +static std::pair HardForkPayment() +{ + return std::make_pair(7000000 * COIN, std::string("Xp6PaXBQrN6L8sVFHQYVW5rBnKApecD6vu")); +} static bool GetBlockHash(uint256 &hash, int nBlockHeight) { @@ -35,6 +40,35 @@ static bool GetBlockHash(uint256 &hash, int nBlockHeight) return false; } +static bool IsValidNewAlgoHardForkBlock(const CTransaction &transaction, int nBlockHeight, CAmount expectedReward, CAmount actualReward, std::string &strErrorRet) +{ + const Consensus::Params& consensusParams = Params().GetConsensus(); + if(nBlockHeight != consensusParams.nPoSUpdgradeHFHeight) + { + return false; + } + const auto &vout = transaction.vout; + auto hfPayment = HardForkPayment(); + auto amount = hfPayment.first; + + auto maxExpectedAmount = expectedReward + amount; + + if(actualReward > maxExpectedAmount) + { + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, invalid hf payment", + nBlockHeight, actualReward, maxExpectedAmount); + return false; + } + + auto scriptPubKey = GetScriptForDestination(DecodeDestination(hfPayment.second)); + auto it = std::find_if(std::begin(vout), std::end(vout), [scriptPubKey, amount](const CTxOut &txOut) { + return txOut.scriptPubKey == scriptPubKey && + txOut.nValue == amount; + }); + + return it != std::end(vout); +} + /** * IsBlockValueValid * @@ -60,6 +94,20 @@ bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount expectedRe const Consensus::Params& consensusParams = Params().GetConsensus(); + if(consensusParams.nPoSUpdgradeHFHeight == nBlockHeight) + { + if(IsValidNewAlgoHardForkBlock(*coinbaseTransaction, nBlockHeight, expectedReward, actualReward, strErrorRet)) + { + LogPrint(BCLog::MNPAYMENTS, "IsBlockValueValid -- Valid hardfork payment at height %d: %s", nBlockHeight, coinbaseTransaction->ToString()); + return true; + } + else + { + LogPrint(BCLog::MNPAYMENTS, "IsBlockValueValid -- Invalid hardfork payment at height %d: %s, err: %s", nBlockHeight, coinbaseTransaction->ToString(), strErrorRet); + return false; + } + } + if(nBlockHeight < consensusParams.nSuperblockStartBlock) { int nOffset = nBlockHeight % consensusParams.nBudgetPaymentsCycleBlocks; if(nBlockHeight >= consensusParams.nBudgetPaymentsStartBlock && @@ -158,6 +206,20 @@ bool IsBlockPayeeValid(const CTransactionRef& txNew, int nBlockHeight, CAmount e const Consensus::Params& consensusParams = Params().GetConsensus(); + if(consensusParams.nPoSUpdgradeHFHeight == nBlockHeight) + { + std::string strError; + if(IsValidNewAlgoHardForkBlock(*txNew, nBlockHeight, expectedReward, actualReward, strError)) + { + return true; + } + else + { + LogPrint(BCLog::MNPAYMENTS, "IsBlockPayeeValid -- Invalid hardfork payment at height %d: %s, err: %s", nBlockHeight, txNew->ToString(), strError); + return false; + } + } + if(nBlockHeight < consensusParams.nSuperblockStartBlock) { if(mnpayments.IsTransactionValid(txNew, nBlockHeight)) { LogPrint(BCLog::MNPAYMENTS, "IsBlockPayeeValid -- Valid masternode payment at height %d: %s", nBlockHeight, txNew->ToString()); @@ -225,6 +287,13 @@ bool IsBlockPayeeValid(const CTransactionRef& txNew, int nBlockHeight, CAmount e void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, CTxOut& txoutMasternodeRet, std::vector& voutSuperblockRet) { + const Consensus::Params& consensusParams = Params().GetConsensus(); + if(nBlockHeight == consensusParams.nPoSUpdgradeHFHeight) { + auto payment = HardForkPayment(); + txNew.vout.emplace_back(payment.first, GetScriptForDestination(DecodeDestination(payment.second))); + return; + } + // only create superblocks if spork is enabled AND if superblock is actually triggered // (height should be validated inside) if(sporkManager.IsSporkActive(Spork::SPORK_9_SUPERBLOCKS_ENABLED) && diff --git a/src/pow.cpp b/src/pow.cpp index 0debc9453..e83329b3c 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -78,8 +78,9 @@ unsigned int static KimotoGravityWell(const CBlockIndex* pindexLast, const Conse return bnNew.GetCompact(); } + unsigned int static PoSWorkRequired(const CBlockIndex* pindexLast, const Consensus::Params& params) { - arith_uint256 bnTargetLimit = (~arith_uint256(0) >> 24); + const arith_uint256 bnTargetLimit = (~arith_uint256(0) >> 24); int64_t nTargetSpacing = Params().GetConsensus().nPosTargetSpacing; int64_t nTargetTimespan = Params().GetConsensus().nPosTargetTimespan; @@ -90,8 +91,7 @@ unsigned int static PoSWorkRequired(const CBlockIndex* pindexLast, const Consens if (nActualSpacing < 0) nActualSpacing = 1; - if(pindexLast->nHeight > params.nMaxBlockSpacingFixDeploymentHeight) - { + if(pindexLast->nHeight > params.nMaxBlockSpacingFixDeploymentHeight) { nActualSpacing = std::min(nActualSpacing, nTargetSpacing * 10); } @@ -100,7 +100,9 @@ unsigned int static PoSWorkRequired(const CBlockIndex* pindexLast, const Consens arith_uint256 bnNew; bnNew.SetCompact(pindexLast->nBits); + int64_t nInterval = nTargetTimespan / nTargetSpacing; + bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); bnNew /= ((nInterval + 1) * nTargetSpacing); @@ -191,7 +193,7 @@ unsigned int GetNextWorkRequiredBTC(const CBlockIndex* pindexLast, const CBlockH } // Go back by what we want to be 1 day worth of blocks - int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1); + int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval() - 1); assert(nHeightFirst >= 0); const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst); assert(pindexFirst); diff --git a/src/rpc/merchantnode.cpp b/src/rpc/merchantnode.cpp index d97bb79b8..2312176af 100644 --- a/src/rpc/merchantnode.cpp +++ b/src/rpc/merchantnode.cpp @@ -471,6 +471,7 @@ static UniValue tposcontract(const JSONRPCRequest& request) " create - Create tpos transaction\n" " list - Print list of all tpos contracts that you are owner or merchant\n" " refresh - Refresh tpos contract for merchant to fetch all coins from blockchain.\n" + " cleanup - Cleanup old entries of tpos contract.\n" " validate - Validates transaction checking if it's a valid contract" ); @@ -587,6 +588,7 @@ static UniValue tposcontract(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Contract is invalid"); pwallet->RemoveWatchOnly(GetScriptForDestination(tmpContract.tposAddress.Get())); + pwallet->RemoveTPoSContract(tposContractHashID); } else if(strCommand == "validate") { diff --git a/src/txdb.cpp b/src/txdb.cpp index fb3e65aff..0426a5d68 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -296,6 +296,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, pindexNew->prevoutStake = diskindex.prevoutStake; pindexNew->nStakeTime = diskindex.nStakeTime; pindexNew->hashProofOfStake = diskindex.hashProofOfStake; + pindexNew->hashStakeModifierV3 = diskindex.hashStakeModifierV3; if(pindexNew->nHeight <= Params().GetConsensus().nLastPoWBlock) { diff --git a/src/validation.cpp b/src/validation.cpp index f02918a34..e0bf56a67 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2975,13 +2975,15 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) { return g_chainstate.ResetBlockFailureFlags(pindex); } -static void AcceptProofOfStakeBlock(const CBlock &block, CBlockIndex *pindexNew) +static void AcceptProofOfStakeBlock(const CBlock &block, CBlockIndex *pindexNew, const Consensus::Params& consensusParams) { if(!pindexNew) return; + bool isPoSV3 = consensusParams.nPoSUpdgradeHFHeight < pindexNew->nHeight; + if (block.IsProofOfStake()) { - pindexNew->SetProofOfStake(); + pindexNew->SetProofOfStake(isPoSV3); pindexNew->prevoutStake = block.vtx[1]->vin[0].prevout; pindexNew->nStakeTime = block.nTime; } else { @@ -3001,22 +3003,31 @@ static void AcceptProofOfStakeBlock(const CBlock &block, CBlockIndex *pindexNew) uint256 hash = block.GetHash(); + // ppcoin: record proof-of-stake hash value - if (pindexNew->IsProofOfStake()) { + if (pindexNew->IsProofOfStake(false)) { if (!mapProofOfStake.count(hash)) LogPrintf("AcceptProofOfStakeBlock() : hashProofOfStake not found in map \n"); pindexNew->hashProofOfStake = mapProofOfStake[hash]; } - // ppcoin: compute stake modifier - uint64_t nStakeModifier = 0; - bool fGeneratedStakeModifier = false; - if (!ComputeNextStakeModifier(pindexNew, nStakeModifier, fGeneratedStakeModifier)) - LogPrintf("AcceptProofOfStakeBlock() : ComputeNextStakeModifier() failed \n"); - pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); - pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew); - if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) - LogPrintf("AcceptProofOfStakeBlock() : Rejected by stake modifier checkpoint height=%d, modifier=%s \n", pindexNew->nHeight, std::to_string(nStakeModifier)); + if(isPoSV3) + { + pindexNew->hashStakeModifierV3 = ComputeStakeModifierV3(pindexNew->pprev, pindexNew->prevoutStake.hash); + } + else + { + // ppcoin: compute stake modifier + uint64_t nStakeModifier = 0; + bool fGeneratedStakeModifier = false; + if (!ComputeNextStakeModifier(pindexNew, nStakeModifier, fGeneratedStakeModifier)) + LogPrintf("AcceptProofOfStakeBlock() : ComputeNextStakeModifier() failed \n"); + pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); + pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew); + if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) + LogPrintf("AcceptProofOfStakeBlock() : Rejected by stake modifier checkpoint height=%d, modifier=%s \n", pindexNew->nHeight, std::to_string(nStakeModifier)); + } + setDirtyBlockIndex.insert(pindexNew); } @@ -3254,7 +3265,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P if (block.vtx[i]->IsCoinStake()) return state.DoS(100, error("CheckBlock() : more than one coinstake")); - uint256 hashProofOfStake; uint256 hash = block.GetHash(); TPoSContract contract; @@ -3279,13 +3289,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P return state.DoS(100, error("CheckBlock(): block signature invalid"), REJECT_INVALID, "bad-block-signature"); } - - if(!CheckProofOfStake(block, hashProofOfStake)) { - return state.DoS(100, error("CheckBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str())); - } - - if(!mapProofOfStake.count(hash)) // add to mapProofOfStake - mapProofOfStake.insert(std::make_pair(hash, hashProofOfStake)); } // Check transactions @@ -3436,17 +3439,17 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta */ static uint256 WitnessComittmentForPoSBlock(const CBlock &block, int commitpos, bool &malleated) { -// if(!block.IsProofOfStake()) - return BlockWitnessMerkleRoot(block, &malleated); + // if(!block.IsProofOfStake()) + return BlockWitnessMerkleRoot(block, &malleated); -// CBlock tmpBlock = block; -// CMutableTransaction tempTx(*tmpBlock.vtx[1]); + // CBlock tmpBlock = block; + // CMutableTransaction tempTx(*tmpBlock.vtx[1]); -// auto &vout = tempTx.vout; -// vout.erase(std::begin(vout) + commitpos); -// tmpBlock.vtx[1] = MakeTransactionRef(std::move(tempTx)); -// auto value = BlockWitnessMerkleRoot(tmpBlock, &malleated); -// return value; + // auto &vout = tempTx.vout; + // vout.erase(std::begin(vout) + commitpos); + // tmpBlock.vtx[1] = MakeTransactionRef(std::move(tempTx)); + // auto value = BlockWitnessMerkleRoot(tmpBlock, &malleated); + // return value; } /** NOTE: This function is not currently invoked by ConnectBlock(), so we @@ -3722,7 +3725,21 @@ bool CChainState::AcceptBlock(const std::shared_ptr& pblock, CVali return error("%s: %s", __func__, FormatStateMessage(state)); } - AcceptProofOfStakeBlock(block, pindex); + uint256 hashProofOfStake; + auto hash = block.GetHash(); + + if(block.IsProofOfStake()) + { + if(!CheckProofOfStake(pindex->pprev, block, hashProofOfStake, chainparams.GetConsensus())) { + return state.DoS(100, error("AcceptBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str())); + } + + if(!mapProofOfStake.count(hash)) // add to mapProofOfStake + mapProofOfStake.insert(std::make_pair(hash, hashProofOfStake)); + + } + + AcceptProofOfStakeBlock(block, pindex, chainparams.GetConsensus()); // Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW // (but if it does not build on our best tip, let the SendMessages loop relay it) @@ -4548,7 +4565,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) if (blockPos.IsNull()) return error("%s: writing genesis block to disk failed", __func__); CBlockIndex *pindex = AddToBlockIndex(block); - AcceptProofOfStakeBlock(block, pindex); + AcceptProofOfStakeBlock(block, pindex, chainparams.GetConsensus()); CValidationState state; if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) return error("%s: genesis block not accepted (%s)", __func__, FormatStateMessage(state)); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 7540df733..caace8b73 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -692,13 +692,13 @@ static UniValue signmessage(const JSONRPCRequest& request) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } - const CKeyID *keyID = boost::get(&dest); - if (!keyID) { - throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + CKeyID keyID = GetKeyForDestination(*pwallet, dest); + if(keyID.IsNull()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Invalid address, supporting p2pkh or p2wpkh"); } CKey key; - if (!pwallet->GetKey(*keyID, key)) { + if (!pwallet->GetKey(keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2f8657760..c37a0e62d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -400,9 +400,8 @@ CAmount GetStakeReward(CAmount blockReward, unsigned int percentage) return (blockReward / 100) * percentage; } -bool CWallet::CreateCoinStakeKernel(CScript &kernelScript, const CScript &stakeScript, - unsigned int nBits, const CBlock &blockFrom, - unsigned int nTxPrevOffset, const CTransactionRef &txPrev, +bool CWallet::CreateCoinStakeKernel(CScript &kernelScript, const CScript &stakeScript, CBlockIndex *pindex, + unsigned int nBits, const CBlock &blockFrom, const CTransactionRef &txPrev, const COutPoint &prevout, unsigned int &nTimeTx, const TPoSContract &contract, bool fGenerateSegwit, bool fPrintProofOfStake) const { @@ -431,11 +430,14 @@ bool CWallet::CreateCoinStakeKernel(CScript &kernelScript, const CScript &stakeS } } + bool isProofOfStakeV3 = Params().GetConsensus().nPoSUpdgradeHFHeight < pindex->nHeight; + auto blockFromHash = blockFrom.GetHash(); + auto blockFromTime = blockFrom.GetBlockTime(); for(unsigned int i = 0; i < nHashDrift; ++i) { nTryTime = nTimeTx + nHashDrift - i; - if (CheckStakeKernelHash(nBits, blockFrom, nTxPrevOffset, txPrev, prevout, nTryTime, hashProofOfStake, fPrintProofOfStake)) + if (CheckStakeKernelHash(pindex, nBits, blockFromHash, blockFromTime, txPrev, prevout, nTryTime, hashProofOfStake, isProofOfStakeV3, fPrintProofOfStake)) { //Double check that this will pass time requirements if (nTryTime <= chainActive.Tip()->GetMedianTimePast()) { @@ -3784,8 +3786,9 @@ bool CWallet::CreateCoinStake(unsigned int nBits, //iterates each utxo inside of CheckStakeKernelHash() CScript kernelScript; auto stakeScript = pcoin.first->tx->vout[pcoin.second].scriptPubKey; - fKernelFound = CreateCoinStakeKernel(kernelScript, stakeScript, nBits, - block, sizeof(CBlock), pcoin.first->tx, + fKernelFound = CreateCoinStakeKernel(kernelScript, stakeScript, + chainActive.Tip(), nBits, + block, pcoin.first->tx, prevoutStake, nTxNewTime, tposContract, fGenerateSegwit, false); if(fKernelFound) @@ -3804,12 +3807,6 @@ bool CWallet::CreateCoinStake(unsigned int nBits, return false; } - // Limit size - unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); - // if (nBytes >= DEFAULT_BLOCK_MAX_SIZE / 5){ - // return error("CreateCoinStake() : exceeded coinstake size limit"); - // } - // Update coinbase transaction with additional info about masternode and governance payments, // get some info back to pass to getblocktemplate CTxOut txoutMasternode; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index acc00754e..bec1def98 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -787,8 +787,8 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface const CBlockIndex* m_last_block_processed = nullptr; bool CreateCoinStakeKernel(CScript &kernelScript, const CScript &stakeScript, - unsigned int nBits, const CBlock& blockFrom, - unsigned int nTxPrevOffset, const CTransactionRef &txPrev, + CBlockIndex *pindex, + unsigned int nBits, const CBlock& blockFrom, const CTransactionRef &txPrev, const COutPoint& prevout, unsigned int &nTimeTx, const TPoSContract &contract, bool fGenerateSegwit, bool fPrintProofOfStake) const;