]> Git Repo - VerusCoin.git/blobdiff - src/wallet/wallet.cpp
Add full, yet to be integrated CC script prechecks, including ID validity pre-check
[VerusCoin.git] / src / wallet / wallet.cpp
index e83a1734e8b6d7291d39fc9020ffcea6421fe271..b02fc3f1b10421fa863e39ac68109d32fe668dd1 100644 (file)
@@ -15,6 +15,7 @@
 #include "init.h"
 #include "key_io.h"
 #include "main.h"
+#include "mmr.h"
 #include "net.h"
 #include "rpc/protocol.h"
 #include "rpc/server.h"
 #include "utilmoneystr.h"
 #include "zcash/Note.hpp"
 #include "crypter.h"
+#include "coins.h"
 #include "wallet/asyncrpcoperation_saplingmigration.h"
 #include "zcash/zip32.h"
+#include "cc/StakeGuard.h"
+#include "pbaas/identity.h"
+#include "pbaas/pbaas.h"
 
 #include <assert.h>
 
@@ -45,6 +50,17 @@ unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
 bool bSpendZeroConfChange = true;
 bool fSendFreeTransactions = false;
 bool fPayAtLeastCustomFee = true;
+#include "komodo_defs.h"
+
+extern int32_t USE_EXTERNAL_PUBKEY;
+extern std::string NOTARY_PUBKEY;
+extern int32_t KOMODO_EXCHANGEWALLET;
+extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
+extern uint160 ASSETCHAINS_CHAINID;
+extern int32_t VERUS_MIN_STAKEAGE;
+CBlockIndex *komodo_chainactive(int32_t height);
+extern std::string DONATION_PUBKEY;
+extern BlockMap mapBlockIndex;
 
 /**
  * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
@@ -437,11 +453,14 @@ bool CWallet::LoadSproutViewingKey(const libzcash::SproutViewingKey &vk)
 
 bool CWallet::AddCScript(const CScript& redeemScript)
 {
+    // if this is an identity, which we currently need to check, we
+    // store the ID as a script in the wallet script storage, but instead of using the
+    // hash of the script, we store it under the name ID
     if (!CCryptoKeyStore::AddCScript(redeemScript))
         return false;
     if (!fFileBacked)
         return true;
-    return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
+    return CWalletDB(strWalletFile).WriteCScript(ScriptOrIdentityID(redeemScript), redeemScript);
 }
 
 bool CWallet::LoadCScript(const CScript& redeemScript)
@@ -451,7 +470,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
      * these. Do not add them to the wallet and warn. */
     if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
     {
-        std::string strAddr = EncodeDestination(CScriptID(redeemScript));
+        std::string strAddr = EncodeDestination(ScriptOrIdentityID(redeemScript));
         LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n",
             __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
         return true;
@@ -460,6 +479,88 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
     return CCryptoKeyStore::AddCScript(redeemScript);
 }
 
+bool CWallet::AddIdentity(const CIdentityMapKey &mapKey, const CIdentityMapValue &identity)
+{
+    // if this is an identity, which we currently need to check, we
+    // store the ID as a script in the wallet script storage, but instead of using the
+    // hash of the script, we store it under the name ID
+    if (!CCryptoKeyStore::AddIdentity(mapKey, identity))
+        return false;
+    if (!fFileBacked)
+        return true;
+    return CWalletDB(strWalletFile).WriteIdentity(mapKey, identity);
+}
+
+bool CWallet::UpdateIdentity(const CIdentityMapKey &mapKey, const CIdentityMapValue &identity)
+{
+    // if this is an identity, which we currently need to check, we
+    // store the ID as a script in the wallet script storage, but instead of using the
+    // hash of the script, we store it under the name ID
+    if (!CCryptoKeyStore::UpdateIdentity(mapKey, identity))
+        return false;
+    if (!fFileBacked)
+        return true;
+    return CWalletDB(strWalletFile).WriteIdentity(mapKey, identity);
+}
+
+bool CWallet::AddUpdateIdentity(const CIdentityMapKey &mapKey, const CIdentityMapValue &identity)
+{
+    // if this is an identity, which we currently need to check, we
+    // store the ID as a script in the wallet script storage, but instead of using the
+    // hash of the script, we store it under the name ID
+    if (!CCryptoKeyStore::AddUpdateIdentity(mapKey, identity))
+        return false;
+    if (!fFileBacked)
+        return true;
+    return CWalletDB(strWalletFile).WriteIdentity(mapKey, identity);
+}
+
+bool CWallet::RemoveIdentity(const CIdentityMapKey &mapKey, const uint256 &txid)
+{
+    CIdentityMapKey localKey = mapKey;
+    std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> toErase;
+    if (localKey.blockHeight == 0)
+    {
+        localKey.blockHeight = 0x7fffffff;
+    }
+    if (!GetIdentity(mapKey, localKey, toErase))
+    {
+        return false;
+    }
+    if (!txid.IsNull())
+    {
+        std::pair<CIdentityMapKey, CIdentityMapValue> idEntry;
+        for (auto id : toErase)
+        {
+            if (id.second.txid == txid)
+            {
+                idEntry = id;
+            }
+        }
+        toErase.clear();
+        if (idEntry.first.IsValid() && idEntry.second.IsValid())
+        {
+            toErase.push_back(idEntry);
+        }
+    }
+    if (!CCryptoKeyStore::RemoveIdentity(mapKey, txid))
+        return false;
+    if (!fFileBacked)
+        return true;
+
+    bool error = false;
+    for (auto idPair : toErase)
+    {
+        error = CWalletDB(strWalletFile).EraseIdentity(idPair.first) ? error : true;
+    }    
+    return error;
+}
+
+bool CWallet::LoadIdentity(const CIdentityMapKey &mapKey, const CIdentityMapValue &identity)
+{
+    return CCryptoKeyStore::AddUpdateIdentity(mapKey, identity);
+}
+
 bool CWallet::AddWatchOnly(const CScript &dest)
 {
     if (!CCryptoKeyStore::AddWatchOnly(dest))
@@ -584,7 +685,7 @@ void CWallet::ChainTip(const CBlockIndex *pindex,
         if (!IsInitialBlockDownload(Params()) &&
             pblock->GetBlockTime() > GetAdjustedTime() - 3 * 60 * 60)
         {
-            RunSaplingMigration(pindex->nHeight);
+            RunSaplingMigration(pindex->GetHeight());
         }
     } else {
         DecrementNoteWitnesses(pindex);
@@ -1019,6 +1120,7 @@ void CWallet::ClearNoteWitnessCache()
         }
     }
     nWitnessCacheSize = 0;
+    //fprintf(stderr,"Clear witness cache\n");
 }
 
 template<typename NoteDataMap>
@@ -1114,8 +1216,8 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
 {
     LOCK(cs_wallet);
     for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
-       ::CopyPreviousWitnesses(wtxItem.second.mapSproutNoteData, pindex->nHeight, nWitnessCacheSize);
-       ::CopyPreviousWitnesses(wtxItem.second.mapSaplingNoteData, pindex->nHeight, nWitnessCacheSize);
+       ::CopyPreviousWitnesses(wtxItem.second.mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize);
+       ::CopyPreviousWitnesses(wtxItem.second.mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize);
     }
 
     if (nWitnessCacheSize < WITNESS_CACHE_SIZE) {
@@ -1141,13 +1243,13 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
 
                 // Increment existing witnesses
                 for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
-                    ::AppendNoteCommitment(wtxItem.second.mapSproutNoteData, pindex->nHeight, nWitnessCacheSize, note_commitment);
+                    ::AppendNoteCommitment(wtxItem.second.mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize, note_commitment);
                 }
 
                 // If this is our note, witness it
                 if (txIsOurs) {
                     JSOutPoint jsoutpt {hash, i, j};
-                    ::WitnessNoteIfMine(mapWallet[hash].mapSproutNoteData, pindex->nHeight, nWitnessCacheSize, jsoutpt, sproutTree.witness());
+                    ::WitnessNoteIfMine(mapWallet[hash].mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize, jsoutpt, sproutTree.witness());
                 }
             }
         }
@@ -1158,21 +1260,21 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
 
             // Increment existing witnesses
             for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
-                ::AppendNoteCommitment(wtxItem.second.mapSaplingNoteData, pindex->nHeight, nWitnessCacheSize, note_commitment);
+                ::AppendNoteCommitment(wtxItem.second.mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize, note_commitment);
             }
 
             // If this is our note, witness it
             if (txIsOurs) {
                 SaplingOutPoint outPoint {hash, i};
-                ::WitnessNoteIfMine(mapWallet[hash].mapSaplingNoteData, pindex->nHeight, nWitnessCacheSize, outPoint, saplingTree.witness());
+                ::WitnessNoteIfMine(mapWallet[hash].mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize, outPoint, saplingTree.witness());
             }
         }
     }
 
     // Update witness heights
     for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
-        ::UpdateWitnessHeights(wtxItem.second.mapSproutNoteData, pindex->nHeight, nWitnessCacheSize);
-        ::UpdateWitnessHeights(wtxItem.second.mapSaplingNoteData, pindex->nHeight, nWitnessCacheSize);
+        ::UpdateWitnessHeights(wtxItem.second.mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize);
+        ::UpdateWitnessHeights(wtxItem.second.mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize);
     }
 
     // For performance reasons, we write out the witness cache in
@@ -1181,8 +1283,10 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
 }
 
 template<typename NoteDataMap>
-void DecrementNoteWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t nWitnessCacheSize)
+bool DecrementNoteWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t nWitnessCacheSize)
 {
+    extern int32_t KOMODO_REWIND;
+
     for (auto& item : noteDataMap) {
         auto* nd = &(item.second);
         // Only decrement witnesses that are not above the current height
@@ -1194,7 +1298,11 @@ void DecrementNoteWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t n
             // Witnesses being decremented should always be either -1
             // (never incremented or decremented) or equal to the height
             // of the block being removed (indexHeight)
-            assert((nd->witnessHeight == -1) || (nd->witnessHeight == indexHeight));
+            if (!((nd->witnessHeight == -1) || (nd->witnessHeight == indexHeight)))
+            {
+                printf("at height %d\n", indexHeight);
+                return false;
+            }
             if (nd->witnesses.size() > 0) {
                 nd->witnesses.pop_front();
             }
@@ -1218,14 +1326,18 @@ void DecrementNoteWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t n
             assert((nWitnessCacheSize - 1) >= nd->witnesses.size());
         }
     }
+    assert(KOMODO_REWIND != 0 || nWitnessCacheSize > 0);
+    return true;
 }
 
 void CWallet::DecrementNoteWitnesses(const CBlockIndex* pindex)
 {
     LOCK(cs_wallet);
     for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
-        ::DecrementNoteWitnesses(wtxItem.second.mapSproutNoteData, pindex->nHeight, nWitnessCacheSize);
-        ::DecrementNoteWitnesses(wtxItem.second.mapSaplingNoteData, pindex->nHeight, nWitnessCacheSize);
+        if (!::DecrementNoteWitnesses(wtxItem.second.mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize))
+            needsRescan = true;
+        if (!::DecrementNoteWitnesses(wtxItem.second.mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize))
+            needsRescan = true;
     }
     nWitnessCacheSize -= 1;
     // TODO: If nWitnessCache is zero, we need to regenerate the caches (#1302)
@@ -1364,6 +1476,257 @@ CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries,
     return txOrdered;
 }
 
+// looks through all wallet UTXOs and checks to see if any qualify to stake the block at the current height. it always returns the qualified
+// UTXO with the smallest coin age if there is more than one, as larger coin age will win more often and is worth saving
+// each attempt consists of taking a VerusHash of the following values:
+//  ASSETCHAINS_MAGIC, nHeight, txid, voutNum
+bool CWallet::VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult, CTransaction &stakeSource, int32_t &voutNum, int32_t nHeight, uint32_t &bnTarget) const
+{
+    arith_uint256 target;
+    arith_uint256 curHash;
+    vector<COutput> vecOutputs;
+    COutput *pwinner = NULL;
+    CBlockIndex *pastBlockIndex;
+    txnouttype whichType;
+    std:vector<std::vector<unsigned char>> vSolutions;
+
+    pBlock->nNonce.SetPOSTarget(bnTarget, pBlock->nVersion);
+    target.SetCompact(bnTarget);
+
+    auto consensusParams = Params().GetConsensus();
+    CValidationState state;
+
+    pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, !consensusParams.fCoinbaseMustBeProtected);
+
+    if (pastBlockIndex = komodo_chainactive(nHeight - 100))
+    {
+        uint256 pastHash = pastBlockIndex->GetVerusEntropyHash();
+        CPOSNonce curNonce;
+        uint32_t srcIndex;
+
+        BOOST_FOREACH(COutput &txout, vecOutputs)
+        {
+            if (txout.fSpendable && (UintToArith256(txout.tx->GetVerusPOSHash(&(pBlock->nNonce), txout.i, nHeight, pastHash)) <= target) && (txout.nDepth >= VERUS_MIN_STAKEAGE))
+            {
+                CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+                int32_t txSize = GetSerializeSize(s, *(CTransaction *)txout.tx);
+
+                //printf("Serialized size of transaction %s is %lu\n", txout.tx->GetHash().GetHex().c_str(), txSize);
+                if (txSize > MAX_TX_SIZE_FOR_STAKING)
+                {
+                    LogPrintf("Transaction %s is too large to stake. Serialized size == %lu\n", txout.tx->GetHash().GetHex().c_str(), txSize);
+                }
+
+                CCoinsViewCache view(pcoinsTip);
+                CMutableTransaction checkStakeTx = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
+                uint256 txHash = txout.tx->GetHash();
+                checkStakeTx.vin.push_back(CTxIn(COutPoint(txHash, txout.i)));
+
+                if (txSize <= MAX_TX_SIZE_FOR_STAKING &&
+                    (!pwinner || UintToArith256(curNonce) > UintToArith256(pBlock->nNonce)) &&
+                    (Solver(txout.tx->vout[txout.i].scriptPubKey, whichType, vSolutions) && (whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH)) &&
+                    !cheatList.IsUTXOInList(COutPoint(txHash, txout.i), nHeight <= 100 ? 1 : nHeight-100) &&
+                    view.AccessCoins(txHash) &&
+                    Consensus::CheckTxInputs(checkStakeTx, state, view, nHeight, consensusParams))
+                {
+                    //printf("Found PoS block\nnNonce:    %s\n", pBlock->nNonce.GetHex().c_str());
+                    pwinner = &txout;
+                    curNonce = pBlock->nNonce;
+                    srcIndex = (nHeight - txout.nDepth) - 1;
+                }
+            }
+        }
+        if (pwinner)
+        {
+            stakeSource = *(pwinner->tx);
+            voutNum = pwinner->i;
+            pBlock->nNonce = curNonce;
+
+            if (CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight) == CActivationHeight::SOLUTION_VERUSV3)
+            {
+                CDataStream txStream = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
+
+                // store:
+                // 1. PBaaS header for this block
+                // 2. source transaction
+                // 3. block index of base MMR being used
+                // 4. source tx block index for proof
+                // 5. full merkle proof of source tx up to prior MMR root
+                // 6. block hash of block of entropyhash
+                // 7. proof of block hash (not full header) in the MMR for the block height of the entropy hash block
+                // all that data includes enough information to verify
+                // prior MMR, blockhash, transaction, entropy hash, and block indexes match
+                // also checks root match & block power
+                auto view = chainActive.GetMMV();
+                pBlock->AddUpdatePBaaSHeader(view.GetRoot());
+
+                txStream << *(CTransaction *)pwinner->tx;
+
+                // start with the tx proof
+                CMerkleBranch branch(pwinner->tx->nIndex, pwinner->tx->vMerkleBranch);
+
+                // add the Merkle proof bridge to the MMR
+                chainActive[srcIndex]->AddMerkleProofBridge(branch);
+
+                // use the block that we got entropy hash from as the validating block
+                // which immediately provides all but unspent proof for PoS block
+                view.resize(pastBlockIndex->GetHeight());
+
+                view.GetProof(branch, srcIndex);
+
+                // store block height of MMR root, block index of entry, and full blockchain proof of transaction with that root
+                txStream << pastBlockIndex->GetHeight();
+                txStream << srcIndex;
+                txStream << branch;
+
+                // now we get a fresh branch for the block proof
+                branch = CMerkleBranch();
+
+                // prove the block 100 blocks ago with its coincident MMR
+                // it must hash to the same value with a different path, providing both a consistency check and
+                // an asserted MMR root for the n - 100 block if matched
+                pastBlockIndex->AddBlockProofBridge(branch);
+                view.GetProof(branch, pastBlockIndex->GetHeight());
+
+                // block proof of the same block using the MMR of that block height, so we don't need to add additional data
+                // beyond the block hash.
+                txStream << pastBlockIndex->GetBlockHash();
+                txStream << branch;
+
+                std::vector<unsigned char> stx(txStream.begin(), txStream.end());
+
+                // printf("\nFound Stake transaction... all proof serialized size == %lu\n", stx.size());
+
+                CVerusSolutionVector(pBlock->nSolution).ResizeExtraData(stx.size());
+
+                pBlock->SetExtraData(stx.data(), stx.size());
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &bnTarget, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey pk) const
+{
+    CTransaction stakeSource;
+    int32_t voutNum, siglen = 0;
+    int64_t nValue;
+    txnouttype whichType;
+    std::vector<std::vector<unsigned char>> vSolutions;
+
+    CBlockIndex *tipindex = chainActive.LastTip();
+    uint32_t stakeHeight = tipindex->GetHeight() + 1;
+    bool extendedStake = stakeHeight >= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight;
+
+    if (!extendedStake)
+        pk = CPubKey();
+
+    bnTarget = lwmaGetNextPOSRequired(tipindex, Params().GetConsensus());
+
+    if (!VerusSelectStakeOutput(pBlock, hashResult, stakeSource, voutNum, stakeHeight, bnTarget) ||
+        !Solver(stakeSource.vout[voutNum].scriptPubKey, whichType, vSolutions))
+    {
+        LogPrintf("Searched for eligible staking transactions, no winners found\n");
+        return 0;
+    }
+
+    bool signSuccess; 
+    SignatureData sigdata; 
+    uint64_t txfee;
+    auto consensusBranchId = CurrentEpochBranchId(stakeHeight, Params().GetConsensus());
+
+    const CKeyStore& keystore = *pwalletMain;
+    txNew.vin.resize(1);
+    txNew.vout.resize(1);
+    txfee = 0;
+    txNew.vin[0].prevout.hash = stakeSource.GetHash();
+    txNew.vin[0].prevout.n = voutNum;
+
+    if (whichType == TX_PUBKEY)
+    {
+        txNew.vout[0].scriptPubKey << ToByteVector(vSolutions[0]) << OP_CHECKSIG;
+        if (!pk.IsValid())
+            pk = CPubKey(vSolutions[0]);
+    }
+    else if (whichType == TX_PUBKEYHASH)
+    {
+        txNew.vout[0].scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(vSolutions[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
+        if (extendedStake && !pk.IsValid())
+        {
+            // we need a pubkey, so try to get one from the key ID, if not there, fail
+            if (!keystore.GetPubKey(CKeyID(uint160(vSolutions[0])), pk))
+                return 0;
+        }
+    }
+    else
+        return 0;
+
+    // if we are staking with the extended format, add the opreturn data required
+    if (extendedStake)
+    {
+        // set expiry to time out after 100 blocks, so we can remove the transaction if it orphans
+        txNew.nExpiryHeight = stakeHeight + 100;
+
+        uint256 srcBlock = uint256();
+        CBlockIndex *pSrcIndex;
+
+        txNew.vout.push_back(CTxOut());
+        CTxOut &txOut1 = txNew.vout[1];
+        txOut1.nValue = 0;
+        if (!GetTransaction(stakeSource.GetHash(), stakeSource, srcBlock))
+            return 0;
+        
+        BlockMap::const_iterator it = mapBlockIndex.find(srcBlock);
+        if (it == mapBlockIndex.end() || (pSrcIndex = it->second) == 0)
+            return 0;
+
+        // !! DISABLE THIS FOR RELEASE: THIS MAKES A CHEAT TRANSACTION FOR EVERY STAKE FOR TESTING
+        //CMutableTransaction cheat;
+        //cheat = CMutableTransaction(txNew);
+        //printf("TESTING ONLY: THIS SHOULD NOT BE ENABLED FOR RELEASE - MAKING CHEAT TRANSACTION FOR TESTING\n");
+        //cheat.vout[1].scriptPubKey << OP_RETURN 
+        //    << CStakeParams(pSrcIndex->GetHeight(), tipindex->GetHeight() + 1, pSrcIndex->GetBlockHash(), pk).AsVector();
+        // !! DOWN TO HERE
+
+        txOut1.scriptPubKey << OP_RETURN 
+            << CStakeParams(pSrcIndex->GetHeight(), tipindex->GetHeight() + 1, tipindex->GetBlockHash(), pk).AsVector();
+
+        // !! DISABLE THIS FOR RELEASE: REMOVE THIS TOO
+        //nValue = cheat.vout[0].nValue = stakeSource.vout[voutNum].nValue - txfee;
+        //cheat.nLockTime = 0;
+        //CTransaction cheatConst(cheat);
+        //SignatureData cheatSig;
+        //if (!ProduceSignature(TransactionSignatureCreator(&keystore, &cheatConst, 0, nValue, SIGHASH_ALL), stakeSource.vout[voutNum].scriptPubKey, cheatSig, consensusBranchId))
+        //    fprintf(stderr,"failed to create cheat test signature\n");
+        //else
+        //{
+        //    uint8_t *ptr;
+        //    UpdateTransaction(cheat,0,cheatSig);
+        //    cheatList.Add(CTxHolder(CTransaction(cheat), tipindex->GetHeight() + 1));
+        //}
+        // !! DOWN TO HERE
+    }
+
+    nValue = txNew.vout[0].nValue = stakeSource.vout[voutNum].nValue - txfee;
+
+    txNew.nLockTime = 0;
+    CTransaction txNewConst(txNew);
+    signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, nValue, SIGHASH_ALL), stakeSource.vout[voutNum].scriptPubKey, sigdata, consensusBranchId);
+    if (!signSuccess)
+        fprintf(stderr,"failed to create signature\n");
+    else
+    {
+        uint8_t *ptr;
+        UpdateTransaction(txNew,0,sigdata);
+        ptr = (uint8_t *)&sigdata.scriptSig[0];
+        siglen = sigdata.scriptSig.size();
+        for (int i=0; i<siglen; i++)
+            utxosig[i] = ptr[i];//, fprintf(stderr,"%02x",ptr[i]);
+    }
+    return(siglen);
+}
+
 void CWallet::MarkDirty()
 {
     {
@@ -1655,15 +2018,86 @@ bool CWallet::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx)
     return !unchangedSproutFlag || !unchangedSaplingFlag;
 }
 
+std::pair<bool, bool> CWallet::CheckAuthority(const std::vector<CTxDestination> addrList, int minSigs)
+{
+    std::pair<bool, bool> canSignCanSpend;
+    std::set<CIdentityID> keySet;
+
+    // determine our status of cansign or canspend for this new ID
+    for (auto key : addrList)
+    {
+        CKeyID keyID = CKeyID(GetDestinationID(key));
+        if (HaveKey(keyID))
+        {
+            keySet.insert(keyID);
+        }
+    }
+
+    // if we have enough keys to fully authorize, it is ours
+    if (keySet.size() >= minSigs)
+    {
+        canSignCanSpend.first = true;
+        canSignCanSpend.second = true;
+    }
+    else if (keySet.size())
+    {
+        canSignCanSpend.first = true;
+        canSignCanSpend.second = false;
+    }
+    return canSignCanSpend;
+}
+
+bool CWallet::MarkIdentityDirty(const CIdentityID &idID)
+{
+    bool found = false;
+    // if we already had signing authority, but not spending, enumerate wallet transactions sent to this ID and mark them dirty
+    // for proper balance calculation
+    for (auto &txidAndWtx : mapWallet)
+    {
+        bool dirty = false;
+        txnouttype txType;
+        std::vector<CTxDestination> addresses;
+        int minSigs;
+        for (auto txout : txidAndWtx.second.vout)
+        {
+            if (txout.scriptPubKey.IsPayToCryptoCondition() && ExtractDestinations(txout.scriptPubKey, txType, addresses, minSigs))
+            {
+                for (auto dest : addresses)
+                {
+                    if (GetDestinationID(dest) == idID)
+                    {
+                        dirty = true;
+                        found = true;
+                        break;
+                    }
+                }
+            }
+        }
+        if (dirty)
+        {
+            txidAndWtx.second.MarkDirty();
+        }
+    }
+    return found;
+}
+
 /**
  * Add a transaction to the wallet, or update it.
  * pblock is optional, but should be provided if the transaction is known to be in a block.
  * If fUpdate is true, existing transactions will be updated.
+ *
+ * If pblock is null, this transaction has either recently entered the mempool from the
+ * network, is re-entering the mempool after a block was disconnected, or is exiting the
+ * mempool because it conflicts with another transaction. In all these cases, if there is
+ * an existing wallet transaction, the wallet transaction's Merkle branch data is _not_
+ * updated; instead, the transaction being in the mempool or conflicted is determined on
+ * the fly in CMerkleTx::GetDepthInMainChain().
  */
 bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate)
 {
     {
         AssertLockHeld(cs_wallet);
+        uint256 txHash = tx.GetHash();
         bool fExisted = mapWallet.count(tx.GetHash()) != 0;
         if (fExisted && !fUpdate) return false;
         auto sproutNoteData = FindMySproutNotes(tx);
@@ -1675,9 +2109,480 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
                 return false;
             }
         }
-        if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0)
+
+        bool canSign = false;
+        bool isMine = false;
+
+        for (auto output : tx.vout)
+        {
+            bool canSpend = false;
+            COptCCParams p;
+
+            if (output.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.version >= p.VERSION_V3)
+            {
+                // assert and fix if we find any spot where this wouldn't be held, rather than
+                // creating a deadlock by attempting to take the cs out of order
+                AssertLockHeld(cs_main);
+
+                uint32_t nHeight = 0;
+                if (pblock)
+                {
+                    auto blkIndexIt = mapBlockIndex.find(pblock->GetHash());
+                    if (blkIndexIt != mapBlockIndex.end())
+                    {
+                        nHeight = blkIndexIt->second->GetHeight();
+                    }
+                    else
+                    {
+                        // doesn't seem like this should ever happen
+                        assert(false);
+                    }
+                }
+
+                CIdentityMapValue identity;
+
+                if (p.evalCode == EVAL_IDENTITY_PRIMARY && p.vData.size() && (*(CIdentity *)&identity = CIdentity(p.vData[0])).IsValid())
+                {
+                    identity.txid = tx.GetHash();
+                    CIdentityMapKey idMapKey = CIdentityMapKey(identity.GetID(), 
+                                                               nHeight, 
+                                                               1, 
+                                                               CIdentityMapKey::VALID);
+
+                    std::set<CKeyID> keySet;
+                    CIdentityID idID(identity.GetID());
+                    int blockOrder = 1;
+                    bool doneWithID = false;
+
+                    std::pair<CIdentityMapKey, CIdentityMapValue> idHistory;
+
+                    // if we are deleting, current identity is what it was, idHistory is what it will be, if adding, current is what it will be, idHistory is what it was
+                    std::pair<bool, bool> wasCanSignCanSpend({false, false});
+                    std::pair<bool, bool> canSignCanSpend({false, false});
+
+                    if (GetIdentity(idID, idHistory, nHeight ? nHeight : INT_MAX))
+                    {
+                        if (idHistory.first.blockHeight == nHeight && idHistory.second.txid != identity.txid)
+                        {
+                            std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> thisHeightIdentities;
+                            CIdentityMapKey heightKey(idID, nHeight);
+                            GetIdentity(heightKey, heightKey, thisHeightIdentities);
+
+                            canSignCanSpend = CheckAuthority(identity.primaryAddresses, identity.minSigs);
+                            std::map<uint256, std::pair<CIdentityMapKey, CIdentityMapValue>> firstIDMap({make_pair(identity.txid, 
+                                                                                                            make_pair(CIdentityMapKey(idID, 
+                                                                                                                                      nHeight,
+                                                                                                                                      thisHeightIdentities.size() + 1, 
+                                                                                                                                      canSignCanSpend.first ? CIdentityMapKey::CAN_SIGN : 0 + canSignCanSpend.second ? CIdentityMapKey::CAN_SPEND : 0), 
+                                                                                                                      identity))});
+                            for (auto &foundID : thisHeightIdentities)
+                            {
+                                firstIDMap[foundID.second.txid] = foundID;
+                            }
+
+                            // now we have all the entries of the specified height, including those from before and the new one in the firstIDMap
+                            // the #1 in the block is one that has none of its input txes in the map. the last is not present in any input tx
+                            // to sort, we make a new map, indexed by the one that it spends, then follow the chain
+                            std::map<uint256, std::pair<CIdentityMapKey, CIdentityMapValue>> indexedByPrior;
+                            std::pair<CIdentityMapKey, CIdentityMapValue> firstInBlock;
+
+                            for (auto &idEntry : firstIDMap)
+                            {
+                                uint256 spendsTxId;
+                                CTransaction entryTx;
+                                uint256 blkHash;
+                                if (!myGetTransaction(idEntry.first, entryTx, blkHash))
+                                {
+                                    LogPrint("%s - error: cannot retrieve transaction %s during sort of identity transactions in block, blockchain state may be corrupt and need resynchronization\n", __func__, idEntry.first.GetHex().c_str());
+                                }
+                                else
+                                {
+                                    bool isFirst = true;
+                                    for (auto &input : entryTx.vin)
+                                    {
+                                        auto idMapIt = firstIDMap.find(input.prevout.hash);
+                                        if (idMapIt != firstIDMap.end())
+                                        {
+                                            indexedByPrior[input.prevout.hash] = idEntry.second;
+                                            isFirst = false;
+                                        }
+                                    }
+                                    if (isFirst)
+                                    {
+                                        // this should first be added solo, so #1 should always be set
+                                        if (idEntry.second.first.blockOrder != 1)
+                                        {
+                                            LogPrint("%s - error: unexpected block order in %s\n", __func__, idEntry.first.GetHex().c_str());
+                                        }
+                                        firstInBlock = idEntry.second;
+                                    }
+                                }
+                            }
+
+                            if (!firstInBlock.first.IsValid())
+                            {
+                                LogPrint("%s - error: missing first in block\n", __func__);
+                            }
+                            else
+                            {
+                                // now validate that from 1st to last, we have order correct
+                                std::pair<CIdentityMapKey, CIdentityMapValue> *pCurID;
+                                int i = 1;
+                                for (pCurID = &firstInBlock; pCurID; i++)
+                                {
+                                    if (pCurID->first.blockOrder != i)
+                                    {
+                                        LogPrint("%s - error: incorrect block order in entry %s\n", __func__, pCurID->second.txid.GetHex().c_str());
+                                        printf("%s - error: incorrect block order in entry %s\n", __func__, pCurID->second.txid.GetHex().c_str());
+                                    }
+                                }
+                            }
+                        }
+                        else if (idHistory.first.blockHeight == nHeight)
+                        {
+                            // this has the same txid as an ID already present in the wallet, so it's either a deletion (only confirmed IDs are stored), or duplicate add that we can ignore
+                            if (pblock)
+                            {
+                                doneWithID = true;
+                            }
+                            else
+                            {
+                                idMapKey = idHistory.first;
+                                RemoveIdentity(idHistory.first, idHistory.second.txid);
+                                idHistory = std::pair<CIdentityMapKey, CIdentityMapValue>();
+                                GetIdentity(idID, idHistory, idHistory.first.blockHeight);
+
+                                wasCanSignCanSpend = CheckAuthority(identity.primaryAddresses, identity.minSigs);
+                            }
+                        }
+
+                        if (pblock)
+                        {
+                            if (idHistory.first.flags & idHistory.first.CAN_SPEND)
+                            {
+                                wasCanSignCanSpend.first = true;
+                                wasCanSignCanSpend.second = true;
+                            }
+                            else if ((idHistory.first.flags & idHistory.first.CAN_SIGN))
+                            {
+                                wasCanSignCanSpend.first = true;
+                            }
+                        }
+                    }
+                    else if (!pblock)
+                    {
+                        // not present, nothing to delete
+                        doneWithID = true;
+                    }
+
+                    if (!doneWithID)
+                    {
+                        if (pblock)
+
+                        {
+                            // if we are deleting, current identity is what it was, idHistory is what it will roll back to, if adding, current is what it will be, idHistory is what it is
+                            canSignCanSpend = CheckAuthority(identity.primaryAddresses, identity.minSigs);
+
+                            // if we don't already have this ID, but we can sign with it, store it
+                            if (!idHistory.second.IsValid() && canSignCanSpend.first)
+                            {
+                                // add
+                                CIdentityMapKey idMapKey = CIdentityMapKey(identity.GetID(), 
+                                                                        nHeight, 
+                                                                        blockOrder, 
+                                                                        idHistory.first.VALID |
+                                                                            (canSignCanSpend.first ? idHistory.first.CAN_SIGN : 0) | 
+                                                                            (canSignCanSpend.second ? idHistory.first.CAN_SPEND : 0));
+                                AddIdentity(idMapKey, identity);
+                            }
+                            else if (((canSignCanSpend.first || canSignCanSpend.first != wasCanSignCanSpend.first) && !(idHistory.first.flags & idHistory.first.BLACKLIST)) || (idHistory.first.flags & idHistory.first.MANUAL_HOLD))
+                            {
+                                CIdentityMapKey idMapKey = CIdentityMapKey(identity.GetID(), 
+                                                                        nHeight, 
+                                                                        blockOrder, 
+                                                                        idHistory.first.VALID | 
+                                                                            (idHistory.first.flags & idHistory.first.MANUAL_HOLD) | 
+                                                                            (canSignCanSpend.first ? idHistory.first.CAN_SIGN : 0) | 
+                                                                            (canSignCanSpend.second ? idHistory.first.CAN_SPEND : 0));
+                                UpdateIdentity(idMapKey, identity);
+                            }
+                        }
+                        else
+                        {
+                            canSignCanSpend = idHistory.first.IsValid() ? make_pair(bool(idHistory.first.flags & idHistory.first.CAN_SIGN), bool(idHistory.first.flags & idHistory.first.CAN_SPEND)) : canSignCanSpend;
+                        }
+                        
+
+                        // By default, we will not automatically add identity associated UTXOs unless the identity is accepted to the wallet manually, to prevent spam and DoS attacks
+                        // when identities are manually added to the wallet, UTXOs signable by that ID and spent transactions that were spent during the time that the ID was cansign
+                        // for this wallet are added to it as well. We may want to add a parameter to allow adding new IDs to be an automatic process, but we don't want someone adding or
+                        // removing an address from an identity they control to be able to affect the wallet of others in any meaningful way without action taken by the entity who owns
+                        // the address.
+                        // we assume that at the time we are set to manual hold, that the wallet is brought up to date with can sign or can spend changes at that time, so if manual hold is set
+                        // we are working on a wallet that should be in sync
+                        /*
+                        if ((!idHistory.second.IsValid() && idMapKey.flags & idMapKey.MANUAL_HOLD) || 
+                                (idHistory.second.IsValid() && idHistory.first.flags & idHistory.first.MANUAL_HOLD) && 
+                            (canSignCanSpend.first != wasCanSignCanSpend.first || canSignCanSpend.second != wasCanSignCanSpend.second))
+                        */
+                        if (canSignCanSpend.first != wasCanSignCanSpend.first || canSignCanSpend.second != wasCanSignCanSpend.second)
+                        {
+                            if (canSignCanSpend.first != wasCanSignCanSpend.first)
+                            {
+                                if (canSignCanSpend.first)
+                                {
+                                    // add all UTXOs that are sent to this address to this wallet
+                                    std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> unspentOutputs;
+                                    if (GetAddressUnspent(idID, CScript::P2ID, unspentOutputs))
+                                    {
+                                        COptCCParams newP;
+                                        auto consensus = Params().GetConsensus();
+                                        for (auto &newOut : unspentOutputs)
+                                        {
+                                            // must not be the current tx, not already present, and be a CC output to be correctly send to an identity
+                                            if (txHash != newOut.first.txhash && GetWalletTx(newOut.first.txhash) == nullptr && newOut.second.script.IsPayToCryptoCondition(newP))
+                                            {
+                                                txnouttype newTypeRet;
+                                                std::vector<CTxDestination> newAddressRet;
+                                                int newNRequired;
+                                                bool newCanSign, newCanSpend;
+                                                if (!ExtractDestinations(newOut.second.script, newTypeRet, newAddressRet, newNRequired, this, &newCanSign, &newCanSpend) && newCanSign)
+                                                {
+                                                    continue;
+                                                }
+                                                uint256 blkHash;
+                                                CTransaction newTx;
+                                                if (myGetTransaction(newOut.first.txhash, newTx, blkHash))
+                                                {
+                                                    auto sprNoteData = FindMySproutNotes(newTx);
+                                                    auto sapNoteDataAndAddressesToAdd = FindMySaplingNotes(newTx);
+                                                    auto sapNoteData = sapNoteDataAndAddressesToAdd.first;
+                                                    auto addrsToAdd = sapNoteDataAndAddressesToAdd.second;
+                                                    for (const auto &addressToAdd : addrsToAdd)
+                                                    {
+                                                        AddSaplingIncomingViewingKey(addressToAdd.second, addressToAdd.first);
+                                                    }
+
+                                                    CWalletTx wtx(this, newTx);
+
+                                                    if (sprNoteData.size() > 0) {
+                                                        wtx.SetSproutNoteData(sprNoteData);
+                                                    }
+
+                                                    if (sapNoteData.size() > 0) {
+                                                        wtx.SetSaplingNoteData(sapNoteData);
+                                                    }
+
+                                                    // Get merkle branch if transaction was found in a block
+                                                    CBlock block;
+                                                    auto blkIndexIt = mapBlockIndex.find(blkHash);
+                                                    if (!blkHash.IsNull() && blkIndexIt != mapBlockIndex.end() && chainActive.Contains(blkIndexIt->second))
+                                                    {
+                                                        // if it's supposed to be in a block, but can't be loaded, don't add without merkle
+                                                        if (!ReadBlockFromDisk(block, blkIndexIt->second, consensus))
+                                                        {
+                                                            continue;
+                                                        }
+                                                        wtx.SetMerkleBranch(block);
+                                                    }
+
+                                                    // Do not flush the wallet here for performance reasons
+                                                    // this is safe, as in case of a crash, we rescan the necessary blocks on startup through our SetBestChain-mechanism
+                                                    CWalletDB walletdb(strWalletFile, "r+", false);
+
+                                                    AddToWallet(wtx, false, &walletdb);
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                                else
+                                {
+                                    // we have gone from canSign to no control over this ID, either by deletion of tx or removal from signers. this will take effect retroactively on deletion and next block on addition
+                                    // 1. remove all transactions that have UTXOs sent to this ID and are no longer can sign or can spend for us from the wallet
+                                    // 2. if deletion, remove all transactions since the last idHistory that are in the wallet due to this ID
+                                    // 3. remove all IDs from the wallet that are found in those removed transactions, are neither canSpend nor canSign, and are neither on manual hold nor present on any remaining transactions
+
+                                    std::set<CIdentityID> idsToCheck = std::set<CIdentityID>({idID});
+
+                                    // first and last blocks to consider when deleting spent transactions from the wallet
+                                    uint32_t deleteSpentFrom, deleteSpentTo = 0;
+                                    
+                                    if (!pblock)
+                                    {
+                                        deleteSpentFrom = idHistory.first.blockHeight;
+                                    }
+                                    else
+                                    {
+                                        deleteSpentFrom = idMapKey.blockHeight;
+                                    }
+
+                                    std::pair<CIdentityMapKey, CIdentityMapValue> nextIdentity;
+
+                                    // in case we have another ID in front of us, limit those that we will remove from the wallet
+                                    if (GetFirstIdentity(idID, nextIdentity, idMapKey.blockHeight + 1))
+                                    {
+                                        deleteSpentTo = nextIdentity.first.blockHeight;
+                                    }
+
+                                    for (auto &txidAndWtx : mapWallet)
+                                    {
+                                        // first check if it is within height range for deletion, and if not, continue
+                                        if (deleteSpentFrom || deleteSpentTo)
+                                        {
+                                            const CBlockIndex *pIndex;
+                                            if (txidAndWtx.second.GetDepthInMainChain(pIndex))
+                                            {
+                                                uint32_t wtxHeight = pIndex->GetHeight();
+                                                if ((deleteSpentFrom || wtxHeight <= deleteSpentFrom) || (deleteSpentTo && wtxHeight > deleteSpentTo))
+                                                {
+                                                    continue;
+                                                }
+                                            }
+                                        }
+
+                                        // if the tx is from this wallet, we will not erase it
+                                        if (IsFromMe(txidAndWtx.second))
+                                        {
+                                            continue;
+                                        }
+
+                                        txnouttype txType;
+                                        std::vector<CTxDestination> addresses;
+                                        int minSigs;
+                                        bool eraseTx = true;
+                                        std::vector<CIdentityID> oneTxIDs;
+                                        int i = 0;
+                                        uint256 hashTx = txidAndWtx.second.GetHash();
+
+                                        // look for a reason not to delete this tx
+                                        for (auto txout : txidAndWtx.second.vout)
+                                        {
+                                            // we only want to remove UTXOs that are sent to this ID, used to be ours, and are no longer cansign
+                                            if (!txout.scriptPubKey.IsPayToCryptoCondition() || IsSpent(hashTx, i))
+                                            {
+                                                // if this is ours, we will not erase the tx
+                                                if (IsMine(txout))
+                                                {
+                                                    eraseTx = false;
+                                                    break;
+                                                }
+                                                continue;
+                                            }
+                                            bool canSignOut = false;
+                                            bool canSpendOut = false;
+
+                                            if (ExtractDestinations(txout.scriptPubKey, txType, addresses, minSigs, this, &canSignOut, &canSpendOut, deleteSpentTo ? deleteSpentTo : INT_MAX))
+                                            {
+                                                if (canSignOut || canSpendOut)
+                                                {
+                                                    // we should keep this transaction anyhow, check next
+                                                    eraseTx = false;
+                                                    break;
+                                                }
+                                                
+                                                for (auto &dest : addresses)
+                                                {
+                                                    if (dest.which() == COptCCParams::ADDRTYPE_ID)
+                                                    {
+                                                        oneTxIDs.push_back(GetDestinationID(dest));
+                                                    }
+                                                }
+                                            }
+
+                                            i++;
+                                        }
+                                        if (eraseTx)
+                                        {
+                                            EraseFromWallet(txidAndWtx.first);
+
+                                            for (auto &checkID : oneTxIDs)
+                                            {
+                                                idsToCheck.insert(checkID);
+                                            }
+                                        }
+                                    }
+
+                                    // now, we've deleted all transactions that were only in the wallet due to our ability to sign with the ID just removed
+                                    // loop through all transactions and remove all IDs found in the remaining transactions from our idsToCheck set after we 
+                                    // have gone through all wallet transactions, we can delete all IDs remaining in the idsToCheck set
+                                    // that are not on manual hold
+                                    for (auto &txidAndWtx : mapWallet)
+                                    {
+                                        for (auto txout : txidAndWtx.second.vout)
+                                        {
+                                            if (!txout.scriptPubKey.IsPayToCryptoCondition())
+                                            {
+                                                continue;
+                                            }
+                                            bool canSignOut = false;
+                                            bool canSpendOut = false;
+                                            txnouttype txType;
+                                            std::vector<CTxDestination> addresses;
+                                            int minSigs;
+                                            if (ExtractDestinations(txout.scriptPubKey, txType, addresses, minSigs, this, &canSignOut, &canSpendOut))
+                                            {
+                                                if (canSignOut || canSpendOut)
+                                                {
+                                                    for (auto &dest : addresses)
+                                                    {
+                                                        if (dest.which() == COptCCParams::ADDRTYPE_ID)
+                                                        {
+                                                            idsToCheck.erase(GetDestinationID(dest));
+                                                            if (!idsToCheck.size())
+                                                            {
+                                                                break;
+                                                            }
+                                                        }
+                                                    }
+                                                }
+                                            }
+                                            if (!idsToCheck.size())
+                                            {
+                                                break;
+                                            }
+                                        }
+                                        if (!idsToCheck.size())
+                                        {
+                                            break;
+                                        }
+                                    }
+                                    // delete all remaining IDs that are not held for manual hold
+                                    for (auto &idToRemove : idsToCheck)
+                                    {
+                                        std::pair<CIdentityMapKey, CIdentityMapValue> identityToRemove;
+
+                                        // if not cansign or canspend, no transactions we care about relating to it and no manual hold, delete the ID from the wallet
+                                        if (GetIdentity(idToRemove, identityToRemove) && 
+                                            !((identityToRemove.first.flags & (identityToRemove.first.CAN_SIGN + identityToRemove.first.CAN_SPEND)) || identityToRemove.first.flags & identityToRemove.first.MANUAL_HOLD))
+                                        {
+                                            RemoveIdentity(CIdentityMapKey(idToRemove));
+                                        }
+                                    }
+                                }
+                            }
+                            else if (canSignCanSpend.second != wasCanSignCanSpend.second)
+                            {
+                                if (!canSignCanSpend.second)
+                                {
+                                    // mark all transactions dirty to recalculate numbers
+                                    for (auto &txidAndWtx : mapWallet)
+                                    {
+                                        txidAndWtx.second.MarkDirty();
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        isMine = IsMine(tx);
+
+        if (fExisted || isMine || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0)
         {
-            CWalletTx wtx(this,tx);
+            CWalletTx wtx(this, tx);
 
             if (sproutNoteData.size() > 0) {
                 wtx.SetSproutNoteData(sproutNoteData);
@@ -1750,6 +2655,17 @@ void CWallet::EraseFromWallet(const uint256 &hash)
     return;
 }
 
+void CWallet::RescanWallet()
+{
+    if (needsRescan)
+    {
+        CBlockIndex *start = chainActive.Height() > 0 ? chainActive[1] : NULL;
+        if (start)
+            ScanForWalletTransactions(start, true);
+        needsRescan = false;
+    }
+}
+
 
 /**
  * Returns a nullifier if the SpendingKey is available
@@ -1962,7 +2878,7 @@ isminetype CWallet::IsMine(const CTxIn &txin) const
         {
             const CWalletTx& prev = (*mi).second;
             if (txin.prevout.n < prev.vout.size())
-                return IsMine(prev.vout[txin.prevout.n]);
+                return (::IsMine(*this, prev.vout[txin.prevout.n].scriptPubKey));
         }
     }
     return ISMINE_NO;
@@ -1977,8 +2893,8 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
         {
             const CWalletTx& prev = (*mi).second;
             if (txin.prevout.n < prev.vout.size())
-                if (IsMine(prev.vout[txin.prevout.n]) & filter)
-                    return prev.vout[txin.prevout.n].nValue;
+                if (::IsMine(*this, prev.vout[txin.prevout.n].scriptPubKey) & filter)
+                    return prev.vout[txin.prevout.n].nValue; // komodo_interest?
         }
     }
     return 0;
@@ -2025,20 +2941,183 @@ CAmount CWallet::GetChange(const CTxOut& txout) const
     return (IsChange(txout) ? txout.nValue : 0);
 }
 
-bool CWallet::IsMine(const CTransaction& tx) const
+typedef vector<unsigned char> valtype;
+unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore);
+
+bool CWallet::IsMine(const CTransaction& tx)
 {
-    BOOST_FOREACH(const CTxOut& txout, tx.vout)
-        if (IsMine(txout))
+    for (int i = 0; i < tx.vout.size(); i++)
+    {
+        if (IsMine(tx, i))
             return true;
+    }
     return false;
 }
 
-bool CWallet::IsFromMe(const CTransaction& tx) const
+// special case handling for non-standard/Verus OP_RETURN script outputs, which need the transaction
+// to determine ownership
+isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum)
 {
-    if (GetDebit(tx, ISMINE_ALL) > 0) {
-        return true;
+    vector<valtype> vSolutions;
+    txnouttype whichType;
+    CScript scriptPubKey = tx.vout[voutNum].scriptPubKey;
+
+    if (scriptPubKey.IsCheckLockTimeVerify())
+    {
+        uint8_t pushOp = scriptPubKey[0];
+        uint32_t scriptStart = pushOp + 3;
+
+        // continue with post CLTV script
+        scriptPubKey = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end());
     }
-    for (const JSDescription& jsdesc : tx.vJoinSplit) {
+
+    COptCCParams p;
+    if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
+    {
+        std::vector<CTxDestination> dests;
+        int minSigs;
+        bool canSign = false;
+        bool canSpend = false;
+
+        if (ExtractDestinations(scriptPubKey, whichType, dests, minSigs, this, &canSign, &canSpend))
+        {
+            if (canSpend)
+            {
+                return ISMINE_SPENDABLE;
+            }
+            else if (canSign)
+            {
+                return ISMINE_WATCH_ONLY;
+            }
+            else
+            {
+                return ISMINE_NO;
+            }
+        }
+        else
+        {
+            return ISMINE_NO;
+        }
+    }
+    else if (!Solver(scriptPubKey, whichType, vSolutions))
+    {
+        if (this->HaveWatchOnly(scriptPubKey))
+            return ISMINE_WATCH_ONLY;
+        return ISMINE_NO;
+    }
+
+    CKeyID keyID;
+    CScriptID scriptID;
+    CScriptExt subscript;
+    int voutNext = voutNum + 1;
+
+    switch (whichType)
+    {
+        case TX_NONSTANDARD:
+        case TX_NULL_DATA:
+            break;
+
+        case TX_CRYPTOCONDITION:
+            // for now, default is that the first value returned will be the target address, subsequent values will be
+            // pubkeys. if we have the first in our wallet, we consider it spendable for now
+            if (vSolutions[0].size() == 33)
+            {
+                keyID = CPubKey(vSolutions[0]).GetID();
+            }
+            else if (vSolutions[0].size() == 20)
+            {
+                keyID = CKeyID(uint160(vSolutions[0]));
+            }
+            if (!keyID.IsNull() && HaveKey(keyID))
+            {
+                return ISMINE_SPENDABLE;
+            }
+            break;
+
+        case TX_PUBKEY:
+            keyID = CPubKey(vSolutions[0]).GetID();
+            if (this->HaveKey(keyID))
+                return ISMINE_SPENDABLE;
+            break;
+
+        case TX_PUBKEYHASH:
+            keyID = CKeyID(uint160(vSolutions[0]));
+            if (this->HaveKey(keyID))
+                return ISMINE_SPENDABLE;
+            break;
+
+        case TX_SCRIPTHASH:
+            scriptID = CScriptID(uint160(vSolutions[0]));
+            if (this->GetCScript(scriptID, subscript)) 
+            {
+                // if this is a CLTV, handle it differently
+                if (subscript.IsCheckLockTimeVerify())
+                {
+                    return (::IsMine(*this, subscript));
+                }
+                else
+                {
+                    isminetype ret = ::IsMine(*this, subscript);
+                    if (ret == ISMINE_SPENDABLE)
+                        return ret;
+                }
+            }
+            else if (tx.vout.size() > (voutNum + 1) &&
+                tx.vout.back().scriptPubKey.size() > 7 &&
+                tx.vout.back().scriptPubKey[0] == OP_RETURN)
+            {
+                // get the opret script from next vout, verify that the front is CLTV and hash matches
+                // if so, remove it and use the solver
+                opcodetype op;
+                std::vector<uint8_t> opretData;
+                CScript::const_iterator it = tx.vout.back().scriptPubKey.begin() + 1;
+                if (tx.vout.back().scriptPubKey.GetOp2(it, op, &opretData))
+                {
+                    if (opretData.size() > 0 && opretData[0] == OPRETTYPE_TIMELOCK)
+                    {
+                        CScript opretScript = CScript(opretData.begin() + 1, opretData.end());
+
+                        if (CScriptID(opretScript) == scriptID &&
+                            opretScript.IsCheckLockTimeVerify())
+                        {
+                            // if we find that this is ours, we need to add this script to the wallet,
+                            // and we can then recognize this transaction
+                            isminetype t = ::IsMine(*this, opretScript);
+                            if (t != ISMINE_NO)
+                            {
+                                this->AddCScript(opretScript);
+                            }
+                            return t;
+                        }
+                    }
+                }
+            }
+            break;
+
+        case TX_MULTISIG:
+            // Only consider transactions "mine" if we own ALL the
+            // keys involved. Multi-signature transactions that are
+            // partially owned (somebody else has a key that can spend
+            // them) enable spend-out-from-under-you attacks, especially
+            // in shared-wallet situations.
+            vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
+            if (HaveKeys(keys, *this) == keys.size())
+                return ISMINE_SPENDABLE;
+            break;
+    }
+
+    if (this->HaveWatchOnly(scriptPubKey))
+        return ISMINE_WATCH_ONLY;
+
+    return ISMINE_NO;
+}
+
+bool CWallet::IsFromMe(const CTransaction& tx) const
+{
+    if (GetDebit(tx, ISMINE_ALL) > 0) {
+        return true;
+    }
+    for (const JSDescription& jsdesc : tx.vJoinSplit) {
         for (const uint256& nullifier : jsdesc.nullifiers) {
             if (IsSproutNullifierFromMe(nullifier)) {
                 return true;
@@ -2065,14 +3144,34 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co
     return nDebit;
 }
 
+CAmount CWallet::GetCredit(const CTransaction& tx, int32_t voutNum, const isminefilter& filter) const
+{
+    if (voutNum >= tx.vout.size() || !MoneyRange(tx.vout[voutNum].nValue))
+        throw std::runtime_error("CWallet::GetCredit(): value out of range");
+    return ((IsMine(tx.vout[voutNum]) & filter) ? tx.vout[voutNum].nValue : 0);
+}
+
+CAmount CWallet::GetReserveCredit(const CTransaction& tx, int32_t voutNum, const isminefilter& filter) const
+{
+    return ((IsMine(tx.vout[voutNum]) & filter) ? tx.vout[voutNum].ReserveOutValue() : 0);
+}
+
+CAmount CWallet::GetReserveCredit(const CTransaction& tx, const isminefilter& filter) const
+{
+    CAmount nCredit = 0;
+    for (int i = 0; i < tx.vout.size(); i++)
+    {
+        nCredit += GetReserveCredit(tx, i, filter);
+    }
+    return nCredit;
+}
+
 CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const
 {
     CAmount nCredit = 0;
-    BOOST_FOREACH(const CTxOut& txout, tx.vout)
+    for (int i = 0; i < tx.vout.size(); i++)
     {
-        nCredit += GetCredit(txout, filter);
-        if (!MoneyRange(nCredit))
-            throw std::runtime_error("CWallet::GetCredit(): value out of range");
+        nCredit += GetCredit(tx, i, filter);
     }
     return nCredit;
 }
@@ -2344,7 +3443,7 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
         if (nDebit > 0)
         {
             // Don't report 'change' txouts
-            if (pwallet->IsChange(txout))
+            if (!(filter & ISMINE_CHANGE) && pwallet->IsChange(txout))
                 continue;
         }
         else if (!(fIsMine & filter))
@@ -2354,8 +3453,7 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
         CTxDestination address;
         if (!ExtractDestination(txout.scriptPubKey, address))
         {
-            LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
-                     this->GetHash().ToString());
+            //LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",this->GetHash().ToString()); complains on the opreturns
             address = CNoDestination();
         }
 
@@ -2423,7 +3521,7 @@ void CWallet::WitnessNoteCommitment(std::vector<uint256> commitments,
 
     while (pindex) {
         CBlock block;
-        ReadBlockFromDisk(block, pindex, Params().GetConsensus());
+        ReadBlockFromDisk(block, pindex, Params().GetConsensus(), 1);
 
         BOOST_FOREACH(const CTransaction& tx, block.vtx)
         {
@@ -2495,10 +3593,10 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
 
         ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
         double dProgressStart = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false);
-        double dProgressTip = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip(), false);
+        double dProgressTip = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.LastTip(), false);
         while (pindex)
         {
-            if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)
+            if (pindex->GetHeight() % 100 == 0 && dProgressTip - dProgressStart > 0.0)
                 ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
 
             CBlock block;
@@ -2517,7 +3615,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
             // state on the path to the tip of our chain
             assert(pcoinsTip->GetSproutAnchorAt(pindex->hashSproutAnchor, sproutTree));
             if (pindex->pprev) {
-                if (Params().GetConsensus().NetworkUpgradeActive(pindex->pprev->nHeight,  Consensus::UPGRADE_SAPLING)) {
+                if (Params().GetConsensus().NetworkUpgradeActive(pindex->pprev->GetHeight(),  Consensus::UPGRADE_SAPLING)) {
                     assert(pcoinsTip->GetSaplingAnchorAt(pindex->pprev->hashFinalSaplingRoot, saplingTree));
                 }
             }
@@ -2527,7 +3625,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
             pindex = chainActive.Next(pindex);
             if (GetTime() >= nNow + 60) {
                 nNow = GetTime();
-                LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex));
+                LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->GetHeight(), Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex));
             }
         }
 
@@ -2570,22 +3668,50 @@ void CWallet::ReacceptWalletTransactions()
         }
     }
 
+    std::vector<uint256> vwtxh;
+
     // Try to add wallet transactions to memory pool
     BOOST_FOREACH(PAIRTYPE(const int64_t, CWalletTx*)& item, mapSorted)
     {
         CWalletTx& wtx = *(item.second);
 
         LOCK(mempool.cs);
-        wtx.AcceptToMemoryPool(false);
+        CValidationState state;
+        // attempt to add them, but don't set any DOS level
+        if (!::AcceptToMemoryPool(mempool, state, wtx, false, NULL, true, 0))
+        {
+            int nDoS;
+            bool invalid = state.IsInvalid(nDoS);
+
+            // log rejection and deletion
+            // printf("ERROR reaccepting wallet transaction %s to mempool, reason: %s, DoS: %d\n", wtx.GetHash().ToString().c_str(), state.GetRejectReason().c_str(), nDoS);
+
+            if (!wtx.IsCoinBase() && invalid && nDoS > 0)
+            {
+                LogPrintf("erasing transaction %s\n", wtx.GetHash().GetHex().c_str());
+                vwtxh.push_back(wtx.GetHash());
+            }
+        }
+    }
+    for (auto hash : vwtxh)
+    {
+        EraseFromWallet(hash);
     }
 }
 
 bool CWalletTx::RelayWalletTransaction()
 {
+    if ( pwallet == 0 )
+    {
+        fprintf(stderr,"unexpected null pwallet in RelayWalletTransaction\n");
+        return(false);
+    }
     assert(pwallet->GetBroadcastTransactions());
     if (!IsCoinBase())
     {
-        if (GetDepthInMainChain() == 0) {
+        if (GetDepthInMainChain() == 0)
+        {
+            // if tx is expired, dont relay
             LogPrintf("Relaying wtx %s\n", GetHash().ToString());
             RelayTransaction((CTransaction)*this);
             return true;
@@ -2637,6 +3763,37 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
     return debit;
 }
 
+CAmount CWalletTx::GetReserveDebit(const isminefilter& filter) const
+{
+    if (vin.empty())
+        return 0;
+
+    CAmount debit = 0;
+    if(filter & ISMINE_SPENDABLE)
+    {
+        if (fReserveDebitCached)
+            debit += nReserveDebitCached;
+        else
+        {
+            nReserveDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE);
+            fReserveDebitCached = true;
+            debit += nReserveDebitCached;
+        }
+    }
+    if(filter & ISMINE_WATCH_ONLY)
+    {
+        if(fWatchReserveDebitCached)
+            debit += nWatchReserveDebitCached;
+        else
+        {
+            nWatchReserveDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY);
+            fWatchReserveDebitCached = true;
+            debit += nWatchReserveDebitCached;
+        }
+    }
+    return debit;
+}
+
 CAmount CWalletTx::GetCredit(const isminefilter& filter) const
 {
     // Must wait until coinbase is safely deep enough in the chain before valuing it
@@ -2670,6 +3827,59 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
     return credit;
 }
 
+bool CWalletTx::HasMatureCoins() const
+{
+    // Must wait until coinbase is safely deep enough in the chain before valuing it
+    if (!(IsCoinBase() && GetBlocksToMaturity() > 0))
+    {
+        return true;
+    }
+    else
+    {
+        for (auto oneout : vout)
+        {
+            if (oneout.scriptPubKey.IsInstantSpend())
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+}
+
+CAmount CWalletTx::GetReserveCredit(const isminefilter& filter) const
+{
+    // Must wait until coinbase is safely deep enough in the chain before valuing it
+    if (IsCoinBase() && GetBlocksToMaturity() > 0)
+        return 0;
+
+    int64_t credit = 0;
+    if (filter & ISMINE_SPENDABLE)
+    {
+        // GetBalance can assume transactions in mapWallet won't change
+        if (fReserveCreditCached)
+            credit += nReserveCreditCached;
+        else
+        {
+            nReserveCreditCached = pwallet->GetReserveCredit(*this, ISMINE_SPENDABLE);
+            fReserveCreditCached = true;
+            credit += nReserveCreditCached;
+        }
+    }
+    if (filter & ISMINE_WATCH_ONLY)
+    {
+        if (fWatchReserveCreditCached)
+            credit += nWatchReserveCreditCached;
+        else
+        {
+            nWatchReserveCreditCached = pwallet->GetReserveCredit(*this, ISMINE_WATCH_ONLY);
+            fWatchReserveCreditCached = true;
+            credit += nWatchReserveCreditCached;
+        }
+    }
+    return credit;
+}
+
 CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
 {
     if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
@@ -2684,6 +3894,20 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
     return 0;
 }
 
+CAmount CWalletTx::GetImmatureReserveCredit(bool fUseCache) const
+{
+    if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
+    {
+        if (fUseCache && fImmatureReserveCreditCached)
+            return nImmatureReserveCreditCached;
+        nImmatureReserveCreditCached = pwallet->GetReserveCredit(*this, ISMINE_SPENDABLE);
+        fImmatureReserveCreditCached = true;
+        return nImmatureReserveCreditCached;
+    }
+
+    return 0;
+}
+
 CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
 {
     if (pwallet == 0)
@@ -2702,8 +3926,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
     {
         if (!pwallet->IsSpent(hashTx, i))
         {
-            const CTxOut &txout = vout[i];
-            nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
+            nCredit += pwallet->GetCredit(*this, i, ISMINE_SPENDABLE);
             if (!MoneyRange(nCredit))
                 throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
         }
@@ -2714,6 +3937,33 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
     return nCredit;
 }
 
+CAmount CWalletTx::GetAvailableReserveCredit(bool fUseCache) const
+{
+    if (pwallet == 0)
+        return 0;
+
+    // Must wait until coinbase is safely deep enough in the chain before valuing it
+    if (IsCoinBase() && GetBlocksToMaturity() > 0)
+        return 0;
+
+    if (fUseCache && fAvailableReserveCreditCached)
+        return nAvailableReserveCreditCached;
+
+    CAmount nCredit = 0;
+    uint256 hashTx = GetHash();
+    for (unsigned int i = 0; i < vout.size(); i++)
+    {
+        if (!pwallet->IsSpent(hashTx, i))
+        {
+            nCredit += pwallet->GetReserveCredit(*this, i, ISMINE_SPENDABLE);
+        }
+    }
+
+    nAvailableReserveCreditCached = nCredit;
+    fAvailableReserveCreditCached = true;
+    return nCredit;
+}
+
 CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const
 {
     if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
@@ -2728,6 +3978,20 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const
     return 0;
 }
 
+CAmount CWalletTx::GetImmatureWatchOnlyReserveCredit(const bool& fUseCache) const
+{
+    if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
+    {
+        if (fUseCache && fImmatureWatchReserveCreditCached)
+            return nImmatureWatchReserveCreditCached;
+        nImmatureWatchReserveCreditCached = pwallet->GetReserveCredit(*this, ISMINE_WATCH_ONLY);
+        fImmatureWatchReserveCreditCached = true;
+        return nImmatureWatchReserveCreditCached;
+    }
+
+    return 0;
+}
+
 CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
 {
     if (pwallet == 0)
@@ -2745,8 +4009,7 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
     {
         if (!pwallet->IsSpent(GetHash(), i))
         {
-            const CTxOut &txout = vout[i];
-            nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
+            nCredit += pwallet->GetCredit(*this, i, ISMINE_WATCH_ONLY);
             if (!MoneyRange(nCredit))
                 throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
         }
@@ -2757,6 +4020,34 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
     return nCredit;
 }
 
+CAmount CWalletTx::GetAvailableWatchOnlyReserveCredit(const bool& fUseCache) const
+{
+    if (pwallet == 0)
+        return 0;
+
+    // Must wait until coinbase is safely deep enough in the chain before valuing it
+    if (IsCoinBase() && GetBlocksToMaturity() > 0)
+        return 0;
+
+    if (fUseCache && fAvailableWatchReserveCreditCached)
+        return nAvailableWatchReserveCreditCached;
+
+    CAmount nCredit = 0;
+    for (unsigned int i = 0; i < vout.size(); i++)
+    {
+        if (!pwallet->IsSpent(GetHash(), i))
+        {
+            nCredit += pwallet->GetReserveCredit(*this, i, ISMINE_WATCH_ONLY);
+            if (!MoneyRange(nCredit))
+                throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
+        }
+    }
+
+    nAvailableWatchReserveCreditCached = nCredit;
+    fAvailableWatchReserveCreditCached = true;
+    return nCredit;
+}
+
 CAmount CWalletTx::GetChange() const
 {
     if (fChangeCached)
@@ -2800,19 +4091,34 @@ std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime)
     LOCK(cs_wallet);
     // Sort them in chronological order
     multimap<unsigned int, CWalletTx*> mapSorted;
+    uint32_t now = (uint32_t)time(NULL);
+    std::vector<uint256> vwtxh;
     BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
     {
         CWalletTx& wtx = item.second;
         // Don't rebroadcast if newer than nTime:
         if (wtx.nTimeReceived > nTime)
             continue;
+        if ( (wtx.nLockTime >= LOCKTIME_THRESHOLD && wtx.nLockTime < now-KOMODO_MAXMEMPOOLTIME) || wtx.hashBlock.IsNull() )
+        {
+            //LogPrintf("skip Relaying wtx %s nLockTime %u vs now.%u\n", wtx.GetHash().ToString(),(uint32_t)wtx.nLockTime,now);
+            //vwtxh.push_back(wtx.GetHash());
+            continue;
+        }
         mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx));
     }
     BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)
     {
-        CWalletTx& wtx = *item.second;
-        if (wtx.RelayWalletTransaction())
-            result.push_back(wtx.GetHash());
+        if ( item.second != 0 )
+        {
+            CWalletTx &wtx = *item.second;
+            if (wtx.RelayWalletTransaction())
+                result.push_back(wtx.GetHash());
+        }
+    }
+    for (auto hash : vwtxh)
+    {
+        EraseFromWallets(hash);
     }
     return result;
 }
@@ -2867,7 +4173,7 @@ CAmount CWallet::GetBalance() const
     return nTotal;
 }
 
-CAmount CWallet::GetUnconfirmedBalance() const
+CAmount CWallet::GetReserveBalance() const
 {
     CAmount nTotal = 0;
     {
@@ -2875,14 +4181,15 @@ CAmount CWallet::GetUnconfirmedBalance() const
         for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
         {
             const CWalletTx* pcoin = &(*it).second;
-            if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
-                nTotal += pcoin->GetAvailableCredit();
+            if (pcoin->IsTrusted())
+                nTotal += pcoin->GetAvailableReserveCredit();
         }
     }
+
     return nTotal;
 }
 
-CAmount CWallet::GetImmatureBalance() const
+CAmount CWallet::GetUnconfirmedBalance() const
 {
     CAmount nTotal = 0;
     {
@@ -2890,13 +4197,14 @@ CAmount CWallet::GetImmatureBalance() const
         for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
         {
             const CWalletTx* pcoin = &(*it).second;
-            nTotal += pcoin->GetImmatureCredit();
+            if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
+                nTotal += pcoin->GetAvailableCredit();
         }
     }
     return nTotal;
 }
 
-CAmount CWallet::GetWatchOnlyBalance() const
+CAmount CWallet::GetUnconfirmedReserveBalance() const
 {
     CAmount nTotal = 0;
     {
@@ -2904,15 +4212,14 @@ CAmount CWallet::GetWatchOnlyBalance() const
         for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
         {
             const CWalletTx* pcoin = &(*it).second;
-            if (pcoin->IsTrusted())
-                nTotal += pcoin->GetAvailableWatchOnlyCredit();
+            if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
+                nTotal += pcoin->GetAvailableReserveCredit();
         }
     }
-
     return nTotal;
 }
 
-CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
+CAmount CWallet::GetImmatureBalance() const
 {
     CAmount nTotal = 0;
     {
@@ -2920,14 +4227,13 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
         for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
         {
             const CWalletTx* pcoin = &(*it).second;
-            if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
-                nTotal += pcoin->GetAvailableWatchOnlyCredit();
+            nTotal += pcoin->GetImmatureCredit();
         }
     }
     return nTotal;
 }
 
-CAmount CWallet::GetImmatureWatchOnlyBalance() const
+CAmount CWallet::GetImmatureReserveBalance() const
 {
     CAmount nTotal = 0;
     {
@@ -2935,28 +4241,122 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
         for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
         {
             const CWalletTx* pcoin = &(*it).second;
-            nTotal += pcoin->GetImmatureWatchOnlyCredit();
+            nTotal += pcoin->GetImmatureReserveCredit();
         }
     }
     return nTotal;
 }
 
-/**
- * populate vCoins with vector of available COutputs.
- */
-void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, bool fIncludeCoinBase) const
+CAmount CWallet::GetWatchOnlyBalance() const
 {
-    vCoins.clear();
-
+    CAmount nTotal = 0;
     {
         LOCK2(cs_main, cs_wallet);
         for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
         {
-            const uint256& wtxid = it->first;
             const CWalletTx* pcoin = &(*it).second;
+            if (pcoin->IsTrusted())
+                nTotal += pcoin->GetAvailableWatchOnlyCredit();
+        }
+    }
 
-            if (!CheckFinalTx(*pcoin))
-                continue;
+    return nTotal;
+}
+
+CAmount CWallet::GetWatchOnlyReserveBalance() const
+{
+    CAmount nTotal = 0;
+    {
+        LOCK2(cs_main, cs_wallet);
+        for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        {
+            const CWalletTx* pcoin = &(*it).second;
+            if (pcoin->IsTrusted())
+                nTotal += pcoin->GetAvailableWatchOnlyReserveCredit();
+        }
+    }
+
+    return nTotal;
+}
+
+CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
+{
+    CAmount nTotal = 0;
+    {
+        LOCK2(cs_main, cs_wallet);
+        for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        {
+            const CWalletTx* pcoin = &(*it).second;
+            if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
+                nTotal += pcoin->GetAvailableWatchOnlyCredit();
+        }
+    }
+    return nTotal;
+}
+
+CAmount CWallet::GetUnconfirmedWatchOnlyReserveBalance() const
+{
+    CAmount nTotal = 0;
+    {
+        LOCK2(cs_main, cs_wallet);
+        for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        {
+            const CWalletTx* pcoin = &(*it).second;
+            if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
+                nTotal += pcoin->GetAvailableWatchOnlyReserveCredit();
+        }
+    }
+    return nTotal;
+}
+
+CAmount CWallet::GetImmatureWatchOnlyBalance() const
+{
+    CAmount nTotal = 0;
+    {
+        LOCK2(cs_main, cs_wallet);
+        for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        {
+            const CWalletTx* pcoin = &(*it).second;
+            nTotal += pcoin->GetImmatureWatchOnlyCredit();
+        }
+    }
+    return nTotal;
+}
+
+CAmount CWallet::GetImmatureWatchOnlyReserveBalance() const
+{
+    CAmount nTotal = 0;
+    {
+        LOCK2(cs_main, cs_wallet);
+        for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        {
+            const CWalletTx* pcoin = &(*it).second;
+            nTotal += pcoin->GetImmatureWatchOnlyReserveCredit();
+        }
+    }
+    return nTotal;
+}
+
+/**
+ * populate vCoins with vector of available COutputs.
+ */
+uint64_t komodo_interestnew(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime);
+uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight);
+
+void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, bool fIncludeCoinBase) const
+{
+    uint64_t interest,*ptr;
+    vCoins.clear();
+
+    {
+        LOCK2(cs_main, cs_wallet);
+        for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        {
+            const uint256& wtxid = it->first;
+            const CWalletTx* pcoin = &(*it).second;
+
+            if (!CheckFinalTx(*pcoin))
+                continue;
 
             if (fOnlyConfirmed && !pcoin->IsTrusted())
                 continue;
@@ -2970,20 +4370,109 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
             int nDepth = pcoin->GetDepthInMainChain();
             if (nDepth < 0)
                 continue;
-
-            for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
+            for (int i = 0; i < pcoin->vout.size(); i++)
+            {
                 isminetype mine = IsMine(pcoin->vout[i]);
                 if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
                     !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) &&
-                    (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected((*it).first, i)))
+                    (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
+                {
+                    if ( KOMODO_EXCHANGEWALLET == 0 )
+                    {
+                        uint32_t locktime; int32_t txheight; CBlockIndex *tipindex;
+                        if ( ASSETCHAINS_SYMBOL[0] == 0 && chainActive.LastTip() != 0 && chainActive.LastTip()->GetHeight() >= 60000 )
+                        {
+                            if ( pcoin->vout[i].nValue >= 10*COIN )
+                            {
+                                if ( (tipindex= chainActive.LastTip()) != 0 )
+                                {
+                                    komodo_accrued_interest(&txheight,&locktime,wtxid,i,0,pcoin->vout[i].nValue,(int32_t)tipindex->GetHeight());
+                                    interest = komodo_interestnew(txheight,pcoin->vout[i].nValue,locktime,tipindex->nTime);
+                                } else interest = 0;
+                                //interest = komodo_interestnew(chainActive.LastTip()->GetHeight()+1,pcoin->vout[i].nValue,pcoin->nLockTime,chainActive.LastTip()->nTime);
+                                if ( interest != 0 )
+                                {
+                                    //printf("wallet nValueRet %.8f += interest %.8f ht.%d lock.%u/%u tip.%u\n",(double)pcoin->vout[i].nValue/COIN,(double)interest/COIN,txheight,locktime,pcoin->nLockTime,tipindex->nTime);
+                                    //fprintf(stderr,"wallet nValueRet %.8f += interest %.8f ht.%d lock.%u tip.%u\n",(double)pcoin->vout[i].nValue/COIN,(double)interest/COIN,chainActive.LastTip()->GetHeight()+1,pcoin->nLockTime,chainActive.LastTip()->nTime);
+                                    //ptr = (uint64_t *)&pcoin->vout[i].nValue;
+                                    //(*ptr) += interest;
+                                    ptr = (uint64_t *)&pcoin->vout[i].interest;
+                                    (*ptr) = interest;
+                                    //pcoin->vout[i].nValue += interest;
+                                }
+                                else
+                                {
+                                    ptr = (uint64_t *)&pcoin->vout[i].interest;
+                                    (*ptr) = 0;
+                                }
+                            }
+                            else
+                            {
+                                ptr = (uint64_t *)&pcoin->vout[i].interest;
+                                (*ptr) = 0;
+                            }
+                        }
+                        else
+                        {
+                            ptr = (uint64_t *)&pcoin->vout[i].interest;
+                            (*ptr) = 0;
+                        }
+                    }
+                    vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO));
+                }
+            }
+        }
+    }
+}
+
+void CWallet::AvailableReserveCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeCoinBase) const
+{
+    vCoins.clear();
+
+    {
+        LOCK2(cs_main, cs_wallet);
+        for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        {
+            const uint256& wtxid = it->first;
+            const CWalletTx* pcoin = &(*it).second;
+
+            if (!CheckFinalTx(*pcoin))
+                continue;
+
+            if (fOnlyConfirmed && !pcoin->IsTrusted())
+                continue;
+
+            if (pcoin->IsCoinBase() && !fIncludeCoinBase)
+                continue;
+
+            if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
+                continue;
+
+            int nDepth = pcoin->GetDepthInMainChain();
+            if (nDepth < 0)
+                continue;
+            for (int i = 0; i < pcoin->vout.size(); i++)
+            {
+                // NOTE: we assume that only zero value outputs can be reserve outputs
+                isminetype mine = IsMine(pcoin->vout[i]);
+                if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
+                    !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue == 0 &&
+                    (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
+                {
+                    COptCCParams p;
+                    if (pcoin->vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_RESERVE_OUTPUT)
+                    {
                         vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO));
+                    }
+                }
             }
         }
     }
 }
 
-static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,unsigned int> > >vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
-                                  vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
+static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,unsigned int> > >vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
 {
     vector<char> vfIncluded;
 
@@ -3028,12 +4517,12 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns
     }
 }
 
-bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,
-                                 set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
+bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
 {
+    int32_t count = 0; //uint64_t lowest_interest = 0;
     setCoinsRet.clear();
+    //memset(interests,0,sizeof(interests));
     nValueRet = 0;
-
     // List of values less than target
     pair<CAmount, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
     coinLowestLarger.first = std::numeric_limits<CAmount>::max();
@@ -3062,16 +4551,30 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
         {
             setCoinsRet.insert(coin.second);
             nValueRet += coin.first;
+            //if ( KOMODO_EXCHANGEWALLET == 0 )
+            //    *interestp += pcoin->vout[i].interest;
             return true;
         }
         else if (n < nTargetValue + CENT)
         {
             vValue.push_back(coin);
             nTotalLower += n;
+            //if ( KOMODO_EXCHANGEWALLET == 0 && count < sizeof(interests)/sizeof(*interests) )
+            //{
+                //fprintf(stderr,"count.%d %.8f\n",count,(double)pcoin->vout[i].interest/COIN);
+                //interests[count++] = pcoin->vout[i].interest;
+            //}
+            if ( nTotalLower > 4*nTargetValue + CENT )
+            {
+                //fprintf(stderr,"why bother with all the utxo if we have double what is needed?\n");
+                break;
+            }
         }
         else if (n < coinLowestLarger.first)
         {
             coinLowestLarger = coin;
+            //if ( KOMODO_EXCHANGEWALLET == 0 )
+            //    lowest_interest = pcoin->vout[i].interest;
         }
     }
 
@@ -3081,6 +4584,8 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
         {
             setCoinsRet.insert(vValue[i].second);
             nValueRet += vValue[i].first;
+            //if ( KOMODO_EXCHANGEWALLET == 0 && i < count )
+            //    *interestp += interests[i];
         }
         return true;
     }
@@ -3091,6 +4596,8 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
             return false;
         setCoinsRet.insert(coinLowestLarger.second);
         nValueRet += coinLowestLarger.first;
+        //if ( KOMODO_EXCHANGEWALLET == 0 )
+        //    *interestp += lowest_interest;
         return true;
     }
 
@@ -3110,6 +4617,8 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
     {
         setCoinsRet.insert(coinLowestLarger.second);
         nValueRet += coinLowestLarger.first;
+        //if ( KOMODO_EXCHANGEWALLET == 0 )
+        //    *interestp += lowest_interest;
     }
     else {
         for (unsigned int i = 0; i < vValue.size(); i++)
@@ -3117,12 +4626,14 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
             {
                 setCoinsRet.insert(vValue[i].second);
                 nValueRet += vValue[i].first;
+                //if ( KOMODO_EXCHANGEWALLET == 0 && i < count )
+                //    *interestp += interests[i];
             }
 
         LogPrint("selectcoins", "SelectCoins() best subset: ");
         for (unsigned int i = 0; i < vValue.size(); i++)
             if (vfBest[i])
-                LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first));
+                LogPrint("selectcoins", "%s", FormatMoney(vValue[i].first));
         LogPrint("selectcoins", "total %s\n", FormatMoney(nBest));
     }
 
@@ -3132,6 +4643,12 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
 bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet,  bool& fOnlyCoinbaseCoinsRet, bool& fNeedCoinbaseCoinsRet, const CCoinControl* coinControl) const
 {
     // Output parameter fOnlyCoinbaseCoinsRet is set to true when the only available coins are coinbase utxos.
+    uint64_t tmp; int32_t retval;
+    //if ( interestp == 0 )
+    //{
+    //    interestp = &tmp;
+    //    *interestp = 0;
+    //}
     vector<COutput> vCoinsNoCoinbase, vCoinsWithCoinbase;
     AvailableCoins(vCoinsNoCoinbase, true, coinControl, false, false);
     AvailableCoins(vCoinsWithCoinbase, true, coinControl, false, true);
@@ -3149,6 +4666,8 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
                 continue;
             }
             value += out.tx->vout[out.i].nValue;
+            if ( KOMODO_EXCHANGEWALLET == 0 )
+                value += out.tx->vout[out.i].interest;
         }
         if (value <= nTargetValue) {
             CAmount valueWithCoinbase = 0;
@@ -3157,11 +4676,12 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
                     continue;
                 }
                 valueWithCoinbase += out.tx->vout[out.i].nValue;
+                if ( KOMODO_EXCHANGEWALLET == 0 )
+                    valueWithCoinbase += out.tx->vout[out.i].interest;
             }
             fNeedCoinbaseCoinsRet = (valueWithCoinbase >= nTargetValue);
         }
     }
-
     // coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
     if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs)
     {
@@ -3170,11 +4690,12 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
             if (!out.fSpendable)
                  continue;
             nValueRet += out.tx->vout[out.i].nValue;
+            //if ( KOMODO_EXCHANGEWALLET == 0 )
+            //    *interestp += out.tx->vout[out.i].interest;
             setCoinsRet.insert(make_pair(out.tx, out.i));
         }
         return (nValueRet >= nTargetValue);
     }
-
     // calculate value from preset inputs and store them
     set<pair<const CWalletTx*, uint32_t> > setPresetCoins;
     CAmount nValueFromPresetInputs = 0;
@@ -3192,106 +4713,760 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
             if (pcoin->vout.size() <= outpoint.n)
                 return false;
             nValueFromPresetInputs += pcoin->vout[outpoint.n].nValue;
+            if ( KOMODO_EXCHANGEWALLET == 0 )
+                nValueFromPresetInputs += pcoin->vout[outpoint.n].interest;
             setPresetCoins.insert(make_pair(pcoin, outpoint.n));
         } else
             return false; // TODO: Allow non-wallet inputs
     }
 
-    // remove preset inputs from vCoins
-    for (vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();)
-    {
-        if (setPresetCoins.count(make_pair(it->tx, it->i)))
-            it = vCoins.erase(it);
-        else
-            ++it;
-    }
+    // remove preset inputs from vCoins
+    for (vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();)
+    {
+        if (setPresetCoins.count(make_pair(it->tx, it->i)))
+            it = vCoins.erase(it);
+        else
+            ++it;
+    }
+    retval = false;
+    if ( nTargetValue <= nValueFromPresetInputs )
+        retval = true;
+    else if ( SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) != 0 )
+        retval = true;
+    else if ( SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) != 0 )
+        retval = true;
+    else if ( bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet) != 0 )
+        retval = true;
+    // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
+    setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
+    // add preset inputs to the total value selected
+    nValueRet += nValueFromPresetInputs;
+    return retval;
+}
+
+bool CWallet::SelectReserveCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
+{
+    int32_t count = 0; //uint64_t lowest_interest = 0;
+    setCoinsRet.clear();
+    //memset(interests,0,sizeof(interests));
+    nValueRet = 0;
+    // List of values less than target
+    pair<CAmount, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
+    coinLowestLarger.first = std::numeric_limits<CAmount>::max();
+    coinLowestLarger.second.first = NULL;
+    vector<pair<CAmount, pair<const CWalletTx*,unsigned int> > > vValue;
+    CAmount nTotalLower = 0;
+
+    random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
+
+    BOOST_FOREACH(const COutput &output, vCoins)
+    {
+        if (!output.fSpendable)
+            continue;
+
+        const CWalletTx *pcoin = output.tx;
+
+        if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs))
+            continue;
+
+        int i = output.i;
+        CAmount n = pcoin->vout[i].scriptPubKey.ReserveOutValue();
+
+        pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
+
+        if (n == nTargetValue)
+        {
+            setCoinsRet.insert(coin.second);
+            nValueRet += coin.first;
+            //if ( KOMODO_EXCHANGEWALLET == 0 )
+            //    *interestp += pcoin->vout[i].interest;
+            return true;
+        }
+        else if (n < nTargetValue + CENT)
+        {
+            vValue.push_back(coin);
+            nTotalLower += n;
+            if ( nTotalLower > 4*nTargetValue + CENT )
+            {
+                //fprintf(stderr,"why bother with all the utxo if we have double what is needed?\n");
+                break;
+            }
+        }
+        else if (n < coinLowestLarger.first)
+        {
+            coinLowestLarger = coin;
+        }
+    }
+
+    if (nTotalLower == nTargetValue)
+    {
+        for (unsigned int i = 0; i < vValue.size(); ++i)
+        {
+            setCoinsRet.insert(vValue[i].second);
+            nValueRet += vValue[i].first;
+        }
+        return true;
+    }
+
+    if (nTotalLower < nTargetValue)
+    {
+        if (coinLowestLarger.second.first == NULL)
+            return false;
+        setCoinsRet.insert(coinLowestLarger.second);
+        nValueRet += coinLowestLarger.first;
+        return true;
+    }
+
+    // Solve subset sum by stochastic approximation
+    sort(vValue.rbegin(), vValue.rend(), CompareValueOnly());
+    vector<char> vfBest;
+    CAmount nBest;
+
+    ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000);
+    if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT)
+        ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000);
+
+    // If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
+    //                                   or the next bigger coin is closer), return the bigger coin
+    if (coinLowestLarger.second.first &&
+        ((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest))
+    {
+        setCoinsRet.insert(coinLowestLarger.second);
+        nValueRet += coinLowestLarger.first;
+    }
+    else {
+        for (unsigned int i = 0; i < vValue.size(); i++)
+            if (vfBest[i])
+            {
+                setCoinsRet.insert(vValue[i].second);
+                nValueRet += vValue[i].first;
+            }
+
+        LogPrint("selectcoins", "SelectCoins() best subset: ");
+        for (unsigned int i = 0; i < vValue.size(); i++)
+            if (vfBest[i])
+                LogPrint("selectcoins", "%s", FormatMoney(vValue[i].first));
+        LogPrint("selectcoins", "total %s\n", FormatMoney(nBest));
+    }
+
+    return true;
+}
+
+bool CWallet::SelectReserveCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet,  bool& fOnlyCoinbaseCoinsRet, bool& fNeedCoinbaseCoinsRet, const CCoinControl* coinControl) const
+{
+    // Output parameter fOnlyCoinbaseCoinsRet is set to true when the only available coins are coinbase utxos.
+    uint64_t tmp; int32_t retval;
+    //if ( interestp == 0 )
+    //{
+    //    interestp = &tmp;
+    //    *interestp = 0;
+    //}
+    vector<COutput> vCoinsNoCoinbase, vCoinsWithCoinbase;
+    AvailableReserveCoins(vCoinsNoCoinbase, true, coinControl, false);
+    AvailableReserveCoins(vCoinsWithCoinbase, true, coinControl, true);
+    fOnlyCoinbaseCoinsRet = vCoinsNoCoinbase.size() == 0 && vCoinsWithCoinbase.size() > 0;
+
+    // If coinbase utxos can only be sent to zaddrs, exclude any coinbase utxos from coin selection.
+    bool fProtectCoinbase = Params().GetConsensus().fCoinbaseMustBeProtected;
+    vector<COutput> vCoins = (fProtectCoinbase) ? vCoinsNoCoinbase : vCoinsWithCoinbase;
+
+    // Output parameter fNeedCoinbaseCoinsRet is set to true if coinbase utxos need to be spent to meet target amount
+    if (fProtectCoinbase && vCoinsWithCoinbase.size() > vCoinsNoCoinbase.size()) {
+        CAmount value = 0;
+        for (const COutput& out : vCoinsNoCoinbase) {
+            if (!out.fSpendable) {
+                continue;
+            }
+            value += out.tx->vout[out.i].ReserveOutValue();
+        }
+        if (value <= nTargetValue) {
+            CAmount valueWithCoinbase = 0;
+            for (const COutput& out : vCoinsWithCoinbase) {
+                if (!out.fSpendable) {
+                    continue;
+                }
+                valueWithCoinbase += out.tx->vout[out.i].ReserveOutValue();
+            }
+            fNeedCoinbaseCoinsRet = (valueWithCoinbase >= nTargetValue);
+        }
+    }
+    // coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
+    if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs)
+    {
+        BOOST_FOREACH(const COutput& out, vCoins)
+        {
+            if (!out.fSpendable)
+                 continue;
+            nValueRet += out.tx->vout[out.i].ReserveOutValue();
+            setCoinsRet.insert(make_pair(out.tx, out.i));
+        }
+        return (nValueRet >= nTargetValue);
+    }
+    // calculate value from preset inputs and store them
+    set<pair<const CWalletTx*, uint32_t> > setPresetCoins;
+    CAmount nValueFromPresetInputs = 0;
+
+    std::vector<COutPoint> vPresetInputs;
+    if (coinControl)
+        coinControl->ListSelected(vPresetInputs);
+    BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs)
+    {
+        map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
+        if (it != mapWallet.end())
+        {
+            const CWalletTx* pcoin = &it->second;
+            // Clearly invalid input, fail
+            if (pcoin->vout.size() <= outpoint.n)
+                return false;
+            nValueFromPresetInputs += pcoin->vout[outpoint.n].ReserveOutValue();
+            setPresetCoins.insert(make_pair(pcoin, outpoint.n));
+        } else
+            return false; // TODO: Allow non-wallet inputs
+    }
+
+    // remove preset inputs from vCoins
+    for (vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();)
+    {
+        if (setPresetCoins.count(make_pair(it->tx, it->i)))
+            it = vCoins.erase(it);
+        else
+            ++it;
+    }
+    retval = false;
+    if ( nTargetValue <= nValueFromPresetInputs )
+        retval = true;
+    else if ( SelectReserveCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) != 0 )
+        retval = true;
+    else if ( SelectReserveCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) != 0 )
+        retval = true;
+    else if ( bSpendZeroConfChange && SelectReserveCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet) != 0 )
+        retval = true;
+    // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
+    setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
+    // add preset inputs to the total value selected
+    nValueRet += nValueFromPresetInputs;
+    return retval;
+}
+
+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason)
+{
+    vector<CRecipient> vecSend;
+
+    // Turn the txout set into a CRecipient vector
+    BOOST_FOREACH(const CTxOut& txOut, tx.vout)
+    {
+        CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false};
+        vecSend.push_back(recipient);
+    }
+
+    CCoinControl coinControl;
+    coinControl.fAllowOtherInputs = true;
+    BOOST_FOREACH(const CTxIn& txin, tx.vin)
+        coinControl.Select(txin.prevout);
+
+    CReserveKey reservekey(this);
+    CWalletTx wtx;
+
+    if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false))
+        return false;
+
+    if (nChangePosRet != -1)
+        tx.vout.insert(tx.vout.begin() + nChangePosRet, wtx.vout[nChangePosRet]);
+
+    // Add new txins (keeping original txin scriptSig/order)
+    BOOST_FOREACH(const CTxIn& txin, wtx.vin)
+    {
+        bool found = false;
+        BOOST_FOREACH(const CTxIn& origTxIn, tx.vin)
+        {
+            if (txin.prevout.hash == origTxIn.prevout.hash && txin.prevout.n == origTxIn.prevout.n)
+            {
+                found = true;
+                break;
+            }
+        }
+        if (!found)
+            tx.vin.push_back(txin);
+    }
+
+    return true;
+}
+
+bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
+                                int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign)
+{
+    uint64_t interest2 = 0; CAmount nValue = 0; unsigned int nSubtractFeeFromAmount = 0;
+    BOOST_FOREACH (const CRecipient& recipient, vecSend)
+    {
+        if (nValue < 0 || recipient.nAmount < 0)
+        {
+            strFailReason = _("Transaction amounts must be positive");
+            return false;
+        }
+        nValue += recipient.nAmount;
+
+        if (recipient.fSubtractFeeFromAmount)
+            nSubtractFeeFromAmount++;
+    }
+    if (vecSend.empty() || nValue < 0)
+    {
+        strFailReason = _("Transaction amounts must be positive");
+        return false;
+    }
+
+    wtxNew.fTimeReceivedIsTxTime = true;
+    wtxNew.BindWallet(this);
+    int nextBlockHeight = chainActive.Height() + 1;
+
+    CMutableTransaction txNew = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight);
+    txNew.nLockTime = (uint32_t)chainActive.LastTip()->nTime + 1; // set to a time close to now
+
+    // Activates after Overwinter network upgrade
+    if (Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_OVERWINTER)) {
+        if (txNew.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD){
+            strFailReason = _("nExpiryHeight must be less than TX_EXPIRY_HEIGHT_THRESHOLD.");
+            return false;
+        }
+    }
+
+    unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING;
+    if (!Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_SAPLING)) {
+        max_tx_size = MAX_TX_SIZE_BEFORE_SAPLING;
+    }
+
+    // Discourage fee sniping.
+    //
+    // However because of a off-by-one-error in previous versions we need to
+    // neuter it by setting nLockTime to at least one less than nBestHeight.
+    // Secondly currently propagation of transactions created for block heights
+    // corresponding to blocks that were just mined may be iffy - transactions
+    // aren't re-accepted into the mempool - we additionally neuter the code by
+    // going ten blocks back. Doesn't yet do anything for sniping, but does act
+    // to shake out wallet bugs like not showing nLockTime'd transactions at
+    // all.
+    txNew.nLockTime = std::max(0, chainActive.Height() - 10);
+
+    // Secondly occasionally randomly pick a nLockTime even further back, so
+    // that transactions that are delayed after signing for whatever reason,
+    // e.g. high-latency mix networks and some CoinJoin implementations, have
+    // better privacy.
+    if (GetRandInt(10) == 0)
+        txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100));
+
+    assert(txNew.nLockTime <= (unsigned int)chainActive.Height());
+    assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
+
+    {
+        LOCK2(cs_main, cs_wallet);
+        {
+            nFeeRet = 0;
+            while (true)
+            {
+                //interest = 0;
+                txNew.vin.clear();
+                txNew.vout.clear();
+                wtxNew.fFromMe = true;
+                nChangePosRet = -1;
+                bool fFirst = true;
+
+                CAmount nTotalValue = nValue;
+                if (nSubtractFeeFromAmount == 0)
+                    nTotalValue += nFeeRet;
+                double dPriority = 0;
+                // vouts to the payees
+                BOOST_FOREACH (const CRecipient& recipient, vecSend)
+                {
+                    CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
+
+                    if (recipient.fSubtractFeeFromAmount)
+                    {
+                        txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
+
+                        if (fFirst) // first receiver pays the remainder not divisible by output count
+                        {
+                            fFirst = false;
+                            txout.nValue -= nFeeRet % nSubtractFeeFromAmount;
+                        }
+                    }
+
+                    COptCCParams p;
+
+                    if (txout.IsDust(::minRelayTxFee) && !(txout.nValue == 0 && txout.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode != EVAL_NONE))
+                    {
+                        if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
+                        {
+                            if (txout.nValue < 0)
+                                strFailReason = _("The transaction amount is too small to pay the fee");
+                            else
+                                strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
+                        }
+                        else
+                            strFailReason = _("Transaction amount too small");
+                        return false;
+                    }
+                    txNew.vout.push_back(txout);
+                }
+
+                // Choose coins to use
+                set<pair<const CWalletTx*,unsigned int> > setCoins;
+                CAmount nValueIn = 0;
+                bool fOnlyCoinbaseCoins = false;
+                bool fNeedCoinbaseCoins = false;
+                interest2 = 0;
+                if (!SelectCoins(nTotalValue, setCoins, nValueIn, fOnlyCoinbaseCoins, fNeedCoinbaseCoins, coinControl))
+                {
+                    if (fOnlyCoinbaseCoins && Params().GetConsensus().fCoinbaseMustBeProtected) {
+                        strFailReason = _("Coinbase funds can only be sent to a zaddr");
+                    } else if (fNeedCoinbaseCoins) {
+                        strFailReason = _("Insufficient funds, coinbase funds can only be spent after they have been sent to a zaddr");
+                    } else {
+                        strFailReason = _("Insufficient funds");
+                    }
+                    return false;
+                }
+                BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+                {
+                    CAmount nCredit = pcoin.first->vout[pcoin.second].nValue;
+                    //The coin age after the next block (depth+1) is used instead of the current,
+                    //reflecting an assumption the user would accept a bit more delay for
+                    //a chance at a free transaction.
+                    //But mempool inputs might still be in the mempool, so their age stays 0
+                    //fprintf(stderr,"nCredit %.8f interest %.8f\n",(double)nCredit/COIN,(double)pcoin.first->vout[pcoin.second].interest/COIN);
+                    if ( KOMODO_EXCHANGEWALLET == 0 && ASSETCHAINS_SYMBOL[0] == 0 )
+                    {
+                        interest2 += pcoin.first->vout[pcoin.second].interest;
+                        //fprintf(stderr,"%.8f ",(double)pcoin.first->vout[pcoin.second].interest/COIN);
+                    }
+                    int age = pcoin.first->GetDepthInMainChain();
+                    if (age != 0)
+                        age += 1;
+                    dPriority += (double)nCredit * age;
+                }
+                //if ( KOMODO_EXCHANGEWALLET != 0 )
+                //{
+                    //fprintf(stderr,"KOMODO_EXCHANGEWALLET disable interest sum %.8f, interest2 %.8f\n",(double)interest/COIN,(double)interest2/COIN);
+                    //interest = 0; // interest2 also
+                //}
+                if ( ASSETCHAINS_SYMBOL[0] == 0 && DONATION_PUBKEY.size() == 66 && interest2 > 5000 )
+                {
+                    CScript scriptDonation = CScript() << ParseHex(DONATION_PUBKEY) << OP_CHECKSIG;
+                    CTxOut newTxOut(interest2,scriptDonation);
+                    int32_t nDonationPosRet = txNew.vout.size() - 1; // dont change first or last
+                    vector<CTxOut>::iterator position = txNew.vout.begin()+nDonationPosRet;
+                    txNew.vout.insert(position, newTxOut);
+                    interest2 = 0;
+                }
+                CAmount nChange = (nValueIn - nValue + interest2);
+                //fprintf(stderr,"wallet change %.8f (%.8f - %.8f) interest2 %.8f total %.8f\n",(double)nChange/COIN,(double)nValueIn/COIN,(double)nValue/COIN,(double)interest2/COIN,(double)nTotalValue/COIN);
+                if (nSubtractFeeFromAmount == 0)
+                    nChange -= nFeeRet;
+
+                if (nChange > 0)
+                {
+                    // Fill a vout to ourself
+                    // TODO: pass in scriptChange instead of reservekey so
+                    // change transaction isn't always pay-to-bitcoin-address
+                    CScript scriptChange;
+
+                    // coin control: send change to custom address
+                    if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))
+                        scriptChange = GetScriptForDestination(coinControl->destChange);
+
+                    // no coin control: send change to newly generated address
+                    else
+                    {
+                        // Note: We use a new key here to keep it from being obvious which side is the change.
+                        //  The drawback is that by not reusing a previous key, the change may be lost if a
+                        //  backup is restored, if the backup doesn't have the new private key for the change.
+                        //  If we reused the old key, it would be possible to add code to look for and
+                        //  rediscover unknown transactions that were written with keys of ours to recover
+                        //  post-backup change.
+
+                        // Reserve a new key pair from key pool
+                        CPubKey vchPubKey;
+                        extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY;
+                        if ( USE_EXTERNAL_PUBKEY == 0 )
+                        {
+                            bool ret;
+                            ret = reservekey.GetReservedKey(vchPubKey);
+                            assert(ret); // should never fail, as we just unlocked
+                            scriptChange = GetScriptForDestination(vchPubKey.GetID());
+                        }
+                        else
+                        {
+                            //fprintf(stderr,"use notary pubkey\n");
+                            scriptChange = CScript() << ParseHex(NOTARY_PUBKEY) << OP_CHECKSIG;
+                        }
+                    }
+
+                    CTxOut newTxOut(nChange, scriptChange);
+
+                    // We do not move dust-change to fees, because the sender would end up paying more than requested.
+                    // This would be against the purpose of the all-inclusive feature.
+                    // So instead we raise the change and deduct from the recipient.
+                    if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee))
+                    {
+                        CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue;
+                        newTxOut.nValue += nDust; // raise change until no more dust
+                        for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
+                        {
+                            if (vecSend[i].fSubtractFeeFromAmount)
+                            {
+                                txNew.vout[i].nValue -= nDust;
+                                if (txNew.vout[i].IsDust(::minRelayTxFee))
+                                {
+                                    strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
+                                    return false;
+                                }
+                                break;
+                            }
+                        }
+                    }
+
+                    // Never create dust outputs; if we would, just
+                    // add the dust to the fee. Valid cryptoconditions with a valid eval function are allowed to create outputs of 0
+                    if (newTxOut.IsDust(::minRelayTxFee))
+                    {
+                        nFeeRet += nChange;
+                        reservekey.ReturnKey();
+                    }
+                    else
+                    {
+                        nChangePosRet = txNew.vout.size() - 1; // dont change first or last
+                        vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosRet;
+                        txNew.vout.insert(position, newTxOut);
+                    }
+                } else reservekey.ReturnKey();
+
+                // Fill vin
+                //
+                // Note how the sequence number is set to max()-1 so that the
+                // nLockTime set above actually works.
+                BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
+                    txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
+                                              std::numeric_limits<unsigned int>::max()-1));
+
+                // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects
+                size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0);
+                {
+                    LOCK(cs_main);
+                    if (Params().GetConsensus().NetworkUpgradeActive(chainActive.Height() + 1, Consensus::UPGRADE_OVERWINTER)) {
+                        limit = 0;
+                    }
+                }
+                if (limit > 0) {
+                    size_t n = txNew.vin.size();
+                    if (n > limit) {
+                        strFailReason = _(strprintf("Too many transparent inputs %zu > limit %zu", n, limit).c_str());
+                        return false;
+                    }
+                }
+
+                // Grab the current consensus branch ID
+                auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
+
+                // Sign
+                int nIn = 0;
+                CTransaction txNewConst(txNew);
+                BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
+                {
+                    bool signSuccess;
+                    const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey;
+                    SignatureData sigdata;
+                    if (sign)
+                        signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata, consensusBranchId);
+                    else
+                        signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata, consensusBranchId);
+
+                    if (!signSuccess)
+                    {
+                        strFailReason = _("Signing transaction failed");
+                        return false;
+                    } else {
+                        UpdateTransaction(txNew, nIn, sigdata);
+                    }
 
-    bool res = nTargetValue <= nValueFromPresetInputs ||
-        SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, vCoins, setCoinsRet, nValueRet) ||
-        SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, vCoins, setCoinsRet, nValueRet) ||
-        (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, vCoins, setCoinsRet, nValueRet));
+                    nIn++;
+                }
 
-    // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
-    setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
+                unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
 
-    // add preset inputs to the total value selected
-    nValueRet += nValueFromPresetInputs;
+                // Remove scriptSigs if we used dummy signatures for fee calculation
+                if (!sign) {
+                    BOOST_FOREACH (CTxIn& vin, txNew.vin)
+                        vin.scriptSig = CScript();
+                }
 
-    return res;
-}
+                // Embed the constructed transaction data in wtxNew.
+                *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew);
 
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason)
-{
-    vector<CRecipient> vecSend;
+                // Limit size
+                if (nBytes >= max_tx_size)
+                {
+                    strFailReason = _("Transaction too large");
+                    return false;
+                }
 
-    // Turn the txout set into a CRecipient vector
-    BOOST_FOREACH(const CTxOut& txOut, tx.vout)
-    {
-        CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false};
-        vecSend.push_back(recipient);
-    }
+                dPriority = wtxNew.ComputePriority(dPriority, nBytes);
 
-    CCoinControl coinControl;
-    coinControl.fAllowOtherInputs = true;
-    BOOST_FOREACH(const CTxIn& txin, tx.vin)
-        coinControl.Select(txin.prevout);
+                // Can we complete this as a free transaction?
+                if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
+                {
+                    // Not enough fee: enough priority?
+                    double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
+                    // Not enough mempool history to estimate: use hard-coded AllowFree.
+                    if (dPriorityNeeded <= 0 && AllowFree(dPriority))
+                        break;
 
-    CReserveKey reservekey(this);
-    CWalletTx wtx;
+                    // Small enough, and priority high enough, to send for free
+                    if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded)
+                        break;
+                }
 
-    if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false))
-        return false;
+                CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
+                if ( nFeeNeeded < 5000 )
+                    nFeeNeeded = 5000;
 
-    if (nChangePosRet != -1)
-        tx.vout.insert(tx.vout.begin() + nChangePosRet, wtx.vout[nChangePosRet]);
+                // If we made it here and we aren't even able to meet the relay fee on the next pass, give up
+                // because we must be at the maximum allowed fee.
+                if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))
+                {
+                    strFailReason = _("Transaction too large for fee policy");
+                    return false;
+                }
 
-    // Add new txins (keeping original txin scriptSig/order)
-    BOOST_FOREACH(const CTxIn& txin, wtx.vin)
-    {
-        bool found = false;
-        BOOST_FOREACH(const CTxIn& origTxIn, tx.vin)
-        {
-            if (txin.prevout.hash == origTxIn.prevout.hash && txin.prevout.n == origTxIn.prevout.n)
-            {
-                found = true;
-                break;
+                if (nFeeRet >= nFeeNeeded)
+                    break; // Done, enough fee included.
+
+                // Include more fee and try again.
+                nFeeRet = nFeeNeeded;
+                continue;
             }
         }
-        if (!found)
-            tx.vin.push_back(txin);
     }
 
     return true;
 }
 
-bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
-                                int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign)
+CAmount ConvertReserveAmount(CAmount inAmount, CAmount reservePrice)
+{
+    arith_uint256 satoshiden(100000000);
+    arith_uint256 bigAmount(inAmount);
+    arith_uint256 bigPrice(reservePrice);
+    return ((bigAmount * bigPrice) / satoshiden).GetLow64();
+}
+
+// almost the same as CreateTransaction with the difference being that input and output are assumed to be the
+// reserve currency of this chain, represented as reserve outputs for both input and output. That means that all
+// outputs must be reserve consuming outputs. Fee converted from reserve, which is the difference between reserve
+// input and reserve output, is calculated based on the current reserve conversion price.
+bool CWallet::CreateReserveTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
+                                       int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign, bool includeRefunds)
 {
-    CAmount nValue = 0;
+    CAmount nValue = 0; 
     unsigned int nSubtractFeeFromAmount = 0;
+
+    // reserve transactions can only be created on fractional reserve currency blockchains
+    if (IsVerusActive())
+    {
+        strFailReason = _("Transactions that accept reserve currency input can only be created on PBaaS blockchains");
+        return false;
+    }
+
+    // make sure that there are recipients, all recipients expect reserve inputs, and amounts are all non-negative
     BOOST_FOREACH (const CRecipient& recipient, vecSend)
     {
-        if (nValue < 0 || recipient.nAmount < 0)
+        COptCCParams p;
+        CReserveOutput ro;
+        if (recipient.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.vData.size() > 0)
         {
-            strFailReason = _("Transaction amounts must be positive");
+            switch (p.evalCode)
+            {
+                case EVAL_RESERVE_OUTPUT:
+                {
+                    ro = CReserveOutput(p.vData[0]);
+                    if (!ro.IsValid())
+                    {
+                        strFailReason = _("Invalid reserve output");
+                        return false;
+                    }
+                    break;
+                }
+                case EVAL_RESERVE_TRANSFER:
+                {
+                    CReserveTransfer rt(p.vData[0]);
+                    if (!rt.IsValid())
+                    {
+                        strFailReason = _("Invalid reserve transfer");
+                        return false;
+                    }
+                    // conversion on a PBaaS reserve chain implies native input
+                    if (rt.flags & CReserveTransfer::CONVERT)
+                    {
+                        strFailReason = _(("Reserve transaction outputs created using " + std::string( __func__) + " must have no native output value").c_str());
+                        return false;
+                    }
+                    nValue += rt.nFees;                     // pre-add fees, since reserve transfer is the only object type that requires that
+                    ro = static_cast<CReserveOutput>(rt);
+                    break;
+                }
+                case EVAL_RESERVE_EXCHANGE:
+                {
+                    CReserveExchange re(p.vData[0]);
+                    if (!re.IsValid())
+                    {
+                        strFailReason = _("Invalid reserve exchange");
+                        return false;
+                    }
+                    // conversion to reserve implies native input
+                    if (re.flags & CReserveExchange::TO_RESERVE)
+                    {
+                        strFailReason = _(("Reserve transaction outputs created using " + std::string( __func__) + " must have no native output value").c_str());
+                        return false;
+                    }
+                    ro = static_cast<CReserveOutput>(re);
+                    break;
+                }
+                default:
+                {
+                    strFailReason = _("All reserve transaction outputs must accommodate reserve currency input");
+                    return false;
+                }
+            }
+        }
+        else if (!recipient.scriptPubKey.IsOpReturn() || recipient.nAmount != 0)
+        {
+            strFailReason = _(("Reserve transaction outputs created using " + std::string( __func__) + " must have no native output value").c_str());
+            return false;
+        }
+
+        if (ro.IsValid())
+        {
+            nValue += ro.nValue;
+        }
+
+        if (ro.nValue < 0 || nValue < 0 || recipient.nAmount < 0)
+        {
+            strFailReason = _("Transaction amounts must not be negative");
             return false;
         }
-        nValue += recipient.nAmount;
 
         if (recipient.fSubtractFeeFromAmount)
             nSubtractFeeFromAmount++;
     }
+
     if (vecSend.empty() || nValue < 0)
     {
-        strFailReason = _("Transaction amounts must be positive");
+        strFailReason = _("Transaction amounts must not be negative");
         return false;
     }
 
     wtxNew.fTimeReceivedIsTxTime = true;
     wtxNew.BindWallet(this);
     int nextBlockHeight = chainActive.Height() + 1;
-    CMutableTransaction txNew = CreateNewContextualCMutableTransaction(
-        Params().GetConsensus(), nextBlockHeight);
+    CMutableTransaction txNew = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight);
+    txNew.nLockTime = (uint32_t)chainActive.LastTip()->nTime + 1; // set to a time close to now
 
     // Activates after Overwinter network upgrade
     if (Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_OVERWINTER)) {
@@ -3334,12 +5509,19 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
             nFeeRet = 0;
             while (true)
             {
+                //interest = 0;
                 txNew.vin.clear();
                 txNew.vout.clear();
                 wtxNew.fFromMe = true;
                 nChangePosRet = -1;
                 bool fFirst = true;
 
+                CCoinbaseCurrencyState currencyState = ConnectedChains.GetCurrencyState(chainActive.LastTip() ? chainActive.LastTip()->GetHeight() : 0);
+                CAmount reservePrice = currencyState.PriceInReserve();
+
+                // dust threshold of reserve is different than native coin, convert
+                CAmount dustThreshold;
+
                 CAmount nTotalValue = nValue;
                 if (nSubtractFeeFromAmount == 0)
                     nTotalValue += nFeeRet;
@@ -3347,32 +5529,107 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
                 // vouts to the payees
                 BOOST_FOREACH (const CRecipient& recipient, vecSend)
                 {
-                    CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
+                    // native output value for a reserve output is generally 0. fees are paid by converting from
+                    // reserve token and the difference between input and output in reserve is the fee
+                    // the actual reserve token output value is in the scriptPubKey as extended CC information
+                    CTxOut txout(0, recipient.scriptPubKey);
 
-                    if (recipient.fSubtractFeeFromAmount)
+                    // here, if we know that it isn't an opret, it will have an output that expects reserve input
+                    if (!recipient.scriptPubKey.IsOpReturn())
                     {
-                        txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
+                        COptCCParams p;
 
-                        if (fFirst) // first receiver pays the remainder not divisible by output count
+                        // already validated above
+                        txout.scriptPubKey.IsPayToCryptoCondition(p);
+
+                        CAmount newVal = 0;
+                        CReserveOutput ro;
+                        CReserveTransfer rt;
+                        CReserveExchange re;
+
+                        switch (p.evalCode)
                         {
-                            fFirst = false;
-                            txout.nValue -= nFeeRet % nSubtractFeeFromAmount;
+                            case EVAL_RESERVE_OUTPUT:
+                            {
+                                ro = CReserveOutput(p.vData[0]);
+                                newVal = ro.nValue;
+                                break;
+                            }
+                            case EVAL_RESERVE_TRANSFER:
+                            {
+                                rt = CReserveTransfer(p.vData[0]);
+                                newVal = rt.nValue;
+                                break;
+                            }
+                            case EVAL_RESERVE_EXCHANGE:
+                            {
+                                re = CReserveExchange(p.vData[0]);
+                                newVal = re.nValue;
+                                break;
+                            }
+                            default:
+                                strFailReason = _("Bad reserve output");
+                                return false;
                         }
-                    }
 
-                    if (txout.IsDust(::minRelayTxFee))
-                    {
-                        if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
+                        if (recipient.fSubtractFeeFromAmount)
                         {
-                            if (txout.nValue < 0)
-                                strFailReason = _("The transaction amount is too small to pay the fee");
+                            CAmount subFee = nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
+
+                            if (fFirst) // first receiver pays the remainder not divisible by output count
+                            {
+                                fFirst = false;
+                                subFee += nFeeRet % nSubtractFeeFromAmount;
+                            }
+
+                            switch (p.evalCode)
+                            {
+                                case EVAL_RESERVE_OUTPUT:
+                                {
+                                    ro.nValue -= subFee;
+                                    newVal = ro.nValue;
+                                    p.vData[0] = ro.AsVector();
+                                    break;
+                                }
+                                case EVAL_RESERVE_TRANSFER:
+                                {
+                                    rt.nValue -= subFee;
+                                    newVal = rt.nValue + rt.nFees;
+                                    p.vData[0] = rt.AsVector();
+                                    break;
+                                }
+                                case EVAL_RESERVE_EXCHANGE:
+                                {
+                                    re.nValue -= subFee;
+                                    newVal = re.nValue;
+                                    p.vData[0] = re.AsVector();
+                                    break;
+                                }
+                                default:
+                                    strFailReason = _("Bad reserve output");
+                                    return false;
+                            }
+
+                            txout.scriptPubKey = txout.scriptPubKey.ReplaceCCParams(p);
+                        }
+
+                        dustThreshold = txout.GetDustThreshold(::minRelayTxFee);
+
+                        if (newVal < dustThreshold)
+                        {
+                            if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
+                            {
+                                if (newVal < 0)
+                                    strFailReason = _("The transaction amount is too small to pay the fee");
+                                else
+                                    strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
+                            }
                             else
-                                strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
+                                strFailReason = _("Transaction amount too small");
+                            return false;
                         }
-                        else
-                            strFailReason = _("Transaction amount too small");
-                        return false;
                     }
+
                     txNew.vout.push_back(txout);
                 }
 
@@ -3381,7 +5638,8 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
                 CAmount nValueIn = 0;
                 bool fOnlyCoinbaseCoins = false;
                 bool fNeedCoinbaseCoins = false;
-                if (!SelectCoins(nTotalValue, setCoins, nValueIn, fOnlyCoinbaseCoins, fNeedCoinbaseCoins, coinControl))
+
+                if (!SelectReserveCoins(nTotalValue, setCoins, nValueIn, fOnlyCoinbaseCoins, fNeedCoinbaseCoins, coinControl))
                 {
                     if (fOnlyCoinbaseCoins && Params().GetConsensus().fCoinbaseMustBeProtected) {
                         strFailReason = _("Coinbase funds can only be sent to a zaddr");
@@ -3394,7 +5652,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
                 }
                 BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
                 {
-                    CAmount nCredit = pcoin.first->vout[pcoin.second].nValue;
+                    CAmount nCredit = pcoin.first->vout[pcoin.second].ReserveOutValue();
                     //The coin age after the next block (depth+1) is used instead of the current,
                     //reflecting an assumption the user would accept a bit more delay for
                     //a chance at a free transaction.
@@ -3405,24 +5663,33 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
                     dPriority += (double)nCredit * age;
                 }
 
-                CAmount nChange = nValueIn - nValue;
+                CAmount nChange = (nValueIn - nValue);
+
                 if (nSubtractFeeFromAmount == 0)
                     nChange -= nFeeRet;
 
                 if (nChange > 0)
                 {
                     // Fill a vout to ourself
-                    // TODO: pass in scriptChange instead of reservekey so
-                    // change transaction isn't always pay-to-bitcoin-address
-                    CScript scriptChange;
+                    CPubKey vchPubKey;
 
                     // coin control: send change to custom address
-                    if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))
-                        scriptChange = GetScriptForDestination(coinControl->destChange);
 
-                    // no coin control: send change to newly generated address
+                    // reserve tokens can currently only be sent to public keys or addresses that are in the current wallet
+                    // since reserve token outputs are CCs by definition
+                    if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))
+                    {
+                        if (!boost::get<CPubKey>(&coinControl->destChange))
+                        {
+                            strFailReason = _("Change address must be public key");
+                            return false;
+                        }
+                        vchPubKey = *(boost::get<CPubKey>(&coinControl->destChange));
+                    }
                     else
                     {
+                        // no coin control: send change to newly generated address
+
                         // Note: We use a new key here to keep it from being obvious which side is the change.
                         //  The drawback is that by not reusing a previous key, the change may be lost if a
                         //  backup is restored, if the backup doesn't have the new private key for the change.
@@ -3431,55 +5698,74 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
                         //  post-backup change.
 
                         // Reserve a new key pair from key pool
-                        CPubKey vchPubKey;
-                        bool ret;
-                        ret = reservekey.GetReservedKey(vchPubKey);
-                        assert(ret); // should never fail, as we just unlocked
-
-                        scriptChange = GetScriptForDestination(vchPubKey.GetID());
+                        extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY;
+                        if ( USE_EXTERNAL_PUBKEY == 0 )
+                        {
+                            bool ret;
+                            ret = reservekey.GetReservedKey(vchPubKey);
+                            assert(ret); // should never fail, as we just unlocked
+                        }
+                        else
+                        {
+                            //fprintf(stderr,"use notary pubkey\n");
+                            vchPubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
+                        }
                     }
 
-                    CTxOut newTxOut(nChange, scriptChange);
+                    // we will send using a reserve output, fee will be paid by converting from reserve
+                    CCcontract_info CC;
+                    CCcontract_info *cp;
+                    cp = CCinit(&CC, EVAL_RESERVE_OUTPUT);
+
+                    std::vector<CTxDestination> dests = std::vector<CTxDestination>({vchPubKey.GetID()});
+
+                    // create the transfer object
+                    CReserveOutput ro(CReserveOutput::VALID, nChange);
 
                     // We do not move dust-change to fees, because the sender would end up paying more than requested.
                     // This would be against the purpose of the all-inclusive feature.
                     // So instead we raise the change and deduct from the recipient.
-                    if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee))
+
+                    // adjust the output amount if possible
+                    if (nSubtractFeeFromAmount > 0 && ro.nValue < dustThreshold)
                     {
-                        CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue;
-                        newTxOut.nValue += nDust; // raise change until no more dust
+                        CAmount nDust = dustThreshold - ro.nValue;
+
+                        ro.nValue += nDust; // raise change until no more dust
+
                         for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
                         {
                             if (vecSend[i].fSubtractFeeFromAmount)
                             {
-                                txNew.vout[i].nValue -= nDust;
-                                if (txNew.vout[i].IsDust(::minRelayTxFee))
+                                CAmount nValue = txNew.vout[i].ReserveOutValue() - nDust;
+                                if (nValue < dustThreshold)
                                 {
                                     strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
                                     return false;
                                 }
+                                txNew.vout[i].SetReserveOutValue(nValue);
                                 break;
                             }
                         }
                     }
 
-                    // Never create dust outputs; if we would, just
+                    // native amount in the output should be 0
+                    CTxOut newTxOut = MakeCC1of1Vout(EVAL_RESERVE_OUTPUT, 0, vchPubKey, dests, ro);
+
+                    // Never create reserve dust outputs; if we would, just
                     // add the dust to the fee.
-                    if (newTxOut.IsDust(::minRelayTxFee))
+                    if (ro.nValue < newTxOut.GetDustThreshold(::minRelayTxFee))
                     {
                         nFeeRet += nChange;
                         reservekey.ReturnKey();
                     }
                     else
                     {
-                        // Insert change txn at random position:
-                        nChangePosRet = GetRandInt(txNew.vout.size()+1);
+                        nChangePosRet = txNew.vout.size() - 1; // dont change first or last
                         vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosRet;
                         txNew.vout.insert(position, newTxOut);
                     }
-                }
-                else
-                    reservekey.ReturnKey();
+                } else reservekey.ReturnKey();
 
                 // Fill vin
                 //
@@ -3567,6 +5853,10 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
                 }
 
                 CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
+                if ( nFeeNeeded < 5000 )
+                    nFeeNeeded = 5000;
+
+                nFeeNeeded = currencyState.NativeToReserve(nFeeNeeded, reservePrice);
 
                 // If we made it here and we aren't even able to meet the relay fee on the next pass, give up
                 // because we must be at the maximum allowed fee.
@@ -3633,6 +5923,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, boost::optional<CReserveKey&>
             // Broadcast
             if (!wtxNew.AcceptToMemoryPool(false))
             {
+                fprintf(stderr,"commit failed\n");
                 // This must not fail. The transaction has already been signed and recorded.
                 LogPrintf("CommitTransaction(): Error: Transaction not valid\n");
                 return false;
@@ -3667,14 +5958,26 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
 }
 
 
-
+void komodo_prefetch(FILE *fp);
 
 DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
 {
     if (!fFileBacked)
         return DB_LOAD_OK;
     fFirstRunRet = false;
+    if ( 0 ) // doesnt help
+    {
+        fprintf(stderr,"loading wallet %s %u\n",strWalletFile.c_str(),(uint32_t)time(NULL));
+        FILE *fp;
+        if ( (fp= fopen(strWalletFile.c_str(),"rb")) != 0 )
+        {
+            komodo_prefetch(fp);
+            fclose(fp);
+        }
+    }
+    //fprintf(stderr,"prefetched wallet %s %u\n",strWalletFile.c_str(),(uint32_t)time(NULL));
     DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);
+    //fprintf(stderr,"loaded wallet %s %u\n",strWalletFile.c_str(),(uint32_t)time(NULL));
     if (nLoadWalletRet == DB_NEED_REWRITE)
     {
         if (CDB::Rewrite(strWalletFile, "\x04pool"))
@@ -3859,7 +6162,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool)
         if (!HaveKey(keypool.vchPubKey.GetID()))
             throw runtime_error("ReserveKeyFromKeyPool(): unknown key in key pool");
         assert(keypool.vchPubKey.IsValid());
-        LogPrintf("keypool reserve %d\n", nIndex);
+        //LogPrintf("keypool reserve %d\n", nIndex);
     }
 }
 
@@ -3881,7 +6184,7 @@ void CWallet::ReturnKey(int64_t nIndex)
         LOCK(cs_wallet);
         setKeyPool.insert(nIndex);
     }
-    LogPrintf("keypool return %d\n", nIndex);
+    //LogPrintf("keypool return %d\n", nIndex);
 }
 
 bool CWallet::GetKeyFromPool(CPubKey& result)
@@ -4267,12 +6570,29 @@ public:
             vKeys.push_back(keyId);
     }
 
+    void operator()(const CPubKey &key) {
+        CKeyID keyId = key.GetID();
+        if (keystore.HaveKey(keyId))
+            vKeys.push_back(keyId);
+    }
+
     void operator()(const CScriptID &scriptId) {
         CScript script;
         if (keystore.GetCScript(scriptId, script))
             Process(script);
     }
 
+    void operator()(const CIdentityID &idId) {
+        std::pair<CIdentityMapKey, CIdentityMapValue> identity;
+        if (keystore.GetIdentity(idId, identity))
+        {
+            for (auto dest : identity.second.primaryAddresses)
+            {
+                boost::apply_visitor(*this, dest);
+            }
+        }
+    }
+
     void operator()(const CNoDestination &none) {}
 };
 
@@ -4308,14 +6628,14 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
         BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock);
         if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) {
             // ... which are already in a block
-            int nHeight = blit->second->nHeight;
+            int nHeight = blit->second->GetHeight();
             BOOST_FOREACH(const CTxOut &txout, wtx.vout) {
                 // iterate over all their outputs
                 CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
                 BOOST_FOREACH(const CKeyID &keyid, vAffected) {
                     // ... and all their affected keys
                     std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid);
-                    if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight)
+                    if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->GetHeight())
                         rit->second = blit->second;
                 }
                 vAffected.clear();
@@ -4432,7 +6752,7 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const
     }
 
     pindexRet = pindex;
-    return chainActive.Height() - pindex->nHeight + 1;
+    return chainActive.Height() - pindex->GetHeight() + 1;
 }
 
 int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
@@ -4447,12 +6767,18 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
 
 int CMerkleTx::GetBlocksToMaturity() const
 {
+    if ( ASSETCHAINS_SYMBOL[0] == 0 )
+        COINBASE_MATURITY = _COINBASE_MATURITY;
     if (!IsCoinBase())
         return 0;
-    return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
+    int32_t depth = GetDepthInMainChain();
+    int32_t ut = UnlockTime(0);
+    int32_t toMaturity = (ut - chainActive.Height()) < 0 ? 0 : ut - chainActive.Height();
+    //printf("depth.%i, unlockTime.%i, toMaturity.%i\n", depth, ut, toMaturity);
+    ut = (COINBASE_MATURITY - depth) < 0 ? 0 : COINBASE_MATURITY - depth;
+    return(ut < toMaturity ? toMaturity : ut);
 }
 
-
 bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee)
 {
     CValidationState state;
This page took 0.118489 seconds and 4 git commands to generate.