changeset 29732:15256d32d3b1

Grantcoin compiles checkpoint
author Troy Benjegerdes <troy@7el.us>
date Fri, 12 Feb 2021 22:48:17 -0600
parents 8acd2fd14a01
children f0e1ee44d172
files src/Makefile src/consensus/tx_verify.cpp src/consensus/tx_verify.h src/grantcoin/grantcoin.h src/grantcoin/params.cpp src/kernel.cpp src/kernel.h src/kernelrecord.cpp src/kernelrecord.h src/make.Darwin.mk src/pow.cpp src/primitives/transaction.cpp src/primitives/transaction.h src/test/README.md src/test/main_tests.cpp src/test/test_bitcoin_main.cpp src/test/test_codecoin.cpp src/wallet/wallet.h
diffstat 18 files changed, 702 insertions(+), 330 deletions(-) [+]
line wrap: on
line diff
--- a/src/Makefile	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/Makefile	Fri Feb 12 22:48:17 2021 -0600
@@ -107,7 +107,7 @@
 #   -l leveldb \
 #   -l memenv
 
-TESTLIBS += -lboost_unit_test_framework -lmemenv 
+TESTLIBS += -lboost_unit_test_framework$(BOOST_LIB_SUFFIX) -lmemenv 
 
 ifndef USE_UPNP
 	override USE_UPNP = -
@@ -508,7 +508,7 @@
 TEST_OBJS := $(patsubst %,$(OBJDIR)/test/%.o,$(TESTS))
 TEST_EXTRA_OBJS = $(OBJDIR)/test/test_codecoin.o $(OBJDIR)/test/test_bitcoin_main.o
 
-$(TEST_OBJS): $(JSON_TEST_H_FILES) $(TESTDIR)
+$(TEST_OBJS): $(JSON_TEST_H_FILES) $(TESTDIR) 
 
 $(TEST_EXTRA_OBJS): $(TESTDIR)
 
--- a/src/consensus/tx_verify.cpp	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/consensus/tx_verify.cpp	Fri Feb 12 22:48:17 2021 -0600
@@ -12,6 +12,11 @@
 #include <script/interpreter.h>
 #include <consensus/validation.h>
 
+#if defined(PPCOINSTAKE)
+#include <kernel.h>
+#include <validation.h>
+#endif
+
 // TODO remove the following dependencies
 #include <chain.h>
 #include <coins.h>
@@ -174,14 +179,25 @@
     CAmount nValueOut = 0;
     for (const auto& txout : tx.vout)
     {
+#if defined(PPCOINSTAKE) || defined(BRAND_grantcoin)
+        if (txout.IsEmpty() && (!tx.IsCoinBase()) && (!tx.IsCoinStake()))
+            return state.DoS(100, false, REJECT_INVALID, "empty-txout");
+#if defined (BRAND_grantcoin)
+        if ((!txout.IsEmpty()) && txout.nValue < CENT) // Cent || CTransaction::nMinTxFee
+            return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toosmall");
+#else
+// TODO: exampine peercoin v0.5 protocol 
+        // peercoin: enforce minimum output amount
+        // v0.5 protocol: zero amount allowed
+        if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT &&
+            !(IsProtocolV05(tx.nTime) && (txout.nValue == 0)))
+            return state.DoS(100, false, REJECT_INVALID, "txout.nValue below minimum");
+#endif
+#endif
         if (txout.nValue < 0)
             return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
         if (txout.nValue > MAX_MONEY)
             return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
-#if defined(PPCOINSTAKE) || defined(BRAND_grantcoin)
-        if ((!txout.IsEmpty()) && txout.nValue < CENT) // Cent || CTransaction::nMinTxFee
-            return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toosmall"));
-#endif
         nValueOut += txout.nValue;
         if (!MoneyRange(nValueOut))
             return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
@@ -237,10 +253,10 @@
 #if defined(BRAND_grantcoin)
 	#warning "can't check previous tx timestamps without CCoins ppcoinstake structures"
 #endif
-			// peercoin: check transaction timestamp
-			if (coins.nTime > nTime)
-				return state.DoS(100, error("CheckInputs() : tx %s timestamp %d earlier than input tx timestamp %d",
-				GetHash().ToString().c_str(), nTime, coins.nTime));
+        // peercoin: check transaction timestamp
+        if (coins.nTime > nTime)
+            return state.DoS(100, error("CheckInputs() : tx %s timestamp %d earlier than input tx timestamp %d",
+        GetHash().ToString().c_str(), nTime, coins.nTime));
 #endif
 
         // Check for negative or overflow input values
@@ -249,28 +265,18 @@
             return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
         }
 #if defined(PPCOINSTAKE)
-		if (coin.IsCoinStake())
-		{
-			// ppcoin: coin stake tx earns reward instead of paying fee
-			uint64_t nCoinAge;
-			if (!GetCoinAge(state, inputs, nCoinAge))
-				return error("CheckInputs() : %s unable to get coin age for coinstake", GetHash().ToString().c_str());
-			int64_t nStakeReward = GetValueOut() - nValueIn;
-			CBlockIndex indexDummy;
-			indexDummy.nHeight = nSpendHeight; /* hack */
-			indexDummy.SetProofOfStake();  /* hack2 */
-#if defined(BRAND_cleanwatercoin)
-			#warning "check this further"
-			int64_t nStakeLimit = indexDummy.GetSeigniorage(0, nCoinAge);
-#else
-			int64_t nStakeLimit = indexDummy.GetSeigniorage(0, nCoinAge) - GetMinFee() + CTransaction::nMinTxFee;
-#endif
-			if (nStakeReward > nStakeLimit)
-				return state.DoS(100, error("CheckInputs() : %s stake reward %" PRId64 " exceeds limit %" PRId64 ,
-					GetHash().ToString().c_str(), nStakeReward, indexDummy.GetSeigniorage(0,nCoinAge) ));
-		}
-		else
-		{ // not needed? fix?
+    if (tx.IsCoinStake())
+    {
+        // peercoin: coin stake tx earns reward instead of paying fee
+        uint64_t nCoinAge;
+        if (!GetCoinAge(tx, inputs, nCoinAge))
+            return state.DoS(100, false, REJECT_INVALID, "unable to get coin age for coinstake");
+        CAmount nStakeReward = tx.GetValueOut() - nValueIn;
+        CAmount nCoinstakeCost = (GetMinFee(tx) < PERKB_TX_FEE) ? 0 : (GetMinFee(tx) - PERKB_TX_FEE);
+        if (nMoneySupply && nStakeReward > GetProofOfStakeReward(nCoinAge, tx.nTime, nMoneySupply) - nCoinstakeCost)
+            return state.DoS(100, false, REJECT_INVALID, "bad-txns-coinstake-too-large");
+        return true;
+    }
 #endif
     }
 
@@ -286,12 +292,35 @@
         return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
     }
 #if defined(PPCOINSTAKE) || defined (BRAND_grantcoin)
-        if (txfee_aux < tx.GetMinFee()) // CODECOIN
-            return state.DoS(100, error("CheckInputs() : %s TxFee not paying required fee=%s, paid=%s",
-	tx.GetHash().ToString(), FormatMoney(tx.GetMinFee()), FormatMoney(txfee_aux)),
-                             REJECT_INVALID, "bad-txns-fee-too-low");
+        if (txfee_aux < GetMinFee(tx)) // CODECOIN
+            return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-too-low", false,
+                strprintf("CheckInputs() : %s TxFee not paying required fee=%s, paid=%s",
+	tx.GetHash().ToString(), FormatMoney(GetMinFee(tx)), FormatMoney(txfee_aux)));
 #endif
 
     txfee = txfee_aux;
     return true;
 }
+
+#if defined(PPCOINSTAKE) || defined(BRAND_grantcoin)
+CAmount GetMinFee(const CTransaction& tx)
+{
+    size_t nBytes = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+    return GetMinFee(nBytes, tx.Time);
+}
+
+CAmount GetMinFee(size_t nBytes, uint32_t nTime)
+{
+    CAmount nMinFee;
+#if !defined(BRAND_grantcoin)
+    if (IsProtocolV07(nTime)) // RFC-0007
+        nMinFee = (nBytes < 100) ? MIN_TX_FEE : (CAmount)(nBytes * (PERKB_TX_FEE / 1000));
+    else
+#endif
+        nMinFee = (1 + (CAmount)nBytes / 1000) * PERKB_TX_FEE;
+
+    if (!MoneyRange(nMinFee))
+        nMinFee = MAX_MONEY;
+    return nMinFee;
+}
+#endif
--- a/src/consensus/tx_verify.h	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/consensus/tx_verify.h	Fri Feb 12 22:48:17 2021 -0600
@@ -27,7 +27,12 @@
  * @param[out] txfee Set to the transaction fee if successful.
  * Preconditions: tx.IsCoinBase() is false.
  */
+#if defined(PPCOINSTAKE)
+struct Params;
+bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, const Consensus::Params& params, uint64_t nMoneySupply=0);
+#else
 bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
+#endif
 } // namespace Consensus
 
 /** Auxiliary functions for transaction validation (ideally should not be exposed) */
@@ -78,4 +83,10 @@
  */
 bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block);
 
+#if defined(PPCOINSTAKE) || defined(BRAND_grantcoin)
+// peercoin: minimum fee for transaction to be accepted in a blockchain.
+CAmount GetMinFee(const CTransaction& tx);
+CAmount GetMinFee(size_t nBytes, uint32_t nTime);
+#endif
+
 #endif // BITCOIN_CONSENSUS_TX_VERIFY_H
--- a/src/grantcoin/grantcoin.h	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/grantcoin/grantcoin.h	Fri Feb 12 22:48:17 2021 -0600
@@ -1,4 +1,4 @@
-// Copyright (c) 2014 Troy Benjegerdes, under AGPLv3
+// Copyright (c) 2014-2021 Troy Benjegerdes, under AGPLv3
 // Distributed under the Affero GNU General public license version 3
 // file COPYING or http://www.gnu.org/licenses/agpl-3.0.html
 #ifndef CODECOIN_grantcoin_H
@@ -23,11 +23,13 @@
 static const int RPC_PORT = 9983;
 static const int RPC_PORT_TESTNET = 9985;
 
-//static const int P2P_PORT = 9982; // deprecated, in params.cpp
-//static const int P2P_PORT_TESTNET = 9984; // deprecated, in params.cpp
+/* from net.h */
+/** Maximum length of incoming protocol messages (no message over 4 MB is currently accep    table). */
+static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
 
 static const int64_t COIN = 1000000;
 static const int64_t CENT = 10000;
+static const int64_t PERKB_TX_FEE = CENT;
 static const int COIN_DECIMALS = 6; /* decimal places for coin */
 #define COIN_DECIMALS_FMT "06"
 
@@ -39,10 +41,10 @@
 inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
 
 
-//static const int STAKE_TARGET_SPACING = 1.5 * 60; // 90-second block spacing 
-//static const unsigned int nStakeMinAge = 60 * 60 * 24; // minimum age for coin age (24 hours)
-//static const unsigned int nStakeMaxAge = 60 * 60 * 24 * 90; // stake age of full weight
-//static const int64 START_BLOCK_PROOF_OF_STAKE = 250000; // PoS allowed starting at this block
+static const int STAKE_TARGET_SPACING = 1.5 * 60; // 90-second block spacing 
+static const unsigned int nStakeMinAge = 60 * 60 * 24; // minimum age for coin age (24 hours)
+static const unsigned int nStakeMaxAge = 60 * 60 * 24 * 90; // stake age of full weight
+static const uint64_t START_BLOCK_PROOF_OF_STAKE = 250000; // PoS allowed starting at this block
 
 extern const unsigned int nMaxClockDrift;
 
--- a/src/grantcoin/params.cpp	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/grantcoin/params.cpp	Fri Feb 12 22:48:17 2021 -0600
@@ -13,27 +13,30 @@
 #include <util.h>
 #include <utilstrencodings.h>
 
-#include <cinttypes>
+#include <cinttypes> /* PRId64 */
 #include <assert.h>
 
-
-#include "arith_uint256.h"
+//#include <grantcoin/seeds.h>
 
 /**
  * Main network
  */
 
+void CChainParams::UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+{
+    consensus.vDeployments[d].nStartTime = nStartTime;
+    consensus.vDeployments[d].nTimeout = nTimeout;
+}
 
 class CMainParams : public CChainParams {
 public:
     CMainParams() {
         strNetworkID = "grantcoin";
         //consensus.powLimit = ~uint256S(0) >> 28; // Reduced initial difficulty from Peercoin's 32
-        //consensus.powLimit = uint256S("0000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
-	consensus.powLimit = ArithToUint256(~arith_uint256(0) >> 28); // TODO: implement operator= for this
-        consensus.nMajorityEnforceBlockUpgrade = 750;
-        consensus.nMajorityRejectBlockOutdated = 950;
-        consensus.nMajorityWindow = 1000;
+        consensus.powLimit = uint256S("0000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
+        //consensus.nMajorityEnforceBlockUpgrade = 750;
+        //consensus.nMajorityRejectBlockOutdated = 950;
+        //consensus.nMajorityWindow = 1000;
         consensus.nPowTargetTimespan = 3.5 * 24 * 60 * 60; // 3.5 days FIXME not used for GRT?
         consensus.nPowTargetSpacing = 1.5 * 60; // 1.5 minutes
         consensus.nMinerConfirmationWindow = 3360;
@@ -50,36 +53,35 @@
         pchMessageStart[3] = 0xe4;
         nDefaultPort = 9982; /* P2P_PORT */
 
-        nMaxTipAge = 24 * 60 * 60;
-
         /**
          * Build the genesis block. Note that the output of the genesis coinbase cannot
          * be spent as it did not originally exist in the database.
          */
         const char* pszTimestamp = "The Courier-Journal 21-MAR-2015 Prince Charles calls for a revolution";
         CMutableTransaction txNew;
-        txNew.nTime = 1427081625;
+        txNew.Time = 1427081625;
         txNew.vin.resize(1);
         txNew.vout.resize(1);
         txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(9999) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
         txNew.vout[0].nValue = 0; /* SetEmpty() used to do this */
         txNew.vout[0].scriptPubKey.clear(); /* maybe put it back?? */
 
-        genesis.vtx.push_back(txNew);
+        CBlock genesis;
+        genesis.nVersion = 1;
+        genesis.nTime    = 1427086539;
+        genesis.nBits    = 0x1d0fffff;
+        genesis.nNonce   = 413974755;
+        genesis.vtx.push_back(MakeTransactionRef(std::move(txNew)));
         genesis.hashPrevBlock.SetNull();  // = 0;  // old way is simpler to read
         genesis.hashMerkleRoot = BlockMerkleRoot(genesis); //genesis.BuildMerkleTree();
-        genesis.nVersion = 1;
-        genesis.nTime    = 1427086539;
-//const CBigNum bnInitialHashTarget(~uint256(0) >> 28);  // Reduced from Peercoin's 40
-        genesis.nBits    = 0x1d0fffff;
-        genesis.nNonce   = 413974755;
 
-        //cout << genesis.ToString();
+        //std::cout << genesis.ToString();
         assert(genesis.hashMerkleRoot == uint256S("0xca7e1b14fe8d66d18650db8fa0c1b2787fa48b4a342fff3b00aa1cc9b0ae85f3"));
         assert(genesis.GetHash() == uint256S("0000000f0483c7cc4433d89e321373d82d86ef5ba8157d8f7b9ef3449283421a"));
 
-        vSeeds.push_back(CDNSSeedData("grantcoin.net", "seed1.grantcoin.net"));
-        vSeeds.push_back(CDNSSeedData("grantcoin.net", "seed2.grantcoin.net"));
+        vSeeds.emplace_back("grt.7el.us", true);
+//        vSeeds.emplace_back("seed1.grantcoin.net", true);
+//        vSeeds.emplace_back("seed2.grantcoin.net", true);
 
         base58Prefixes[PUBKEY_ADDRESS] = {  38 }; // grantcoin: addresses begin with 'G'
         base58Prefixes[SCRIPT_ADDRESS] = {  97 }; // grantcoin: addresses begin with 'g'
@@ -87,29 +89,27 @@
         base58Prefixes[EXT_PUBLIC_KEY] = {0x04,0x88,0xB2,0x1E};
         base58Prefixes[EXT_SECRET_KEY] = {0x04,0x88,0xAD,0xE4};
 
-        fMiningRequiresPeers = true;
+        //fMiningRequiresPeers = true;
         fDefaultConsistencyChecks = false;
         fRequireStandard = true;
         fMineBlocksOnDemand = false;
-        fTestnetToBeDeprecatedFieldRPC = false;
+        //fTestnetToBeDeprecatedFieldRPC = false;
 
         checkpointData = {
-	{
+            {
 		{  11111, uint256S("00000005e6192125f526f1a8f24b47253282259d6c59adf2039157c523f5ef6c")},
 		{ 100000, uint256S("000000000000266c129bdcc8549e1517a2d3ec2a5c8aedad7f77ae12426be4d4")},
 		{ 200000, uint256S("00000000000019f86a0086b0d5acdf95a8c7a6da2e1cf9c052f8ed6a7a01957f")},
-		//  300000 00000000000019f86a0086b0d5acdf95a8c7a6da2e1cf9c052f8ed6a7a01957f	
-		// 400000 0000000000004937c23ec9c7e8b007370d99b18220624e12d27f9e80723080a0
-		// 500000 000000000000014c803fb1b57d8b05d70d1765ca2d932093b8ff1e00e35b15d3
-		// 600000 0000000000001b44b2e64698557c16224d37331c0d0b69442084dde82dfa9b78
-		// 700000 000000000000751463730a2ede3218249ee7917889a8cf7396ac881cadab5ac7
 		{ 800000, uint256S("00000000004add0b66dab5d4dadae283211f7de0dbd37d2ba6504b603c9b2599")},
 		{ 900000, uint256S("000000000008bef9e174e237d87c067a7f17b289647011465e1a6430fec7a40b")},
 		{ 994000, uint256S("0000000000003599eee46c88fa2d0a52d8b978dd4642f1787e4c465f43b7b2b2")},	
+	    }
 	};
     }
 };
 
+
+#if 0
 /**
  * Testnet (Grantcoin v1)
  */
@@ -128,12 +128,10 @@
         consensus.nPowTargetTimespan = 3.5 * 24 * 60 * 60; // 3.5 days
         consensus.nPowTargetSpacing = 1.5 * 60; // 1.5 minutes
 
-        nMaxTipAge = 0x7fffffff;
-
         //! Modify the genesis block for the testnet
         const char* pszTimestamp = "Reuters 10-OCT-2015 Hundreds of thousands protest in Berlin against EU-US trade deal";
         CMutableTransaction txNew;
-        txNew.nTime = 1444509104;
+        txNew.Time = 1444509104;
         txNew.vin.resize(1);
         txNew.vout.resize(1);
         txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(9999) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
@@ -155,7 +153,7 @@
 
         vFixedSeeds.clear();
         vSeeds.clear();
-        vSeeds.emplace_back("testseed.grantcoin.org", "testseed.grt.7el.us");
+        vSeeds.emplace_back("testseed.grt.7el.us", true);
 
         base58Prefixes[PUBKEY_ADDRESS] = { 65 };  // grantcoin test blockchain: addresses begin with 'T'
         base58Prefixes[SCRIPT_ADDRESS] = { 127 }; // grantcoin test blockchain: addresses begin with 't'
@@ -163,7 +161,7 @@
         base58Prefixes[EXT_PUBLIC_KEY] = { 0x04,0x35,0x87,0xCF};
         base58Prefixes[EXT_SECRET_KEY] = { 0x04,0x35,0x83,0x94};
 
-        fMiningRequiresPeers = true;
+        //fMiningRequiresPeers = true;
         fDefaultConsistencyChecks = false;
         fRequireStandard = false;
         fMineBlocksOnDemand = false;
@@ -262,35 +260,44 @@
         bech32_hrp = "bcrt";
     }
 };
+#endif
 
-static CChainParams *pCurrentParams = 0;
+static std::unique_ptr<CChainParams> globalChainParams;
 
 const CChainParams &Params() {
-    assert(pCurrentParams);
-    return *pCurrentParams;
+    assert(globalChainParams);
+    return *globalChainParams;
 }
 
-CChainParams& Params(const std::string& chain)
+std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain)
 {
     if (chain == CBaseChainParams::MAIN)
-            return mainParams;
+        return std::unique_ptr<CChainParams>(new CMainParams());
+#if 0
     else if (chain == CBaseChainParams::TESTNET)
-            return testNetParams;
-    else
-        throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
+        return std::unique_ptr<CChainParams>(new CTestNetParams());
+    else if (chain == CBaseChainParams::REGTEST)
+        return std::unique_ptr<CChainParams>(new CRegTestParams());
+#endif
+    throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
 }
 
 void SelectParams(const std::string& network)
 {
     SelectBaseParams(network);
-    pCurrentParams = &Params(network);
+    globalChainParams = CreateChainParams(network);
+}
+
+void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+{
+    globalChainParams->UpdateVersionBitsParameters(d, nStartTime, nTimeout);
 }
 
 #include "consensus/validation.h"
+#include "arith_uint256.h"
 #include "chain.h"
 #include "txdb.h"
 #include "utilmoneystr.h"
-#include "arith_uint256.h"
 
 using namespace std;
 using namespace boost;
@@ -303,6 +310,8 @@
 
 const std::string strMessageMagic = "Grantcoin Signed Message:\n";
 
+const std::string CURRENCY_UNIT = "GRT";
+
 int64_t GetProofOfWorkRewardTestNet(int nHeight)
 {
     int64_t nSubsidy = COIN;
@@ -397,13 +406,17 @@
 {
     static int64_t nRewardCoinYear = CENT;  // creation amount per coin-year
     int64_t nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
+#if 0
     if (fDebug && GetBoolArg("-printcreation", false))
         printf("GetProofOfStakeReward(): create=%s nCoinAge=%" PRId64 "\n", FormatMoney(nSubsidy).c_str(), nCoinAge);
+#else
+#warning "fix -printcreation"
+#endif
     return nSubsidy;
 }
 
 /*
- * Get the allow Seigniorage (money creation, or reward) of the current
+ * Get the allowed Seigniorage (money creation, or reward) of the current
  * block. If CoinAge is > 0, this is a proof of stake block.
  */
 int64_t CBlockIndex::GetSeigniorage(int64_t nFees, int64_t CoinAge) const
--- a/src/kernel.cpp	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/kernel.cpp	Fri Feb 12 22:48:17 2021 -0600
@@ -1,63 +1,131 @@
-// Copyright (c) 2009-2012 *coin developers
-// where * = (Nu, Bit, Lite, PP, Peerunity, Blu, Cat, Solar, URO, Grant ...)
-// Previously distributed under the MIT/X11 software license, see the
+// Copyright (c) 2012-2020 The Peercoin developers
+// Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
-// Copyright (c) 2014-2015 Troy Benjegerdes, under AGPLv3
-// Distributed under the Affero GNU General public license version 3
-// file COPYING or http://www.gnu.org/licenses/agpl-3.0.html
 
+#include <kernel.h>
+#include <chainparams.h>
+#include <util.h>
+#include <validation.h>
+#include <streams.h>
+#include <timedata.h>
+#include <bignum.h>
+#include <txdb.h>
+#include <consensus/validation.h>
+#include <random.h>
+#include <script/interpreter.h>
 
 #include <boost/assign/list_of.hpp>
 
-#include "kernel.h"
-#include "db.h"
-#include "txdb.h"
-
 using namespace std;
 
-#if defined(PPCOIN_PROTOCOL_SWITCH)
 // Protocol switch time of v0.3 kernel protocol
 unsigned int nProtocolV03SwitchTime     = 1363800000;
 unsigned int nProtocolV03TestSwitchTime = 1359781000;
 // Protocol switch time of v0.4 kernel protocol
 unsigned int nProtocolV04SwitchTime     = 1399300000;
 unsigned int nProtocolV04TestSwitchTime = 1395700000;
-// TxDB upgrade time for v0.4 protocol
-// Note: v0.4 upgrade does not require block chain re-download. However,
-//       user must upgrade before the protocol switch deadline, otherwise
-//       re-download of blockchain is required. The timestamp of upgrade
-//       is recorded in transaction database to alert user of the requirement.
-unsigned int nProtocolV04UpgradeTime    = 0;
+// Protocol switch time of v0.5 kernel protocol
+unsigned int nProtocolV05SwitchTime     = 1461700000;
+unsigned int nProtocolV05TestSwitchTime = 1447700000;
+// Protocol switch time of v0.6 kernel protocol
+// supermajority hardfork: actual fork will happen later than switch time
+const unsigned int nProtocolV06SwitchTime     = 1513050000; // Tue 12 Dec 03:40:00 UTC 2017
+const unsigned int nProtocolV06TestSwitchTime = 1508198400; // Tue 17 Oct 00:00:00 UTC 2017
+// Protocol switch time for 0.7 kernel protocol
+const unsigned int nProtocolV07SwitchTime     = 1552392000; // Tue 12 Mar 12:00:00 UTC 2019
+const unsigned int nProtocolV07TestSwitchTime = 1541505600; // Tue 06 Nov 12:00:00 UTC 2018
+// Switch time for new BIPs from bitcoin 0.16.x
+const uint32_t nBTC16BIPsSwitchTime = 1569931200; // Tue 01 Oct 12:00:00 UTC 2019
+const uint32_t nBTC16BIPsTestSwitchTime = 1554811200; // Tue 09 Apr 12:00:00 UTC 2019
+// Protocol switch time for v0.9 kernel protocol
+const unsigned int nProtocolV09SwitchTime     = 1591617600; // Mon  8 Jun 12:00:00 UTC 2020
+const unsigned int nProtocolV09TestSwitchTime = 1581940800; // Mon 17 Feb 12:00:00 UTC 2020
+
+// Hard checkpoints of stake modifiers to ensure they are deterministic
+static std::map<int, unsigned int> mapStakeModifierCheckpoints =
+    boost::assign::map_list_of
+    ( 0, 0x0e00670bu )
+    ( 19080, 0xad4e4d29u )
+    ( 30583, 0xdc7bf136u )
+    ( 99999, 0xf555cfd2u )
+    (219999, 0x91b7444du )
+    (336000, 0x6c3c8048u )
+    (371850, 0x9b850bdfu )
+    (407813, 0x46fe50b5u )
+    (443561, 0x114a6e38u )
+    (455470, 0x9b7af181u )
+    (479189, 0xe04fb8e0u )
+    ;
+
+static std::map<int, unsigned int> mapStakeModifierTestnetCheckpoints =
+    boost::assign::map_list_of
+    ( 0, 0x0e00670bu )
+    ( 19080, 0x3711dc3au )
+    ( 30583, 0xb480fadeu )
+    ( 99999, 0x9a62eaecu )
+    (219999, 0xeafe96c3u )
+    (336000, 0x8330dc09u )
+    (372751, 0xafb94e2fu )
+    (382019, 0x7f5cf5ebu )
+    (408500, 0x68cadee2u )
+    (412691, 0x93138e67u )
+    (441667, 0x3303fc25u )
+    (444932, 0x4b8bf2e3u )
+    ;
 
 // Whether the given coinstake is subject to new v0.3 protocol
 bool IsProtocolV03(unsigned int nTimeCoinStake)
 {
-    return (nTimeCoinStake >= (fTestNet? nProtocolV03TestSwitchTime : nProtocolV03SwitchTime));
+    return (nTimeCoinStake >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV03TestSwitchTime : nProtocolV03SwitchTime));
 }
 
 // Whether the given block is subject to new v0.4 protocol
 bool IsProtocolV04(unsigned int nTimeBlock)
 {
-    return (nTimeBlock >= (fTestNet? nProtocolV04TestSwitchTime : nProtocolV04SwitchTime));
+    return (nTimeBlock >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV04TestSwitchTime : nProtocolV04SwitchTime));
 }
-#endif /* PPCOIN_PROTOCOL_SWITCH */
 
-// Modifier interval: time to elapse before new modifier is computed
-// Set to 3-hour for production network and 20-minute for test network
-unsigned int nModifierInterval = MODIFIER_INTERVAL;
+// Whether the given transaction is subject to new v0.5 protocol
+bool IsProtocolV05(unsigned int nTimeTx)
+{
+    return (nTimeTx >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV05TestSwitchTime : nProtocolV05SwitchTime));
+}
 
-extern std::map<int, uint32_t> mapStakeModifierCheckpoints;
+// Whether a given block is subject to new v0.6 protocol
+// Test against previous block index! (always available)
+bool IsProtocolV06(const CBlockIndex* pindexPrev)
+{
+  if (pindexPrev->nTime < (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV06TestSwitchTime : nProtocolV06SwitchTime))
+    return false;
 
-#if defined(PPCOINSTAKE)
+  // if 900 of the last 1,000 blocks are version 2 or greater (90/100 if testnet):
+  // Soft-forking PoS can be dangerous if the super majority is too low
+  // The stake majority will decrease after the fork
+  // since only coindays of updated nodes will get destroyed.
+  if ((Params().NetworkIDString() == CBaseChainParams::MAIN && IsSuperMajority(2, pindexPrev, 900, 1000)) ||
+      (Params().NetworkIDString() != CBaseChainParams::MAIN && IsSuperMajority(2, pindexPrev, 90, 100)))
+    return true;
 
-// Get time weight
-int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd)
+  return false;
+}
+
+// Whether a given transaction is subject to new v0.7 protocol
+bool IsProtocolV07(unsigned int nTimeTx)
 {
-    // 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
+    bool fTestNet = Params().NetworkIDString() != CBaseChainParams::MAIN;
+    return (nTimeTx >= (fTestNet? nProtocolV07TestSwitchTime : nProtocolV07SwitchTime));
+}
 
-    return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64_t)nStakeMaxAge);
+bool IsBTC16BIPsEnabled(uint32_t nTimeTx)
+{
+    bool fTestNet = Params().NetworkIDString() != CBaseChainParams::MAIN;
+    return (nTimeTx >= (fTestNet? nBTC16BIPsTestSwitchTime : nBTC16BIPsSwitchTime));
+}
+
+// Whether a given timestamp is subject to new v0.9 protocol
+bool IsProtocolV09(unsigned int nTime)
+{
+  return (nTime >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV09TestSwitchTime : nProtocolV09SwitchTime));
 }
 
 // Get the last stake modifier and its generation time from a given block
@@ -67,9 +135,8 @@
         return error("GetLastStakeModifier: null pindex");
     while (pindex && pindex->pprev && !pindex->GeneratedStakeModifier())
         pindex = pindex->pprev;
-    if (!pindex->GeneratedStakeModifier()){
+    if (!pindex->GeneratedStakeModifier())
         return error("GetLastStakeModifier: no generation at genesis block");
-    }
     nStakeModifier = pindex->nStakeModifier;
     nModifierTime = pindex->GetBlockTime();
     return true;
@@ -79,8 +146,7 @@
 static int64_t GetStakeModifierSelectionIntervalSection(int nSection)
 {
     assert (nSection >= 0 && nSection < 64);
-    int64_t a = nModifierInterval * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1)));
-	return a;
+    return (Params().GetConsensus().nModifierInterval * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1))));
 }
 
 // Get stake modifier selection interval (in seconds)
@@ -88,9 +154,7 @@
 {
     int64_t nSelectionInterval = 0;
     for (int nSection=0; nSection<64; nSection++)
-    {
         nSelectionInterval += GetStakeModifierSelectionIntervalSection(nSection);
-    }
     return nSelectionInterval;
 }
 
@@ -104,12 +168,12 @@
     const CBlockIndex** pindexSelected)
 {
     bool fSelected = false;
-    uint256 hashBest = 0;
+    arith_uint256 hashBest = 0;
     *pindexSelected = (const CBlockIndex*) 0;
-    BOOST_FOREACH(const PAIRTYPE(int64_t, uint256)& item, vSortedByTimestamp)
+    for (const auto& item : vSortedByTimestamp)
     {
         if (!mapBlockIndex.count(item.second))
-            return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString().c_str());
+            return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString());
         const CBlockIndex* pindex = mapBlockIndex[item.second];
         if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop)
             break;
@@ -120,7 +184,7 @@
         uint256 hashProof = pindex->IsProofOfStake()? pindex->hashProofOfStake : pindex->GetBlockHash();
         CDataStream ss(SER_GETHASH, 0);
         ss << hashProof << nStakeModifierPrev;
-        uint256 hashSelection = Hash(ss.begin(), ss.end());
+        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
@@ -138,8 +202,8 @@
             *pindexSelected = (const CBlockIndex*) pindex;
         }
     }
-    if (fDebug && GetBoolArg("-printstakemodifier"))
-        printf("SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString().c_str());
+    if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printstakemodifier", false))
+        LogPrintf("SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString());
     return fSelected;
 }
 
@@ -152,23 +216,19 @@
 // selected block of a given block group in the past.
 // The selection of a block is based on a hash of the block's proof-hash and
 // the previous stake modifier.
-// Stake modifier is recomputed at a fixed time interval instead of every 
+// Stake modifier is recomputed at a fixed time interval instead of every
 // block. This is to make it difficult for an attacker to gain control of
 // additional bits in the stake modifier, even after generating a chain of
 // blocks.
-bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier)
+bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t &nStakeModifier, bool& fGeneratedStakeModifier)
 {
-    //LOCK(mapBlockIndex.cs_cleanup);  /* from nubits */
-    LOCK(cs_main); /* just in case */
+    const Consensus::Params& params = Params().GetConsensus();
     const CBlockIndex* pindexPrev = pindexCurrent->pprev;
     nStakeModifier = 0;
     fGeneratedStakeModifier = false;
     if (!pindexPrev)
     {
         fGeneratedStakeModifier = true;
-		if (fDebug){
-			printf("ComputeNextStakeModifier nHeight=%d returned %" PRId64 "\n", 0, nStakeModifier);
-		}
         return true;  // genesis block's modifier is 0
     }
     // First find current stake modifier and its generation block time
@@ -176,50 +236,35 @@
     int64_t nModifierTime = 0;
     if (!GetLastStakeModifier(pindexPrev, nStakeModifier, nModifierTime))
         return error("ComputeNextStakeModifier: unable to get last modifier");
-    if (fDebug)
+    if (gArgs.GetBoolArg("-debug", false))
+        LogPrintf("ComputeNextStakeModifier: prev modifier=0x%016x time=%s epoch=%u\n", nStakeModifier, DateTimeStrFormat(nModifierTime), (unsigned int)nModifierTime);
+    if (nModifierTime / params.nModifierInterval >= pindexPrev->GetBlockTime() / params.nModifierInterval)
     {
-        printf("ComputeNextStakeModifier: prev modifier=0x%016" PRIx64" time=% " PRId64 "\n", nStakeModifier, nModifierTime);
-    }
-    if (nModifierTime / nModifierInterval >= pindexPrev->GetBlockTime() / nModifierInterval)
-    {
-        if (fDebug)
-        {
-            printf("ComputeNextStakeModifier: no new interval keep current modifier: pindexPrev nHeight=%d nTime=%u\n", pindexPrev->nHeight, (unsigned int)pindexPrev->GetBlockTime());
-        }
+        if (gArgs.GetBoolArg("-debug", false))
+            LogPrintf("ComputeNextStakeModifier: no new interval keep current modifier: pindexPrev nHeight=%d nTime=%u\n", pindexPrev->nHeight, (unsigned int)pindexPrev->GetBlockTime());
         return true;
     }
-#if defined(BRAND_cleanwatercoin)
-    // Not quite the same as ppcoin v0.4 protocol. 
-    // TODO: figure out how this is exploitable
-    if (nModifierTime / nModifierInterval >= pindexPrev->GetBlockTime() / nModifierInterval)
-        return true;
-#else
-    if (nModifierTime / nModifierInterval >= pindexCurrent->GetBlockTime() / nModifierInterval)
+    if (nModifierTime / params.nModifierInterval >= pindexCurrent->GetBlockTime() / params.nModifierInterval)
     {
         // v0.4+ requires current block timestamp also be in a different modifier interval
         if (IsProtocolV04(pindexCurrent->nTime))
         {
-            if (fDebug)
-            {
-                printf("ComputeNextStakeModifier: (v0.4+) no new interval keep current modifier: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
-            }
+            if (gArgs.GetBoolArg("-debug", false))
+                LogPrintf("ComputeNextStakeModifier: (v0.4+) no new interval keep current modifier: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
             return true;
         }
         else
         {
-            if (fDebug)
-            {
-                printf("ComputeNextStakeModifier: v0.3 modifier at block %s not meeting v0.4+ protocol: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->GetBlockHash().ToString().c_str(), pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
-            }
+            if (gArgs.GetBoolArg("-debug", false))
+                LogPrintf("ComputeNextStakeModifier: v0.3 modifier at block %s not meeting v0.4+ protocol: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->GetBlockHash().ToString(), pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
         }
     }
-#endif
 
     // Sort candidate blocks by timestamp
     vector<pair<int64_t, uint256> > vSortedByTimestamp;
-    vSortedByTimestamp.reserve(64 * nModifierInterval / STAKE_TARGET_SPACING);
+    vSortedByTimestamp.reserve(64 * params.nModifierInterval / params.nStakeTargetSpacing);
     int64_t nSelectionInterval = GetStakeModifierSelectionInterval();
-    int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / nModifierInterval) * nModifierInterval - nSelectionInterval;
+    int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / params.nModifierInterval) * params.nModifierInterval - nSelectionInterval;
     const CBlockIndex* pindex = pindexPrev;
     while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart)
     {
@@ -227,8 +272,26 @@
         pindex = pindex->pprev;
     }
     int nHeightFirstCandidate = pindex ? (pindex->nHeight + 1) : 0;
-    reverse(vSortedByTimestamp.begin(), vSortedByTimestamp.end());
-    sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end());
+
+    // Shuffle before sort
+    for(int i = vSortedByTimestamp.size() - 1; i > 1; --i)
+    std::swap(vSortedByTimestamp[i], vSortedByTimestamp[GetRand(i)]);
+
+    sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end(), [] (const pair<int64_t, uint256> &a, const pair<int64_t, uint256> &b)
+    {
+        if (a.first != b.first)
+            return a.first < b.first;
+        // Timestamp equals - compare block hashes
+        const uint32_t *pa = a.second.GetDataPtr();
+        const uint32_t *pb = b.second.GetDataPtr();
+        int cnt = 256 / 32;
+        do {
+            --cnt;
+            if (pa[cnt] != pb[cnt])
+                return pa[cnt] < pb[cnt];
+        } while(cnt);
+            return false; // Elements are equal
+    });
 
     // Select 64 blocks from candidate blocks to generate stake modifier
     uint64_t nStakeModifierNew = 0;
@@ -245,13 +308,13 @@
         nStakeModifierNew |= (((uint64_t)pindex->GetStakeEntropyBit()) << nRound);
         // add the selected block from candidates to selected list
         mapSelectedBlocks.insert(make_pair(pindex->GetBlockHash(), pindex));
-        if (fDebug && GetBoolArg("-printstakemodifier"))
-            printf("ComputeNextStakeModifier: selected round %d stop=%" PRId64 " height=%d bit=%d\n",
-                nRound, nSelectionIntervalStop, pindex->nHeight, pindex->GetStakeEntropyBit());
+        if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printstakemodifier", false))
+            LogPrintf("ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n",
+                nRound, DateTimeStrFormat(nSelectionIntervalStop), pindex->nHeight, pindex->GetStakeEntropyBit());
     }
 
     // Print selection map for visualization of the selected blocks
-    if (fDebug && GetBoolArg("-printstakemodifier"))
+    if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printstakemodifier", false))
     {
         string strSelectionMap = "";
         // '-' indicates proof-of-work blocks not selected
@@ -264,57 +327,52 @@
                 strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "=");
             pindex = pindex->pprev;
         }
-        BOOST_FOREACH(const PAIRTYPE(uint256, const CBlockIndex*)& item, mapSelectedBlocks)
+        for (const auto& item : mapSelectedBlocks)
         {
             // '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");
         }
-        printf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap.c_str());
+        LogPrintf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap);
     }
-    if (fDebug)
-    {
-        printf("ComputeNextStakeModifier: new modifier=0x%016" PRIx64" time=%" PRId64"\n", nStakeModifierNew, pindexPrev->GetBlockTime());
-    }
+    if (gArgs.GetBoolArg("-debug", false))
+        LogPrintf("ComputeNextStakeModifier: new modifier=0x%016x time=%s\n", nStakeModifierNew, DateTimeStrFormat(pindexPrev->GetBlockTime()));
 
     nStakeModifier = nStakeModifierNew;
     fGeneratedStakeModifier = true;
     return true;
 }
 
-// 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
-static bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier,
-        int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake)
+// V0.5: Stake modifier used to hash for a stake kernel is chosen as the stake
+// modifier that is (nStakeMinAge minus a selection interval) earlier than the
+// stake, thus at least a selection interval later than the coin generating the // kernel, as the generating coin is from at least nStakeMinAge ago.
+static bool GetKernelStakeModifierV05(CBlockIndex* pindexPrev, unsigned int nTimeTx, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake)
 {
-    nStakeModifier = 0;
-    if (!mapBlockIndex.count(hashBlockFrom))
+    const Consensus::Params& params = Params().GetConsensus();
+    const CBlockIndex* pindex = pindexPrev;
+    nStakeModifierHeight = pindex->nHeight;
+    nStakeModifierTime = pindex->GetBlockTime();
+    int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
+
+    if (nStakeModifierTime + params.nStakeMinAge - nStakeModifierSelectionInterval <= (int64_t) nTimeTx)
     {
-        return error("GetKernelStakeModifier() : block not indexed");
+        // Best block is still more than
+        // (nStakeMinAge minus a selection interval) older than kernel timestamp
+        if (fPrintProofOfStake)
+            return error("GetKernelStakeModifier() : best block %s at height %d too old for stake",
+                pindex->GetBlockHash().ToString(), pindex->nHeight);
+        else
+            return false;
     }
-    const CBlockIndex* pindexFrom = mapBlockIndex[hashBlockFrom];
-    nStakeModifierHeight = pindexFrom->nHeight;
-    nStakeModifierTime = pindexFrom->GetBlockTime();
-    int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
-    const CBlockIndex* pindex = pindexFrom;
-    // loop to find the stake modifier later by a selection interval
-    while (nStakeModifierTime < pindexFrom->GetBlockTime() + nStakeModifierSelectionInterval)
+    // loop to find the stake modifier earlier by 
+    // (nStakeMinAge minus a selection interval)
+    while (nStakeModifierTime + params.nStakeMinAge - nStakeModifierSelectionInterval >(int64_t) nTimeTx)
     {
-        if (!pindex->pnext)
-        {   // reached best block; may happen if node is behind on block chain
-            if (fPrintProofOfStake || (pindex->GetBlockTime() + nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime()))
-                return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s",
-                    pindex->GetBlockHash().ToString().c_str(), pindex->nHeight, hashBlockFrom.ToString().c_str());
-            else
-            {
-                if (fDebug && GetBoolArg("-printstakemodifierfalse"))
-			printf(">> nStakeModifierTime = %" PRId64 ", pindexFrom->GetBlockTime() = %" PRId64
-				", nStakeModifierSelectionInterval = %" PRId64"\n",
-				nStakeModifierTime, pindexFrom->GetBlockTime(), nStakeModifierSelectionInterval);
-                return false;
+        if (!pindex->pprev)
+        {   // reached genesis block; should not happen
+            return error("GetKernelStakeModifier() : reached genesis block");
         }
-        }
-        pindex = pindex->pnext;
+        pindex = pindex->pprev;
         if (pindex->GeneratedStakeModifier())
         {
             nStakeModifierHeight = pindex->nHeight;
@@ -325,7 +383,70 @@
     return true;
 }
 
-// ppcoin kernel protocol
+// V0.3: 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
+static bool GetKernelStakeModifierV03(CBlockIndex* pindexPrev, uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake)
+{
+    const Consensus::Params& params = Params().GetConsensus();
+    nStakeModifier = 0;
+    if (!mapBlockIndex.count(hashBlockFrom))
+        return error("GetKernelStakeModifier() : block not indexed");
+    const CBlockIndex* pindexFrom = mapBlockIndex[hashBlockFrom];
+    nStakeModifierHeight = pindexFrom->nHeight;
+    nStakeModifierTime = pindexFrom->GetBlockTime();
+    int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
+
+
+    // we need to iterate index forward but we cannot depend on chainActive.Next()
+    // because there is no guarantee that we are checking blocks in active chain.
+    // So, we construct a temporary chain that we will iterate over.
+    // pindexFrom - this block contains coins that are used to generate PoS
+    // pindexPrev - this is a block that is previous to PoS block that we are checking, you can think of it as tip of our chain
+    std::vector<CBlockIndex*> tmpChain;
+    int32_t nDepth = pindexPrev->nHeight - (pindexFrom->nHeight-1); // -1 is used to also include pindexFrom
+    tmpChain.reserve(nDepth);
+    CBlockIndex* it = pindexPrev;
+    for (int i=1; i<=nDepth && !chainActive.Contains(it); i++) {
+        tmpChain.push_back(it);
+        it = it->pprev;
+    }
+    std::reverse(tmpChain.begin(), tmpChain.end());
+    size_t n = 0;
+
+    const CBlockIndex* pindex = pindexFrom;
+    // loop to find the stake modifier later by a selection interval
+    while (nStakeModifierTime < pindexFrom->GetBlockTime() + nStakeModifierSelectionInterval)
+    {
+        const CBlockIndex* old_pindex = pindex;
+        pindex = (!tmpChain.empty() && pindex->nHeight >= tmpChain[0]->nHeight - 1)? tmpChain[n++] : chainActive.Next(pindex);
+        if (n > tmpChain.size() || pindex == NULL) // check if tmpChain[n+1] exists
+        {   // reached best block; may happen if node is behind on block chain
+            if (fPrintProofOfStake || (old_pindex->GetBlockTime() + params.nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime()))
+                return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s",
+                    old_pindex->GetBlockHash().ToString(), old_pindex->nHeight, hashBlockFrom.ToString());
+            else
+                return false;
+        }
+        if (pindex->GeneratedStakeModifier())
+        {
+            nStakeModifierHeight = pindex->nHeight;
+            nStakeModifierTime = pindex->GetBlockTime();
+        }
+    }
+    nStakeModifier = pindex->nStakeModifier;
+    return true;
+}
+
+// Get the stake modifier specified by the protocol to hash for a stake kernel
+static bool GetKernelStakeModifier(CBlockIndex* pindexPrev, uint256 hashBlockFrom, unsigned int nTimeTx, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake)
+{
+    if (IsProtocolV05(nTimeTx))
+        return GetKernelStakeModifierV05(pindexPrev, nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake);
+    else
+        return GetKernelStakeModifierV03(pindexPrev, hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake);
+}
+
+// peercoin kernel protocol
 // coinstake must meet hash target according to the protocol:
 // kernel (input 0) must meet the formula
 //     hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDayWeight
@@ -333,6 +454,9 @@
 // amount of coin age one owns.
 // The reason this hash is chosen is the following:
 //   nStakeModifier: 
+//       (v0.5) uses dynamic stake modifier around 21 days before the kernel,
+//              versus static stake modifier about 9 days after the staked
+//              coin (txPrev) used in v0.3
 //       (v0.3) scrambles computation to make it very difficult to precompute
 //              future proof-of-stake at the time of the coin's confirmation
 //       (v0.2) nBits (deprecated): encodes all past block timestamps
@@ -348,27 +472,24 @@
 //   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 CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake)
+bool CheckStakeKernelHash(unsigned int nBits, CBlockIndex* pindexPrev, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake)
 {
-    if (nTimeTx < txPrev.nTime)  // Transaction timestamp violation
+    const Consensus::Params& params = Params().GetConsensus();
+    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 + params.nStakeMinAge > nTimeTx) // Min age requirement
         return error("CheckStakeKernelHash() : min age violation");
 
     CBigNum bnTargetPerCoinDay;
     bnTargetPerCoinDay.SetCompact(nBits);
-    int64_t nValueIn = txPrev.vout[prevout.n].nValue;
-
-    // v0.3 protocol kernel hash weight starts from 0 at the min age
+    int64_t nValueIn = txPrev->vout[prevout.n].nValue;
+    // v0.3 protocol kernel hash weight starts from 0 at the 30-day min age
     // this change increases active coins participating the hash and helps
     // to secure the network when proof-of-stake difficulty is low
-    int64_t nTimeWeight = min((int64_t)nTimeTx - txPrev.nTime, (int64_t)nStakeMaxAge) - (IsProtocolV03(nTimeTx)? nStakeMinAge : 0);
+    int64_t nTimeWeight = min((int64_t)nTimeTx - txPrev->nTime, params.nStakeMaxAge) - (IsProtocolV03(nTimeTx)? params.nStakeMinAge : 0);
     CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60);
-    if(fDebug && GetBoolArg("-printstakehashtime"))
-        printf(">>> CheckStakeKernelHash: nTimeTx = %d, txPrev.nTime = %d, nTimeWeight = %" PRId64"\n",
-		nTimeTx, txPrev.nTime, nTimeWeight);
     // Calculate hash
     CDataStream ss(SER_GETHASH, 0);
     uint64_t nStakeModifier = 0;
@@ -376,76 +497,65 @@
     int64_t nStakeModifierTime = 0;
     if (IsProtocolV03(nTimeTx))  // v0.3 protocol
     {
-        if (!GetKernelStakeModifier(blockFrom.GetHash(), nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake))
+        if (!GetKernelStakeModifier(pindexPrev, blockFrom.GetHash(), nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake))
             return false;
         ss << nStakeModifier;
     }
     else // v0.2 protocol
     {
-		assert("CheckStakeKernelHasH: we should not be here: v0.2 protocol");
         ss << nBits;
     }
 
-    ss << nTimeBlockFrom << nTxPrevOffset << txPrev.nTime << prevout.n << nTimeTx;
+    ss << nTimeBlockFrom << nTxPrevOffset << txPrev->nTime << prevout.n << nTimeTx;
     hashProofOfStake = Hash(ss.begin(), ss.end());
     if (fPrintProofOfStake)
     {
         if (IsProtocolV03(nTimeTx))
-            printf("CheckStakeKernelHash() : using modifier 0x%016" PRIx64 " at height=%d timestamp=%" PRId64 " for block from height=%d timestamp=%" PRId64 "\n",
+            LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
                 nStakeModifier, nStakeModifierHeight,
-                nStakeModifierTime,
+                DateTimeStrFormat(nStakeModifierTime),
                 mapBlockIndex[blockFrom.GetHash()]->nHeight,
-                blockFrom.GetBlockTime());
-        printf("CheckStakeKernelHash() : check protocol=%s modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
-            IsProtocolV03(nTimeTx)? "0.3" : "0.2",
+                DateTimeStrFormat(blockFrom.GetBlockTime()));
+        LogPrintf("CheckStakeKernelHash() : check protocol=%s modifier=0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
+            IsProtocolV05(nTimeTx)? "0.5" : (IsProtocolV03(nTimeTx)? "0.3" : "0.2"),
             IsProtocolV03(nTimeTx)? nStakeModifier : (uint64_t) nBits,
-            nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
-            hashProofOfStake.ToString().c_str());
+            nTimeBlockFrom, nTxPrevOffset, txPrev->nTime, prevout.n, nTimeTx,
+            hashProofOfStake.ToString());
     }
 
     // Now check if proof-of-stake hash meets target protocol
     if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
         return false;
-    if (fDebug && !fPrintProofOfStake)
+    if (gArgs.GetBoolArg("-debug", false) && !fPrintProofOfStake)
     {
         if (IsProtocolV03(nTimeTx))
-            printf("CheckStakeKernelHash() : using modifier 0x%016" PRIx64 " at height=%d timestamp=%" PRId64 " for block from height=%d timestamp=%" PRId64 "\n",
+            LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
                 nStakeModifier, nStakeModifierHeight, 
-                nStakeModifierTime,
+                DateTimeStrFormat(nStakeModifierTime),
                 mapBlockIndex[blockFrom.GetHash()]->nHeight,
-                blockFrom.GetBlockTime());
-        printf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
+                DateTimeStrFormat(blockFrom.GetBlockTime()));
+        LogPrintf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
             IsProtocolV03(nTimeTx)? "0.3" : "0.2",
             IsProtocolV03(nTimeTx)? nStakeModifier : (uint64_t) nBits,
-            nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
-            hashProofOfStake.ToString().c_str());
+            nTimeBlockFrom, nTxPrevOffset, txPrev->nTime, prevout.n, nTimeTx,
+            hashProofOfStake.ToString());
     }
     return true;
 }
 
 // Check kernel hash target and coinstake signature
-bool CheckProofOfStake(CValidationState &state, const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake)
+bool CheckProofOfStake(CValidationState &state, CBlockIndex* pindexPrev, const CTransactionRef& tx, unsigned int nBits, uint256& hashProofOfStake)
 {
-    if (!tx.IsCoinStake())
-        return error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString().c_str());
+    if (!tx->IsCoinStake())
+        return error("CheckProofOfStake() : called on non-coinstake %s", tx->GetHash().ToString());
 
     // Kernel (input 0) must match the stake hash target per coin age (nBits)
-    const CTxIn& txin = tx.vin[0];
+    const CTxIn& txin = tx->vin[0];
 
     // Transaction index is required to get to block header
     if (!fTxIndex)
         return error("CheckProofOfStake() : transaction index not available");
 
-    // First try finding the previous transaction in database
-    CCoinsViewCache view(*pcoinsTip, true);
-    CCoins coins;
-    if (!view.GetCoins(txin.prevout.hash, coins))
-        return state.DoS(1, error("CheckProofOfStake() : txPrev not found")); // previous transaction not in main chain, may occur during initial download
-
-    // Verify signature
-    if (!VerifySignature(coins, tx, 0, true, 0))
-        return state.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString().c_str()));
-
     // Get transaction index for the previous transaction
     CDiskTxPos postx;
     if (!pblocktree->ReadTxIndex(txin.prevout.hash, postx))
@@ -453,22 +563,32 @@
 
     // Read txPrev and header of its block
     CBlockHeader header;
-    CTransaction txPrev;
+    CTransactionRef txPrev;
     {
         CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
         try {
             file >> header;
-            fseek(file, postx.nTxOffset, SEEK_CUR);
+            fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
             file >> txPrev;
         } catch (std::exception &e) {
             return error("%s() : deserialize or I/O error in CheckProofOfStake()", __PRETTY_FUNCTION__);
         }
-        if (txPrev.GetHash() != txin.prevout.hash)
+        if (txPrev->GetHash() != txin.prevout.hash)
             return error("%s() : txid mismatch in CheckProofOfStake()", __PRETTY_FUNCTION__);
     }
 
-    if (!CheckStakeKernelHash(nBits, header, postx.nTxOffset + sizeof(CBlockHeader), txPrev, txin.prevout, tx.nTime, hashProofOfStake, fDebug))
-        return state.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString().c_str(), hashProofOfStake.ToString().c_str())); // may occur during initial download or if behind on block chain sync
+    // Verify signature
+    {
+        int nIn = 0;
+        const CTxOut& prevOut = txPrev->vout[tx->vin[nIn].prevout.n];
+        TransactionSignatureChecker checker(&(*tx), nIn, prevOut.nValue, PrecomputedTransactionData(*tx));
+
+        if (!VerifyScript(tx->vin[nIn].scriptSig, prevOut.scriptPubKey, &(tx->vin[nIn].scriptWitness), SCRIPT_VERIFY_P2SH, checker, nullptr))
+            return state.DoS(100, false, REJECT_INVALID, "invalid-pos-script", false, strprintf("%s: VerifyScript failed on coinstake %s", __func__, tx->GetHash().ToString()));
+    }
+
+    if (!CheckStakeKernelHash(nBits, pindexPrev, header, postx.nTxOffset + CBlockHeader::NORMAL_SERIALIZE_SIZE, txPrev, txin.prevout, tx->nTime, hashProofOfStake, gArgs.GetBoolArg("-debug", false)))
+        return state.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx->GetHash().ToString(), hashProofOfStake.ToString())); // may occur during initial download or if behind on block chain sync
 
     return true;
 }
@@ -479,34 +599,72 @@
     if (IsProtocolV03(nTimeTx))  // v0.3 protocol
         return (nTimeBlock == nTimeTx);
     else // v0.2 protocol
-        return ((nTimeTx <= nTimeBlock) && (nTimeBlock <= nTimeTx + nMaxClockDrift));
+        return ((nTimeTx <= nTimeBlock) && (nTimeBlock <= nTimeTx + MAX_FUTURE_BLOCK_TIME_PREV9));
 }
 
 // Get stake modifier checksum
 unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex)
 {
-    assert (pindex->pprev || pindex->GetBlockHash() == hashGenesisBlock);
+    assert (pindex->pprev || pindex->GetBlockHash() == Params().GetConsensus().hashGenesisBlock);
     // Hash previous checksum with flags, hashProofOfStake and nStakeModifier
     CDataStream ss(SER_GETHASH, 0);
     if (pindex->pprev)
         ss << pindex->pprev->nStakeModifierChecksum;
     ss << pindex->nFlags << pindex->hashProofOfStake << pindex->nStakeModifier;
-    uint256 hashChecksum = Hash(ss.begin(), ss.end());
+    arith_uint256 hashChecksum = UintToArith256(Hash(ss.begin(), ss.end()));
     hashChecksum >>= (256 - 32);
-    return hashChecksum.Get64();
+    return hashChecksum.GetLow64();
 }
 
 // Check stake modifier hard checkpoints
 bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum)
 {
-#warning "stake modifier checkpoints disabled"
-    if (fTestNet) return true; // Testnet has no checkpoints
-#if 0
-    if (mapStakeModifierCheckpoints.count(nHeight))
+    bool fTestNet = Params().NetworkIDString() == CBaseChainParams::TESTNET;
+    if (fTestNet && mapStakeModifierTestnetCheckpoints.count(nHeight))
+        return nStakeModifierChecksum == mapStakeModifierTestnetCheckpoints[nHeight];
+
+    if (!fTestNet && mapStakeModifierCheckpoints.count(nHeight))
         return nStakeModifierChecksum == mapStakeModifierCheckpoints[nHeight];
-#else
-#warning "stake modifier checkpoints disabled"
-#endif
+
     return true;
 }
-#endif /* PPCOINSTAKE */
+
+bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck)
+{
+    unsigned int nFound = 0;
+    for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; pstart = pstart->pprev )
+    {
+        if (!pstart->IsProofOfStake())
+            continue;
+
+        if (pstart->nVersion >= minVersion)
+            ++nFound;
+
+        i++;
+    }
+    return (nFound >= nRequired);
+}
+
+// peercoin: entropy bit for stake modifier if chosen by modifier
+unsigned int GetStakeEntropyBit(const CBlock& block)
+{
+    unsigned int nEntropyBit = 0;
+    if (IsProtocolV04(block.nTime))
+    {
+        nEntropyBit = UintToArith256(block.GetHash()).GetLow64() & 1llu;// last bit of block hash
+        if (gArgs.GetBoolArg("-printstakemodifier", false))
+            LogPrintf("GetStakeEntropyBit(v0.4+): nTime=%u hashBlock=%s entropybit=%d\n", block.nTime, block.GetHash().ToString(), nEntropyBit);
+    }
+    else
+    {
+        // old protocol for entropy bit pre v0.4
+        uint160 hashSig = Hash160(block.vchBlockSig);
+        if (gArgs.GetBoolArg("-printstakemodifier", false))
+            LogPrintf("GetStakeEntropyBit(v0.3): nTime=%u hashSig=%s", block.nTime, hashSig.ToString());
+        nEntropyBit = hashSig.GetDataPtr()[4] >> 31;  // take the first bit of the hash
+        if (gArgs.GetBoolArg("-printstakemodifier", false))
+            LogPrintf(" entropybit=%d\n", nEntropyBit);
+    }
+    return nEntropyBit;
+}
+
--- a/src/kernel.h	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/kernel.h	Fri Feb 12 22:48:17 2021 -0600
@@ -1,7 +1,5 @@
-// Copyright (c) 2012-2013 The PPCoin developers
-// Copyright (c) 2009-2015 The *coin developers
-// where * = (Nu, Bit, Lite, PP, Peerunity, Blu, Cat, Solar, URO, ...)
-// Previously distributed under the MIT/X11 software license, see the
+// Copyright (c) 2012-2020 The Peercoin developers
+// Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 // Copyright (c) 2014-2015 Troy Benjegerdes, under AGPLv3
 // Distributed under the Affero GNU General public license version 3
@@ -9,38 +7,49 @@
 #ifndef CODECOIN_KERNEL_H
 #define CODECOIN_KERNEL_H
 
-#include "codecoin.h"
-#include "main.h"
+#include <codecoin.h>
+#include <primitives/transaction.h> // CTransaction(Ref)
 
+class CBlockIndex;
+class CValidationState;
+class CBlockHeader;
+class CBlock;
 
-#if defined(PPCOIN_PROTOCOL_SWITCH) /* legacy compatibility */
+
+// MODIFIER_INTERVAL_RATIO:
+// ratio of group interval length between the last group and the first group
+static const int MODIFIER_INTERVAL_RATIO = 3;
+
 // Protocol switch time of v0.3 kernel protocol
 extern unsigned int nProtocolV03SwitchTime;
 extern unsigned int nProtocolV03TestSwitchTime;
-// TxDB upgrade time for v0.4 protocol
-extern unsigned int nProtocolV04UpgradeTime;
 
 // Whether a given coinstake is subject to new v0.3 protocol
 bool IsProtocolV03(unsigned int nTimeCoinStake);
 // Whether a given block is subject to new v0.4 protocol
 bool IsProtocolV04(unsigned int nTimeBlock);
-#else
-// Whether a given coinstake is subject to new v0.3 protocol
-inline bool IsProtocolV03(unsigned int nTimeCoinStake){ return true; };
-// Whether a given block is subject to new v0.4 protocol
-inline bool IsProtocolV04(unsigned int nTimeBlock){ return true; };
-#endif
+// Whether a given transaction is subject to new v0.5 protocol
+bool IsProtocolV05(unsigned int nTimeTx);
+// Whether a given block is subject to new v0.6 protocol
+// Test against previous block index! (always available)
+bool IsProtocolV06(const CBlockIndex *pindexPrev);
+// Whether a given transaction is subject to new v0.7 protocol
+bool IsProtocolV07(unsigned int nTimeTx);
+// Whether a given block is subject to new BIPs from bitcoin 0.16.x
+bool IsBTC16BIPsEnabled(uint32_t nTimeTx);
+// Whether a given timestamp is subject to new v0.9 protocol
+bool IsProtocolV09(unsigned int nTimeTx);
 
 // Compute the hash modifier for proof-of-stake
 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 CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake=false);
+bool CheckStakeKernelHash(unsigned int nBits, CBlockIndex* pindexPrev, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake=false);
 
 // Check kernel hash target and coinstake signature
 // Sets hashProofOfStake on success return
-bool CheckProofOfStake(CValidationState &state, const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake);
+bool CheckProofOfStake(CValidationState &state, CBlockIndex* pindexPrev, const CTransactionRef &tx, unsigned int nBits, uint256& hashProofOfStake);
 
 // Check whether the coinstake timestamp meets protocol
 bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx);
@@ -51,8 +60,9 @@
 // Check stake modifier hard checkpoints
 bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum);
 
-// Get time weight using supplied timestamps
-// TODO: actually use this
-int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd);
+bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck);
+
+// peercoin: entropy bit for stake modifier if chosen by modifier
+unsigned int GetStakeEntropyBit(const CBlock& block);
 
 #endif // CODECOIN_KERNEL_H
--- a/src/kernelrecord.cpp	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/kernelrecord.cpp	Fri Feb 12 22:48:17 2021 -0600
@@ -1,8 +1,10 @@
-#include "kernelrecord.h"
+#include <kernelrecord.h>
 
-#include "wallet.h"
-#include "base58.h"
+#include <wallet/wallet.h>
+#include <base58.h>
+#include <timedata.h>
 
+#include <math.h>
 using namespace std;
 
 bool KernelRecord::showTransaction(const CWalletTx &wtx)
@@ -10,15 +12,10 @@
     if (wtx.IsCoinBase())
     {
         if (wtx.GetDepthInMainChain() < 2)
-        {
             return false;
-        }
-    }
-
-    if(!wtx.IsConfirmed())
-    {
-        return false;
-    }
+    } else
+        if (wtx.GetDepthInMainChain() == 0)
+            return false;
 
     return true;
 }
@@ -28,17 +25,18 @@
  */
 vector<KernelRecord> KernelRecord::decomposeOutput(const CWallet *wallet, const CWalletTx &wtx)
 {
+    const Consensus::Params& params = Params().GetConsensus();
     vector<KernelRecord> parts;
     int64_t nTime = wtx.GetTxTime();
     uint256 hash = wtx.GetHash();
     std::map<std::string, std::string> mapValue = wtx.mapValue;
-    int nDayWeight = (min((GetAdjustedTime() - nTime), (int64_t)nStakeMaxAge) - nStakeMinAge) / 86400;
+    int nDayWeight = (min((GetAdjustedTime() - nTime), params.nStakeMaxAge) - params.nStakeMinAge) / 86400;
 
     if (showTransaction(wtx))
     {
-        for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
+        for (size_t nOut = 0; nOut < wtx.tx->vout.size(); nOut++)
         {
-            CTxOut txOut = wtx.vout[nOut];
+            CTxOut txOut = wtx.tx->vout[nOut];
             if( wallet->IsMine(txOut) ) {
                 CTxDestination address;
                 std::string addrStr;
@@ -48,15 +46,14 @@
                 if (ExtractDestination(txOut.scriptPubKey, address))
                 {
                     // Sent to Bitcoin Address
-                    addrStr = CBitcoinAddress(address).ToString();
+                    addrStr = EncodeDestination(address);
                 }
                 else
                 {
                     // Sent to IP, or other non-address transaction like OP_EVAL
                     addrStr = mapValue["to"];
                 }
-
-                parts.push_back(KernelRecord(hash, nTime, addrStr, txOut.nValue, wtx.IsSpent(nOut), coinAge));
+                parts.push_back(KernelRecord(hash, nTime, addrStr, txOut.nValue, nOut, wallet->IsSpent(hash, nOut), coinAge));
             }
         }
     }
@@ -76,9 +73,10 @@
 
 double KernelRecord::getProbToMintStake(double difficulty, int timeOffset) const
 {
+    const Consensus::Params& params = Params().GetConsensus();
     double maxTarget = pow(static_cast<double>(2), 224);
     double target = maxTarget / difficulty;
-    int dayWeight = (min((GetAdjustedTime() - nTime) + timeOffset, (int64_t)nStakeMaxAge) - nStakeMinAge) / 86400;
+    int dayWeight = (min((GetAdjustedTime() - nTime) + timeOffset, params.nStakeMaxAge) - params.nStakeMinAge) / 86400;
     uint64_t coinAge = max(nValue * dayWeight / COIN, (int64_t)0);
     return target * coinAge / pow(static_cast<double>(2), 256);
 }
--- a/src/kernelrecord.h	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/kernelrecord.h	Fri Feb 12 22:48:17 2021 -0600
@@ -1,7 +1,10 @@
-#ifndef KERNELRECORD_H
-#define KERNELRECORD_H
+// Copyright (c) 2012-2020 The Peercoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifndef PEERCOIN_KERNELRECORD_H
+#define PEERCOIN_KERNELRECORD_H
 
-#include "uintBIG.h"
+#include <uint256.h>
 
 class CWallet;
 class CWalletTx;
@@ -21,9 +24,9 @@
 
     KernelRecord(uint256 hash, int64_t nTime,
                  const std::string &address,
-                 int64_t nValue, bool spent, int64_t coinAge):
+                 int64_t nValue, int idx, bool spent, int64_t coinAge):
         hash(hash), nTime(nTime), address(address), nValue(nValue),
-        idx(0), spent(spent), coinAge(coinAge), prevMinutes(0), prevDifficulty(0), prevProbability(0)
+        idx(idx), spent(spent), coinAge(coinAge), prevMinutes(0), prevDifficulty(0), prevProbability(0)
     {
     }
 
@@ -49,4 +52,4 @@
     double prevProbability;
 };
 
-#endif // KERNELRECORD_H
+#endif // PEERCOIN_KERNELRECORD_H
--- a/src/make.Darwin.mk	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/make.Darwin.mk	Fri Feb 12 22:48:17 2021 -0600
@@ -1,23 +1,33 @@
 OS_DEFS=-DMAC_OSX
 
+
+ifdef BREW
 BREW=/usr/local/opt
 
 BOOST=boost\@1.66
 BDB=berkeley-db\@53
 OPENSSL=openssl\@1.1
 
+OS_EXTRA_INCLUDE_PATH= \
+        $(BREW)/$(OPENSSL)/include \
+        $(BREW)/$(BOOST)/include \
+        $(BREW)/$(BDB)/include
+
+OS_EXTRA_LIB_PATH = \
+        $(BREW)/$(OPENSSL)/lib \
+        $(BREW)/$(BOOST)/lib \
+        $(BREW)/$(BDB)/lib \
+	/usr/local/lib
+else
+OS_EXTRA_INCLUDE_PATH= \
+	/opt/local/include \
+	/usr/local/BerkeleyDB.5.3/include
+
+OS_EXTRA_LIB_PATH= \
+	/opt/local/lib \
+	/usr/local/BerkeleyDB.5.3/lib
+endif
+
 BOOST_LIB_SUFFIX=-mt
 
-
-OS_EXTRA_INCLUDE_PATH= \
-	$(BREW)/$(OPENSSL)/include \
-	$(BREW)/$(BOOST)/include \
-	$(BREW)/$(BDB)/include
-
-OS_EXTRA_LIB_PATH = \
-	$(BREW)/$(OPENSSL)/lib \
-	$(BREW)/$(BOOST)/lib \
-	$(BREW)/$(BDB)/lib \
-	/usr/local/lib
-
 OS_LDFLAGS = $(addprefix -L,$(OS_EXTRA_LIB_PATH))
--- a/src/pow.cpp	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/pow.cpp	Fri Feb 12 22:48:17 2021 -0600
@@ -52,7 +52,7 @@
     int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
     assert(nHeightFirst >= 0);
     const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
-#elif defined(BRAND_litecoin)
+#elif defined(BRAND_litecoin) || defined(BRAND_grantcoin)
     // Litecoin: This fixes an issue where a 51% attack can change difficulty at will.
     // Go back the full period unless it's the first retarget after genesis. Code courtesy of Art Forz
     int blockstogoback = params.DifficultyAdjustmentInterval()-1;
@@ -91,7 +91,7 @@
     bnNew *= nActualTimespan;
     bnNew /= params.nPowTargetTimespan;
 
-#elif defined(BRAND_litecoin)
+#elif defined(BRAND_litecoin) || defined(BRAND_grantcoin)
     arith_uint256 bnOld;
     bnNew.SetCompact(pindexLast->nBits);
     bnOld = bnNew;
--- a/src/primitives/transaction.cpp	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/primitives/transaction.cpp	Fri Feb 12 22:48:17 2021 -0600
@@ -114,8 +114,17 @@
 SegwitTx::SegwitTx(MutableSegwitTx &&tx) : Transaction(tx, true) {}
 
 #if defined(BRAND_grantcoin)
+MutableStakeTx::MutableStakeTx(): MutableTransaction(), Time(0) {}
+MutableStakeTx::MutableStakeTx(const StakeTx & tx): MutableTransaction(tx), Time(tx.Time) {}
+
 StakeTx::StakeTx() : Transaction(), Time(0) {}
 
+/* TODO: is this boilerplate really necessary to duplicate? */
+uint256 StakeTx::ComputeHash() const
+{
+    return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
+}
+
 StakeTx::StakeTx(const MutableStakeTx &tx)
 	: Transaction(tx, false), Time(tx.Time){
 	uint256 *phash = const_cast <uint256*> (&hash);
--- a/src/primitives/transaction.h	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/primitives/transaction.h	Fri Feb 12 22:48:17 2021 -0600
@@ -166,6 +166,20 @@
         return (nValue == -1);
     }
 
+#if defined(BRAND_grantcoin)
+    // TODO move this to StakeTxOut
+    void SetEmpty()
+    {
+        nValue = 0;
+        scriptPubKey.clear();
+    }
+
+    bool IsEmpty() const
+    {
+        return (nValue == 0 && scriptPubKey.empty());
+    }
+#endif
+
     friend bool operator==(const TxOut& a, const TxOut& b)
     {
         return (a.nValue       == b.nValue &&
@@ -326,14 +340,19 @@
 {
 public:
     // Default transaction version.
+#if defined(PPCOINSTAKE) || defined(BRAND_grantcoin) // hack for grantcoin/ppcoin
+    // TODO: fix this with proper initializers
+    static const int32_t CURRENT_VERSION=1;
+    static const int32_t MAX_STANDARD_VERSION=1;
+
+#else // bitcoin/litecoin
     static const int32_t CURRENT_VERSION=2;
-
     // Changing the default transaction version requires a two step process: first
     // adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date
     // bumping the default CURRENT_VERSION at which point both CURRENT_VERSION and
     // MAX_STANDARD_VERSION will be equal.
     static const int32_t MAX_STANDARD_VERSION=2;
-
+#endif
     // The local variables are made const to prevent unintended modification
     // without updating the cached hash value. However, CTransaction is not
     // actually immutable; deserialization and assignment are implemented,
@@ -453,6 +472,13 @@
 #if defined(BRAND_grantcoin) /* FIXME update this later */
 class MutableStakeTx;
 
+enum GetMinFee_mode
+{       
+    GMF_BLOCK,
+    GMF_RELAY,
+    GMF_SEND,
+};
+
 class StakeTx : public Transaction
 {
 public:
@@ -483,6 +509,15 @@
         return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() >= 1);
     }
 
+    bool IsCoinStake() const
+    {
+        // ppcoin: the coin stake transaction is marked with the first output empty
+        // givecoin: Do we want this, or something else?
+        return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty());
+    }
+
+    int64_t GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, enum GetMinFee_mode mode=GMF_BLOCK, unsigned int nBytes=0) const;
+
     /* ugly compat for witness code. */
     bool HasWitness() const
     {
--- a/src/test/README.md	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/test/README.md	Fri Feb 12 22:48:17 2021 -0600
@@ -5,7 +5,7 @@
 
 After configuring, they can be run with `make check`.
 
-To run the bitcoind tests manually, launch `src/test/test_bitcoin`. To recompile
+To run the bitcoind tests manually, launch `src/test/test_codecoin`. To recompile
 after a test file was modified, run `make` and then run the test again. If you
 modify a non-test file, use `make -C src/test` to recompile only what's needed
 to run the bitcoind tests.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/main_tests.cpp	Fri Feb 12 22:48:17 2021 -0600
@@ -0,0 +1,77 @@
+// Copyright (c) 2014-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <validation.h>
+#include <net.h>
+
+#include <test/test_bitcoin.h>
+
+#include <boost/signals2/signal.hpp>
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(main_tests, TestingSetup)
+
+static void TestBlockSubsidyHalvings(const Consensus::Params& consensusParams)
+{
+    int maxHalvings = 64;
+    CAmount nInitialSubsidy = 50 * COIN;
+
+    CAmount nPreviousSubsidy = nInitialSubsidy * 2; // for height == 0
+    BOOST_CHECK_EQUAL(nPreviousSubsidy, nInitialSubsidy * 2);
+    for (int nHalvings = 0; nHalvings < maxHalvings; nHalvings++) {
+        int nHeight = nHalvings * consensusParams.nSubsidyHalvingInterval;
+        CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams);
+        BOOST_CHECK(nSubsidy <= nInitialSubsidy);
+        BOOST_CHECK_EQUAL(nSubsidy, nPreviousSubsidy / 2);
+        nPreviousSubsidy = nSubsidy;
+    }
+    BOOST_CHECK_EQUAL(GetBlockSubsidy(maxHalvings * consensusParams.nSubsidyHalvingInterval, consensusParams), 0);
+}
+
+static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval)
+{
+    Consensus::Params consensusParams;
+    consensusParams.nSubsidyHalvingInterval = nSubsidyHalvingInterval;
+    TestBlockSubsidyHalvings(consensusParams);
+}
+
+BOOST_AUTO_TEST_CASE(block_subsidy_test)
+{
+    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+    TestBlockSubsidyHalvings(chainParams->GetConsensus()); // As in main
+    TestBlockSubsidyHalvings(150); // As in regtest
+    TestBlockSubsidyHalvings(1000); // Just another interval
+}
+
+BOOST_AUTO_TEST_CASE(subsidy_limit_test)
+{
+    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+    CAmount nSum = 0;
+    for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) {
+        CAmount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus());
+        BOOST_CHECK(nSubsidy <= 50 * COIN);
+        nSum += nSubsidy * 1000;
+        BOOST_CHECK(MoneyRange(nSum));
+    }
+    BOOST_CHECK_EQUAL(nSum, 2099999997690000ULL);
+}
+
+bool ReturnFalse() { return false; }
+bool ReturnTrue() { return true; }
+
+BOOST_AUTO_TEST_CASE(test_combiner_all)
+{
+    boost::signals2::signal<bool (), CombinerAll> Test;
+    BOOST_CHECK(Test());
+    Test.connect(&ReturnFalse);
+    BOOST_CHECK(!Test());
+    Test.connect(&ReturnTrue);
+    BOOST_CHECK(!Test());
+    Test.disconnect(&ReturnFalse);
+    BOOST_CHECK(Test());
+    Test.disconnect(&ReturnTrue);
+    BOOST_CHECK(Test());
+}
+BOOST_AUTO_TEST_SUITE_END()
--- a/src/test/test_bitcoin_main.cpp	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/test/test_bitcoin_main.cpp	Fri Feb 12 22:48:17 2021 -0600
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2016 The Bitcoin Core developers
+// Copyright (c) 2011-2017 The Bitcoin Core developers
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
@@ -6,6 +6,8 @@
 
 #include <net.h>
 
+#include <memory>
+
 #include <boost/test/unit_test.hpp>
 
 std::unique_ptr<CConnman> g_connman;
--- a/src/test/test_codecoin.cpp	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/test/test_codecoin.cpp	Fri Feb 12 22:48:17 2021 -0600
@@ -1,5 +1,6 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2011-2016 The Bitcoin Core developers
+// Copyright (c) 2011-2017 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
 // Copyright (c) 2014-2017 Troy Benjegerdes, under AGPLv3
 // Distributed under the Affero GNU General public license version 3
 // file COPYING or http://www.gnu.org/licenses/agpl-3.0.html
@@ -39,6 +40,12 @@
 extern bool fPrintToConsole;
 extern void noui_connect();
 
+std::ostream& operator<<(std::ostream& os, const uint256& num)
+{
+    os << num.ToString();
+    return os;
+}
+
 BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
 {
         SHA256AutoDetect();
@@ -71,9 +78,9 @@
         fs::create_directories(pathTemp);
         gArgs.ForceSetArg("-datadir", pathTemp.string());
 
-        // Note that because we don't bother running a scheduler thread here,
-        // callbacks via CValidationInterface are unreliable, but that's OK,
-        // our unit tests aren't testing multiple parts of the code at once.
+        // We have to run a scheduler thread to prevent ActivateBestChain
+        // from blocking due to queue overrun.
+        threadGroup.create_thread(boost::bind(&CScheduler::serviceQueue, &scheduler));
         GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
 
         mempool.setSanityCheck(1.0);
--- a/src/wallet/wallet.h	Sat Jun 09 23:13:07 2018 -0500
+++ b/src/wallet/wallet.h	Fri Feb 12 22:48:17 2021 -0600
@@ -62,6 +62,14 @@
 static const CAmount DEFAULT_DISCARD_FEE = 10000;
 //! -mintxfee default
 static const CAmount DEFAULT_TRANSACTION_MINFEE = 100000;
+#elif defined(BRAND_grantcoin)
+#warning "double-check this"
+//! -fallbackfee default
+static const CAmount DEFAULT_FALLBACK_FEE = 2000000;
+//! -m_discard_rate default
+static const CAmount DEFAULT_DISCARD_FEE = 10000;
+//! -mintxfee default
+static const CAmount DEFAULT_TRANSACTION_MINFEE = 100000;
 #else
 #error "Fix this later"
 #endif