]> Git Repo - VerusCoin.git/blobdiff - src/miner.cpp
Check for creation of invalid staking transaction
[VerusCoin.git] / src / miner.cpp
index 672689662144cc2089a9e3f59ea268783a1c5e34..eb56c7e6562c9d765b6da3c52d5d555da93b6635 100644 (file)
@@ -1,7 +1,7 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2014 The Bitcoin Core developers
 // Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or https://www.opensource.org/licenses/mit-license.php .
 
 #include "miner.h"
 #ifdef ENABLE_MINING
@@ -31,9 +31,7 @@
 #include "ui_interface.h"
 #include "util.h"
 #include "utilmoneystr.h"
-#ifdef ENABLE_WALLET
-#include "wallet/wallet.h"
-#endif
+#include "validationinterface.h"
 
 #include "zcash/Address.hpp"
 #include "transaction_builder.h"
@@ -49,6 +47,7 @@
 
 #include "pbaas/pbaas.h"
 #include "pbaas/notarization.h"
+#include "pbaas/identity.h"
 #include "rpc/pbaasrpc.h"
 #include "transaction_builder.h"
 
@@ -127,7 +126,8 @@ 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 int32_t VERUS_MIN_STAKEAGE, ASSETCHAINS_EQUIHASH, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA, ASSETCHAINS_LWMAPOS, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[];
+extern uint32_t ASSETCHAINS_ALGO;
 extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
 extern uint160 ASSETCHAINS_CHAINID;
 extern uint160 VERUS_CHAINID;
@@ -140,7 +140,7 @@ 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;
+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);
@@ -170,12 +170,17 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int &
 
     int32_t nHeight = pindexPrev->GetHeight() + 1;
 
-    if (CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight) >= CConstVerusSolutionVector::activationHeight.SOLUTION_VERUSV3)
+    if (CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight) >= CConstVerusSolutionVector::activationHeight.ACTIVATE_PBAAS)
     {
         // coinbase should already be finalized in the new version
         if (buildMerkle)
         {
             pblock->hashMerkleRoot = pblock->BuildMerkleTree();
+            pblock->SetPrevMMRRoot(ChainMerkleMountainView(chainActive.GetMMR(), pindexPrev->GetHeight()).GetRoot());
+            BlockMMRange mmRange(pblock->BuildBlockMMRTree());
+            BlockMMView mmView(mmRange);
+            pblock->SetBlockMMRRoot(mmView.GetRoot());
+            pblock->AddUpdatePBaaSHeader();
         }
 
         UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
@@ -188,7 +193,7 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int &
             mmvRoot = mmv.GetRoot();
         }
 
-        pblock->AddUpdatePBaaSHeader(mmvRoot);
+        pblock->AddUpdatePBaaSHeader();
 
         // POS blocks have already had their solution space filled, and there is no actual extra nonce, extradata is used
         // for POS proof, so don't modify it
@@ -239,7 +244,205 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int &
     }
 }
 
-CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake)
+extern CWallet *pwalletMain;
+
+CPubKey GetSolutionPubKey(const std::vector<std::vector<unsigned char>> &vSolutions, txnouttype txType)
+{
+    CPubKey pk;
+
+    if (txType == TX_PUBKEY)
+    {
+        pk = CPubKey(vSolutions[0]);
+    }
+    else if(txType == TX_PUBKEYHASH)
+    {
+        // we need to have this in our wallet to get the public key
+        LOCK(pwalletMain->cs_wallet);
+        pwalletMain->GetPubKey(CKeyID(uint160(vSolutions[0])), pk);
+    }
+    else if (txType == TX_CRYPTOCONDITION)
+    {
+        if (vSolutions[0].size() == 33)
+        {
+            pk = CPubKey(vSolutions[0]);
+        }
+        else if (vSolutions[0].size() == 34 && vSolutions[0][0] == COptCCParams::ADDRTYPE_PK)
+        {
+            pk = CPubKey(std::vector<unsigned char>(vSolutions[0].begin() + 1, vSolutions[0].end()));
+        }
+        else if (vSolutions[0].size() == 20)
+        {
+            LOCK(pwalletMain->cs_wallet);
+            pwalletMain->GetPubKey(CKeyID(uint160(vSolutions[0])), pk);
+        }
+        else if (vSolutions[0].size() == 21 && vSolutions[0][0] == COptCCParams::ADDRTYPE_ID)
+        {
+            // destination is an identity, see if we can get its first public key
+            std::pair<CIdentityMapKey, CIdentityMapValue> identity;
+
+            if (pwalletMain->GetIdentity(CIdentityID(uint160(std::vector<unsigned char>(vSolutions[0].begin() + 1, vSolutions[0].end()))), identity) && 
+                identity.second.IsValidUnrevoked() && 
+                identity.second.primaryAddresses.size())
+            {
+                CPubKey pkTmp = boost::apply_visitor<GetPubKeyForPubKey>(GetPubKeyForPubKey(), identity.second.primaryAddresses[0]);
+                if (pkTmp.IsValid())
+                {
+                    pk = pkTmp;
+                }
+                else
+                {
+                    LOCK(pwalletMain->cs_wallet);
+                    pwalletMain->GetPubKey(CKeyID(GetDestinationID(identity.second.primaryAddresses[0])), pk);
+                }
+            }
+        }
+    }
+    return pk;
+}
+
+CPubKey GetScriptPublicKey(const CScript &scriptPubKey)
+{
+    txnouttype typeRet;
+    std::vector<std::vector<unsigned char>> vSolutions;
+    if (Solver(scriptPubKey, typeRet, vSolutions))
+    {
+        return GetSolutionPubKey(vSolutions, typeRet);
+    }
+    return CPubKey();
+}
+
+void ProcessNewImports(const uint160 &sourceChainID, const CTransaction &lastConfirmed, int32_t nHeight)
+{
+    if (CConstVerusSolutionVector::GetVersionByHeight(nHeight) < CActivationHeight::ACTIVATE_PBAAS || 
+        CConstVerusSolutionVector::activationHeight.IsActivationHeight(CActivationHeight::ACTIVATE_PBAAS, nHeight))
+    {
+        return;
+    }
+    uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
+
+    // get any pending imports from the source chain. if the source chain is this chain, we don't need notarization
+    CCurrencyDefinition thisChain = ConnectedChains.ThisChain();
+
+    CTransaction lastImportTx;
+
+    // we need to find the last unspent import transaction
+    std::vector<CAddressUnspentDbEntry> unspentOutputs;
+
+    bool found = false;
+
+    if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(sourceChainID, EVAL_CROSSCHAIN_IMPORT)), 1, unspentOutputs))
+    {
+        // if one spends the prior one, get the one that is not spent
+        for (auto txidx : unspentOutputs)
+        {
+            uint256 blkHash;
+            CTransaction itx;
+            if (myGetTransaction(txidx.first.txhash, lastImportTx, blkHash) &&
+                CCrossChainImport(lastImportTx).IsValid() &&
+                (lastImportTx.IsCoinBase() ||
+                (myGetTransaction(lastImportTx.vin[0].prevout.hash, itx, blkHash) &&
+                CCrossChainImport(itx).IsValid())))
+            {
+                found = true;
+                break;
+            }
+        }
+    }
+
+    if (found && pwalletMain)
+    {
+        UniValue params(UniValue::VARR);
+        UniValue param(UniValue::VOBJ);
+
+        CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
+        int i;
+        for (i = 0; i < lastImportTx.vout.size(); i++)
+        {
+            COptCCParams p;
+            if (lastImportTx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
+            {
+                txTemplate.vin.push_back(CTxIn(lastImportTx.GetHash(), (uint32_t)i));
+                break;
+            }
+        }
+
+        UniValue result = NullUniValue;
+        if (i < lastImportTx.vout.size())
+        {
+            param.push_back(Pair("name", EncodeDestination(CIdentityID(thisChain.GetID()))));
+            param.push_back(Pair("lastimporttx", EncodeHexTx(lastImportTx)));
+            param.push_back(Pair("lastconfirmednotarization", EncodeHexTx(lastConfirmed)));
+            param.push_back(Pair("importtxtemplate", EncodeHexTx(txTemplate)));
+            param.push_back(Pair("totalimportavailable", lastImportTx.vout[txTemplate.vin[0].prevout.n].nValue));
+            params.push_back(param);
+
+            try
+            {
+                if (sourceChainID == thisChain.GetID())
+                {
+                    UniValue getlatestimportsout(const UniValue& params, bool fHelp);
+                    result = getlatestimportsout(params, false);
+                }
+                else
+                {
+                    result = find_value(RPCCallRoot("getlatestimportsout", params), "result");
+                }
+            } catch (exception e)
+            {
+                printf("Could not get latest imports from notary chain\n");
+            }
+        }
+
+        if (result.isArray() && result.size())
+        {
+            LOCK(pwalletMain->cs_wallet);
+
+            uint256 lastImportHash = lastImportTx.GetHash();
+            for (int i = 0; i < result.size(); i++)
+            {
+                CTransaction itx;
+                if (result[i].isStr() && DecodeHexTx(itx, result[i].get_str()) && itx.vin.size() && itx.vin[0].prevout.hash == lastImportHash)
+                {
+                    // sign the transaction spending the last import and add to mempool
+                    CMutableTransaction mtx(itx);
+                    CCrossChainImport cci(lastImportTx);
+
+                    bool signSuccess;
+                    SignatureData sigdata;
+                    CAmount value;
+                    const CScript *pScriptPubKey;
+
+                    signSuccess = ProduceSignature(
+                        TransactionSignatureCreator(pwalletMain, &itx, 0, lastImportTx.vout[itx.vin[0].prevout.n].nValue, SIGHASH_ALL), lastImportTx.vout[itx.vin[0].prevout.n].scriptPubKey, sigdata, consensusBranchId);
+
+                    if (!signSuccess)
+                    {
+                        break;
+                    }
+
+                    UpdateTransaction(mtx, 0, sigdata);
+                    itx = CTransaction(mtx);
+
+                    // commit to mempool and remove any conflicts
+                    std::list<CTransaction> removed;
+                    mempool.removeConflicts(itx, removed);
+                    CValidationState state;
+                    if (!myAddtomempool(itx, &state))
+                    {
+                        LogPrintf("Failed to add import transactions to the mempool due to: %s\n", state.GetRejectReason().c_str());
+                        printf("Failed to add import transactions to the mempool due to: %s\n", state.GetRejectReason().c_str());
+                        break;  // if we failed to add one, the others will fail to spend it
+                    }
+
+                    lastImportTx = itx;
+                    lastImportHash = itx.GetHash();
+                }
+            }
+        }
+    }
+}
+
+CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake)
 {
     CScript scriptPubKeyIn(_scriptPubKeyIn);
 
@@ -247,14 +450,15 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
     // mining reward based on its weight relative to the total
     std::vector<pair<int, CScript>> minerOutputs = scriptPubKeyIn.size() ? std::vector<pair<int, CScript>>({make_pair((int)1, scriptPubKeyIn)}) : std::vector<pair<int, CScript>>();
 
-    // TODO: when we accept a parameter of the minerOutputs vector, remove this comment but not the check
     CTxDestination firstDestination;
     if (!(scriptPubKeyIn.size() && ConnectedChains.SetLatestMiningOutputs(minerOutputs, firstDestination) || isStake))
     {
-        fprintf(stderr,"%s: Must have valid miner outputs, including script with valid PK or PKH destination.\n", __func__);
+        fprintf(stderr,"%s: Must have valid miner outputs, including script with valid PK, PKH, or Verus ID destination.\n", __func__);
         return NULL;
     }
 
+    CPubKey pk;
+
     if (minerOutputs.size())
     {
         int64_t shareCheck = 0;
@@ -267,11 +471,10 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                 return NULL;
             }
         }
+        pk = GetScriptPublicKey(minerOutputs[0].second);
     }
 
-    CPubKey pk = boost::apply_visitor<GetPubKeyForPubKey>(GetPubKeyForPubKey(), firstDestination);
-
-    uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params();
+    uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime;
     //fprintf(stderr,"create new block\n");
     // Create new block
     if ( gpucount < 0 )
@@ -298,7 +501,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
     // -regtest only: allow overriding block.nVersion with
     // -blockversion=N to test forking scenarios
-    if (Params().MineBlocksOnDemand())
+    if (chainparams.MineBlocksOnDemand())
         pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
     
     // Add dummy coinbase tx placeholder as first transaction
@@ -311,6 +514,9 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
     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));
+
+    unsigned int nMaxIDSize = nBlockMaxSize / 2;
+    unsigned int nCurrentIDSize = 0;
     
     // How much of the block should be dedicated to high-priority transactions,
     // included regardless of the fees they pay
@@ -327,17 +533,24 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
     // if this is a reserve currency, update the currency state from the coinbase of the last block
     bool isVerusActive = IsVerusActive();
-    CPBaaSChainDefinition &thisChain = ConnectedChains.ThisChain();
-    CCoinbaseCurrencyState currencyState = CCoinbaseCurrencyState(CCurrencyState(thisChain.conversion, thisChain.premine, 0, 0, 0), 0, 0, CReserveOutput(), 0, 0, 0);
-    CAmount exchangeRate;
+    CCurrencyDefinition &thisChain = ConnectedChains.ThisChain();
+    CAmount prealloc = 0;
+    for (auto &onePair : thisChain.preAllocation)
+    {
+        prealloc += onePair.second;
+    }
+    CCoinbaseCurrencyState currencyState = CCoinbaseCurrencyState(CCurrencyState(thisChain.currencies,
+                                                                                    thisChain.weights,
+                                                                                    thisChain.contributions,
+                                                                                    prealloc, 0, 0));
+
+    std::vector<CAmount> exchangeRate(thisChain.currencies.size());
 
     // we will attempt to spend any cheats we see
     CTransaction cheatTx;
     boost::optional<CTransaction> cheatSpend;
     uint256 cbHash;
 
-    extern CWallet *pwalletMain;
-
     CBlockIndex* pindexPrev = 0;
     {
         LOCK2(cs_main, mempool.cs);
@@ -345,7 +558,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         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);
+        bool sapling = consensusParams.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_SAPLING);
 
         const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
         uint32_t proposedTime = GetAdjustedTime();
@@ -389,7 +602,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         {
             // 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))
+            if (!(fHavePruned && !(ppast->nStatus & BLOCK_HAVE_DATA) && ppast->nTx > 0) && ReadBlockFromDisk(b, ppast, chainparams.GetConsensus(), 1))
             {
                 CTransaction &stakeTx = b.vtx[b.vtx.size() - 1];
 
@@ -427,7 +640,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
                         if (hasInput)
                         {
-                            // this is a send from a t-address to a sapling address, which we don't have an ovk for. 
+                            // 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.
@@ -441,7 +654,15 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
                                 tb.AddOpRet(mtx.vout[mtx.vout.size() - 1].scriptPubKey);
 
-                                cheatSpend = tb.Build();
+                                TransactionBuilderResult buildResult(tb.Build());
+                                if (!buildResult.IsError() && buildResult.IsTx())
+                                {
+                                    cheatSpend = buildResult.GetTxOrThrow();
+                                }
+                                else
+                                {
+                                    LogPrintf("Error building cheat catcher transaction: %s\n", buildResult.GetError().c_str());
+                                }
                             }
                         }
                     }
@@ -516,20 +737,8 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             if (!minerOutputs.size())
             {
                 minerOutputs.push_back(make_pair((int)1, txStaked.vout[0].scriptPubKey));
-                txnouttype typeRet;
-                std::vector<std::vector<unsigned char>> vSolutions;
-                if (Solver(txStaked.vout[0].scriptPubKey, typeRet, vSolutions))
-                {
-                    if (typeRet == TX_PUBKEY)
-                    {
-                        pk = CPubKey(vSolutions[0]);
-                    }
-                    else
-                    {
-                        pwalletMain->GetPubKey(CKeyID(uint160(vSolutions[0])), pk);
-                    }
-                }
-                ConnectedChains.SetLatestMiningOutputs(minerOutputs, firstDestination);
+                pk = GetScriptPublicKey(txStaked.vout[0].scriptPubKey);
+                ExtractDestination(minerOutputs[0].second, firstDestination);
             }
         }
 
@@ -582,6 +791,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         //    until it is exhausted
         CTxOut premineOut, chainDefinitionOut, importThreadOut, exportThreadOut, currencyStateOut, notarizationOut;
         CMutableTransaction newNotarizationTx, newConversionOutputTx;
+        int currencyStateOutNum = 0, notarizationOutNum = 0;
 
         // size of conversion tx
         std::vector<CInputDescriptor> conversionInputs;
@@ -603,9 +813,11 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
         int64_t pbaasTransparentIn = 0;
         int64_t pbaasTransparentOut = 0;
+        //extern int64_t ASSETCHAINS_SUPPLY;
+        //printf("%lu premine\n", ASSETCHAINS_SUPPLY);
         int64_t blockSubsidy = GetBlockSubsidy(nHeight, consensusParams);
 
-        uint160 thisChainID = ConnectedChains.ThisChain().GetChainID();
+        uint160 thisChainID = ConnectedChains.ThisChain().GetID();
 
         uint256 mmrRoot;
         vector<CInputDescriptor> notarizationInputs;
@@ -620,14 +832,6 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         CMutableTransaction coinbaseTx = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
         coinbaseTx.vin.push_back(CTxIn(uint256(), (uint32_t)-1, CScript() << nHeight << OP_0));
 
-        // default outputs for mining and before stake guard or fee calculation
-        // store the relative weight in the amount output to convert later to a relative portion
-        // of the reward + fees
-        for (auto &spk : minerOutputs)
-        {
-            coinbaseTx.vout.push_back(CTxOut(spk.first, spk.second));
-        }
-
         // we will update amounts and fees later, but convert the guarded output now for validity checking and size estimate
         if (isStake)
         {
@@ -636,20 +840,26 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             CStakeParams p;
             if (ValidateStakeTransaction(stakeTx, p, false))
             {
-                if (!p.pk.IsValid())
+                if (p.Version() < p.VERSION_EXTENDED_STAKE && !p.pk.IsValid())
                 {
                     LogPrintf("CreateNewBlock: invalid public key\n");
                     fprintf(stderr,"CreateNewBlock: invalid public key\n");
                     return NULL;
                 }
-                for (auto &cbOutput : coinbaseTx.vout)
+                coinbaseTx.vout.push_back(CTxOut(1, CScript()));
+                if (!MakeGuardedOutput(1, p.pk, stakeTx, coinbaseTx.vout.back()))
                 {
-                    if (!MakeGuardedOutput(cbOutput.nValue, p.pk, stakeTx, cbOutput))
-                    {
-                        LogPrintf("CreateNewBlock: failed to make GuardedOutput on staking coinbase\n");
-                        fprintf(stderr,"CreateNewBlock: failed to make GuardedOutput on staking coinbase\n");
-                        return NULL;
-                    }
+                    LogPrintf("CreateNewBlock: failed to make GuardedOutput on staking coinbase\n");
+                    fprintf(stderr,"CreateNewBlock: failed to make GuardedOutput on staking coinbase\n");
+                    return NULL;
+                }
+                COptCCParams optP;
+                if (!coinbaseTx.vout.back().scriptPubKey.IsPayToCryptoCondition(optP) || !p.IsValid())
+                {
+                    MakeGuardedOutput(1, p.pk, stakeTx, coinbaseTx.vout.back());
+                    LogPrintf("%s: created invalid staking coinbase\n", __func__);
+                    fprintf(stderr,"%s: created invalid staking coinbase\n", __func__);
+                    return NULL;
                 }
             }
             else
@@ -659,10 +869,20 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                 return NULL;
             }
         }
+        else
+        {
+            // default outputs for mining and before stake guard or fee calculation
+            // store the relative weight in the amount output to convert later to a relative portion
+            // of the reward + fees
+            for (auto &spk : minerOutputs)
+            {
+                coinbaseTx.vout.push_back(CTxOut(spk.first, spk.second));
+            }
+        }
 
         CAmount totalEmission = blockSubsidy;
 
-        // make earned notarization only if this is not the notary chain and we have enough subsidy
+        // make earned notarization only if this is not the Verus chain and we have enough subsidy
         if (!isVerusActive)
         {
             // if we don't have a connected root PBaaS chain, we can't properly check
@@ -672,26 +892,20 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             // get current currency state differently, depending on height
             if (nHeight == 1)
             {
-                blockSubsidy -= GetBlockOnePremine();       // separate subsidy, which can go to miner, from premine
-
-                if (!notaryConnected)
+                if (!notaryConnected || !ConnectedChains.readyToStart)
                 {
-                    // cannt make block 1 unless we can properly notarize that the launch chain is past the start block
+                    // cannot make block 1 unless we can properly notarize that the launch chain is past the start block
                     return NULL;
                 }
 
-                // if some amount of pre-conversion was allowed
-                if (thisChain.maxpreconvert)
+                // if some amount of pre-conversion was allowed, we need to check with all eligible currency
+                // chains or systems to determine how much in each currency is available for preconversion
+                // TODO: support querying multiple systems... initial support for VRSC and VRSCTEST-homed currencies
+                if (thisChain.maxPreconvert.size() && thisChain.maxPreconvert.size() == thisChain.currencies.size())
                 {
-                    // this is invalid
-                    if (thisChain.conversion <= 0)
-                    {
-                        return NULL;
-                    }
-
                     // get the total amount pre-converted
                     UniValue params(UniValue::VARR);
-                    params.push_back(ASSETCHAINS_CHAINID.GetHex());
+                    params.push_back(EncodeDestination(CIdentityID(ASSETCHAINS_CHAINID)));
 
                     UniValue result;
                     try
@@ -715,77 +929,110 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                         return NULL;
                     }
 
-                    if (currencyState.ReserveIn < ConnectedChains.ThisChain().minpreconvert)
+                    CCurrencyValueMap preConverted = CCurrencyValueMap(ConnectedChains.ThisChain().currencies, currencyState.reserveIn);
+                    CCurrencyValueMap minPreconvert = CCurrencyValueMap(ConnectedChains.ThisChain().currencies, ConnectedChains.ThisChain().minPreconvert);
+
+                    if (preConverted < minPreconvert)
                     {
-                        // no matter what happens, we should be able to get a valid currency state of some sort, if not, fail
+                        // we must reach minimums in all currencies to launch
                         LogPrintf("This chain did not receive the minimum currency contributions and cannot launch. Pre-launch contributions to this chain can be refunded.\n");
                         printf("This chain did not receive the minimum currency contributions and cannot launch. Pre-launch contributions to this chain can be refunded.\n");
                         return NULL;
                     }
+
+                    thisChain.preconverted = preConverted.AsCurrencyVector(thisChain.currencies);
+                    thisChain.conversions = currencyState.conversionPrice;
                 }
 
-                // add needed block one coinbase outputs
-                // send normal reward to the miner, premine to the address in the chain definition, and pre-converted to the
-                // import thread out
-                if (GetBlockOnePremine())
+                CAmount blockOnePremine = thisChain.GetTotalPreallocation();
+                SetBlockOnePremine(blockOnePremine);
+                totalEmission = GetBlockSubsidy(nHeight, consensusParams);
+                blockSubsidy = totalEmission - blockOnePremine;
+
+                // add needed block one coinbase outputs for preallocation
+                if (blockOnePremine)
                 {
-                    premineOut = CTxOut(GetBlockOnePremine(), GetScriptForDestination(CTxDestination(ConnectedChains.ThisChain().address)));
-                    coinbaseTx.vout.push_back(premineOut);
+                    std::vector<CTxOut> tmpOut;
+                    for (auto &onePremine : ConnectedChains.ThisChain().GetPreAllocationAmounts())
+                    {
+                        premineOut = CTxOut(onePremine.second, GetScriptForDestination(CTxDestination(CIdentityID(onePremine.first))));
+                        tmpOut.push_back(premineOut);
+                    }
+                    if (tmpOut.size())
+                    {
+                        coinbaseTx.vout.insert(coinbaseTx.vout.end(), tmpOut.begin(), tmpOut.end());
+                    }
                 }
 
-                // chain definition - always
-                // make the chain definition output
-                vKeys.clear();
-                cp = CCinit(&CC, EVAL_PBAASDEFINITION);
+                // now, we have pre-mine outputs calculated and created for either absolute or percentage-based
+                // pre-allocation
+
+                // following that, we have either 1 stake guarded output to the staker or delegate,
+                // or some number of miner outputs
 
-                // send this to EVAL_PBAASDEFINITION address as a destination, locked by the default pubkey
+                // now, start adding additional outputs, including chain-definition output for Notary chain and all currencies in ConnectedChains
+                ConnectedChains.LoadReserveCurrencies();
+
+                // create a currency definition output for this currency, the notary currency, and all reserves
+                CCcontract_info CC;
+                CCcontract_info *cp;
+                cp = CCinit(&CC, EVAL_CURRENCY_DEFINITION);
                 pkCC = CPubKey(ParseHex(CC.CChexstr));
-                vKeys.push_back(CKeyID(CCrossChainRPCData::GetConditionID(thisChainID, EVAL_PBAASDEFINITION)));
-                thisChain.preconverted = currencyState.ReserveIn;   // update known, preconverted amount
 
-                chainDefinitionOut = MakeCC1of1Vout(EVAL_PBAASDEFINITION, 0, pkCC, vKeys, thisChain);
-                coinbaseTx.vout.push_back(chainDefinitionOut);
+                std::vector<CTxDestination> indexDests({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_CURRENCY_DEFINITION))});
+                std::vector<CTxDestination> dests({pkCC});
 
-                // import - only spendable for reserve currency or currency with preconversion to allow import of conversions, this output will include
-                // all pre-converted coins and all pre-conversion fees, denominated in Verus reserve currency
-                // chain definition - always
-                // make the chain definition output
-                vKeys.clear();
-                cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
+                coinbaseTx.vout.push_back(CTxOut(0,
+                                            MakeMofNCCScript(CConditionObj<CCurrencyDefinition>(EVAL_CURRENCY_DEFINITION, dests, 1, 
+                                                             &ConnectedChains.ThisChain()),
+                                            &indexDests)));
 
-                pkCC = CPubKey(ParseHex(CC.CChexstr));
+                for (auto &oneCur : ConnectedChains.reserveCurrencies)
+                {
+                    indexDests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_CURRENCY_DEFINITION)),
+                                                            CKeyID(oneCur.second.GetConditionID(EVAL_CURRENCY_DEFINITION))});
+                    coinbaseTx.vout.push_back(CTxOut(0,
+                                              MakeMofNCCScript(CConditionObj<CCurrencyDefinition>(EVAL_CURRENCY_DEFINITION, dests, 1, &oneCur.second),
+                                              &indexDests)));
+                }
+
+                if (!ConnectedChains.reserveCurrencies.count(ConnectedChains.NotaryChain().GetID()))
+                {
+                    indexDests = std::vector<CTxDestination>({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_CURRENCY_DEFINITION)),
+                                                            CKeyID(ConnectedChains.NotaryChain().chainDefinition.GetConditionID(EVAL_CURRENCY_DEFINITION))});
+                    coinbaseTx.vout.push_back(CTxOut(0,
+                                              MakeMofNCCScript(CConditionObj<CCurrencyDefinition>(EVAL_CURRENCY_DEFINITION, dests, 1, 
+                                                               &ConnectedChains.NotaryChain().chainDefinition),
+                                              &indexDests)));
+                }
 
-                // import thread is specific to the chain importing from
-                vKeys.push_back(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.notaryChain.GetChainID(), EVAL_CROSSCHAIN_IMPORT)));
+                // create the import thread output
+                cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
+                pkCC = CPubKey(ParseHex(CC.CChexstr));
 
-                // log import of all reserve in as well as the fees that are passed through the initial ReserveOut in the initial import
-                importThreadOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_IMPORT, 
-                                                 currencyState.ReserveToNative(thisChain.preconverted, thisChain.conversion), pkCC, vKeys, 
-                                                 CCrossChainImport(ConnectedChains.NotaryChain().GetChainID(), currencyState.ReserveIn + currencyState.ReserveOut.nValue));
+                // import thread from PBaaS parent
+                indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.notaryChain.GetID(), EVAL_CROSSCHAIN_IMPORT))});
+                dests = std::vector<CTxDestination>({pkCC});
 
-                coinbaseTx.vout.push_back(importThreadOut);
+                CCrossChainImport cci = CCrossChainImport(ConnectedChains.notaryChain.GetID(), CCurrencyValueMap());
+                coinbaseTx.vout.push_back(CTxOut(currencyState.ReserveToNativeRaw(CCurrencyValueMap(thisChain.currencies, thisChain.preconverted), thisChain.conversions),
+                                                 MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &cci), &indexDests)));
 
-                // export - currently only spendable for reserve currency, but added for future capabilities
-                vKeys.clear();
+                // export thread to PBaaS parent
                 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
-
                 pkCC = CPubKey(ParseHex(CC.CChexstr));
-                vKeys.push_back(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.NotaryChain().GetChainID(), EVAL_CROSSCHAIN_EXPORT)));
+                indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.notaryChain.GetID(), EVAL_CROSSCHAIN_EXPORT))});
+                dests = std::vector<CTxDestination>({pkCC});
 
-                exportThreadOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, 0, pkCC, vKeys, 
-                                                 CCrossChainExport(ConnectedChains.NotaryChain().GetChainID(), 0, 0, 0));
-                coinbaseTx.vout.push_back(exportThreadOut);
+                CCrossChainExport ccx(ConnectedChains.NotaryChain().GetID(), 0, CCurrencyValueMap(), CCurrencyValueMap());
+                coinbaseTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests)));
             }
             else
             {
                 CBlock block;
                 assert(nHeight > 1);
                 currencyState = ConnectedChains.GetCurrencyState(nHeight - 1);
-                currencyState.Fees = 0;
-                currencyState.ConversionFees = 0;
-                currencyState.NativeIn = 0;
-                currencyState.ReserveIn = 0;
-                currencyState.ReserveOut.nValue = 0;
+                currencyState.ClearForNextBlock();
 
                 if (!currencyState.IsValid())
                 {
@@ -800,22 +1047,26 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             // premine is an emission that is factored in before this
             currencyState.UpdateWithEmission(totalEmission);
 
-            // always add currency state output for coinbase
+            // add currency state output to coinbase
             vKeys.clear();
             cp = CCinit(&CC, EVAL_CURRENCYSTATE);
 
             CPubKey currencyOutPK(ParseHex(cp->CChexstr));
-            vKeys.push_back(CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(thisChainID, EVAL_CURRENCYSTATE))));
+            std::vector<CTxDestination> indexDests({CKeyID(CCrossChainRPCData::GetConditionID(thisChainID, EVAL_CURRENCYSTATE))});
+            std::vector<CTxDestination> dests({currencyOutPK});
 
-            // make an output that either carries zero coins pre-converting, or the initial supply for block 1, conversion amounts will be adjusted later
-            currencyStateOut = MakeCC1of1Vout(EVAL_CURRENCYSTATE, 0, currencyOutPK, vKeys, currencyState);
+            // pre-conversions go to the import thread
+            // conversions for this block, if any, will be processed
+            // below
+            coinbaseTx.vout.push_back(CTxOut(currencyState.ReserveToNativeRaw(CCurrencyValueMap(thisChain.currencies, thisChain.preconverted), thisChain.conversions),
+                                                MakeMofNCCScript(CConditionObj<CCoinbaseCurrencyState>(EVAL_CURRENCYSTATE, dests, 1, &currencyState), &indexDests)));
 
-            coinbaseTx.vout.push_back(currencyStateOut);
+            currencyStateOutNum = coinbaseTx.vout.size() - 1;
 
             if (notaryConnected)
             {
                 // if we have access to our notary daemon
-                // create a notarization if we would qualify, and add it to the mempool and block
+                // create a notarization if we would qualify to do so. add it to the mempool and next block
                 CTransaction prevTx, crossTx, lastConfirmed, lastImportTx;
                 ChainMerkleMountainView mmv = chainActive.GetMMV();
                 mmrRoot = mmv.GetRoot();
@@ -871,6 +1122,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                     CPBaaSNotarization pbn(newNotarizationTx);
                     notarizationOut = MakeCC1of1Vout(EVAL_EARNEDNOTARIZATION, needed, pkCC, vKeys, pbn);
                     coinbaseTx.vout.push_back(notarizationOut);
+                    notarizationOutNum = coinbaseTx.vout.size() - 1;
 
                     // place the notarization
                     pblock->vtx.push_back(CTransaction(newNotarizationTx));
@@ -889,134 +1141,75 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                 // if we have a last confirmed notarization, then check for new imports from the notary chain
                 if (lastConfirmed.vout.size())
                 {
-                    // we need to find the last unspent import transaction
-                    std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
+                    ProcessNewImports(ConnectedChains.NotaryChain().GetID(), lastConfirmed, nHeight);
+                }
+            }
+        }
+        else
+        {
+            if (nHeight == 1)
+            {
+                SetBlockOnePremine(thisChain.GetTotalPreallocation());
+            }
+            totalEmission = GetBlockSubsidy(nHeight, consensusParams);
+            blockSubsidy = totalEmission;
+            currencyState.UpdateWithEmission(totalEmission);
 
-                    bool found = false;
+            if (CConstVerusSolutionVector::activationHeight.IsActivationHeight(CActivationHeight::ACTIVATE_PBAAS, nHeight))
+            {
+                // at activation height for PBaaS on VRSC or VRSCTEST, add currency definition, import, and export outputs to the coinbase
+                // create a currency definition output for this currency, the notary currency, and all reserves
+                CCcontract_info CC;
+                CCcontract_info *cp;
+                cp = CCinit(&CC, EVAL_CURRENCY_DEFINITION);
+                pkCC = CPubKey(ParseHex(CC.CChexstr));
 
-                    // we cannot get export to a chain that has shut down
-                    // if the chain definition is spent, a chain is inactive
-                    if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.NotaryChain().GetChainID(), EVAL_CROSSCHAIN_IMPORT)), 1, unspentOutputs))
-                    {
-                        // if one spends the prior one, get the one that is not spent
-                        for (auto txidx : unspentOutputs)
-                        {
-                            uint256 blkHash;
-                            CTransaction itx;
-                            if (myGetTransaction(txidx.first.txhash, lastImportTx, blkHash) &&
-                                CCrossChainImport(lastImportTx).IsValid() &&
-                                (lastImportTx.IsCoinBase() ||
-                                (myGetTransaction(lastImportTx.vin[0].prevout.hash, itx, blkHash) &&
-                                CCrossChainImport(itx).IsValid())))
-                            {
-                                found = true;
-                                break;
-                            }
-                        }
-                    }
+                std::vector<CTxDestination> indexDests({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_CURRENCY_DEFINITION))});
+                std::vector<CTxDestination> dests({pkCC});
 
-                    if (found && pwalletMain)
-                    {
-                        UniValue params(UniValue::VARR);
-                        UniValue param(UniValue::VOBJ);
+                coinbaseTx.vout.push_back(CTxOut(0,
+                                            MakeMofNCCScript(CConditionObj<CCurrencyDefinition>(EVAL_CURRENCY_DEFINITION, dests, 1, 
+                                                             &ConnectedChains.ThisChain()),
+                                            &indexDests)));
+            }
+        }
 
-                        CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
-                        int i;
-                        for (i = 0; i < lastImportTx.vout.size(); i++)
-                        {
-                            COptCCParams p;
-                            if (lastImportTx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_IMPORT)
-                            {
-                                txTemplate.vin.push_back(CTxIn(lastImportTx.GetHash(), (uint32_t)i));
-                                break;
-                            }
-                        }
+        // on all chains, we add an export and import to ourselves at PBaaS activation height (1 for PBaaS chains)
+        if (CConstVerusSolutionVector::activationHeight.IsActivationHeight(CActivationHeight::ACTIVATE_PBAAS, nHeight))
+        {
+            // create the import thread output
+            cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
+            pkCC = CPubKey(ParseHex(CC.CChexstr));
 
-                        UniValue result = NullUniValue;
-                        if (i < lastImportTx.vout.size())
-                        {
-                            param.push_back(Pair("name", thisChain.name));
-                            param.push_back(Pair("lastimporttx", EncodeHexTx(lastImportTx)));
-                            param.push_back(Pair("lastconfirmednotarization", EncodeHexTx(lastConfirmed)));
-                            param.push_back(Pair("importtxtemplate", EncodeHexTx(txTemplate)));
-                            param.push_back(Pair("totalimportavailable", lastImportTx.vout[txTemplate.vin[0].prevout.n].nValue));
-                            params.push_back(param);
-
-                            try
-                            {
-                                result = find_value(RPCCallRoot("getlatestimportsout", params), "result");
-                            } catch (exception e)
-                            {
-                                printf("Could not get latest imports from notary chain\n");
-                            }
-                        }
+            // import thread from self
+            std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetID(), EVAL_CROSSCHAIN_IMPORT))});
+            std::vector<CTxDestination> dests = std::vector<CTxDestination>({pkCC});
 
-                        if (result.isArray() && result.size())
-                        {
-                            LOCK(pwalletMain->cs_wallet);
+            CCrossChainImport cci = CCrossChainImport(ConnectedChains.ThisChain().GetID(), CCurrencyValueMap());
+            coinbaseTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &cci), &indexDests)));
 
-                            uint256 lastImportHash = lastImportTx.GetHash();
-                            for (int i = 0; i < result.size(); i++)
-                            {
-                                CTransaction itx;
-                                if (result[i].isStr() && DecodeHexTx(itx, result[i].get_str()) && itx.vin.size() && itx.vin[0].prevout.hash == lastImportHash)
-                                {
-                                    // sign the transaction spending the last import and add to mempool
-                                    CMutableTransaction mtx(itx);
-                                    CCrossChainImport cci(lastImportTx);
-
-                                    bool signSuccess;
-                                    SignatureData sigdata;
-                                    CAmount value;
-                                    const CScript *pScriptPubKey;
-
-                                    const CScript virtualCC;
-                                    CTxOut virtualCCOut;
-
-                                    signSuccess = ProduceSignature(
-                                        TransactionSignatureCreator(pwalletMain, &itx, 0, lastImportTx.vout[itx.vin[0].prevout.n].nValue, SIGHASH_ALL), lastImportTx.vout[itx.vin[0].prevout.n].scriptPubKey, sigdata, consensusBranchId);
-
-                                    if (!signSuccess)
-                                    {
-                                        break;
-                                    }
-
-                                    UpdateTransaction(mtx, 0, sigdata);
-                                    itx = CTransaction(mtx);
-
-                                    // commit to mempool and remove any conflicts
-                                    std::list<CTransaction> removed;
-                                    mempool.removeConflicts(itx, removed);
-                                    CValidationState state;
-                                    if (!myAddtomempool(itx, &state))
-                                    {
-                                        LogPrintf("Failed to add import transactions to the mempool due to: %s\n", state.GetRejectReason().c_str());
-                                        printf("Failed to add import transactions to the mempool due to: %s\n", state.GetRejectReason().c_str());
-                                        break;  // if we failed to add one, the others will fail to spend it
-                                    }
-
-                                    lastImportTx = itx;
-                                    lastImportHash = itx.GetHash();
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        else
-        {
-            currencyState.UpdateWithEmission(totalEmission);
+            // export thread to self
+            cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
+            pkCC = CPubKey(ParseHex(CC.CChexstr));
+            indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(ConnectedChains.ThisChain().GetID(), EVAL_CROSSCHAIN_EXPORT))});
+            dests = std::vector<CTxDestination>({pkCC});
+
+            CCrossChainExport ccx(ConnectedChains.ThisChain().GetID(), 0, CCurrencyValueMap(), CCurrencyValueMap());
+            coinbaseTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests)));
         }
 
-        // coinbase should have all necessary outputs (TODO: timelock is not supported or finished yet)
+        // process any imports from the current chain to itself, to suport token launches, etc.
+        // TODO: should also add refund checking here
+        ProcessNewImports(ConnectedChains.ThisChain().GetID(), CTransaction(), nHeight);
+
+        // coinbase should have all necessary outputs (TODO: timelock is not supported yet)
         uint32_t nCoinbaseSize = GetSerializeSize(coinbaseTx, SER_NETWORK, PROTOCOL_VERSION);
         nBlockSize += nCoinbaseSize;
 
         // now create the priority array, including market order reserve transactions, since they can always execute, leave limits for later
         bool haveReserveTransactions = false;
         uint32_t reserveExchangeLimitSize = 0;
-        std::vector<const CTransaction *> limitOrders;
+        std::vector<CReserveTransactionDescriptor> limitOrders;
 
         // now add transactions from the mem pool to the priority heap
         for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
@@ -1044,7 +1237,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             COrphan* porphan = NULL;
             double dPriority = 0;
             CAmount nTotalIn = 0;
-            CAmount nTotalReserveIn = 0;
+            CCurrencyValueMap totalReserveIn;
             bool fMissingInputs = false;
             CReserveTransactionDescriptor rtxd;
             bool isReserve = mempool.IsKnownReserveTransaction(hash, rtxd);
@@ -1068,7 +1261,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                     // if is is a failed conversion, drop through
                     if (!rtxd.IsFillOrKillFail())
                     {
-                        limitOrders.push_back(&tx);
+                        limitOrders.push_back(rtxd);
                         reserveExchangeLimitSize += GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
                         continue;
                     }
@@ -1076,11 +1269,20 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                 if (isReserve)
                 {
                     nTotalIn += rtxd.nativeIn;
-                    nTotalReserveIn += rtxd.reserveIn;
+                    totalReserveIn += rtxd.ReserveInputMap();
+                    if (rtxd.IsIdentity() && CNameReservation(tx).IsValid())
+                    {
+                        nCurrentIDSize += GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+                        if (nCurrentIDSize > nMaxIDSize)
+                        {
+                            continue;
+                        }
+                    }
                 }
                 BOOST_FOREACH(const CTxIn& txin, tx.vin)
                 {
-                    CAmount nValueIn = 0, nReserveValueIn = 0;
+                    CAmount nValueIn = 0;
+                    CCurrencyValueMap reserveValueIn;
 
                     // Read prev transaction
                     if (!view.HaveCoins(txin.prevout.hash))
@@ -1110,31 +1312,30 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
                         const CTransaction &otx = mempool.mapTx.find(txin.prevout.hash)->GetTx();
                         // consider reserve outputs and set priority according to their value here as well
-                        if (!isReserve)
+                        if (isReserve)
                         {
-                            nTotalIn += otx.vout[txin.prevout.n].nValue;
+                            totalReserveIn += otx.vout[txin.prevout.n].ReserveOutValue();
                         }
+                        nTotalIn += otx.vout[txin.prevout.n].nValue;
                         continue;
                     }
                     const CCoins* coins = view.AccessCoins(txin.prevout.hash);
                     assert(coins);
 
-                    // consider reserve outputs and set priority according to their value here as well
                     if (isReserve)
                     {
-                        nReserveValueIn = coins->vout[txin.prevout.n].ReserveOutValue();
+                        reserveValueIn = coins->vout[txin.prevout.n].ReserveOutValue();
                     }
 
                     nValueIn = coins->vout[txin.prevout.n].nValue;
                     int nConf = nHeight - coins->nHeight;
 
-                    dPriority += ((double)((nReserveValueIn ? currencyState.ReserveToNative(nReserveValueIn) : 0) + nValueIn)) * nConf;
+                    dPriority += ((double)((reserveValueIn.valueMap.size() ? currencyState.ReserveToNative(reserveValueIn) : 0) + nValueIn)) * nConf;
 
-                    // reserve is totaled differently
                     if (!isReserve)
                     {
                         nTotalIn += nValueIn;
-                        nTotalReserveIn += nReserveValueIn;
+                        totalReserveIn += reserveValueIn;
                     }
                 }
                 nTotalIn += tx.GetShieldedValueIn();
@@ -1146,20 +1347,21 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
             dPriority = tx.ComputePriority(dPriority, nTxSize);
             
-            CAmount nDeltaValueIn = nTotalIn + (nTotalReserveIn ? currencyState.ReserveToNative(nTotalReserveIn) : 0);
+            CAmount nDeltaValueIn = nTotalIn + (totalReserveIn.valueMap.size() ? currencyState.ReserveToNative(totalReserveIn) : 0);
             CAmount nFeeValueIn = nDeltaValueIn;
             mempool.ApplyDeltas(hash, dPriority, nDeltaValueIn);
 
             CAmount nativeEquivalentOut = 0;
 
             // if there is reserve in, or this is a reserveexchange transaction, calculate fee properly
-            if (isReserve & rtxd.reserveOut)
+            if (isReserve && rtxd.ReserveOutputMap().valueMap.size())
             {
                 // if this has reserve currency out, convert it to native currency for fee calculation
-                nativeEquivalentOut = currencyState.ReserveToNative(rtxd.reserveOut);
+                nativeEquivalentOut = currencyState.ReserveToNative(rtxd.ReserveOutputMap());
             }
 
-            CFeeRate feeRate(isReserve ? rtxd.AllFeesAsNative(currencyState) + currencyState.ReserveToNative(rtxd.reserveConversionFees) + rtxd.nativeConversionFees : nFeeValueIn - (tx.GetValueOut() + nativeEquivalentOut), nTxSize);
+            CFeeRate feeRate(isReserve ? rtxd.AllFeesAsNative(currencyState) + currencyState.ReserveToNative(rtxd.ReserveConversionFeesMap()) + rtxd.nativeConversionFees : 
+                                         nFeeValueIn - (tx.GetValueOut() + nativeEquivalentOut), nTxSize);
 
             if (porphan)
             {
@@ -1255,7 +1457,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             // 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))
+            if (!ContextualCheckInputs(tx, state, view, nHeight, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
             {
                 //fprintf(stderr,"context failure\n");
                 continue;
@@ -1315,9 +1517,9 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         if (haveReserveTransactions)
         {
             std::vector<CReserveTransactionDescriptor> reserveFills;
-            std::vector<const CTransaction *> expiredFillOrKills;
-            std::vector<const CTransaction *> noFills;
-            std::vector<const CTransaction *> rejects;
+            std::vector<CReserveTransactionDescriptor> noFills;
+            std::vector<const CReserveTransactionDescriptor *> expiredFillOrKills;
+            std::vector<const CReserveTransactionDescriptor *> rejects;
 
             // identify all reserve transactions in the block to calculate fees
             for (int i = 0; i < reservePositions.size(); i++)
@@ -1339,11 +1541,15 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             // TODO:PBAAS - NEED TO ADD SIGOPS LIMIT TO THIS FOR HARDENING
             CCoinbaseCurrencyState newState = currencyState.MatchOrders(limitOrders,
                                                                         reserveFills,
-                                                                        expiredFillOrKills,
                                                                         noFills,
+                                                                        expiredFillOrKills,
                                                                         rejects,
-                                                                        exchangeRate, nHeight, conversionInputs,
-                                                                        nBlockMaxSize - autoTxSize, &newBlockSize, &newConversionOutputTx);
+                                                                        exchangeRate, 
+                                                                        nHeight, 
+                                                                        conversionInputs,
+                                                                        nBlockMaxSize - autoTxSize, 
+                                                                        &newBlockSize, 
+                                                                        &newConversionOutputTx);
 
             // TODO:PBAAS - account for the edge case where we have too large expected fills and have no room
             // for transactions that we would otherwise take
@@ -1404,56 +1610,44 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             // keep one placeholder for txCoinbase output as input and remake with the correct exchange rate
             for (auto fill : reserveFills)
             {
-                fill.AddConversionInOuts(newConversionOutputTx, conversionInputs, exchangeRate, &currencyState);
+                fill.AddConversionInOuts(newConversionOutputTx, conversionInputs, CCurrencyValueMap(currencyState.currencies, exchangeRate), &currencyState);
             }
         }
 
         // first calculate and distribute block rewards, including fees in the minerOutputs vector
         CAmount rewardTotalShareAmount = 0;
-        CAmount rewardTotal = blockSubsidy + currencyState.ConversionFees + nFees;
+        CAmount rewardTotal = blockSubsidy + 
+                              currencyState.nativeConversionFees + 
+                              currencyState.ReserveToNativeRaw(CCurrencyValueMap(currencyState.currencies, currencyState.conversionFees), exchangeRate) + 
+                              currencyState.ReserveToNativeRaw(CCurrencyValueMap(currencyState.currencies, currencyState.fees), exchangeRate) + 
+                              nFees;
 
         CAmount rewardLeft = notarizationTxIndex ? rewardTotal - notarizationOut.nValue : rewardTotal;
 
-        for (auto &outputShare : minerOutputs)
+        // now that we have the total reward, update the coinbase outputs
+        if (isStake)
         {
-            rewardTotalShareAmount += outputShare.first;
+            coinbaseTx.vout[0].nValue = rewardLeft;
         }
-
-        int cbOutIdx;
-        for (cbOutIdx = 0; cbOutIdx < minerOutputs.size(); cbOutIdx++)
+        else
         {
-            CAmount amount = (arith_uint256(rewardTotal) * arith_uint256(minerOutputs[cbOutIdx].first) / arith_uint256(rewardTotalShareAmount)).GetLow64();
-            if (rewardLeft <= amount || (cbOutIdx + 1) == minerOutputs.size())
+            for (auto &outputShare : minerOutputs)
             {
-                amount = rewardLeft;
+                rewardTotalShareAmount += outputShare.first;
             }
-            rewardLeft -= amount;
-            coinbaseTx.vout[cbOutIdx].nValue = amount;
-            // the only valid CC output we currently support on coinbases is stake guard, which does not need to be modified for this
-        }
-
-        // premineOut - done
-        if (premineOut.scriptPubKey.size())
-        {
-            cbOutIdx++;
-        }
-
-        // chainDefinitionOut - done
-        if (chainDefinitionOut.scriptPubKey.size())
-        {
-            cbOutIdx++;
-        }
-
-        // importThreadOut - done
-        if (importThreadOut.scriptPubKey.size())
-        {
-            cbOutIdx++;
-        }
 
-        // exportThreadOut - done
-        if (exportThreadOut.scriptPubKey.size())
-        {
-            cbOutIdx++;
+            int cbOutIdx;
+            for (cbOutIdx = 0; cbOutIdx < minerOutputs.size(); cbOutIdx++)
+            {
+                CAmount amount = (arith_uint256(rewardTotal) * arith_uint256(minerOutputs[cbOutIdx].first) / arith_uint256(rewardTotalShareAmount)).GetLow64();
+                if (rewardLeft <= amount || (cbOutIdx + 1) == minerOutputs.size())
+                {
+                    amount = rewardLeft;
+                }
+                rewardLeft -= amount;
+                coinbaseTx.vout[cbOutIdx].nValue = amount;
+                // the only valid CC output we currently support on coinbases is stake guard, which does not need to be modified for this
+            }
         }
 
         // currencyStateOut - update currency state, output is present whether or not there is a conversion transaction
@@ -1469,10 +1663,16 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             {
                 CTransaction convertTx(newConversionOutputTx);
                 currencyStateOut.nValue = convertTx.GetValueOut();
-                currencyState.ReserveOut.nValue = convertTx.GetReserveValueOut();
+
+                auto reserveOutMap = convertTx.GetReserveValueOut();
+                for (int i = 0; i < currencyState.currencies.size(); i++)
+                {
+                    auto it = reserveOutMap.valueMap.find(currencyState.currencies[i]);
+                    currencyState.reserveOut[i] = (it != reserveOutMap.valueMap.end()) ? it->second : 0;
+                }
 
                 // the coinbase is not finished, store index placeholder here now and fixup hash later
-                newConversionOutputTx.vin[0] = CTxIn(uint256(), cbOutIdx);
+                newConversionOutputTx.vin[0] = CTxIn(uint256(), currencyStateOutNum);
             }
             else
             {
@@ -1480,8 +1680,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                 newConversionOutputTx.vout.clear();
             }
 
-            coinbaseTx.vout[cbOutIdx] = currencyStateOut;
-            cbOutIdx++;
+            coinbaseTx.vout[currencyStateOutNum] = currencyStateOut;
         }
 
         // notarizationOut - update currencyState in notarization
@@ -1491,7 +1690,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             int i;
             for (i = 0; i < newNotarizationTx.vout.size(); i++)
             {
-                if (newNotarizationTx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.evalCode == EVAL_EARNEDNOTARIZATION)
+                if (newNotarizationTx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_EARNEDNOTARIZATION)
                 {
                     break;
                 }
@@ -1511,19 +1710,14 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
             p.vData[0] = nz.AsVector();
             notarizationOut.scriptPubKey.ReplaceCCParams(p);
 
-            coinbaseTx.vout[cbOutIdx] = notarizationOut;
+            coinbaseTx.vout[notarizationOutNum] = notarizationOut;
 
             // now that the coinbase is finished, finish and place conversion transaction before the stake transaction
-            newNotarizationTx.vin.push_back(CTxIn(uint256(), cbOutIdx));
-
-            cbOutIdx++;
+            newNotarizationTx.vin.push_back(CTxIn(uint256(), notarizationOutNum));
 
             pblock->vtx[notarizationTxIndex] = newNotarizationTx;
         }
 
-        // this should be the end of the outputs
-        assert(cbOutIdx == coinbaseTx.vout.size());
-
         nLastBlockTx = nBlockTx;
         nLastBlockSize = nBlockSize;
 
@@ -1610,9 +1804,6 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                 CAmount value;
                 const CScript *pScriptPubKey;
 
-                const CScript virtualCC;
-                CTxOut virtualCCOut;
-
                 // if this is our coinbase input, different signing
                 if (i)
                 {
@@ -1683,9 +1874,6 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
                 CAmount value;
                 const CScript *pScriptPubKey;
 
-                const CScript virtualCC;
-                CTxOut virtualCCOut;
-
                 // if this is our coinbase input, we won't find it elsewhere
                 if (i < notarizationInputs.size())
                 {
@@ -1797,7 +1985,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
         {
             CValidationState state;
             //fprintf(stderr,"check validity\n");
-            if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) // invokes CC checks
+            if ( !TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) // invokes CC checks
             {
                 throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed");
             }
@@ -1808,7 +1996,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
     // setup the header and buid the Merkle tree
     unsigned int extraNonce;
-    IncrementExtraNonce(pblock, pindexPrev, extraNonce);
+    IncrementExtraNonce(pblock, pindexPrev, extraNonce, true);
 
     return pblocktemplate.release();
 }
@@ -1863,6 +2051,30 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
 
 #ifdef ENABLE_MINING
 
+class MinerAddressScript : public CReserveScript
+{
+    // CReserveScript requires implementing this function, so that if an
+    // internal (not-visible) wallet address is used, the wallet can mark it as
+    // important when a block is mined (so it then appears to the user).
+    // If -mineraddress is set, the user already knows about and is managing the
+    // address, so we don't need to do anything here.
+    void KeepScript() {}
+};
+
+void GetScriptForMinerAddress(boost::shared_ptr<CReserveScript> &script)
+{
+    CTxDestination addr = DecodeDestination(GetArg("-mineraddress", ""));
+    if (!IsValidDestination(addr)) {
+        return;
+    }
+
+    boost::shared_ptr<MinerAddressScript> mAddr(new MinerAddressScript());
+    CKeyID keyID = boost::get<CKeyID>(addr);
+
+    script = mAddr;
+    script->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
+}
+
 #ifdef ENABLE_WALLET
 //////////////////////////////////////////////////////////////////////////////
 //
@@ -1898,10 +2110,10 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
             //scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
         }
     }
-    return CreateNewBlock(scriptPubKey, gpucount, isStake);
+    return CreateNewBlock(Params(), scriptPubKey, gpucount, isStake);
 }
 
-void komodo_broadcast(CBlock *pblock,int32_t limit)
+void komodo_broadcast(const CBlock *pblock,int32_t limit)
 {
     int32_t n = 1;
     //fprintf(stderr,"broadcast new block t.%u\n",(uint32_t)time(NULL));
@@ -1928,9 +2140,9 @@ static bool ProcessBlockFound(CBlock* pblock)
 #endif // ENABLE_WALLET
 {
     int32_t height = chainActive.LastTip()->GetHeight()+1;
-    LogPrintf("%s\n", pblock->ToString());
+    //LogPrintf("%s\n", pblock->ToString());
     LogPrintf("generated %s height.%d\n", FormatMoney(pblock->vtx[0].vout[0].nValue), height);
-    
+
     // Found a solution
     {
         if (pblock->hashPrevBlock != chainActive.LastTip()->GetBlockHash())
@@ -1968,9 +2180,9 @@ static bool ProcessBlockFound(CBlock* pblock)
 #endif
     //fprintf(stderr,"process new block\n");
 
-    // Process this block the same as if we had received it from another node
+    // Process this block (almost) the same as if we had received it from another node
     CValidationState state;
-    if (!ProcessNewBlock(1,chainActive.LastTip()->GetHeight()+1,state, NULL, pblock, true, NULL))
+    if (!ProcessNewBlock(1, chainActive.LastTip()->GetHeight()+1, state, Params(), NULL, pblock, true, NULL))
         return error("VerusMiner: ProcessNewBlock, block not accepted");
     
     TrackMinedBlock(pblock->GetHash());
@@ -2120,18 +2332,22 @@ void static VerusStaker(CWallet *pwallet)
 
             // try to stake a block
             CBlockTemplate *ptr = NULL;
-            if (Mining_height > VERUS_MIN_STAKEAGE)
-                ptr = CreateNewBlockWithKey(reservekey, Mining_height, 0, true);
+
+            // get height locally for consistent reporting
+            int32_t newHeight = Mining_height;
+
+            if (newHeight > VERUS_MIN_STAKEAGE)
+                ptr = CreateNewBlockWithKey(reservekey, newHeight, 0, true);
 
             // TODO - putting this output here tends to help mitigate announcing a staking height earlier than
             // announcing the last block win when we start staking before a block's acceptance has been
             // acknowledged by the mining thread - a better solution may be to put the output on the submission
             // thread.
-            if ( ptr == 0 && Mining_height != lastStakingHeight )
+            if ( ptr == 0 && newHeight != lastStakingHeight )
             {
-                printf("Staking height %d for %s\n", Mining_height, ASSETCHAINS_SYMBOL);
+                printf("Staking height %d for %s\n", newHeight, ASSETCHAINS_SYMBOL);
             }
-            lastStakingHeight = Mining_height;
+            lastStakingHeight = newHeight;
 
             if ( ptr == 0 )
             {
@@ -2196,15 +2412,21 @@ void static VerusStaker(CWallet *pwallet)
             {
                 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("Found block %d \n", newHeight);
                 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)
+                    CTransaction::_GetVerusPOSHash(&(pblock->nNonce), 
+                                                     sTx.vin[0].prevout.hash, 
+                                                     sTx.vin[0].prevout.n, 
+                                                     newHeight, 
+                                                     chainActive.GetVerusEntropyHash(Mining_height), 
+                                                     sTx.vout[0].nValue).GetHex().c_str(), 
+                                                     ArithToUint256(post).GetHex().c_str());
+                if (unlockTime > newHeight && subsidy >= ASSETCHAINS_TIMELOCKGTE)
                     printf("- timelocked until block %i\n", unlockTime);
                 else
                     printf("\n");
@@ -2255,6 +2477,8 @@ void static BitcoinMiner_noeq()
     CReserveKey reservekey(pwallet);
 #endif
 
+    miningTimer.clear();
+
     const CChainParams& chainparams = Params();
     // Each thread has its own counter
     unsigned int nExtraNonce = 0;
@@ -2292,9 +2516,6 @@ void static BitcoinMiner_noeq()
     try {
         printf("Mining %s with %s\n", ASSETCHAINS_SYMBOL, ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
 
-        // v2 hash writer
-        CVerusHashV2bWriter ss2 = CVerusHashV2bWriter(SER_GETHASH, PROTOCOL_VERSION);
-
         while (true)
         {
             miningTimer.stop();
@@ -2365,6 +2586,8 @@ void static BitcoinMiner_noeq()
                     // 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);
                 }
+                miningTimer.stop();
+                miningTimer.clear();
                 return;
             }
             CBlock *pblock = &pblocktemplate->block;
@@ -2373,8 +2596,18 @@ void static BitcoinMiner_noeq()
             bool mergeMining = false;
             savebits = pblock->nBits;
 
-            bool verusHashV2 = pblock->nVersion == CBlockHeader::VERUS_V2;
-            bool verusSolutionV3 = CConstVerusSolutionVector::Version(pblock->nSolution) == CActivationHeight::SOLUTION_VERUSV3;
+            uint32_t solutionVersion = CConstVerusSolutionVector::Version(pblock->nSolution);
+            if (pblock->nVersion != CBlockHeader::VERUS_V2)
+            {
+                // must not be in sync
+                printf("Mining on incorrect block version.\n");
+                sleep(2);
+                continue;
+            }
+            bool verusSolutionPBaaS = solutionVersion >= CActivationHeight::ACTIVATE_PBAAS;
+
+            // v2 hash writer with adjustments for the current height
+            CVerusHashV2bWriter ss2 = CVerusHashV2bWriter(SER_GETHASH, PROTOCOL_VERSION, solutionVersion);
 
             if ( ASSETCHAINS_SYMBOL[0] != 0 )
             {
@@ -2391,11 +2624,11 @@ void static BitcoinMiner_noeq()
                 }
             }
 
-            // this builds the Merkle tree and sets our easiest target
-            IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, false, &savebits);
+            // set our easiest target, if V3+, no need to rebuild the merkle tree
+            IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, verusSolutionPBaaS ? false : true, &savebits);
 
             // update PBaaS header
-            if (verusSolutionV3)
+            if (verusSolutionPBaaS)
             {
                 if (!IsVerusActive() && ConnectedChains.IsVerusPBaaSAvailable())
                 {
@@ -2451,27 +2684,10 @@ void static BitcoinMiner_noeq()
                 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);
-            }
-
             uint64_t count;
             uint64_t hashesToGo = 0;
             uint64_t totalDone = 0;
 
-            if (!verusHashV2)
-            {
-                // must not be in sync
-                printf("Mining on incorrect block version.\n");
-                sleep(2);
-                continue;
-            }
-
             int64_t subsidy = (int64_t)(pblock->vtx[0].vout[0].nValue);
             count = ((ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] >> 3) + 1) / ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
             CVerusHashV2 *vh2 = &ss2.GetState();
@@ -2554,7 +2770,7 @@ void static BitcoinMiner_noeq()
                             // pickup/remove any new/deleted headers
                             if (ConnectedChains.dirty || (pblock->NumPBaaSHeaders() < ConnectedChains.mergeMinedChains.size() + 1))
                             {
-                                IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, false, &savebits);
+                                IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, verusSolutionPBaaS ? false : true, &savebits);
 
                                 hashTarget.SetCompact(savebits);
                                 uintTarget = ArithToUint256(hashTarget);
@@ -2565,7 +2781,7 @@ void static BitcoinMiner_noeq()
                             uint64_t start = i * hashesToGo + totalDone;
                             hashesToGo -= totalDone;
 
-                            if (verusSolutionV3)
+                            if (verusSolutionPBaaS)
                             {
                                 // mine on canonical header for merge mining
                                 CPBaaSPreHeader savedHeader(*pblock);
@@ -2611,8 +2827,7 @@ void static BitcoinMiner_noeq()
                             {
                                 // if we'll not drop through, update hashcount
                                 {
-                                    LOCK(cs_metrics);
-                                    nHashCount += totalDone;
+                                    miningTimer += totalDone;
                                     totalDone = 0;
                                 }
                             }
@@ -2670,8 +2885,7 @@ void static BitcoinMiner_noeq()
                     }
 
                     {
-                        LOCK(cs_metrics);
-                        nHashCount += totalDone;
+                        miningTimer += totalDone;
                     }
                 }
                 
@@ -2722,29 +2936,29 @@ void static BitcoinMiner_noeq()
     catch (const boost::thread_interrupted&)
     {
         miningTimer.stop();
+        miningTimer.clear();
         LogPrintf("%s miner terminated\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]);
         throw;
     }
     catch (const std::runtime_error &e)
     {
         miningTimer.stop();
+        miningTimer.clear();
         LogPrintf("%s miner runtime error: %s\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO], e.what());
         return;
     }
     miningTimer.stop();
+    miningTimer.clear();
 }
 
-#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);
@@ -2753,8 +2967,8 @@ void static BitcoinMiner()
     // Each thread has its own counter
     unsigned int nExtraNonce = 0;
     
-    unsigned int n = chainparams.EquihashN();
-    unsigned int k = chainparams.EquihashK();
+    unsigned int n = chainparams.GetConsensus().EquihashN();
+    unsigned int k = chainparams.GetConsensus().EquihashK();
     uint8_t *script; uint64_t total,checktoshis; int32_t i,j,gpucount=KOMODO_MAXGPUCOUNT,notaryid = -1;
     while ( (ASSETCHAIN_INIT == 0 || KOMODO_INITDONE == 0) )
     {
@@ -2802,7 +3016,7 @@ void static BitcoinMiner()
                         //LOCK(cs_vNodes);
                         fvNodesEmpty = vNodes.empty();
                     }
-                    if (!fvNodesEmpty )//&& !IsInitialBlockDownload())
+                    if (!fvNodesEmpty && !IsInitialBlockDownload(chainparams))
                         break;
                     MilliSleep(15000);
                     //fprintf(stderr,"fvNodesEmpty %d IsInitialBlockDownload(%s) %d\n",(int32_t)fvNodesEmpty,ASSETCHAINS_SYMBOL,(int32_t)IsInitialBlockDownload());
@@ -3037,7 +3251,7 @@ void static BitcoinMiner()
                         fprintf(stderr," mined %s block %d!\n",ASSETCHAINS_SYMBOL,Mining_height);
                     }
                     CValidationState state;
-                    if ( !TestBlockValidity(state,B, chainActive.LastTip(), true, false))
+                    if ( !TestBlockValidity(state, Params(), B, chainActive.LastTip(), true, false))
                     {
                         h = UintToArith256(B.GetHash());
                         for (z=31; z>=0; z--)
@@ -3197,13 +3411,16 @@ void static BitcoinMiner()
         miningTimer.stop();
         c.disconnect();
     }
-    
+
 #ifdef ENABLE_WALLET
     void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
 #else
     void GenerateBitcoins(bool fGenerate, int nThreads)
 #endif
     {
+        static CCriticalSection cs_startmining;
+
+        LOCK(cs_startmining);
         if (!AreParamsInitialized())
         {
             return;
@@ -3250,10 +3467,11 @@ void static BitcoinMiner()
 
         if (nThreads < 0)
             nThreads = GetNumCores();
-        
+
         if (minerThreads != NULL)
         {
             minerThreads->interrupt_all();
+            minerThreads->join_all();
             delete minerThreads;
             minerThreads = NULL;
         }
This page took 0.079784 seconds and 4 git commands to generate.