]> Git Repo - VerusCoin.git/blobdiff - src/miner.cpp
Update root chain name
[VerusCoin.git] / src / miner.cpp
index ac7158b6bed9cf0088351a9ca19620a939bc9fc5..976c57bb072c11b8534bdb5036a5e8ee9bac1b6d 100644 (file)
@@ -10,7 +10,7 @@
 
 #include "amount.h"
 #include "chainparams.h"
-#include "cc/CoinbaseGuard.h"
+#include "cc/StakeGuard.h"
 #include "importcoin.h"
 #include "consensus/consensus.h"
 #include "consensus/upgrades.h"
@@ -35,6 +35,9 @@
 #include "wallet/wallet.h"
 #endif
 
+#include "zcash/Address.hpp"
+#include "transaction_builder.h"
+
 #include "sodium.h"
 
 #include <boost/thread.hpp>
@@ -44,6 +47,9 @@
 #endif
 #include <mutex>
 
+#include "pbaas/pbaas.h"
+#include "pbaas/notarization.h"
+
 using namespace std;
 
 //////////////////////////////////////////////////////////////////////////////
@@ -103,7 +109,12 @@ public:
 
 void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
 {
-    pblock->nTime = 1 + std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+    pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+
+    // Updating time can change work required on testnet:
+    if (consensusParams.nPowAllowMinDifficultyBlocksAfterHeight != boost::none) {
+        pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
+    }
 }
 
 #include "komodo_defs.h"
@@ -111,10 +122,17 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams,
 extern CCriticalSection cs_metrics;
 extern int32_t KOMODO_MININGTHREADS,KOMODO_LONGESTCHAIN,ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAIN_INIT,KOMODO_INITDONE,KOMODO_ON_DEMAND,KOMODO_INITDONE,KOMODO_PASSPORT_INITDONE;
 extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_STAKED;
+extern bool VERUS_MINTBLOCKS;
 extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[];
 extern const char *ASSETCHAINS_ALGORITHMS[];
 extern int32_t VERUS_MIN_STAKEAGE, ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA, ASSETCHAINS_LWMAPOS, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[];
 extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
+extern uint160 ASSETCHAINS_CHAINID;
+extern uint160 VERUS_CHAINID;
+extern std::string VERUS_CHAINNAME;
+extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK;
+extern string PBAAS_HOST, PBAAS_USERPASS;
+extern int32_t PBAAS_PORT;
 extern std::string NOTARY_PUBKEY,ASSETCHAINS_OVERRIDE_PUBKEY;
 void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len);
 
@@ -146,7 +164,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
     uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params();
     //fprintf(stderr,"create new block\n");
-  // Create new block
+    // Create new block
     if ( gpucount < 0 )
         gpucount = KOMODO_MAXGPUCOUNT;
     std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
@@ -156,7 +174,20 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         return NULL;
     }
     CBlock *pblock = &pblocktemplate->block; // pointer for convenience
-     // -regtest only: allow overriding block.nVersion with
+
+    // set version according to the current tip height, add solution if it is
+    // VerusHash
+    if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH)
+    {
+        pblock->nSolution.resize(Eh200_9.SolutionWidth);
+    }
+    else
+    {
+        pblock->nSolution.clear();
+    }
+    pblock->SetVersionByHeight(chainActive.LastTip()->GetHeight() + 1);
+
+    // -regtest only: allow overriding block.nVersion with
     // -blockversion=N to test forking scenarios
     if (Params().MineBlocksOnDemand())
         pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
@@ -183,12 +214,20 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
     
     // Collect memory pool transactions into the block
     CAmount nFees = 0;
+
+    // we will attempt to spend any cheats we see
+    CTransaction cheatTx;
+    boost::optional<CTransaction> cheatSpend;
+    uint256 cbHash;
+
     CBlockIndex* pindexPrev = 0;
     {
         LOCK2(cs_main, mempool.cs);
         pindexPrev = chainActive.LastTip();
         const int nHeight = pindexPrev->GetHeight() + 1;
-        uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus());
+        const Consensus::Params &consensusParams = chainparams.GetConsensus();
+        uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, consensusParams);
+        bool sapling = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_SAPLING);
 
         const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
         uint32_t proposedTime = GetAdjustedTime();
@@ -218,7 +257,147 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         
         // This vector will be sorted into a priority queue:
         vector<TxPriority> vecPriority;
-        vecPriority.reserve(mempool.mapTx.size());
+        vecPriority.reserve(mempool.mapTx.size() + 1);
+
+        // check if we should add cheat transaction
+        CBlockIndex *ppast;
+        CTransaction cb;
+        int cheatHeight = nHeight - COINBASE_MATURITY < 1 ? 1 : nHeight - COINBASE_MATURITY;
+        if (cheatCatcher &&
+            sapling && chainActive.Height() > 100 && 
+            (ppast = chainActive[cheatHeight]) && 
+            ppast->IsVerusPOSBlock() && 
+            cheatList.IsHeightOrGreaterInList(cheatHeight))
+        {
+            // get the block and see if there is a cheat candidate for the stake tx
+            CBlock b;
+            if (!(fHavePruned && !(ppast->nStatus & BLOCK_HAVE_DATA) && ppast->nTx > 0) && ReadBlockFromDisk(b, ppast, 1))
+            {
+                CTransaction &stakeTx = b.vtx[b.vtx.size() - 1];
+
+                if (cheatList.IsCheatInList(stakeTx, &cheatTx))
+                {
+                    // make and sign the cheat transaction to spend the coinbase to our address
+                    CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
+
+                    uint32_t voutNum;
+                    // get the first vout with value
+                    for (voutNum = 0; voutNum < b.vtx[0].vout.size(); voutNum++)
+                    {
+                        if (b.vtx[0].vout[voutNum].nValue > 0)
+                            break;
+                    }
+
+                    // send to the same pub key as the destination of this block reward
+                    if (MakeCheatEvidence(mtx, b.vtx[0], voutNum, cheatTx))
+                    {
+                        extern CWallet *pwalletMain;
+                        LOCK(pwalletMain->cs_wallet);
+                        TransactionBuilder tb = TransactionBuilder(consensusParams, nHeight);
+                        cb = b.vtx[0];
+                        cbHash = cb.GetHash();
+
+                        bool hasInput = false;
+                        for (uint32_t i = 0; i < cb.vout.size(); i++)
+                        {
+                            // add the spends with the cheat
+                            if (cb.vout[i].nValue > 0)
+                            {
+                                tb.AddTransparentInput(COutPoint(cbHash,i), cb.vout[0].scriptPubKey, cb.vout[0].nValue);
+                                hasInput = true;
+                            }
+                        }
+
+                        if (hasInput)
+                        {
+                            // this is a send from a t-address to a sapling address, which we don't have an ovk for. 
+                            // Instead, generate a common one from the HD seed. This ensures the data is
+                            // recoverable, at least for us, while keeping it logically separate from the ZIP 32
+                            // Sapling key hierarchy, which the user might not be using.
+                            uint256 ovk;
+                            HDSeed seed;
+                            if (pwalletMain->GetHDSeed(seed)) {
+                                ovk = ovkForShieldingFromTaddr(seed);
+
+                                // send everything to Sapling address
+                                tb.SendChangeTo(cheatCatcher.value(), ovk);
+
+                                tb.AddOpRet(mtx.vout[mtx.vout.size() - 1].scriptPubKey);
+
+                                cheatSpend = tb.Build();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (cheatSpend)
+        {
+            cheatTx = cheatSpend.value();
+            std::list<CTransaction> removed;
+            mempool.removeConflicts(cheatTx, removed);
+            printf("Found cheating stake! Adding cheat spend for %.8f at block #%d, coinbase tx\n%s\n",
+                (double)cb.GetValueOut() / (double)COIN, nHeight, cheatSpend.value().vin[0].prevout.hash.GetHex().c_str());
+
+            // add to mem pool and relay
+            if (myAddtomempool(cheatTx))
+            {
+                RelayTransaction(cheatTx);
+            }
+        }
+
+        // if we are a PBaaS chain, first make sure we don't start prematurely, and if
+        // we should make an earned notarization, make it and set index to non-zero value
+        int32_t pbaasNotarizationTx = 0;
+        int64_t pbaasTransparentIn = 0;
+        int32_t pbaasCoinbaseInstantSpendOut = 0;
+        if (!IsVerusActive())
+        {
+            // if we don't have a connected root PBaaS chain, we can't properly check
+            // and notarize the start block, so we have to pass and wait
+            if (nHeight != 1 || (ConnectedChains.IsVerusPBaaSAvailable() && ConnectedChains.notaryChainHeight >= PBAAS_STARTBLOCK))
+            {
+                // if we have access to our parent daemon
+                // create a notarization, if we would qualify, and add it to the mempool and block
+                CMutableTransaction newNotarizationTx;
+                CTransaction prevTx, crossTx;
+                ChainMerkleMountainView mmv = chainActive.GetMMV();
+                uint256 mmrRoot = mmv.GetRoot();
+                if (CreateEarnedNotarization(newNotarizationTx, prevTx, crossTx, nHeight, mmrRoot))
+                {
+                    // we have a valid, earned notarization transaction. we still need to verify:
+                    // 1. it has enough input for its outputs. it can be returned with less than enough, 
+                    // and if so, take it as instant-spend from the coinbase. instant-spend and notarization 
+                    // threads on a PBaaS chain can only be used for notarization, and can never convert to 
+                    // available supply
+
+                    // Fetch previous transactions (inputs):
+                    for (const CTxIn& txin : newNotarizationTx.vin)
+                    {
+                        const uint256& prevHash = txin.prevout.hash;
+                        const CCoins *pcoins = view.AccessCoins(prevHash); // this is certainly allowed to fail
+                        pbaasTransparentIn += pcoins && (pcoins->vout.size() > txin.prevout.n) ? pcoins->vout[txin.prevout.n].nValue : 0;
+                    }
+                    pblock->vtx.push_back(CTransaction(newNotarizationTx));
+                    pblocktemplate->vTxFees.push_back(0);
+                    pblocktemplate->vTxSigOps.push_back(-1); // updated at end
+                    pbaasNotarizationTx = pblock->vtx.size() - 1;
+                }
+                else if (nHeight == 1)
+                {
+                    // failed to notarize at block 1
+                    return NULL;
+                }
+            }
+            else
+            {
+                // can't mine block 1 unless we have a connection to Verus and can notarize
+                return NULL;
+            }
+        }
+
+        // now add transactions from the mem pool
         for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
              mi != mempool.mapTx.end(); ++mi)
         {
@@ -227,17 +406,19 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
             ? nMedianTimePast
             : pblock->GetBlockTime();
-            
+
             if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight))
             {
                 //fprintf(stderr,"coinbase.%d finaltx.%d expired.%d\n",tx.IsCoinBase(),IsFinalTx(tx, nHeight, nLockTimeCutoff),IsExpiredTx(tx, nHeight));
                 continue;
             }
+
             if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,nHeight,(uint32_t)pblock->nTime,0) < 0 )
             {
                 //fprintf(stderr,"CreateNewBlock: komodo_validate_interest failure nHeight.%d nTime.%u vs locktime.%u\n",nHeight,(uint32_t)pblock->nTime,(uint32_t)tx.nLockTime);
                 continue;
             }
+
             COrphan* porphan = NULL;
             double dPriority = 0;
             CAmount nTotalIn = 0;
@@ -310,7 +491,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             else
                 vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx())));
         }
-        
+
         // Collect transactions into block
         uint64_t nBlockSize = 1000;
         uint64_t nBlockTx = 0;
@@ -434,7 +615,6 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
 
         int32_t stakeHeight = chainActive.Height() + 1;
-        bool extendedStake = (Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight >= stakeHeight);
 
         //LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x\n", nBlockSize,blocktime,pblock->nBits);
         if ( ASSETCHAINS_SYMBOL[0] != 0 && isStake )
@@ -467,7 +647,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
                 // after Sapling, stake transactions have a fee, but it is recovered in the reward
                 // this ensures that a rebroadcast goes through quickly to begin staking again
-                txfees = extendedStake ? DEFAULT_STAKE_TXFEE : 0;
+                txfees = sapling ? DEFAULT_STAKE_TXFEE : 0;
 
                 pblock->vtx.push_back(txStaked);
                 pblocktemplate->vTxFees.push_back(txfees);
@@ -479,22 +659,22 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         }
         
         // Create coinbase tx
-        CMutableTransaction txNew = CreateNewContextualCMutableTransaction(chainparams.GetConsensus(), nHeight);
+        CMutableTransaction txNew = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
         txNew.vin.resize(1);
         txNew.vin[0].prevout.SetNull();
         txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
 
         txNew.vout.resize(1);
         txNew.vout[0].scriptPubKey = scriptPubKeyIn;
-        txNew.vout[0].nValue = GetBlockSubsidy(nHeight,chainparams.GetConsensus()) + nFees;
+        txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees;
 
-        // once we get to Sapling, enable CC CoinbaseGuard for stake transactions
-        if (isStake && extendedStake)
+        // once we get to Sapling, enable CC StakeGuard for stake transactions
+        if (isStake && sapling)
         {
             // if there is a specific destination, use it
             CTransaction stakeTx = pblock->vtx[pblock->vtx.size() - 1];
             CStakeParams p;
-            if (ValidateStakeTransaction(stakeTx, p))
+            if (ValidateStakeTransaction(stakeTx, p, false))
             {
                 if (!p.pk.IsValid() || !MakeGuardedOutput(txNew.vout[0].nValue, p.pk, stakeTx, txNew.vout[0]))
                 {
@@ -553,6 +733,79 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             //printf("autocreate commision vout\n");
         }
 
+        // if we need an instant out to be a source of funds for the notarization transaction, make it here
+        if (pbaasNotarizationTx)
+        {
+            extern CWallet *pwalletMain;
+            LOCK(pwalletMain->cs_wallet);
+
+            // now add an input from the coinbase to the notarization
+            CMutableTransaction mntx(pblock->vtx[pbaasNotarizationTx]);
+            CTransaction ntx = pblock->vtx[pbaasNotarizationTx];
+
+            int64_t pbaasTxValueOut = ntx.GetValueOut();
+            int numOutputs = ntx.vout.size() - (ntx.vout[ntx.vout.size() - 1].scriptPubKey.IsOpReturn() ? 1 : 0);
+            if (pbaasNotarizationTx != 0 && pbaasTxValueOut > pbaasTransparentIn)
+            {
+                int64_t needed = pbaasTxValueOut - pbaasTransparentIn;
+                if (needed > PBAAS_MINNOTARIZATIONOUTPUT * numOutputs)
+                {
+                    fprintf(stderr,"CreateNewBlock: too much output from earned notarization transaction\n");
+                    return NULL;
+                }
+
+                auto coinbaseOutIt = txNew.vout.end();
+                pbaasCoinbaseInstantSpendOut = txNew.vout.size();
+                if ((coinbaseOutIt - 1)->scriptPubKey.IsOpReturn())
+                {
+                    coinbaseOutIt -= 1;
+                    pbaasCoinbaseInstantSpendOut -= 1;
+                }
+
+                CCcontract_info CC;
+                CCcontract_info *cp;
+                vector<CTxDestination> vKeys;
+
+                // make the earned notarization output
+                cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
+                // need to be able to send this to EVAL_PBAASDEFINITION address as a destination, locked by the default pubkey
+                CPubKey pk = CPubKey(std::vector<unsigned char>(CC.CChexstr, CC.CChexstr + strlen(CC.CChexstr)));
+
+                vKeys.push_back(CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(VERUS_CHAINID, EVAL_EARNEDNOTARIZATION))));
+
+                // output duplicate notarization as coinbase output for instant spend to notarization
+                // these coins will never join the supply pool, so they do not need to be considered as
+                // part of the total value out sum of this coinbase
+                CPBaaSNotarization pbn(ntx);
+                txNew.vout.insert(coinbaseOutIt, MakeCC1of1Vout(EVAL_EARNEDNOTARIZATION, PBAAS_MINNOTARIZATIONOUTPUT, pk, vKeys, pbn));
+                mntx.vin.push_back(CTxIn(txNew.GetHash(), pbaasCoinbaseInstantSpendOut));
+            }
+
+            // we need to sign mntx and put it back
+            int nIn = 0;
+            ntx = CTransaction(mntx);
+
+            for (int i = 0; i < ntx.vin.size(); i++)
+            {
+                bool signSuccess;
+                const CCoins *coins = view.AccessCoins(ntx.vin[i].prevout.hash);
+                const CScript& scriptPubKey = coins->vout[ntx.vin[i].prevout.n].scriptPubKey;
+                SignatureData sigdata;
+
+                signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &ntx, i, coins->vout[ntx.vin[i].prevout.n].nValue, SIGHASH_ALL), scriptPubKey, sigdata, consensusBranchId);
+
+                if (!signSuccess)
+                {
+                    fprintf(stderr,"CreateNewBlock: failure to sign earned notarization\n");
+                    return NULL;
+                } else {
+                    UpdateTransaction(mntx, i, sigdata);
+                }
+            }
+            pblock->vtx[pbaasNotarizationTx] = mntx;
+            pblocktemplate->vTxSigOps[pbaasNotarizationTx] = GetLegacySigOpCount(mntx);
+        }
+
         pblock->vtx[0] = txNew;
         pblocktemplate->vTxFees[0] = -nFees;
 
@@ -578,8 +831,9 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
             pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
         }
-        pblock->nSolution.clear();
+
         pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
+
         if ( ASSETCHAINS_SYMBOL[0] == 0 && IS_KOMODO_NOTARY != 0 && My_notaryid >= 0 )
         {
             uint32_t r;
@@ -717,17 +971,20 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
     }
     else
     {
-        if (!reservekey.GetReservedKey(pubkey))
+        if (!isStake)
         {
-            return NULL;
+            if (!reservekey.GetReservedKey(pubkey))
+            {
+                return NULL;
+            }
+            scriptPubKey.resize(35);
+            ptr = (uint8_t *)pubkey.begin();
+            scriptPubKey[0] = 33;
+            for (i=0; i<33; i++)
+                scriptPubKey[i+1] = ptr[i];
+            scriptPubKey[34] = OP_CHECKSIG;
+            //scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
         }
-        scriptPubKey.resize(35);
-        ptr = (uint8_t *)pubkey.begin();
-        scriptPubKey[0] = 33;
-        for (i=0; i<33; i++)
-            scriptPubKey[i+1] = ptr[i];
-        scriptPubKey[34] = OP_CHECKSIG;
-        //scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
     }
     return CreateNewBlock(scriptPubKey, gpucount, isStake);
 }
@@ -763,7 +1020,6 @@ static bool ProcessBlockFound(CBlock* pblock)
     
     // Found a solution
     {
-        //LOCK(cs_main);
         if (pblock->hashPrevBlock != chainActive.LastTip()->GetBlockHash())
         {
             uint256 hash; int32_t i;
@@ -816,27 +1072,63 @@ int32_t FOUND_BLOCK,KOMODO_MAYBEMINED;
 extern int32_t KOMODO_LASTMINED,KOMODO_INSYNC;
 int32_t roundrobin_delay;
 arith_uint256 HASHTarget,HASHTarget_POW;
+int32_t komodo_longestchain();
 
 // wait for peers to connect
-int32_t waitForPeers(const CChainParams &chainparams)
+void waitForPeers(const CChainParams &chainparams)
 {
     if (chainparams.MiningRequiresPeers())
     {
         bool fvNodesEmpty;
         {
+            boost::this_thread::interruption_point();
             LOCK(cs_vNodes);
             fvNodesEmpty = vNodes.empty();
         }
-        if (fvNodesEmpty || !IsInSync())
+        int longestchain = komodo_longestchain();
+        int lastlongest = 0;
+        if (fvNodesEmpty || IsNotInSync() || (longestchain != 0 && longestchain > chainActive.LastTip()->GetHeight()))
         {
+            int loops = 0, blockDiff = 0, newDiff = 0;
+            
             do {
                 if (fvNodesEmpty)
-                    MilliSleep(1000 + rand() % 4000);
                 {
+                    MilliSleep(1000 + rand() % 4000);
+                    boost::this_thread::interruption_point();
                     LOCK(cs_vNodes);
                     fvNodesEmpty = vNodes.empty();
+                    loops = 0;
+                    blockDiff = 0;
+                    lastlongest = 0;
                 }
-            } while (fvNodesEmpty || !IsInSync());
+                else if ((newDiff = IsNotInSync()) > 0)
+                {
+                    if (blockDiff != newDiff)
+                    {
+                        blockDiff = newDiff;
+                    }
+                    else
+                    {
+                        if (++loops <= 5)
+                        {
+                            MilliSleep(1000);
+                        }
+                        else break;
+                    }
+                    lastlongest = 0;
+                }
+                else if (!fvNodesEmpty && !IsNotInSync() && longestchain > chainActive.LastTip()->GetHeight())
+                {
+                    // the only thing may be that we are seeing a long chain that we'll never get
+                    // don't wait forever
+                    if (lastlongest == 0)
+                    {
+                        MilliSleep(3000);
+                        lastlongest = longestchain;
+                    }
+                }
+            } while (fvNodesEmpty || IsNotInSync());
             MilliSleep(100 + rand() % 400);
         }
     }
@@ -858,6 +1150,16 @@ CBlockIndex *get_chainactive(int32_t height)
     return(0);
 }
 
+/*
+ * When called, this checks to see if Verus daemon is running and available. If so, it calls to get the latest
+ * notarization data and all information necessary to make a notarization transaction for the current chain, or the
+ * Verus chain.
+ */
+void static UpdateNotarizationData()
+{
+
+}
+
 /*
  * A separate thread to stake, while the miner threads mine.
  */
@@ -867,14 +1169,14 @@ void static VerusStaker(CWallet *pwallet)
     RenameThread("verus-staker");
 
     const CChainParams& chainparams = Params();
+    auto consensusParams = chainparams.GetConsensus();
 
     // Each thread has its own key
     CReserveKey reservekey(pwallet);
 
     // Each thread has its own counter
     unsigned int nExtraNonce = 0;
-    std::vector<unsigned char> solnPlaceholder = std::vector<unsigned char>();
-    solnPlaceholder.resize(Eh200_9.SolutionWidth);
+
     uint8_t *script; uint64_t total,checktoshis; int32_t i,j;
 
     while ( (ASSETCHAIN_INIT == 0 || KOMODO_INITDONE == 0) ) //chainActive.Tip()->GetHeight() != 235300 &&
@@ -894,20 +1196,28 @@ void static VerusStaker(CWallet *pwallet)
     } while (pindexPrev != pindexCur);
 
     try {
+        static int32_t lastStakingHeight = 0;
+
         while (true)
         {
             waitForPeers(chainparams);
             CBlockIndex* pindexPrev = chainActive.LastTip();
-            printf("Staking height %d for %s\n", pindexPrev->GetHeight() + 1, ASSETCHAINS_SYMBOL);
 
             // Create new block
             unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
+
             if ( Mining_height != pindexPrev->GetHeight()+1 )
             {
                 Mining_height = pindexPrev->GetHeight()+1;
                 Mining_start = (uint32_t)time(NULL);
             }
 
+            if ( Mining_height != lastStakingHeight )
+            {
+                printf("Staking height %d for %s\n", Mining_height, ASSETCHAINS_SYMBOL);
+                lastStakingHeight = Mining_height;
+            }
+
             // Check for stop or if block needs to be rebuilt
             boost::this_thread::interruption_point();
 
@@ -920,7 +1230,7 @@ void static VerusStaker(CWallet *pwallet)
             {
                 // wait to try another staking block until after the tip moves again
                 while ( chainActive.LastTip() == pindexPrev )
-                    sleep(1);
+                    MilliSleep(100);
                 continue;
             }
 
@@ -944,13 +1254,23 @@ void static VerusStaker(CWallet *pwallet)
             //
             int64_t nStart = GetTime();
 
-            // take up the necessary space for alignment
-            pblock->nSolution = solnPlaceholder;
-
             // we don't use this, but IncrementExtraNonce is the function that builds the merkle tree
             unsigned int nExtraNonce = 0;
             IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
 
+            // update PBaaS header
+            if (CConstVerusSolutionVector::activationHeight.ActiveVersion(Mining_height) == CActivationHeight::SOLUTION_VERUSV3)
+            {
+                uint256 mmvRoot;
+                {
+                    LOCK(cs_main);
+                    // set the PBaaS header
+                    ChainMerkleMountainView mmv = chainActive.GetMMV();
+                    mmvRoot = mmv.GetRoot();
+                }
+                pblock->AddUpdatePBaaSHeader(mmvRoot);
+            }
+
             if (vNodes.empty() && chainparams.MiningRequiresPeers())
             {
                 if ( Mining_height > ASSETCHAINS_MINHEIGHT )
@@ -978,9 +1298,9 @@ void static VerusStaker(CWallet *pwallet)
 
             uint256 hashTarget = ArithToUint256(arith_uint256().SetCompact(pblock->nBits));
 
-            pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
+            pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
 
-            UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
+            UpdateTime(pblock, consensusParams, pindexPrev);
 
             ProcessBlockFound(pblock, *pwallet, reservekey);
 
@@ -991,8 +1311,9 @@ void static VerusStaker(CWallet *pwallet)
             arith_uint256 post;
             post.SetCompact(pblock->GetVerusPOSTarget());
             pindexPrev = get_chainactive(Mining_height - 100);
-            printf("  hash: %s  \ntarget: %s\n", 
-                CTransaction::_GetVerusPOSHash(&(pblock->nNonce), pblock->vtx[pblock->vtx.size()-1].vin[0].prevout.hash, 0, Mining_height, pindexPrev->GetBlockHeader().GetVerusEntropyHash(Mining_height - 100), pblock->vtx[pblock->vtx.size()-1].vout[0].nValue).GetHex().c_str(), ArithToUint256(post).GetHex().c_str());
+            CTransaction &sTx = pblock->vtx[pblock->vtx.size()-1];
+            printf("POS hash: %s  \ntarget:   %s\n", 
+                CTransaction::_GetVerusPOSHash(&(pblock->nNonce), sTx.vin[0].prevout.hash, sTx.vin[0].prevout.n, Mining_height, pindexPrev->GetBlockHeader().GetVerusEntropyHash(Mining_height - 100), sTx.vout[0].nValue).GetHex().c_str(), ArithToUint256(post).GetHex().c_str());
             if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE)
                 printf("- timelocked until block %i\n", unlockTime);
             else
@@ -1021,6 +1342,10 @@ void static VerusStaker(CWallet *pwallet)
     }
 }
 
+typedef bool (*minefunction)(CBlockHeader &bh, CVerusHashV2bWriter &vhw, uint256 &finalHash, uint256 &target, uint64_t start, uint64_t *count);
+bool mine_verus_v2(CBlockHeader &bh, CVerusHashV2bWriter &vhw, uint256 &finalHash, uint256 &target, uint64_t start, uint64_t *count);
+bool mine_verus_v2_port(CBlockHeader &bh, CVerusHashV2bWriter &vhw, uint256 &finalHash, uint256 &target, uint64_t start, uint64_t *count);
+
 void static BitcoinMiner_noeq(CWallet *pwallet)
 #else
 void static BitcoinMiner_noeq()
@@ -1037,8 +1362,7 @@ void static BitcoinMiner_noeq()
     const CChainParams& chainparams = Params();
     // Each thread has its own counter
     unsigned int nExtraNonce = 0;
-    std::vector<unsigned char> solnPlaceholder = std::vector<unsigned char>();
-    solnPlaceholder.resize(Eh200_9.SolutionWidth);
+
     uint8_t *script; uint64_t total,checktoshis; int32_t i,j;
 
     while ( (ASSETCHAIN_INIT == 0 || KOMODO_INITDONE == 0) ) //chainActive.Tip()->GetHeight() != 235300 &&
@@ -1062,25 +1386,31 @@ void static BitcoinMiner_noeq()
     // this will not stop printing more than once in all cases, but it will allow us to print in all cases
     // and print duplicates rarely without having to synchronize
     static CBlockIndex *lastChainTipPrinted;
+    static int32_t lastMiningHeight = 0;
 
     miningTimer.start();
 
     try {
         printf("Mining %s with %s\n", ASSETCHAINS_SYMBOL, ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+
+        // v2 hash writer
+        CVerusHashV2bWriter ss2 = CVerusHashV2bWriter(SER_GETHASH, PROTOCOL_VERSION);
+
         while (true)
         {
             miningTimer.stop();
             waitForPeers(chainparams);
 
             pindexPrev = chainActive.LastTip();
-            sleep(1);
 
-            // prevent forking on startup before the diff algorithm kicks in
-            if (pindexPrev->GetHeight() < 50 || pindexPrev != chainActive.LastTip())
+            // prevent forking on startup before the diff algorithm kicks in,
+            // but only for a startup Verus test chain. PBaaS chains have the difficulty inherited from
+            // their parent
+            if (chainparams.MiningRequiresPeers() && ((IsVerusActive() && pindexPrev->GetHeight() < 50) || pindexPrev != chainActive.LastTip()))
             {
                 do {
                     pindexPrev = chainActive.LastTip();
-                    MilliSleep(5000 + rand() % 5000);
+                    MilliSleep(3000 + rand() % 3000);
                 } while (pindexPrev != chainActive.LastTip());
             }
 
@@ -1089,15 +1419,14 @@ void static BitcoinMiner_noeq()
             if ( Mining_height != pindexPrev->GetHeight()+1 )
             {
                 Mining_height = pindexPrev->GetHeight()+1;
+                if (lastMiningHeight != Mining_height)
+                {
+                    lastMiningHeight = Mining_height;
+                    printf("Mining height %d\n", Mining_height);
+                }
                 Mining_start = (uint32_t)time(NULL);
             }
 
-            if (lastChainTipPrinted != pindexPrev)
-            {
-                printf("Mining height %d\n", Mining_height);
-                lastChainTipPrinted = pindexPrev;
-            }
-
             miningTimer.start();
 
 #ifdef ENABLE_WALLET
@@ -1110,6 +1439,7 @@ void static BitcoinMiner_noeq()
                 static uint32_t counter;
                 if ( counter++ < 100 )
                     fprintf(stderr,"created illegal block, retry\n");
+                MilliSleep(500);
                 continue;
             }
 
@@ -1126,6 +1456,14 @@ void static BitcoinMiner_noeq()
                 return;
             }
             CBlock *pblock = &pblocktemplate->block;
+
+            uint32_t savebits;
+            bool mergeMining = false;
+            savebits = pblock->nBits;
+
+            bool verusHashV2 = pblock->nVersion == CBlockHeader::VERUS_V2;
+            bool verusSolutionV3 = CConstVerusSolutionVector::Version(pblock->nSolution) == CActivationHeight::SOLUTION_VERUSV3;
+
             if ( ASSETCHAINS_SYMBOL[0] != 0 )
             {
                 if ( ASSETCHAINS_REWARD[0] == 0 && !ASSETCHAINS_LASTERA )
@@ -1140,18 +1478,121 @@ void static BitcoinMiner_noeq()
                     } else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT);
                 }
             }
+
+            // this builds the Merkle tree
             IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
+
+            // update PBaaS header
+            if (verusSolutionV3)
+            {
+                uint256 mmvRoot;
+                {
+                    LOCK(cs_main);
+                    ChainMerkleMountainView mmv = chainActive.GetMMV();
+                    mmvRoot = mmv.GetRoot();
+                }
+                pblock->AddUpdatePBaaSHeader(mmvRoot);
+
+                if (IsVerusActive())
+                {
+                    // combine all merge mined headers into this header
+                    // and get the easiest target of all chains in savebits
+                    if (!(savebits = ConnectedChains.CombineBlocks(*pblock)))
+                    {
+                        savebits = pblock->nBits;
+                    }
+
+                    LOCK(cs_main);
+                    // TODO: REMOVE OR COMMENT TESTS
+                    // tests to validate a few transactions and all past blocks
+                    ChainMerkleMountainView mmv = chainActive.GetMMV();
+                    mmvRoot = mmv.GetRoot();
+                    for (uint32_t i = 1; i <= pindexPrev->GetHeight(); i += 10)
+                    {
+                        CBlockIndex *pindex = chainActive[i - 1];
+                        mmv.resize(i);
+                        uint256 testRoot = mmv.GetRoot();
+                        uint32_t testHeight = ((unsigned char *)&testRoot)[0] < i ? (i - ((unsigned char *)&testRoot)[0]) - 1 : i - 1;
+                        CMerkleBranch branchMerkle, branchBlock;
+                        chainActive.GetBlockProof(mmv, branchBlock, testHeight);
+                        chainActive.GetMerkleProof(mmv, branchMerkle, testHeight);
+                        uint256 merkleAnswer = branchMerkle.SafeCheck(chainActive[testHeight]->hashMerkleRoot);
+                        uint256 blockAnswer = branchBlock.SafeCheck(chainActive[testHeight]->GetBlockHash());
+                        if (merkleAnswer != testRoot)
+                        {
+                            printf("Failed merkle proof at testheight: %u\nexpected:   %s\ncalculated: %s\n", testHeight, testRoot.GetHex().c_str(), merkleAnswer.GetHex().c_str());
+                            printf("Bits for left (0) and right (1): \n");
+                            std::vector<unsigned char> proofBits = ChainMerkleMountainView::GetProofBits(testHeight, i);
+                            printf("right\n");
+                            for (auto bit : proofBits)
+                            {
+                                printf("%s\n", bit ? "left" : "right");
+                            }
+                        }
+                        if (blockAnswer != testRoot)
+                        {
+                            printf("Failed block proof at testheight: %u\nexpected:   %s\ncalculated: %s\n", testHeight, testRoot.GetHex().c_str(), blockAnswer.GetHex().c_str());
+                            printf("Bits for left (0) and right (1): \n");
+                            std::vector<unsigned char> proofBits = ChainMerkleMountainView::GetProofBits(testHeight, i);
+                            printf("left\n");
+                            for (auto bit : proofBits)
+                            {
+                                printf("%s\n", bit ? "left" : "right");
+                            }
+                        }
+                        const CMMRPowerNode *ppower = mmv.GetRootNode();
+                        if (!ppower || ppower->Work() != pindex->chainPower.chainWork)
+                        {
+                            printf("Work did not match:\nexpected:   %s\ncalculated: %s\n", ArithToUint256(pindex->chainPower.chainWork).GetHex().c_str(), ArithToUint256(ppower->Work()).GetHex().c_str());
+                        }
+                        if (!ppower || ppower->Stake() != pindex->chainPower.chainStake)
+                        {
+                            printf("Stake did not match:\nexpected:   %s\ncalculated: %s\n", ArithToUint256(pindex->chainPower.chainStake).GetHex().c_str(), ArithToUint256(ppower->Stake()).GetHex().c_str());
+                        }
+                    }
+                    // END TESTS
+                }
+                else
+                {
+                    // submit the block for merge mining if Verus is present
+                    // otherwise, mine solo
+                    if (ConnectedChains.IsVerusPBaaSAvailable())
+                    {
+
+                        UniValue params(UniValue::VARR);
+                        params.push_back(EncodeHexBlk(*pblock));
+                        params.push_back(ASSETCHAINS_SYMBOL);
+                        params.push_back(PBAAS_HOST);
+                        params.push_back(PBAAS_PORT);
+                        params.push_back(PBAAS_USERPASS);
+                        try
+                        {
+                            params = RPCCallRoot("addmergedblock", params);
+                        } catch (std::exception e)
+                        {
+                            printf("Failed to connect to %s chain\n", ConnectedChains.notaryChain.chainDefinition.name.c_str());
+                            params = UniValue(e.what());
+                        }
+                        if (mergeMining = params.isNull())
+                        {
+                            printf("Merge mining -- deferring to %s as the actual mining chain\n", ConnectedChains.notaryChain.chainDefinition.name.c_str());
+                        }
+                    }
+                }
+            }
+
             LogPrintf("Running %s miner with %u transactions in block (%u bytes)\n",ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO],
                        pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION));
             //
             // Search
             //
-            uint32_t savebits; int64_t nStart = GetTime();
+            int64_t nStart = GetTime();
 
-            pblock->nSolution = solnPlaceholder;
-            savebits = pblock->nBits;
-            arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
-            arith_uint256 mask(ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO]);
+            arith_uint256 hashTarget = arith_uint256().SetCompact(savebits);
+            uint256 uintTarget = ArithToUint256(hashTarget);
+
+            arith_uint256 ourTarget;
+            ourTarget.SetCompact(pblock->nBits);
 
             Mining_start = 0;
 
@@ -1162,7 +1603,7 @@ void static BitcoinMiner_noeq()
                     lastChainTipPrinted = chainActive.LastTip();
                     printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
                 }
-                MilliSleep(250);
+                MilliSleep(100);
                 continue;
             }
 
@@ -1175,78 +1616,188 @@ void static BitcoinMiner_noeq()
                 fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%%\n",percPoS,(int32_t)ASSETCHAINS_STAKED);
             }
 
-            while (true)
+            uint64_t count, hashesToGo = 0;
+            if (!verusHashV2)
             {
-                arith_uint256 arNonce = UintToArith256(pblock->nNonce);
+                // must not be in sync
+                printf("Mining on incorrect block version.\n");
+                sleep(2);
+                continue;
+            }
+
+            count = ((ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] >> 3) + 1) / ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
+            CVerusHashV2 *vh2 = &ss2.GetState();
+            u128 *hashKey;
+            verusclhasher &vclh = vh2->vclh;
+            minefunction mine_verus;
+            mine_verus = IsCPUVerusOptimized() ? &mine_verus_v2 : &mine_verus_v2_port;
 
-                CVerusHashWriter ss = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
-                ss << *((CBlockHeader *)pblock);
-                int64_t *extraPtr = ss.xI64p();
-                CVerusHash &vh = ss.GetState();
+            while (true)
+            {
                 uint256 hashResult = uint256();
-                vh.ClearExtra();
-                int64_t i, count = ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1;
-                int64_t hashesToGo = ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
 
-                // for speed check NONCEMASK at a time
-                for (i = 0; i < count; i++)
-                {
-                    *extraPtr = i;
-                    vh.ExtraHash((unsigned char *)&hashResult);
+                unsigned char *curBuf;
 
-                    if ( UintToArith256(hashResult) <= hashTarget )
+                if (mergeMining)
+                {
+                    // loop for about one minute before refreshing the block
+                    for (uint64_t i = 0; i < 240; i++)
                     {
-                        if (pblock->nSolution.size() != 1344)
+                        boost::this_thread::interruption_point();
+                        MilliSleep(250);
+
+                        if (vNodes.empty() && chainparams.MiningRequiresPeers())
                         {
-                            LogPrintf("ERROR: Block solution is not 1344 bytes as it should be");
-                            sleep(5);
-                            break;
+                            if ( Mining_height > ASSETCHAINS_MINHEIGHT )
+                            {
+                                fprintf(stderr,"no nodes, attempting reconnect\n");
+                                break;
+                            }
                         }
 
-                        SetThreadPriority(THREAD_PRIORITY_NORMAL);
-
-                        *((int64_t *)&(pblock->nSolution.data()[pblock->nSolution.size() - 15])) = i;
-
-                        int32_t unlockTime = komodo_block_unlocktime(Mining_height);
-                        int64_t subsidy = (int64_t)(pblock->vtx[0].vout[0].nValue);
+                        if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
+                        {
+                            break;
+                        }
 
-                        LogPrintf("Using %s algorithm:\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
-                        LogPrintf("proof-of-work found  \n  hash: %s  \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex());
-                        printf("Found block %d \n", Mining_height );
-                        printf("mining reward %.8f %s!\n", (double)subsidy / (double)COIN, ASSETCHAINS_SYMBOL);
-                        printf("  hash: %s  \ntarget: %s\n", pblock->GetHash().GetHex().c_str(), hashTarget.GetHex().c_str());
-                        if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE)
-                            printf("- timelocked until block %i\n", unlockTime);
-                        else
-                            printf("\n");
-#ifdef ENABLE_WALLET
-                        ProcessBlockFound(pblock, *pwallet, reservekey);
-#else
-                        ProcessBlockFound(pblock));
-#endif
-                        SetThreadPriority(THREAD_PRIORITY_LOWEST);
-                        break;
-                    }
-                    // check periodically if we're stale
-                    if (!--hashesToGo)
-                    {
                         if ( pindexPrev != chainActive.LastTip() )
                         {
                             if (lastChainTipPrinted != chainActive.LastTip())
                             {
                                 lastChainTipPrinted = chainActive.LastTip();
-                                printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
+                                printf("Block %d added to chain\n\n", lastChainTipPrinted->GetHeight());
                             }
                             break;
                         }
-                        hashesToGo = ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
                     }
                 }
-
+                else
                 {
-                    LOCK(cs_metrics);
-                    nHashCount += i;
+                    // check NONCEMASK at a time
+                    for (uint64_t i = 0; i < count; i++)
+                    {
+                        // this is the merge mining loop, which enables us to drop out and queue a header anytime we earn a block that is good enough for a
+                        // merge mined block, but not our own
+                        uint64_t totalDone = 0;
+                        bool blockFound;
+                        arith_uint256 arithHash;
+                        do
+                        {
+                            // hashesToGo gets updated with actual number run for metrics
+                            hashesToGo = ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
+                            uint64_t start = i * hashesToGo;
+                            hashesToGo -= totalDone;
+
+                            if (verusSolutionV3)
+                            {
+                                // mine on canonical header for merge mining
+                                CPBaaSPreHeader savedHeader(*pblock);
+                                pblock->ClearNonCanonicalData();
+                                blockFound = (*mine_verus)(*pblock, ss2, hashResult, uintTarget, start, &hashesToGo);
+                                savedHeader.SetBlockData(*pblock);
+                            }
+                            else
+                            {
+                                blockFound = (*mine_verus)(*pblock, ss2, hashResult, uintTarget, start, &hashesToGo);
+                            }
+
+                            arithHash = UintToArith256(hashResult);
+                            totalDone += hashesToGo;
+                            if (blockFound && IsVerusActive())
+                            {
+                                ConnectedChains.QueueNewBlockHeader(*pblock);
+                                if (arithHash > ourTarget)
+                                {
+                                    // all blocks qualified with this hash will be submitted
+                                    // until we redo the block, we might as well not try again with anything over this hash
+                                    hashTarget = arithHash;
+                                    uintTarget = ArithToUint256(hashTarget);
+                                }
+                            }
+                            hashesToGo = totalDone;
+                        } while (blockFound && arithHash > ourTarget);
+
+                        if (!blockFound || arithHash > ourTarget)
+                        {
+                            // Check for stop or if block needs to be rebuilt
+                            boost::this_thread::interruption_point();
+                            if ( pindexPrev != chainActive.LastTip() )
+                            {
+                                if (lastChainTipPrinted != chainActive.LastTip())
+                                {
+                                    lastChainTipPrinted = chainActive.LastTip();
+                                    printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
+                                }
+                                break;
+                            }
+                            else
+                            {
+                                {
+                                    LOCK(cs_metrics);
+                                    nHashCount += hashesToGo;
+                                }
+                                continue;
+                            }
+                        }
+                        else
+                        {
+                            // Check for stop or if block needs to be rebuilt
+                            boost::this_thread::interruption_point();
+
+                            if (pblock->nSolution.size() != 1344)
+                            {
+                                LogPrintf("ERROR: Block solution is not 1344 bytes as it should be");
+                                break;
+                            }
+
+                            SetThreadPriority(THREAD_PRIORITY_NORMAL);
+
+                            int32_t unlockTime = komodo_block_unlocktime(Mining_height);
+                            int64_t subsidy = (int64_t)(pblock->vtx[0].vout[0].nValue);
+
+#ifdef VERUSHASHDEBUG
+                            std::string validateStr = hashResult.GetHex();
+                            std::string hashStr = pblock->GetHash().GetHex();
+                            uint256 *bhalf1 = (uint256 *)vh2->CurBuffer();
+                            uint256 *bhalf2 = bhalf1 + 1;
+#else
+                            std::string hashStr = hashResult.GetHex();
+#endif
+
+                            LogPrintf("Using %s algorithm:\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+                            LogPrintf("proof-of-work found  \n  hash: %s  \ntarget: %s\n", hashStr, ArithToUint256(ourTarget).GetHex());
+                            printf("Found block %d \n", Mining_height );
+                            printf("mining reward %.8f %s!\n", (double)subsidy / (double)COIN, ASSETCHAINS_SYMBOL);
+#ifdef VERUSHASHDEBUG
+                            printf("  hash: %s\n   val: %s  \ntarget: %s\n\n", hashStr.c_str(), validateStr.c_str(), ArithToUint256(ourTarget).GetHex().c_str());
+                            printf("intermediate %lx\n", intermediate);
+                            printf("Curbuf: %s%s\n", bhalf1->GetHex().c_str(), bhalf2->GetHex().c_str());
+                            bhalf1 = (uint256 *)verusclhasher_key.get();
+                            bhalf2 = bhalf1 + ((vh2->vclh.keyMask + 1) >> 5);
+                            printf("   Key: %s%s\n", bhalf1->GetHex().c_str(), bhalf2->GetHex().c_str());
+#else
+                            printf("  hash: %s\ntarget: %s", hashStr.c_str(), ArithToUint256(ourTarget).GetHex().c_str());
+#endif
+                            if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE)
+                                printf(" - timelocked until block %i\n", unlockTime);
+                            else
+                                printf("\n");
+#ifdef ENABLE_WALLET
+                            ProcessBlockFound(pblock, *pwallet, reservekey);
+#else
+                            ProcessBlockFound(pblock);
+#endif
+                            SetThreadPriority(THREAD_PRIORITY_LOWEST);
+                            break;
+                        }
+                    }
+
+                    {
+                        LOCK(cs_metrics);
+                        nHashCount += hashesToGo;
+                    }
                 }
+                
 
                 // Check for stop or if block needs to be rebuilt
                 boost::this_thread::interruption_point();
@@ -1271,17 +1822,18 @@ void static BitcoinMiner_noeq()
                     if (lastChainTipPrinted != chainActive.LastTip())
                     {
                         lastChainTipPrinted = chainActive.LastTip();
-                        printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
+                        printf("Block %d added to chain\n\n", lastChainTipPrinted->GetHeight());
                     }
                     break;
                 }
 
 #ifdef _WIN32
-                printf("%llu mega hashes complete - working\n", (ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1) / 1048576);
+                printf("%llu mega hashes complete - working\n", ((ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] >> 3) + 1) / 1048576);
 #else
-                printf("%lu mega hashes complete - working\n", (ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1) / 1048576);
+                printf("%lu mega hashes complete - working\n", ((ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] >> 3) + 1) / 1048576);
 #endif
                 break;
+
             }
         }
     }
@@ -1735,8 +2287,8 @@ void static BitcoinMiner()
                     /*if ( NOTARY_PUBKEY33[0] == 0 )
                     {
                         int32_t percPoS;
-                        UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
-                        if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks)
+                        UpdateTime(pblock, consensusParams, pindexPrev);
+                        if (consensusParams.fPowAllowMinDifficultyBlocks)
                         {
                             // Changing pblock->nTime can change work required on testnet:
                             HASHTarget.SetCompact(pblock->nBits);
@@ -1770,8 +2322,50 @@ void static BitcoinMiner()
     void GenerateBitcoins(bool fGenerate, int nThreads)
 #endif
     {
+        if (!AreParamsInitialized())
+        {
+            return;
+        }
+
+        // if we are supposed to catch stake cheaters, there must be a valid sapling parameter, we need it at
+        // initialization, and this is the first time we can get it. store the Sapling address here
+        extern boost::optional<libzcash::SaplingPaymentAddress> cheatCatcher;
+        extern std::string VERUS_CHEATCATCHER;
+        libzcash::PaymentAddress addr = DecodePaymentAddress(VERUS_CHEATCATCHER);
+        if (VERUS_CHEATCATCHER.size() > 0 && IsValidPaymentAddress(addr))
+        {
+            try
+            {
+                cheatCatcher = boost::get<libzcash::SaplingPaymentAddress>(addr);
+            } 
+            catch (...)
+            {
+            }
+        }
+
+        VERUS_MINTBLOCKS = (VERUS_MINTBLOCKS && ASSETCHAINS_LWMAPOS != 0);
+
+        if (fGenerate == true || VERUS_MINTBLOCKS)
+        {
+            mapArgs["-gen"] = "1";
+
+            if (VERUS_CHEATCATCHER.size() > 0)
+            {
+                if (cheatCatcher == boost::none)
+                {
+                    LogPrintf("ERROR: -cheatcatcher parameter is invalid Sapling payment address\n");
+                    fprintf(stderr, "-cheatcatcher parameter is invalid Sapling payment address\n");
+                }
+                else
+                {
+                    LogPrintf("StakeGuard searching for double stakes on %s\n", VERUS_CHEATCATCHER.c_str());
+                    fprintf(stderr, "StakeGuard searching for double stakes on %s\n", VERUS_CHEATCATCHER.c_str());
+                }
+            }
+        }
+
         static boost::thread_group* minerThreads = NULL;
-        
+
         if (nThreads < 0)
             nThreads = GetNumCores();
         
@@ -1786,13 +2380,16 @@ void static BitcoinMiner()
         if ( nThreads == 0 && ASSETCHAINS_STAKED )
             nThreads = 1;
 
-        if ((nThreads == 0 && ASSETCHAINS_LWMAPOS == 0) || !fGenerate)
+        if (!fGenerate)
             return;
 
         minerThreads = new boost::thread_group();
 
+        // add the PBaaS thread when mining or staking
+        minerThreads->create_thread(boost::bind(&CConnectedChains::SubmissionThreadStub));
+
 #ifdef ENABLE_WALLET
-        if (ASSETCHAINS_LWMAPOS != 0)
+        if (VERUS_MINTBLOCKS && pwallet != NULL)
         {
             minerThreads->create_thread(boost::bind(&VerusStaker, pwallet));
         }
This page took 0.064775 seconds and 4 git commands to generate.