From 286522c489d8c562bd0e9331d76baed6b05652f0 Mon Sep 17 00:00:00 2001 From: Yurii Oleksyshyn Date: Mon, 31 May 2021 16:45:57 +0300 Subject: [PATCH] Implemented extra inputs coinstake validation check. Updated version. Updated consensus rules --- configure.ac | 2 +- src/chainparams.cpp | 3 ++ src/consensus/params.h | 1 + src/consensus/tx_verify.cpp | 19 ++++++++ src/kernel.cpp | 36 ++++++++++++++ src/kernel.h | 2 + src/test/txvalidationcache_tests.cpp | 73 ++++++++++++++-------------- src/validation.cpp | 26 ++++++---- 8 files changed, 115 insertions(+), 47 deletions(-) diff --git a/configure.ac b/configure.ac index 3f60bb5d6..e366dcfec 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ 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, 26) +define(_CLIENT_VERSION_REVISION, 28) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2020) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 55a4dedbb..0424381fa 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -100,6 +100,7 @@ class CMainParams : public CChainParams { consensus.nPosTargetTimespan = 60 * 40; consensus.nPoSUpdgradeHFHeight = 898488; // 4 December 2019 consensus.nTPoSSignatureUpgradeHFHeight = 1348224; // tpos signature update HF, 8 October 2020 + consensus.nCoinstakeExtraInputsValidationHFHeight = 1688696; // coinstake validation HF, 29 May 2021 consensus.nMerchantnodeMinimumConfirmations = 1; consensus.nMasternodeMinimumConfirmations = 15; consensus.nStakeMinAge = 60 * 60; @@ -245,6 +246,7 @@ class CTestNetParams : public CChainParams { consensus.nStakeMaxAge = 60 * 60 * 24; // one day consensus.nCoinbaseMaturity = 20; consensus.nTPoSContractSignatureDeploymentTime = 1522782000; + consensus.nCoinstakeExtraInputsValidationHFHeight = 0; consensus.nRuleChangeActivationThreshold = 30; // 75% for testchains consensus.nMinerConfirmationWindow = 40; // nPowTargetTimespan / nPowTargetSpacing consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; @@ -360,6 +362,7 @@ class CRegTestParams : public CChainParams { consensus.nPosTargetTimespan = 60 * 40; consensus.nPoSUpdgradeHFHeight = 0; consensus.nTPoSSignatureUpgradeHFHeight = 80; + consensus.nCoinstakeExtraInputsValidationHFHeight = 0; consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = true; consensus.fPoSNoRetargeting = true; diff --git a/src/consensus/params.h b/src/consensus/params.h index 2edbcf9a6..3e9da8512 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -103,6 +103,7 @@ struct Params { int nMaxBlockSpacingFixDeploymentHeight; int nPoSUpdgradeHFHeight; int nTPoSSignatureUpgradeHFHeight; + int nCoinstakeExtraInputsValidationHFHeight; }; } // namespace Consensus diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 037235cdd..e0e0073b9 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -205,6 +205,21 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe return true; } +static bool IsInputGovernanceLocked(COutPoint outpoint, int nSpendHeight) { + static std::set setLockedOutpoints { + COutPoint(uint256S("e59e8e9ad8f289ca15bcaea450d9579e4de070308a19785c8d461a33abbe2717"), 1), + COutPoint(uint256S("ca971cc6cc76c0686224d54dec2067160fae5d057df44a9ba3d296f85879e047"), 1), + COutPoint(uint256S("ca971cc6cc76c0686224d54dec2067160fae5d057df44a9ba3d296f85879e047"), 2), + COutPoint(uint256S("2a87b52d96f5d38932dcdf10fddb54781301a2707af4a217050fc96881e5a786"), 0), + }; + + if (nSpendHeight < Consensus::Params().nCoinstakeExtraInputsValidationHFHeight) { + return false; + } + + return setLockedOutpoints.count(outpoint); +} + bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee) { // are the actual inputs available? @@ -219,6 +234,10 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c const Coin& coin = inputs.AccessCoin(prevout); assert(!coin.IsSpent()); + if (IsInputGovernanceLocked(prevout, nSpendHeight)) { + return state.Invalid(false, REJECT_INVALID, "governance-spend-locked-outpoint", strprintf("tried to spend locked outpoint %s", prevout.ToString().c_str())); + } + // If prev is coinbase, check that it's matured if (coin.IsCoinBase() || coin.IsCoinStake()) { if(nSpendHeight - coin.nHeight < COINBASE_MATURITY) { diff --git a/src/kernel.cpp b/src/kernel.cpp index c30aea9ea..5e04c26c9 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -494,6 +494,30 @@ bool CheckKernelScript(CScript scriptVin, CScript scriptVout) return extractKeyID(scriptVin) == extractKeyID(scriptVout); } + +bool CheckKernelExtraInputs(const CTransactionRef& tx, const CScript& scriptKernel, const Consensus::Params& params) +{ + if (!tx->IsCoinStake()) + return error("CheckKernelExtraInputs() : called on non-coinstake %s", tx->GetHash().ToString().c_str()); + + const auto& vin = tx->vin; + for (size_t i = 0; i < vin.size(); ++i) { + const auto& in = vin[i]; + uint256 hashBlock; + CTransactionRef txPrev; + + if (!GetTransaction(in.prevout.hash, txPrev, params, hashBlock, true)) + return error("CheckKernelExtraInputs() : INFO: read txPrev failed"); + + const auto& prevOut = txPrev->vout[in.prevout.n]; + if (scriptKernel != prevOut.scriptPubKey) { + return error("CheckKernelExtraInputs() : invalid input at position %d for coinstake %s", i, tx->GetHash().ToString().c_str()); + } + } + + return true; +} + // Check kernel hash target and coinstake signature bool CheckProofOfStake(const CBlockIndex *pindexPrev, const CBlock &block, uint256& hashProofOfStake, const Consensus::Params ¶ms) { @@ -528,6 +552,13 @@ bool CheckProofOfStake(const CBlockIndex *pindexPrev, const CBlock &block, uint2 else return error("CheckProofOfStake() : read block failed"); + if(IsCoinstakeExtraInputValidationHardForkActivated(pindex->nHeight)) { + // any coinstake transaction has to have scripts only from kernel. + if (!CheckKernelExtraInputs(tx, prevTxOut.scriptPubKey, params)) { + return error("CheckProofOfStake() : extra inputs check failed"); + } + } + // Read block header CBlock blockprev; if (!ReadBlockFromDisk(blockprev, pindex->GetBlockPos(), params)) @@ -573,3 +604,8 @@ bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierCheck return true; } +bool IsCoinstakeExtraInputValidationHardForkActivated(int nChainHeight) +{ + return nChainHeight >= Params().GetConsensus().nCoinstakeExtraInputsValidationHFHeight; +} + diff --git a/src/kernel.h b/src/kernel.h index 97fd9e5ad..30f56ebe2 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -28,6 +28,8 @@ 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; +bool IsCoinstakeExtraInputValidationHardForkActivated(int nChainHeight); + uint256 ComputeStakeModifierV3(const CBlockIndex* pindexPrev, const uint256& kernel); bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 7042b132a..230e62139 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -3,23 +3,23 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include -#include +#include #include +#include #include -#include #include -#include