]> Git Repo - VerusCoin.git/blobdiff - src/miner.cpp
Integrate latest Zcash fixes and update for non-latin user names
[VerusCoin.git] / src / miner.cpp
index e2e5d2f7ef473129013d5176c4c9be762eba3895..46d6695630e84aaf27a07b91387508885de88f92 100644 (file)
 
 #include "amount.h"
 #include "chainparams.h"
+#include "cc/StakeGuard.h"
+#include "importcoin.h"
 #include "consensus/consensus.h"
 #include "consensus/upgrades.h"
 #include "consensus/validation.h"
 #ifdef ENABLE_MINING
 #include "crypto/equihash.h"
+#include "crypto/verus_hash.h"
 #endif
 #include "hash.h"
 #include "key_io.h"
@@ -32,6 +35,9 @@
 #include "wallet/wallet.h"
 #endif
 
+#include "zcash/Address.hpp"
+#include "transaction_builder.h"
+
 #include "sodium.h"
 
 #include <boost/thread.hpp>
@@ -63,7 +69,7 @@ public:
     set<uint256> setDependsOn;
     CFeeRate feeRate;
     double dPriority;
-
+    
     COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0)
     {
     }
@@ -77,10 +83,10 @@ typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
 class TxPriorityCompare
 {
     bool byFee;
-
+    
 public:
     TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
-
+    
     bool operator()(const TxPriority& a, const TxPriority& b)
     {
         if (byFee)
@@ -108,52 +114,117 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams,
     }
 }
 
-CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
+#include "komodo_defs.h"
+
+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 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);
+
+extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33];
+uint32_t Mining_start,Mining_height;
+int32_t My_notaryid = -1;
+int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp);
+int32_t komodo_pax_opreturn(int32_t height,uint8_t *opret,int32_t maxsize);
+int32_t komodo_baseid(char *origbase);
+int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t nTime,int32_t dispflag);
+int64_t komodo_block_unlocktime(uint32_t nHeight);
+uint64_t komodo_commission(const CBlock *block);
+int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig);
+int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey &pk);
+int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33);
+
+CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake)
 {
-    const CChainParams& chainparams = Params();
+    CScript scriptPubKeyIn(_scriptPubKeyIn);
+
+    CPubKey pk = CPubKey();
+    std::vector<std::vector<unsigned char>> vAddrs;
+    txnouttype txT;
+    if (Solver(scriptPubKeyIn, txT, vAddrs))
+    {
+        if (txT == TX_PUBKEY)
+            pk = CPubKey(vAddrs[0]);
+    }
+
+    uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params();
+    //fprintf(stderr,"create new block\n");
     // Create new block
+    if ( gpucount < 0 )
+        gpucount = KOMODO_MAXGPUCOUNT;
     std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
     if(!pblocktemplate.get())
+    {
+        fprintf(stderr,"pblocktemplate.get() failure\n");
         return NULL;
+    }
     CBlock *pblock = &pblocktemplate->block; // pointer for convenience
-
-    // -regtest only: allow overriding block.nVersion with
+     // -regtest only: allow overriding block.nVersion with
     // -blockversion=N to test forking scenarios
     if (Params().MineBlocksOnDemand())
         pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
-
+    
     // Add dummy coinbase tx as first transaction
     pblock->vtx.push_back(CTransaction());
     pblocktemplate->vTxFees.push_back(-1); // updated at end
     pblocktemplate->vTxSigOps.push_back(-1); // updated at end
-
+    
     // Largest block you're willing to create:
     unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);
     // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
     nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize));
-
+    
     // How much of the block should be dedicated to high-priority transactions,
     // included regardless of the fees they pay
     unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE);
     nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
-
+    
     // Minimum block size you want to create; block will be filled with free transactions
     // until there are no more or the block reaches this size:
     unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE);
     nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
-
+    
     // 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);
-        CBlockIndex* pindexPrev = chainActive.Tip();
-        const int nHeight = pindexPrev->nHeight + 1;
-        uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus());
-        pblock->nTime = GetAdjustedTime();
+        pindexPrev = chainActive.LastTip();
+        const int nHeight = pindexPrev->GetHeight() + 1;
+        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();
-        CCoinsViewCache view(pcoinsTip);
+        uint32_t proposedTime = GetAdjustedTime();
+        if (proposedTime == nMedianTimePast)
+        {
+            // too fast or stuck, this addresses the too fast issue, while moving
+            // forward as quickly as possible
+            for (int i; i < 100; i++)
+            {
+                proposedTime = GetAdjustedTime();
+                if (proposedTime == nMedianTimePast)
+                    MilliSleep(10);
+            }
+        }
+        pblock->nTime = GetAdjustedTime();
 
+        CCoinsViewCache view(pcoinsTip);
+        uint32_t expired; uint64_t commission;
+        
         SaplingMerkleTree sapling_tree;
         assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree));
 
@@ -161,79 +232,184 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
         list<COrphan> vOrphan; // list memory doesn't move
         map<uint256, vector<COrphan*> > mapDependers;
         bool fPrintPriority = GetBoolArg("-printpriority", false);
-
+        
         // 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)
+        {
+            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);
+            }
+        }
+
+        // now add transactions from the mem pool
         for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
              mi != mempool.mapTx.end(); ++mi)
         {
             const CTransaction& tx = mi->GetTx();
-
+            
             int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
-                                    ? nMedianTimePast
-                                    : pblock->GetBlockTime();
+            ? 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;
             bool fMissingInputs = false;
-            BOOST_FOREACH(const CTxIn& txin, tx.vin)
+            if (tx.IsCoinImport())
             {
-                // Read prev transaction
-                if (!view.HaveCoins(txin.prevout.hash))
+                CAmount nValueIn = GetCoinImportValue(tx);
+                nTotalIn += nValueIn;
+                dPriority += (double)nValueIn * 1000;  // flat multiplier
+            } else {
+                BOOST_FOREACH(const CTxIn& txin, tx.vin)
                 {
-                    // This should never happen; all transactions in the memory
-                    // pool should connect to either transactions in the chain
-                    // or other transactions in the memory pool.
-                    if (!mempool.mapTx.count(txin.prevout.hash))
+                    // Read prev transaction
+                    if (!view.HaveCoins(txin.prevout.hash))
                     {
-                        LogPrintf("ERROR: mempool transaction missing input\n");
-                        if (fDebug) assert("mempool transaction missing input" == 0);
-                        fMissingInputs = true;
-                        if (porphan)
-                            vOrphan.pop_back();
-                        break;
-                    }
+                        // This should never happen; all transactions in the memory
+                        // pool should connect to either transactions in the chain
+                        // or other transactions in the memory pool.
+                        if (!mempool.mapTx.count(txin.prevout.hash))
+                        {
+                            LogPrintf("ERROR: mempool transaction missing input\n");
+                            if (fDebug) assert("mempool transaction missing input" == 0);
+                            fMissingInputs = true;
+                            if (porphan)
+                                vOrphan.pop_back();
+                            break;
+                        }
 
-                    // Has to wait for dependencies
-                    if (!porphan)
-                    {
-                        // Use list for automatic deletion
-                        vOrphan.push_back(COrphan(&tx));
-                        porphan = &vOrphan.back();
+                        // Has to wait for dependencies
+                        if (!porphan)
+                        {
+                            // Use list for automatic deletion
+                            vOrphan.push_back(COrphan(&tx));
+                            porphan = &vOrphan.back();
+                        }
+                        mapDependers[txin.prevout.hash].push_back(porphan);
+                        porphan->setDependsOn.insert(txin.prevout.hash);
+                        nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
+                        continue;
                     }
-                    mapDependers[txin.prevout.hash].push_back(porphan);
-                    porphan->setDependsOn.insert(txin.prevout.hash);
-                    nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
-                    continue;
-                }
-                const CCoins* coins = view.AccessCoins(txin.prevout.hash);
-                assert(coins);
+                    const CCoins* coins = view.AccessCoins(txin.prevout.hash);
+                    assert(coins);
 
-                CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
-                nTotalIn += nValueIn;
+                    CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
+                    nTotalIn += nValueIn;
 
-                int nConf = nHeight - coins->nHeight;
+                    int nConf = nHeight - coins->nHeight;
 
-                dPriority += (double)nValueIn * nConf;
+                    dPriority += (double)nValueIn * nConf;
+                }
+                nTotalIn += tx.GetShieldedValueIn();
             }
-            nTotalIn += tx.GetShieldedValueIn();
 
             if (fMissingInputs) continue;
-
+            
             // Priority is sum(valuein * age) / modified_txsize
             unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
             dPriority = tx.ComputePriority(dPriority, nTxSize);
-
+            
             uint256 hash = tx.GetHash();
             mempool.ApplyDeltas(hash, dPriority, nTotalIn);
-
+            
             CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
-
+            
             if (porphan)
             {
                 porphan->dPriority = dPriority;
@@ -246,40 +422,48 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
         // Collect transactions into block
         uint64_t nBlockSize = 1000;
         uint64_t nBlockTx = 0;
+        int64_t interest;
         int nBlockSigOps = 100;
         bool fSortedByFee = (nBlockPrioritySize <= 0);
-
+        
         TxPriorityCompare comparer(fSortedByFee);
         std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
-
+        
         while (!vecPriority.empty())
         {
             // Take highest priority transaction off the priority queue:
             double dPriority = vecPriority.front().get<0>();
             CFeeRate feeRate = vecPriority.front().get<1>();
             const CTransaction& tx = *(vecPriority.front().get<2>());
-
+            
             std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
             vecPriority.pop_back();
-
+            
             // Size limits
             unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
-            if (nBlockSize + nTxSize >= nBlockMaxSize)
+            if (nBlockSize + nTxSize >= nBlockMaxSize-512) // room for extra autotx
+            {
+                //fprintf(stderr,"nBlockSize %d + %d nTxSize >= %d nBlockMaxSize\n",(int32_t)nBlockSize,(int32_t)nTxSize,(int32_t)nBlockMaxSize);
                 continue;
-
+            }
+            
             // Legacy limits on sigOps:
             unsigned int nTxSigOps = GetLegacySigOpCount(tx);
-            if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
+            if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS-1)
+            {
+                //fprintf(stderr,"A nBlockSigOps %d + %d nTxSigOps >= %d MAX_BLOCK_SIGOPS-1\n",(int32_t)nBlockSigOps,(int32_t)nTxSigOps,(int32_t)MAX_BLOCK_SIGOPS);
                 continue;
-
+            }
             // Skip free transactions if we're past the minimum block size:
             const uint256& hash = tx.GetHash();
             double dPriorityDelta = 0;
             CAmount nFeeDelta = 0;
             mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
             if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
+            {
+                //fprintf(stderr,"fee rate skip\n");
                 continue;
-
+            }
             // Prioritise by fee once past the priority size or we run out of high-priority
             // transactions:
             if (!fSortedByFee &&
@@ -289,24 +473,30 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
                 comparer = TxPriorityCompare(fSortedByFee);
                 std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
             }
-
+            
             if (!view.HaveInputs(tx))
+            {
+                //fprintf(stderr,"dont have inputs\n");
                 continue;
-
-            CAmount nTxFees = view.GetValueIn(tx)-tx.GetValueOut();
-
+            }
+            CAmount nTxFees = view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime)-tx.GetValueOut();
+            
             nTxSigOps += GetP2SHSigOpCount(tx, view);
-            if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
+            if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS-1)
+            {
+                //fprintf(stderr,"B nBlockSigOps %d + %d nTxSigOps >= %d MAX_BLOCK_SIGOPS-1\n",(int32_t)nBlockSigOps,(int32_t)nTxSigOps,(int32_t)MAX_BLOCK_SIGOPS);
                 continue;
-
+            }
             // Note that flags: we don't want to set mempool/IsStandard()
             // policy here, but we still have to ensure that the block we
             // create only contains transactions that are valid in new blocks.
             CValidationState state;
             PrecomputedTransactionData txdata(tx);
             if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
+            {
+                //fprintf(stderr,"context failure\n");
                 continue;
-
+            }
             UpdateCoins(tx, view, nHeight);
 
             BOOST_FOREACH(const OutputDescription &outDescription, tx.vShieldedOutput) {
@@ -321,13 +511,12 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
             ++nBlockTx;
             nBlockSigOps += nTxSigOps;
             nFees += nTxFees;
-
+            
             if (fPrintPriority)
             {
-                LogPrintf("priority %.1f fee %s txid %s\n",
-                    dPriority, feeRate.ToString(), tx.GetHash().ToString());
+                LogPrintf("priority %.1f fee %s txid %s\n",dPriority, feeRate.ToString(), tx.GetHash().ToString());
             }
-
+            
             // Add transactions that depend on this one to the priority queue
             if (mapDependers.count(hash))
             {
@@ -345,102 +534,249 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
                 }
             }
         }
-
+        
         nLastBlockTx = nBlockTx;
         nLastBlockSize = nBlockSize;
-        LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize);
+        blocktime = 1 + std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+        //pblock->nTime = blocktime + 1;
+        pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
+
+        int32_t stakeHeight = chainActive.Height() + 1;
+
+        //LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x\n", nBlockSize,blocktime,pblock->nBits);
+        if ( ASSETCHAINS_SYMBOL[0] != 0 && isStake )
+        {
+            uint64_t txfees,utxovalue; uint32_t txtime; uint256 utxotxid; int32_t i,siglen,numsigs,utxovout; uint8_t utxosig[128],*ptr;
+            CMutableTransaction txStaked = CreateNewContextualCMutableTransaction(Params().GetConsensus(), stakeHeight);
+
+            //if ( blocktime > pindexPrev->GetMedianTimePast()+60 )
+            //    blocktime = pindexPrev->GetMedianTimePast() + 60;
+            if (ASSETCHAINS_LWMAPOS != 0)
+            {
+                uint32_t nBitsPOS;
+                arith_uint256 posHash;
+
+                siglen = verus_staked(pblock, txStaked, nBitsPOS, posHash, utxosig, pk);
+                blocktime = GetAdjustedTime();
+
+                // change the scriptPubKeyIn to the same output script exactly as the staking transaction
+                if (siglen > 0)
+                    scriptPubKeyIn = CScript(txStaked.vout[0].scriptPubKey);
+            }
+            else
+            {
+                siglen = komodo_staked(txStaked, pblock->nBits, &blocktime, &txtime, &utxotxid, &utxovout, &utxovalue, utxosig);
+            }
 
+            if ( siglen > 0 )
+            {
+                CAmount txfees;
+
+                // 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 = sapling ? DEFAULT_STAKE_TXFEE : 0;
+
+                pblock->vtx.push_back(txStaked);
+                pblocktemplate->vTxFees.push_back(txfees);
+                pblocktemplate->vTxSigOps.push_back(GetLegacySigOpCount(txStaked));
+                nFees += txfees;
+                pblock->nTime = blocktime;
+                //printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeight()+1,blocktime,(uint32_t)(GetAdjustedTime() - (blocktime-13)));
+            } else return(0); //fprintf(stderr,"no utxos eligible for staking\n");
+        }
+        
         // 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());
-        // Set to 0 so expiry height does not apply to coinbase txs
+        txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees;
+
+        // 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, false))
+            {
+                if (!p.pk.IsValid() || !MakeGuardedOutput(txNew.vout[0].nValue, p.pk, stakeTx, txNew.vout[0]))
+                {
+                    fprintf(stderr,"CreateNewBlock: failed to make GuardedOutput on staking coinbase\n");
+                    return 0;
+                }
+            }
+            else
+            {
+                fprintf(stderr,"CreateNewBlock: invalid stake transaction\n");
+                return 0;
+            }
+        }
+
         txNew.nExpiryHeight = 0;
+        txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
 
-        if ((nHeight > 0) && (nHeight <= chainparams.GetConsensus().GetLastFoundersRewardBlockHeight())) {
-            // Founders reward is 20% of the block subsidy
-            auto vFoundersReward = txNew.vout[0].nValue / 5;
-            // Take some reward away from us
-            txNew.vout[0].nValue -= vFoundersReward;
+        if ( ASSETCHAINS_SYMBOL[0] == 0 && IS_KOMODO_NOTARY != 0 && My_notaryid >= 0 )
+            txNew.vout[0].nValue += 5000;
 
-            // And give it to the founders
-            txNew.vout.push_back(CTxOut(vFoundersReward, chainparams.GetFoundersRewardScriptAtHeight(nHeight)));
-        }
+        // check if coinbase transactions must be time locked at current subsidy and prepend the time lock
+        // to transaction if so, cast for GTE operator
+        if ((uint64_t)(txNew.vout[0].nValue) >= ASSETCHAINS_TIMELOCKGTE)
+        {
+            int32_t opretlen, p2shlen, scriptlen;
+            CScriptExt opretScript = CScriptExt();
 
-        // Add fees
-        txNew.vout[0].nValue += nFees;
-        txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
+            txNew.vout.resize(2);
+
+            // prepend time lock to original script unless original script is P2SH, in which case, we will leave the coins
+            // protected only by the time lock rather than 100% inaccessible
+            opretScript.AddCheckLockTimeVerify(komodo_block_unlocktime(nHeight));
+            if (scriptPubKeyIn.IsPayToScriptHash() || scriptPubKeyIn.IsPayToCryptoCondition())
+            {
+                fprintf(stderr,"CreateNewBlock: attempt to add timelock to pay2sh or pay2cc\n");
+                return 0;
+            }
+            
+            opretScript += scriptPubKeyIn;
+
+            txNew.vout[0].scriptPubKey = CScriptExt().PayToScriptHash(CScriptID(opretScript));
+            txNew.vout[1].scriptPubKey = CScriptExt().OpReturnScript(opretScript, OPRETTYPE_TIMELOCK);
+            txNew.vout[1].nValue = 0;
+        } // timelocks and commissions are currently incompatible due to validation complexity of the combination
+        else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && ASSETCHAINS_COMMISSION != 0 && (commission= komodo_commission((CBlock*)&pblocktemplate->block)) != 0 )
+        {
+            int32_t i; uint8_t *ptr;
+            txNew.vout.resize(2);
+            txNew.vout[1].nValue = commission;
+            txNew.vout[1].scriptPubKey.resize(35);
+            ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0];
+            ptr[0] = 33;
+            for (i=0; i<33; i++)
+                ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i];
+            ptr[34] = OP_CHECKSIG;
+            //printf("autocreate commision vout\n");
+        }
 
         pblock->vtx[0] = txNew;
         pblocktemplate->vTxFees[0] = -nFees;
 
-        // Randomise nonce
-        arith_uint256 nonce = UintToArith256(GetRandHash());
-        // Clear the top and bottom 16 bits (for local use as thread flags and counters)
-        nonce <<= 32;
-        nonce >>= 16;
-        pblock->nNonce = ArithToUint256(nonce);
+        // if not Verus stake, setup nonce, otherwise, leave it alone
+        if (!isStake || ASSETCHAINS_LWMAPOS == 0)
+        {
+            // Randomise nonce
+            arith_uint256 nonce = UintToArith256(GetRandHash());
 
+            // Clear the top 16 and bottom 16 or 24 bits (for local use as thread flags and counters)
+            nonce <<= ASSETCHAINS_NONCESHIFT[ASSETCHAINS_ALGO];
+            nonce >>= 16;
+            pblock->nNonce = ArithToUint256(nonce);
+        }
+        
         // Fill in header
         pblock->hashPrevBlock  = pindexPrev->GetBlockHash();
         pblock->hashFinalSaplingRoot   = sapling_tree.root();
-        UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
-        pblock->nBits          = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
+
+        // all Verus PoS chains need this data in the block at all times
+        if ( ASSETCHAINS_LWMAPOS || ASSETCHAINS_SYMBOL[0] == 0 || ASSETCHAINS_STAKED == 0 || KOMODO_MININGTHREADS > 0 )
+        {
+            UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
+            pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
+        }
         pblock->nSolution.clear();
         pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
-
-        CValidationState state;
-        if (!TestBlockValidity(state, *pblock, pindexPrev, false, false))
-            throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed");
-    }
-
-    return pblocktemplate.release();
-}
-
-#ifdef ENABLE_WALLET
-boost::optional<CScript> GetMinerScriptPubKey(CReserveKey& reservekey)
-#else
-boost::optional<CScript> GetMinerScriptPubKey()
-#endif
-{
-    CKeyID keyID;
-    CTxDestination addr = DecodeDestination(GetArg("-mineraddress", ""));
-    if (IsValidDestination(addr)) {
-        keyID = boost::get<CKeyID>(addr);
-    } else {
-#ifdef ENABLE_WALLET
-        CPubKey pubkey;
-        if (!reservekey.GetReservedKey(pubkey)) {
-            return boost::optional<CScript>();
+        if ( ASSETCHAINS_SYMBOL[0] == 0 && IS_KOMODO_NOTARY != 0 && My_notaryid >= 0 )
+        {
+            uint32_t r;
+            CMutableTransaction txNotary = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height() + 1);
+            if ( pblock->nTime < pindexPrev->nTime+60 )
+                pblock->nTime = pindexPrev->nTime + 60;
+            if ( gpucount < 33 )
+            {
+                uint8_t tmpbuffer[40]; uint32_t r; int32_t n=0; uint256 randvals;
+                memcpy(&tmpbuffer[n],&My_notaryid,sizeof(My_notaryid)), n += sizeof(My_notaryid);
+                memcpy(&tmpbuffer[n],&Mining_height,sizeof(Mining_height)), n += sizeof(Mining_height);
+                memcpy(&tmpbuffer[n],&pblock->hashPrevBlock,sizeof(pblock->hashPrevBlock)), n += sizeof(pblock->hashPrevBlock);
+                vcalc_sha256(0,(uint8_t *)&randvals,tmpbuffer,n);
+                memcpy(&r,&randvals,sizeof(r));
+                pblock->nTime += (r % (33 - gpucount)*(33 - gpucount));
+            }
+            if ( komodo_notaryvin(txNotary,NOTARY_PUBKEY33) > 0 )
+            {
+                CAmount txfees = 5000;
+                pblock->vtx.push_back(txNotary);
+                pblocktemplate->vTxFees.push_back(txfees);
+                pblocktemplate->vTxSigOps.push_back(GetLegacySigOpCount(txNotary));
+                nFees += txfees;
+                pblocktemplate->vTxFees[0] = -nFees;
+                //*(uint64_t *)(&pblock->vtx[0].vout[0].nValue) += txfees;
+                //fprintf(stderr,"added notaryvin\n");
+            }
+            else
+            {
+                fprintf(stderr,"error adding notaryvin, need to create 0.0001 utxos\n");
+                return(0);
+            }
+        }
+        else if ( ASSETCHAINS_CC == 0 && pindexPrev != 0 && ASSETCHAINS_STAKED == 0 && (ASSETCHAINS_SYMBOL[0] != 0 || IS_KOMODO_NOTARY == 0 || My_notaryid < 0) )
+        {
+            CValidationState state;
+            //fprintf(stderr,"check validity\n");
+            if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) // invokes CC checks
+            {
+                throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed");
+            }
+            //fprintf(stderr,"valid\n");
         }
-        keyID = pubkey.GetID();
-#else
-        return boost::optional<CScript>();
-#endif
-    }
-
-    CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
-    return scriptPubKey;
-}
-
-#ifdef ENABLE_WALLET
-CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey)
-{
-    boost::optional<CScript> scriptPubKey = GetMinerScriptPubKey(reservekey);
-#else
-CBlockTemplate* CreateNewBlockWithKey()
-{
-    boost::optional<CScript> scriptPubKey = GetMinerScriptPubKey();
-#endif
-
-    if (!scriptPubKey) {
-        return NULL;
     }
-    return CreateNewBlock(*scriptPubKey);
+    //fprintf(stderr,"done new block\n");
+    return pblocktemplate.release();
 }
+/*
+ #ifdef ENABLE_WALLET
+ boost::optional<CScript> GetMinerScriptPubKey(CReserveKey& reservekey)
+ #else
+ boost::optional<CScript> GetMinerScriptPubKey()
+ #endif
+ {
+ CKeyID keyID;
+ CBitcoinAddress addr;
+ if (addr.SetString(GetArg("-mineraddress", ""))) {
+ addr.GetKeyID(keyID);
+ } else {
+ #ifdef ENABLE_WALLET
+ CPubKey pubkey;
+ if (!reservekey.GetReservedKey(pubkey)) {
+ return boost::optional<CScript>();
+ }
+ keyID = pubkey.GetID();
+ #else
+ return boost::optional<CScript>();
+ #endif
+ }
+ CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
+ return scriptPubKey;
+ }
+ #ifdef ENABLE_WALLET
+ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey)
+ {
+ boost::optional<CScript> scriptPubKey = GetMinerScriptPubKey(reservekey);
+ #else
+ CBlockTemplate* CreateNewBlockWithKey()
+ {
+ boost::optional<CScript> scriptPubKey = GetMinerScriptPubKey();
+ #endif
+ if (!scriptPubKey) {
+ return NULL;
+ }
+ return CreateNewBlock(*scriptPubKey);
+ }*/
 
 //////////////////////////////////////////////////////////////////////////////
 //
@@ -459,329 +795,1168 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int&
         hashPrevBlock = pblock->hashPrevBlock;
     }
     ++nExtraNonce;
-    unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
+    unsigned int nHeight = pindexPrev->GetHeight()+1; // Height first in coinbase required for block.version=2
     CMutableTransaction txCoinbase(pblock->vtx[0]);
     txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
     assert(txCoinbase.vin[0].scriptSig.size() <= 100);
-
+    
     pblock->vtx[0] = txCoinbase;
     pblock->hashMerkleRoot = pblock->BuildMerkleTree();
 }
 
 #ifdef ENABLE_WALLET
+//////////////////////////////////////////////////////////////////////////////
+//
+// Internal miner
+//
+
+CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, int32_t gpucount, bool isStake)
+{
+    CPubKey pubkey; CScript scriptPubKey; uint8_t *ptr; int32_t i;
+    if ( nHeight == 1 && ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 )
+    {
+        scriptPubKey = CScript() << ParseHex(ASSETCHAINS_OVERRIDE_PUBKEY) << OP_CHECKSIG;
+    }
+    else if ( USE_EXTERNAL_PUBKEY != 0 )
+    {
+        //fprintf(stderr,"use notary pubkey\n");
+        scriptPubKey = CScript() << ParseHex(NOTARY_PUBKEY) << OP_CHECKSIG;
+    }
+    else
+    {
+        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;
+    }
+    return CreateNewBlock(scriptPubKey, gpucount, isStake);
+}
+
+void komodo_broadcast(CBlock *pblock,int32_t limit)
+{
+    int32_t n = 1;
+    //fprintf(stderr,"broadcast new block t.%u\n",(uint32_t)time(NULL));
+    {
+        LOCK(cs_vNodes);
+        BOOST_FOREACH(CNode* pnode, vNodes)
+        {
+            if ( pnode->hSocket == INVALID_SOCKET )
+                continue;
+            if ( (rand() % n) == 0 )
+            {
+                pnode->PushMessage("block", *pblock);
+                if ( n++ > limit )
+                    break;
+            }
+        }
+    }
+    //fprintf(stderr,"finished broadcast new block t.%u\n",(uint32_t)time(NULL));
+}
+
 static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
 #else
 static bool ProcessBlockFound(CBlock* pblock)
 #endif // ENABLE_WALLET
 {
     LogPrintf("%s\n", pblock->ToString());
-    LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue));
-
+    LogPrintf("generated %s height.%d\n", FormatMoney(pblock->vtx[0].vout[0].nValue),chainActive.LastTip()->GetHeight()+1);
+    
     // Found a solution
     {
-        LOCK(cs_main);
-        if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
-            return error("ZcashMiner: generated block is stale");
+        if (pblock->hashPrevBlock != chainActive.LastTip()->GetBlockHash())
+        {
+            uint256 hash; int32_t i;
+            hash = pblock->hashPrevBlock;
+            for (i=31; i>=0; i--)
+                fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
+            fprintf(stderr," <- prev (stale)\n");
+            hash = chainActive.LastTip()->GetBlockHash();
+            for (i=31; i>=0; i--)
+                fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
+            fprintf(stderr," <- chainTip (stale)\n");
+            
+            return error("KomodoMiner: generated block is stale");
+        }
     }
-
+    
 #ifdef ENABLE_WALLET
-    if (GetArg("-mineraddress", "").empty()) {
-        // Remove key from key pool
-        reservekey.KeepKey();
+    // Remove key from key pool
+    if ( IS_KOMODO_NOTARY == 0 )
+    {
+        if (GetArg("-mineraddress", "").empty()) {
+            // Remove key from key pool
+            reservekey.KeepKey();
+        }
     }
-
     // Track how many getdata requests this block gets
+    //if ( 0 )
     {
+        //fprintf(stderr,"lock cs_wallet\n");
         LOCK(wallet.cs_wallet);
         wallet.mapRequestCount[pblock->GetHash()] = 0;
     }
 #endif
+    //fprintf(stderr,"process new block\n");
 
     // Process this block the same as if we had received it from another node
     CValidationState state;
-    if (!ProcessNewBlock(state, NULL, pblock, true, NULL))
-        return error("ZcashMiner: ProcessNewBlock, block not accepted");
-
+    if (!ProcessNewBlock(1,chainActive.LastTip()->GetHeight()+1,state, NULL, pblock, true, NULL))
+        return error("KomodoMiner: ProcessNewBlock, block not accepted");
+    
     TrackMinedBlock(pblock->GetHash());
-
+    komodo_broadcast(pblock,16);
     return true;
 }
 
+int32_t komodo_baseid(char *origbase);
+int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t *blocktimes,int32_t *nonzpkeysp,int32_t height);
+arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc);
+int32_t FOUND_BLOCK,KOMODO_MAYBEMINED;
+extern int32_t KOMODO_LASTMINED,KOMODO_INSYNC;
+int32_t roundrobin_delay;
+arith_uint256 HASHTarget,HASHTarget_POW;
+
+// wait for peers to connect
+int32_t waitForPeers(const CChainParams &chainparams)
+{
+    if (chainparams.MiningRequiresPeers())
+    {
+        bool fvNodesEmpty;
+        {
+            boost::this_thread::interruption_point();
+            LOCK(cs_vNodes);
+            fvNodesEmpty = vNodes.empty();
+        }
+        if (fvNodesEmpty || IsNotInSync())
+        {
+            int loops = 0, blockDiff = 0, newDiff = 0;
+            
+            do {
+                if (fvNodesEmpty)
+                {
+                    MilliSleep(1000 + rand() % 4000);
+                    boost::this_thread::interruption_point();
+                    LOCK(cs_vNodes);
+                    fvNodesEmpty = vNodes.empty();
+                    loops = 0;
+                    blockDiff = 0;
+                }
+                if ((newDiff = IsNotInSync()) > 1)
+                {
+                    if (blockDiff != newDiff)
+                    {
+                        blockDiff = newDiff;
+                    }
+                    else
+                    {
+                        if (++loops <= 10)
+                        {
+                            MilliSleep(1000);
+                        }
+                        else break;
+                    }
+                }
+            } while (fvNodesEmpty || IsNotInSync());
+            MilliSleep(100 + rand() % 400);
+        }
+    }
+}
+
 #ifdef ENABLE_WALLET
-void static BitcoinMiner(CWallet *pwallet)
-#else
-void static BitcoinMiner()
-#endif
+CBlockIndex *get_chainactive(int32_t height)
 {
-    LogPrintf("ZcashMiner started\n");
-    SetThreadPriority(THREAD_PRIORITY_LOWEST);
-    RenameThread("zcash-miner");
+    if ( chainActive.LastTip() != 0 )
+    {
+        if ( height <= chainActive.LastTip()->GetHeight() )
+        {
+            LOCK(cs_main);
+            return(chainActive[height]);
+        }
+        // else fprintf(stderr,"get_chainactive height %d > active.%d\n",height,chainActive.Tip()->GetHeight());
+    }
+    //fprintf(stderr,"get_chainactive null chainActive.Tip() height %d\n",height);
+    return(0);
+}
+
+/*
+ * A separate thread to stake, while the miner threads mine.
+ */
+void static VerusStaker(CWallet *pwallet)
+{
+    LogPrintf("Verus staker thread started\n");
+    RenameThread("verus-staker");
+
     const CChainParams& chainparams = Params();
+    auto consensusParams = chainparams.GetConsensus();
 
-#ifdef ENABLE_WALLET
     // Each thread has its own key
     CReserveKey reservekey(pwallet);
-#endif
 
     // 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;
 
-    unsigned int n = chainparams.EquihashN();
-    unsigned int k = chainparams.EquihashK();
+    while ( (ASSETCHAIN_INIT == 0 || KOMODO_INITDONE == 0) ) //chainActive.Tip()->GetHeight() != 235300 &&
+    {
+        sleep(1);
+        if ( komodo_baseid(ASSETCHAINS_SYMBOL) < 0 )
+            break;
+    }
+
+    // try a nice clean peer connection to start
+    CBlockIndex *pindexPrev, *pindexCur;
+    do {
+        pindexPrev = chainActive.LastTip();
+        MilliSleep(5000 + rand() % 5000);
+        waitForPeers(chainparams);
+        pindexCur = chainActive.LastTip();
+    } while (pindexPrev != pindexCur);
+
+    try {
+        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);
+            }
+
+            // Check for stop or if block needs to be rebuilt
+            boost::this_thread::interruption_point();
+
+            // try to stake a block
+            CBlockTemplate *ptr = NULL;
+            if (Mining_height > VERUS_MIN_STAKEAGE)
+                ptr = CreateNewBlockWithKey(reservekey, Mining_height, 0, true);
+
+            if ( ptr == 0 )
+            {
+                // wait to try another staking block until after the tip moves again
+                while ( chainActive.LastTip() == pindexPrev )
+                    sleep(1);
+                continue;
+            }
+
+            unique_ptr<CBlockTemplate> pblocktemplate(ptr);
+            if (!pblocktemplate.get())
+            {
+                if (GetArg("-mineraddress", "").empty()) {
+                    LogPrintf("Error in %s staker: Keypool ran out, please call keypoolrefill before restarting the mining thread\n",
+                              ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+                } else {
+                    // Should never reach here, because -mineraddress validity is checked in init.cpp
+                    LogPrintf("Error in %s staker: Invalid %s -mineraddress\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO], ASSETCHAINS_SYMBOL);
+                }
+                return;
+            }
+
+            CBlock *pblock = &pblocktemplate->block;
+            LogPrintf("Staking with %u transactions in block (%u bytes)\n", pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION));
+            //
+            // Search
+            //
+            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);
+
+            if (vNodes.empty() && chainparams.MiningRequiresPeers())
+            {
+                if ( Mining_height > ASSETCHAINS_MINHEIGHT )
+                {
+                    fprintf(stderr,"no nodes, attempting reconnect\n");
+                    continue;
+                }
+            }
+
+            if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
+            {
+                fprintf(stderr,"timeout, retrying\n");
+                continue;
+            }
+
+            if ( pindexPrev != chainActive.LastTip() )
+            {
+                printf("Block %d added to chain\n", chainActive.LastTip()->GetHeight());
+                MilliSleep(250);
+                continue;
+            }
+
+            int32_t unlockTime = komodo_block_unlocktime(Mining_height);
+            int64_t subsidy = (int64_t)(pblock->vtx[0].vout[0].nValue);
+
+            uint256 hashTarget = ArithToUint256(arith_uint256().SetCompact(pblock->nBits));
+
+            pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
+
+            UpdateTime(pblock, consensusParams, pindexPrev);
+
+            ProcessBlockFound(pblock, *pwallet, reservekey);
+
+            LogPrintf("Using %s algorithm:\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+            LogPrintf("Staked block found  \n  hash: %s  \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex());
+            printf("Found block %d \n", Mining_height );
+            printf("staking reward %.8f %s!\n", (double)subsidy / (double)COIN, ASSETCHAINS_SYMBOL);
+            arith_uint256 post;
+            post.SetCompact(pblock->GetVerusPOSTarget());
+            pindexPrev = get_chainactive(Mining_height - 100);
+            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
+                printf("\n");
+
+            // Check for stop or if block needs to be rebuilt
+            boost::this_thread::interruption_point();
+
+            sleep(3);
+
+            // In regression test mode, stop mining after a block is found.
+            if (chainparams.MineBlocksOnDemand()) {
+                throw boost::thread_interrupted();
+            }
+        }
+    }
+    catch (const boost::thread_interrupted&)
+    {
+        LogPrintf("VerusStaker terminated\n");
+        throw;
+    }
+    catch (const std::runtime_error &e)
+    {
+        LogPrintf("VerusStaker runtime error: %s\n", e.what());
+        return;
+    }
+}
+
+void static BitcoinMiner_noeq(CWallet *pwallet)
+#else
+void static BitcoinMiner_noeq()
+#endif
+{
+    LogPrintf("%s miner started\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+    RenameThread("verushash-miner");
+
+#ifdef ENABLE_WALLET
+    // Each thread has its own key
+    CReserveKey reservekey(pwallet);
+#endif
+
+    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 &&
+    {
+        sleep(1);
+        if ( komodo_baseid(ASSETCHAINS_SYMBOL) < 0 )
+            break;
+    }
+
+    SetThreadPriority(THREAD_PRIORITY_LOWEST);
+
+    // try a nice clean peer connection to start
+    CBlockIndex *pindexPrev, *pindexCur;
+    do {
+        pindexPrev = chainActive.LastTip();
+        MilliSleep(5000 + rand() % 5000);
+        waitForPeers(chainparams);
+        pindexCur = chainActive.LastTip();
+    } while (pindexPrev != pindexCur);
+
+    // 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;
+
+    miningTimer.start();
+
+    try {
+        printf("Mining %s with %s\n", ASSETCHAINS_SYMBOL, ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+        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())
+            {
+                do {
+                    pindexPrev = chainActive.LastTip();
+                    MilliSleep(5000 + rand() % 5000);
+                } while (pindexPrev != chainActive.LastTip());
+            }
+
+            // 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 (lastChainTipPrinted != pindexPrev)
+            {
+                printf("Mining height %d\n", Mining_height);
+                lastChainTipPrinted = pindexPrev;
+            }
+
+            miningTimer.start();
+
+#ifdef ENABLE_WALLET
+            CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, Mining_height, 0);
+#else
+            CBlockTemplate *ptr = CreateNewBlockWithKey();
+#endif
+            if ( ptr == 0 )
+            {
+                static uint32_t counter;
+                if ( counter++ < 100 )
+                    fprintf(stderr,"created illegal block, retry\n");
+                continue;
+            }
+
+            unique_ptr<CBlockTemplate> pblocktemplate(ptr);
+            if (!pblocktemplate.get())
+            {
+                if (GetArg("-mineraddress", "").empty()) {
+                    LogPrintf("Error in %s miner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n",
+                              ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+                } else {
+                    // Should never reach here, because -mineraddress validity is checked in init.cpp
+                    LogPrintf("Error in %s miner: Invalid %s -mineraddress\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO], ASSETCHAINS_SYMBOL);
+                }
+                return;
+            }
+            CBlock *pblock = &pblocktemplate->block;
+            if ( ASSETCHAINS_SYMBOL[0] != 0 )
+            {
+                if ( ASSETCHAINS_REWARD[0] == 0 && !ASSETCHAINS_LASTERA )
+                {
+                    if ( pblock->vtx.size() == 1 && pblock->vtx[0].vout.size() == 1 && Mining_height > ASSETCHAINS_MINHEIGHT )
+                    {
+                        static uint32_t counter;
+                        if ( counter++ < 10 )
+                            fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",ASSETCHAINS_SYMBOL);
+                        sleep(10);
+                        continue;
+                    } else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT);
+                }
+            }
+            IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
+            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();
+
+            pblock->nSolution = solnPlaceholder;
+            savebits = pblock->nBits;
+            arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
+            arith_uint256 mask(ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO]);
+
+            Mining_start = 0;
+
+            if ( pindexPrev != chainActive.LastTip() )
+            {
+                if (lastChainTipPrinted != chainActive.LastTip())
+                {
+                    lastChainTipPrinted = chainActive.LastTip();
+                    printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
+                }
+                MilliSleep(250);
+                continue;
+            }
+
+            if ( ASSETCHAINS_STAKED != 0 )
+            {
+                int32_t percPoS,z;
+                hashTarget = komodo_PoWtarget(&percPoS,hashTarget,Mining_height,ASSETCHAINS_STAKED);
+                for (z=31; z>=0; z--)
+                    fprintf(stderr,"%02x",((uint8_t *)&hashTarget)[z]);
+                fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%%\n",percPoS,(int32_t)ASSETCHAINS_STAKED);
+            }
+
+            while (true)
+            {
+                arith_uint256 arNonce = UintToArith256(pblock->nNonce);
+
+                CVerusHashWriter ss = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
+                ss << *((CBlockHeader *)pblock);
+                int64_t *extraPtr = ss.xI64p();
+                CVerusHash &vh = ss.GetState();
+                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);
+
+                    if ( UintToArith256(hashResult) <= hashTarget )
+                    {
+                        if (pblock->nSolution.size() != 1344)
+                        {
+                            LogPrintf("ERROR: Block solution is not 1344 bytes as it should be");
+                            sleep(5);
+                            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);
+
+                        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());
+                            }
+                            break;
+                        }
+                        hashesToGo = ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
+                    }
+                }
+
+                {
+                    LOCK(cs_metrics);
+                    nHashCount += i;
+                }
+
+                // Check for stop or if block needs to be rebuilt
+                boost::this_thread::interruption_point();
+
+                if (vNodes.empty() && chainparams.MiningRequiresPeers())
+                {
+                    if ( Mining_height > ASSETCHAINS_MINHEIGHT )
+                    {
+                        fprintf(stderr,"no nodes, attempting reconnect\n");
+                        break;
+                    }
+                }
+
+                if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
+                {
+                    fprintf(stderr,"timeout, retrying\n");
+                    break;
+                }
+
+                if ( pindexPrev != chainActive.LastTip() )
+                {
+                    if (lastChainTipPrinted != chainActive.LastTip())
+                    {
+                        lastChainTipPrinted = chainActive.LastTip();
+                        printf("Block %d added to chain\n", lastChainTipPrinted->GetHeight());
+                    }
+                    break;
+                }
+
+#ifdef _WIN32
+                printf("%llu mega hashes complete - working\n", (ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1) / 1048576);
+#else
+                printf("%lu mega hashes complete - working\n", (ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1) / 1048576);
+#endif
+                break;
 
-    std::string solver = GetArg("-equihashsolver", "default");
+            }
+        }
+    }
+    catch (const boost::thread_interrupted&)
+    {
+        miningTimer.stop();
+        LogPrintf("%s miner terminated\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
+        throw;
+    }
+    catch (const std::runtime_error &e)
+    {
+        miningTimer.stop();
+        LogPrintf("%s miner runtime error: %s\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO], e.what());
+        return;
+    }
+    miningTimer.stop();
+}
+
+#ifdef ENABLE_WALLET
+void static BitcoinMiner(CWallet *pwallet)
+#else
+void static BitcoinMiner()
+#endif
+{
+    LogPrintf("KomodoMiner started\n");
+    SetThreadPriority(THREAD_PRIORITY_LOWEST);
+    RenameThread("komodo-miner");
+    const CChainParams& chainparams = Params();
+    
+#ifdef ENABLE_WALLET
+    // Each thread has its own key
+    CReserveKey reservekey(pwallet);
+#endif
+    
+    // Each thread has its own counter
+    unsigned int nExtraNonce = 0;
+    
+    unsigned int n = chainparams.EquihashN();
+    unsigned int k = chainparams.EquihashK();
+    uint8_t *script; uint64_t total,checktoshis; int32_t i,j,gpucount=KOMODO_MAXGPUCOUNT,notaryid = -1;
+    while ( (ASSETCHAIN_INIT == 0 || KOMODO_INITDONE == 0) )
+    {
+        sleep(1);
+        if ( komodo_baseid(ASSETCHAINS_SYMBOL) < 0 )
+            break;
+    }
+    if ( ASSETCHAINS_SYMBOL[0] == 0 )
+        komodo_chosennotary(&notaryid,chainActive.LastTip()->GetHeight(),NOTARY_PUBKEY33,(uint32_t)chainActive.LastTip()->GetBlockTime());
+    if ( notaryid != My_notaryid )
+        My_notaryid = notaryid;
+    std::string solver;
+    //if ( notaryid >= 0 || ASSETCHAINS_SYMBOL[0] != 0 )
+    solver = "tromp";
+    //else solver = "default";
     assert(solver == "tromp" || solver == "default");
     LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k);
-
+    if ( ASSETCHAINS_SYMBOL[0] != 0 )
+        fprintf(stderr,"notaryid.%d Mining.%s with %s\n",notaryid,ASSETCHAINS_SYMBOL,solver.c_str());
     std::mutex m_cs;
     bool cancelSolver = false;
     boost::signals2::connection c = uiInterface.NotifyBlockTip.connect(
-        [&m_cs, &cancelSolver](const uint256& hashNewTip) mutable {
-            std::lock_guard<std::mutex> lock{m_cs};
-            cancelSolver = true;
-        }
-    );
+                                                                       [&m_cs, &cancelSolver](const uint256& hashNewTip) mutable {
+                                                                           std::lock_guard<std::mutex> lock{m_cs};
+                                                                           cancelSolver = true;
+                                                                       }
+                                                                       );
     miningTimer.start();
-
+    
     try {
-        while (true) {
-            if (chainparams.MiningRequiresPeers()) {
+        if ( ASSETCHAINS_SYMBOL[0] != 0 )
+            fprintf(stderr,"try %s Mining with %s\n",ASSETCHAINS_SYMBOL,solver.c_str());
+        while (true)
+        {
+            if (chainparams.MiningRequiresPeers()) //chainActive.LastTip()->GetHeight() != 235300 &&
+            {
+                //if ( ASSETCHAINS_SEED != 0 && chainActive.LastTip()->GetHeight() < 100 )
+                //    break;
                 // Busy-wait for the network to come online so we don't waste time mining
                 // on an obsolete chain. In regtest mode we expect to fly solo.
                 miningTimer.stop();
                 do {
                     bool fvNodesEmpty;
                     {
-                        LOCK(cs_vNodes);
+                        //LOCK(cs_vNodes);
                         fvNodesEmpty = vNodes.empty();
                     }
-                    if (!fvNodesEmpty && !IsInitialBlockDownload())
+                    if (!fvNodesEmpty )//&& !IsInitialBlockDownload())
                         break;
-                    MilliSleep(1000);
+                    MilliSleep(15000);
+                    //fprintf(stderr,"fvNodesEmpty %d IsInitialBlockDownload(%s) %d\n",(int32_t)fvNodesEmpty,ASSETCHAINS_SYMBOL,(int32_t)IsInitialBlockDownload());
+                    
                 } while (true);
+                //fprintf(stderr,"%s Found peers\n",ASSETCHAINS_SYMBOL);
                 miningTimer.start();
             }
-
             //
             // Create new block
             //
             unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
-            CBlockIndex* pindexPrev = chainActive.Tip();
+            CBlockIndex* pindexPrev = chainActive.LastTip();
+            if ( Mining_height != pindexPrev->GetHeight()+1 )
+            {
+                Mining_height = pindexPrev->GetHeight()+1;
+                Mining_start = (uint32_t)time(NULL);
+            }
+            if ( ASSETCHAINS_SYMBOL[0] != 0 && ASSETCHAINS_STAKED == 0 )
+            {
+                //fprintf(stderr,"%s create new block ht.%d\n",ASSETCHAINS_SYMBOL,Mining_height);
+                //sleep(3);
+            }
 
 #ifdef ENABLE_WALLET
-            unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
+            // notaries always default to staking
+            CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, pindexPrev->GetHeight()+1, gpucount, ASSETCHAINS_STAKED != 0 && GetArg("-genproclimit", 0) == 0);
 #else
-            unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey());
+            CBlockTemplate *ptr = CreateNewBlockWithKey();
 #endif
+            if ( ptr == 0 )
+            {
+                static uint32_t counter;
+                if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 )
+                    fprintf(stderr,"created illegal block, retry\n");
+                sleep(1);
+                continue;
+            }
+            //fprintf(stderr,"get template\n");
+            unique_ptr<CBlockTemplate> pblocktemplate(ptr);
             if (!pblocktemplate.get())
             {
                 if (GetArg("-mineraddress", "").empty()) {
-                    LogPrintf("Error in ZcashMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
+                    LogPrintf("Error in KomodoMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
                 } else {
                     // Should never reach here, because -mineraddress validity is checked in init.cpp
-                    LogPrintf("Error in ZcashMiner: Invalid -mineraddress\n");
+                    LogPrintf("Error in KomodoMiner: Invalid -mineraddress\n");
                 }
                 return;
             }
             CBlock *pblock = &pblocktemplate->block;
+            if ( ASSETCHAINS_SYMBOL[0] != 0 )
+            {
+                if ( ASSETCHAINS_REWARD[0] == 0 && !ASSETCHAINS_LASTERA )
+                {
+                    if ( pblock->vtx.size() == 1 && pblock->vtx[0].vout.size() == 1 && Mining_height > ASSETCHAINS_MINHEIGHT )
+                    {
+                        static uint32_t counter;
+                        if ( counter++ < 10 )
+                            fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",ASSETCHAINS_SYMBOL);
+                        sleep(10);
+                        continue;
+                    } else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT);
+                }
+            }
             IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
-
-            LogPrintf("Running ZcashMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
-                ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
-
+            //fprintf(stderr,"Running KomodoMiner.%s with %u transactions in block\n",solver.c_str(),(int32_t)pblock->vtx.size());
+            LogPrintf("Running KomodoMiner.%s with %u transactions in block (%u bytes)\n",solver.c_str(),pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION));
             //
             // Search
             //
-            int64_t nStart = GetTime();
-            arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
-
-            while (true) {
+            uint8_t pubkeys[66][33]; arith_uint256 bnMaxPoSdiff; uint32_t blocktimes[66]; int mids[256],nonzpkeys,i,j,externalflag; uint32_t savebits; int64_t nStart = GetTime();
+            pblock->nBits         = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
+            savebits = pblock->nBits;
+            HASHTarget = arith_uint256().SetCompact(savebits);
+            roundrobin_delay = ROUNDROBIN_DELAY;
+            if ( ASSETCHAINS_SYMBOL[0] == 0 && notaryid >= 0 )
+            {
+                j = 65;
+                if ( (Mining_height >= 235300 && Mining_height < 236000) || (Mining_height % KOMODO_ELECTION_GAP) > 64 || (Mining_height % KOMODO_ELECTION_GAP) == 0 || Mining_height > 1000000 )
+                {
+                    int32_t dispflag = 0;
+                    if ( notaryid <= 3 || notaryid == 32 || (notaryid >= 43 && notaryid <= 45) &&notaryid == 51 || notaryid == 52 || notaryid == 56 || notaryid == 57 )
+                        dispflag = 1;
+                    komodo_eligiblenotary(pubkeys,mids,blocktimes,&nonzpkeys,pindexPrev->GetHeight());
+                    if ( nonzpkeys > 0 )
+                    {
+                        for (i=0; i<33; i++)
+                            if( pubkeys[0][i] != 0 )
+                                break;
+                        if ( i == 33 )
+                            externalflag = 1;
+                        else externalflag = 0;
+                        if ( IS_KOMODO_NOTARY != 0 )
+                        {
+                            for (i=1; i<66; i++)
+                                if ( memcmp(pubkeys[i],pubkeys[0],33) == 0 )
+                                    break;
+                            if ( externalflag == 0 && i != 66 && mids[i] >= 0 )
+                                printf("VIOLATION at %d, notaryid.%d\n",i,mids[i]);
+                            for (j=gpucount=0; j<65; j++)
+                            {
+                                if ( dispflag != 0 )
+                                {
+                                    if ( mids[j] >= 0 )
+                                        fprintf(stderr,"%d ",mids[j]);
+                                    else fprintf(stderr,"GPU ");
+                                }
+                                if ( mids[j] == -1 )
+                                    gpucount++;
+                            }
+                            if ( dispflag != 0 )
+                                fprintf(stderr," <- prev minerids from ht.%d notary.%d gpucount.%d %.2f%% t.%u\n",pindexPrev->GetHeight(),notaryid,gpucount,100.*(double)gpucount/j,(uint32_t)time(NULL));
+                        }
+                        for (j=0; j<65; j++)
+                            if ( mids[j] == notaryid )
+                                break;
+                        if ( j == 65 )
+                            KOMODO_LASTMINED = 0;
+                    } else fprintf(stderr,"no nonz pubkeys\n");
+                    if ( (Mining_height >= 235300 && Mining_height < 236000) || (j == 65 && Mining_height > KOMODO_MAYBEMINED+1 && Mining_height > KOMODO_LASTMINED+64) )
+                    {
+                        HASHTarget = arith_uint256().SetCompact(KOMODO_MINDIFF_NBITS);
+                        fprintf(stderr,"I am the chosen one for %s ht.%d\n",ASSETCHAINS_SYMBOL,pindexPrev->GetHeight()+1);
+                    } //else fprintf(stderr,"duplicate at j.%d\n",j);
+                } else Mining_start = 0;
+            } else Mining_start = 0;
+            if ( ASSETCHAINS_STAKED != 0 )
+            {
+                int32_t percPoS,z; bool fNegative,fOverflow;
+                HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED);
+                HASHTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow);
+                if ( ASSETCHAINS_STAKED < 100 )
+                {
+                    for (z=31; z>=0; z--)
+                        fprintf(stderr,"%02x",((uint8_t *)&HASHTarget_POW)[z]);
+                    fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%%\n",percPoS,(int32_t)ASSETCHAINS_STAKED);
+                }
+            }
+            while (true)
+            {
+                if ( KOMODO_INSYNC == 0 )
+                {
+                    fprintf(stderr,"Mining when blockchain might not be in sync longest.%d vs %d\n",KOMODO_LONGESTCHAIN,Mining_height);
+                    if ( KOMODO_LONGESTCHAIN != 0 && Mining_height >= KOMODO_LONGESTCHAIN )
+                        KOMODO_INSYNC = 1;
+                    sleep(3);
+                }
                 // Hash state
+                KOMODO_CHOSEN_ONE = 0;
+                
                 crypto_generichash_blake2b_state state;
                 EhInitialiseState(n, k, state);
-
                 // I = the block header minus nonce and solution.
                 CEquihashInput I{*pblock};
                 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
                 ss << I;
-
                 // H(I||...
                 crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size());
-
                 // H(I||V||...
                 crypto_generichash_blake2b_state curr_state;
                 curr_state = state;
-                crypto_generichash_blake2b_update(&curr_state,
-                                                  pblock->nNonce.begin(),
-                                                  pblock->nNonce.size());
-
+                crypto_generichash_blake2b_update(&curr_state,pblock->nNonce.begin(),pblock->nNonce.size());
                 // (x_1, x_2, ...) = A(I, V, n, k)
-                LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n",
-                         solver, pblock->nNonce.ToString());
-
+                LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n",solver, pblock->nNonce.ToString());
+                arith_uint256 hashTarget;
+                if ( KOMODO_MININGTHREADS > 0 && ASSETCHAINS_STAKED > 0 && ASSETCHAINS_STAKED < 100 && Mining_height > 10 )
+                    hashTarget = HASHTarget_POW;
+                else hashTarget = HASHTarget;
                 std::function<bool(std::vector<unsigned char>)> validBlock =
 #ifdef ENABLE_WALLET
-                        [&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams]
+                [&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams]
 #else
-                        [&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams]
+                [&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams]
 #endif
-                        (std::vector<unsigned char> soln) {
+                (std::vector<unsigned char> soln) {
+                    int32_t z; arith_uint256 h; CBlock B;
                     // Write the solution to the hash and compute the result.
                     LogPrint("pow", "- Checking solution against target\n");
                     pblock->nSolution = soln;
                     solutionTargetChecks.increment();
-
-                    if (UintToArith256(pblock->GetHash()) > hashTarget) {
+                    B = *pblock;
+                    h = UintToArith256(B.GetHash());
+                    /*for (z=31; z>=16; z--)
+                        fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
+                    fprintf(stderr," mined ");
+                    for (z=31; z>=16; z--)
+                        fprintf(stderr,"%02x",((uint8_t *)&HASHTarget)[z]);
+                    fprintf(stderr," hashTarget ");
+                    for (z=31; z>=16; z--)
+                        fprintf(stderr,"%02x",((uint8_t *)&HASHTarget_POW)[z]);
+                    fprintf(stderr," POW\n");*/
+                    if ( h > hashTarget )
+                    {
+                        //if ( ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0 )
+                        //    sleep(1);
                         return false;
                     }
-
+                    if ( IS_KOMODO_NOTARY != 0 && B.nTime > GetAdjustedTime() )
+                    {
+                        //fprintf(stderr,"need to wait %d seconds to submit block\n",(int32_t)(B.nTime - GetAdjustedTime()));
+                        while ( GetAdjustedTime() < B.nTime-2 )
+                        {
+                            sleep(1);
+                            if ( chainActive.LastTip()->GetHeight() >= Mining_height )
+                            {
+                                fprintf(stderr,"new block arrived\n");
+                                return(false);
+                            }
+                        }
+                    }
+                    if ( ASSETCHAINS_STAKED == 0 )
+                    {
+                        if ( IS_KOMODO_NOTARY != 0 )
+                        {
+                            int32_t r;
+                            if ( (r= ((Mining_height + NOTARY_PUBKEY33[16]) % 64) / 8) > 0 )
+                                MilliSleep((rand() % (r * 1000)) + 1000);
+                        }
+                    }
+                    else
+                    {
+                        while ( B.nTime-57 > GetAdjustedTime() )
+                        {
+                            sleep(1);
+                            if ( chainActive.LastTip()->GetHeight() >= Mining_height )
+                                return(false);
+                        }
+                        uint256 tmp = B.GetHash();
+                        int32_t z; for (z=31; z>=0; z--)
+                            fprintf(stderr,"%02x",((uint8_t *)&tmp)[z]);
+                        fprintf(stderr," mined %s block %d!\n",ASSETCHAINS_SYMBOL,Mining_height);
+                    }
+                    CValidationState state;
+                    if ( !TestBlockValidity(state,B, chainActive.LastTip(), true, false))
+                    {
+                        h = UintToArith256(B.GetHash());
+                        for (z=31; z>=0; z--)
+                            fprintf(stderr,"%02x",((uint8_t *)&h)[z]);
+                        fprintf(stderr," Invalid block mined, try again\n");
+                        return(false);
+                    }
+                    KOMODO_CHOSEN_ONE = 1;
                     // Found a solution
                     SetThreadPriority(THREAD_PRIORITY_NORMAL);
-                    LogPrintf("ZcashMiner:\n");
-                    LogPrintf("proof-of-work found  \n  hash: %s  \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex());
+                    LogPrintf("KomodoMiner:\n");
+                    LogPrintf("proof-of-work found  \n  hash: %s  \ntarget: %s\n", B.GetHash().GetHex(), HASHTarget.GetHex());
 #ifdef ENABLE_WALLET
-                    if (ProcessBlockFound(pblock, *pwallet, reservekey)) {
+                    if (ProcessBlockFound(&B, *pwallet, reservekey)) {
 #else
-                    if (ProcessBlockFound(pblock)) {
+                        if (ProcessBlockFound(&B)) {
 #endif
-                        // Ignore chain updates caused by us
+                            // Ignore chain updates caused by us
+                            std::lock_guard<std::mutex> lock{m_cs};
+                            cancelSolver = false;
+                        }
+                        KOMODO_CHOSEN_ONE = 0;
+                        SetThreadPriority(THREAD_PRIORITY_LOWEST);
+                        // In regression test mode, stop mining after a block is found.
+                        if (chainparams.MineBlocksOnDemand()) {
+                            // Increment here because throwing skips the call below
+                            ehSolverRuns.increment();
+                            throw boost::thread_interrupted();
+                        }
+                        return true;
+                    };
+                    std::function<bool(EhSolverCancelCheck)> cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) {
                         std::lock_guard<std::mutex> lock{m_cs};
-                        cancelSolver = false;
-                    }
-                    SetThreadPriority(THREAD_PRIORITY_LOWEST);
-
-                    // In regression test mode, stop mining after a block is found.
-                    if (chainparams.MineBlocksOnDemand()) {
-                        // Increment here because throwing skips the call below
-                        ehSolverRuns.increment();
-                        throw boost::thread_interrupted();
-                    }
-
-                    return true;
-                };
-                std::function<bool(EhSolverCancelCheck)> cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) {
-                    std::lock_guard<std::mutex> lock{m_cs};
-                    return cancelSolver;
-                };
-
-                // TODO: factor this out into a function with the same API for each solver.
-                if (solver == "tromp") {
-                    // Create solver and initialize it.
-                    equi eq(1);
-                    eq.setstate(&curr_state);
-
-                    // Initialization done, start algo driver.
-                    eq.digit0(0);
-                    eq.xfull = eq.bfull = eq.hfull = 0;
-                    eq.showbsizes(0);
-                    for (u32 r = 1; r < WK; r++) {
-                        (r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0);
+                        return cancelSolver;
+                    };
+                    
+                    // TODO: factor this out into a function with the same API for each solver.
+                    if (solver == "tromp" ) { //&& notaryid >= 0 ) {
+                        // Create solver and initialize it.
+                        equi eq(1);
+                        eq.setstate(&curr_state);
+                        
+                        // Initialization done, start algo driver.
+                        eq.digit0(0);
                         eq.xfull = eq.bfull = eq.hfull = 0;
-                        eq.showbsizes(r);
-                    }
-                    eq.digitK(0);
-                    ehSolverRuns.increment();
-
-                    // Convert solution indices to byte array (decompress) and pass it to validBlock method.
-                    for (size_t s = 0; s < eq.nsols; s++) {
-                        LogPrint("pow", "Checking solution %d\n", s+1);
-                        std::vector<eh_index> index_vector(PROOFSIZE);
-                        for (size_t i = 0; i < PROOFSIZE; i++) {
-                            index_vector[i] = eq.sols[s][i];
+                        eq.showbsizes(0);
+                        for (u32 r = 1; r < WK; r++) {
+                            (r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0);
+                            eq.xfull = eq.bfull = eq.hfull = 0;
+                            eq.showbsizes(r);
                         }
-                        std::vector<unsigned char> sol_char = GetMinimalFromIndices(index_vector, DIGITBITS);
-
-                        if (validBlock(sol_char)) {
-                            // If we find a POW solution, do not try other solutions
-                            // because they become invalid as we created a new block in blockchain.
-                            break;
+                        eq.digitK(0);
+                        ehSolverRuns.increment();
+                        
+                        // Convert solution indices to byte array (decompress) and pass it to validBlock method.
+                        for (size_t s = 0; s < eq.nsols; s++) {
+                            LogPrint("pow", "Checking solution %d\n", s+1);
+                            std::vector<eh_index> index_vector(PROOFSIZE);
+                            for (size_t i = 0; i < PROOFSIZE; i++) {
+                                index_vector[i] = eq.sols[s][i];
+                            }
+                            std::vector<unsigned char> sol_char = GetMinimalFromIndices(index_vector, DIGITBITS);
+                            
+                            if (validBlock(sol_char)) {
+                                // If we find a POW solution, do not try other solutions
+                                // because they become invalid as we created a new block in blockchain.
+                                break;
+                            }
+                        }
+                    } else {
+                        try {
+                            // If we find a valid block, we rebuild
+                            bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled);
+                            ehSolverRuns.increment();
+                            if (found) {
+                                int32_t i; uint256 hash = pblock->GetHash();
+                                for (i=0; i<32; i++)
+                                    fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
+                                fprintf(stderr," <- %s Block found %d\n",ASSETCHAINS_SYMBOL,Mining_height);
+                                FOUND_BLOCK = 1;
+                                KOMODO_MAYBEMINED = Mining_height;
+                                break;
+                            }
+                        } catch (EhSolverCancelledException&) {
+                            LogPrint("pow", "Equihash solver cancelled\n");
+                            std::lock_guard<std::mutex> lock{m_cs};
+                            cancelSolver = false;
                         }
                     }
-                } else {
-                    try {
-                        // If we find a valid block, we rebuild
-                        bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled);
-                        ehSolverRuns.increment();
-                        if (found) {
+                    
+                    // Check for stop or if block needs to be rebuilt
+                    boost::this_thread::interruption_point();
+                    // Regtest mode doesn't require peers
+                    if ( FOUND_BLOCK != 0 )
+                    {
+                        FOUND_BLOCK = 0;
+                        fprintf(stderr,"FOUND_BLOCK!\n");
+                        //sleep(2000);
+                    }
+                    if (vNodes.empty() && chainparams.MiningRequiresPeers())
+                    {
+                        if ( ASSETCHAINS_SYMBOL[0] == 0 || Mining_height > ASSETCHAINS_MINHEIGHT )
+                        {
+                            fprintf(stderr,"no nodes, break\n");
                             break;
                         }
-                    } catch (EhSolverCancelledException&) {
-                        LogPrint("pow", "Equihash solver cancelled\n");
-                        std::lock_guard<std::mutex> lock{m_cs};
-                        cancelSolver = false;
                     }
-                }
-
-                // Check for stop or if block needs to be rebuilt
-                boost::this_thread::interruption_point();
-                // Regtest mode doesn't require peers
-                if (vNodes.empty() && chainparams.MiningRequiresPeers())
-                    break;
-                if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
-                    break;
-                if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
-                    break;
-                if (pindexPrev != chainActive.Tip())
-                    break;
-
-                // Update nNonce and nTime
-                pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
-                UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
-                if (chainparams.GetConsensus().nPowAllowMinDifficultyBlocksAfterHeight != boost::none)
-                {
-                    // Changing pblock->nTime can change work required on testnet:
-                    hashTarget.SetCompact(pblock->nBits);
+                    if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
+                    {
+                        //if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 )
+                        fprintf(stderr,"0xffff, break\n");
+                        break;
+                    }
+                    if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
+                    {
+                        if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 )
+                            fprintf(stderr,"timeout, break\n");
+                        break;
+                    }
+                    if ( pindexPrev != chainActive.LastTip() )
+                    {
+                        if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 )
+                            fprintf(stderr,"Tip advanced, break\n");
+                        break;
+                    }
+                    // Update nNonce and nTime
+                    pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
+                    pblock->nBits = savebits;
+                    /*if ( NOTARY_PUBKEY33[0] == 0 )
+                    {
+                        int32_t percPoS;
+                        UpdateTime(pblock, consensusParams, pindexPrev);
+                        if (consensusParams.fPowAllowMinDifficultyBlocks)
+                        {
+                            // Changing pblock->nTime can change work required on testnet:
+                            HASHTarget.SetCompact(pblock->nBits);
+                            HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED);
+                        }
+                    }*/
                 }
             }
         }
-    }
-    catch (const boost::thread_interrupted&)
-    {
-        miningTimer.stop();
-        c.disconnect();
-        LogPrintf("ZcashMiner terminated\n");
-        throw;
-    }
-    catch (const std::runtime_error &e)
-    {
+        catch (const boost::thread_interrupted&)
+        {
+            miningTimer.stop();
+            c.disconnect();
+            LogPrintf("KomodoMiner terminated\n");
+            throw;
+        }
+        catch (const std::runtime_error &e)
+        {
+            miningTimer.stop();
+            c.disconnect();
+            LogPrintf("KomodoMiner runtime error: %s\n", e.what());
+            return;
+        }
         miningTimer.stop();
         c.disconnect();
-        LogPrintf("ZcashMiner runtime error: %s\n", e.what());
-        return;
     }
-    miningTimer.stop();
-    c.disconnect();
-}
-
+    
 #ifdef ENABLE_WALLET
-void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
+    void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
 #else
-void GenerateBitcoins(bool fGenerate, int nThreads)
+    void GenerateBitcoins(bool fGenerate, int nThreads)
 #endif
-{
-    static boost::thread_group* minerThreads = NULL;
+    {
+        // 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))
+        {
+            cheatCatcher = boost::get<libzcash::SaplingPaymentAddress>(addr);
+        }
+        else
+        {
+            if (VERUS_CHEATCATCHER.size() > 0)
+                fprintf(stderr, "-cheatcatcher parameter is invalid Sapling payment address\n");
+        }    
+
+        static boost::thread_group* minerThreads = NULL;
+        
+        if (nThreads < 0)
+            nThreads = GetNumCores();
+        
+        if (minerThreads != NULL)
+        {
+            minerThreads->interrupt_all();
+            delete minerThreads;
+            minerThreads = NULL;
+        }
 
-    if (nThreads < 0)
-        nThreads = GetNumCores();
+        //fprintf(stderr,"nThreads.%d fGenerate.%d\n",(int32_t)nThreads,fGenerate);
+        if ( nThreads == 0 && ASSETCHAINS_STAKED )
+            nThreads = 1;
 
-    if (minerThreads != NULL)
-    {
-        minerThreads->interrupt_all();
-        delete minerThreads;
-        minerThreads = NULL;
-    }
+        if ((nThreads == 0 || !fGenerate) && (VERUS_MINTBLOCKS == 0 || pwallet == NULL))
+            return;
 
-    if (nThreads == 0 || !fGenerate)
-        return;
+        minerThreads = new boost::thread_group();
 
-    minerThreads = new boost::thread_group();
-    for (int i = 0; i < nThreads; i++) {
 #ifdef ENABLE_WALLET
-        minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
+        if (ASSETCHAINS_LWMAPOS != 0 && VERUS_MINTBLOCKS)
+        {
+            minerThreads->create_thread(boost::bind(&VerusStaker, pwallet));
+        }
+#endif
+
+        for (int i = 0; i < nThreads; i++) {
+
+#ifdef ENABLE_WALLET
+            if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH)
+                minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
+            else
+                minerThreads->create_thread(boost::bind(&BitcoinMiner_noeq, pwallet));
 #else
-        minerThreads->create_thread(&BitcoinMiner);
+            if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH)
+                minerThreads->create_thread(&BitcoinMiner);
+            else
+                minerThreads->create_thread(&BitcoinMiner_noeq);
 #endif
+        }
     }
-}
-
+    
 #endif // ENABLE_MINING
This page took 0.086224 seconds and 4 git commands to generate.