]> 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 be05fb9c4d9d38d75b6e14aa2d807bcf89276f48..b02fc3f1b10421fa863e39ac68109d32fe668dd1 100644 (file)
@@ -60,6 +60,7 @@ 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)
@@ -2096,6 +2097,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
 {
     {
         AssertLockHeld(cs_wallet);
+        uint256 txHash = tx.GetHash();
         bool fExisted = mapWallet.count(tx.GetHash()) != 0;
         if (fExisted && !fUpdate) return false;
         auto sproutNoteData = FindMySproutNotes(tx);
@@ -2109,13 +2111,14 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
         }
 
         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)
+            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
@@ -2141,14 +2144,14 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
                 if (p.evalCode == EVAL_IDENTITY_PRIMARY && p.vData.size() && (*(CIdentity *)&identity = CIdentity(p.vData[0])).IsValid())
                 {
                     identity.txid = tx.GetHash();
-                    CIdentityMapKey idMapKey =CIdentityMapKey(identity.GetNameID(), 
-                                                              nHeight, 
-                                                              1, 
-                                                              CIdentityMapKey::VALID);
+                    CIdentityMapKey idMapKey = CIdentityMapKey(identity.GetID(), 
+                                                               nHeight, 
+                                                               1, 
+                                                               CIdentityMapKey::VALID);
 
                     std::set<CKeyID> keySet;
-                    CIdentityID idID(identity.GetNameID());
-                    int blockOrder = 0;
+                    CIdentityID idID(identity.GetID());
+                    int blockOrder = 1;
                     bool doneWithID = false;
 
                     std::pair<CIdentityMapKey, CIdentityMapValue> idHistory;
@@ -2161,10 +2164,79 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
                     {
                         if (idHistory.first.blockHeight == nHeight && idHistory.second.txid != identity.txid)
                         {
-                            // if we find two identity updates at the same height, we need to determine all of the identities with this ID in this block
-                            // and make sure we set the order properly for all of them. the last one is the one that takes effect after the block is
-                            // mined
-                            // TODO - FINISH
+                            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)
                         {
@@ -2206,6 +2278,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
                     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);
@@ -2214,7 +2287,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
                             if (!idHistory.second.IsValid() && canSignCanSpend.first)
                             {
                                 // add
-                                CIdentityMapKey idMapKey = CIdentityMapKey(identity.GetNameID(), 
+                                CIdentityMapKey idMapKey = CIdentityMapKey(identity.GetID(), 
                                                                         nHeight, 
                                                                         blockOrder, 
                                                                         idHistory.first.VALID |
@@ -2224,7 +2297,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
                             }
                             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.GetNameID(), 
+                                CIdentityMapKey idMapKey = CIdentityMapKey(identity.GetID(), 
                                                                         nHeight, 
                                                                         blockOrder, 
                                                                         idHistory.first.VALID | 
@@ -2247,13 +2320,83 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
                         // 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)
+                                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
@@ -2422,82 +2565,24 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
                             {
                                 if (!canSignCanSpend.second)
                                 {
-
+                                    // mark all transactions dirty to recalculate numbers
+                                    for (auto &txidAndWtx : mapWallet)
+                                    {
+                                        txidAndWtx.second.MarkDirty();
+                                    }
                                 }
                             }
                         }
                     }
                 }
-
-
-
-
-
-                // figure out if we can sign or spend this transaction as of this block and if we can sign, add any identity definitions needed to sign the transaction to the wallet
-                // TODO - FINISH - we may need to add an optional height to extract destinations
-                //if (ExtractDestinations(output.scriptPubKey,))
-
-
-                CCcontract_info C;
-                CKeyID pkID;
-
-                if (CCinit(&C, p.evalCode))
-                {
-                    pkID = CPubKey(ParseHex(C.CChexstr)).GetID();
-                }
-
-                // check all destinations, including IDs
-                std::set<CTxDestination> keySet;
-                bool watchOnly = false;
-                for (auto key : p.vKeys)
-                {
-                    if (key.which() == COptCCParams::ADDRTYPE_SH)
-                    {
-                        CScript idScript;
-                        CIdentity idp;
-                        std::set<CKeyID> ownKeySet;
-                        if (GetCScript(CScriptID(GetDestinationID(key)), idScript) && (idp = CIdentity(idScript)).IsValid() && (idp.GetNameID() == GetDestinationID(key)))
-                        {
-                            for (auto oneKey : idp.primaryAddresses)
-                            {
-                                ownKeySet.insert(GetDestinationID(oneKey));
-                            }
-                            if (ownKeySet.size())
-                            {
-                                watchOnly = true;
-                            }
-                            if (ownKeySet.size() >= idp.minSigs)
-                            {
-                                keySet.insert(key);
-                            }
-                        }
-                    }
-                    else
-                    {
-                        CKeyID keyID = CKeyID(GetDestinationID(key));
-                        if (HaveKey(keyID))
-                        {
-                            keySet.insert(key);
-                        }
-                    }
-                }
-                if (keySet.size() >= p.m)
-                {
-                    return ISMINE_SPENDABLE;
-                }
-                else
-                {
-                    return watchOnly ? ISMINE_WATCH_ONLY : ISMINE_NO;
-                }
             }
         }
 
+        isMine = IsMine(tx);
 
-
-        bool 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);
@@ -2515,22 +2600,6 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
             // 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);
 
-            // if this is an ID, add the ID as a script
-            if (isMine)
-            {
-                CIdentity identity;
-                int i;
-                for (i = 0; !identity.IsValid() && i < tx.vout.size(); i++)
-                {
-                    identity = CIdentity(tx.vout[i].scriptPubKey);
-                }
-
-                if (identity.IsValid())
-                {
-                    AddCScript(tx.vout[i].scriptPubKey);
-                }
-            }
-
             return AddToWallet(wtx, false, &walletdb);
         }
     }
@@ -2920,6 +2989,10 @@ isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum)
             {
                 return ISMINE_WATCH_ONLY;
             }
+            else
+            {
+                return ISMINE_NO;
+            }
         }
         else
         {
@@ -5017,7 +5090,9 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
                         }
                     }
 
-                    if (txout.IsDust(::minRelayTxFee))
+                    COptCCParams p;
+
+                    if (txout.IsDust(::minRelayTxFee) && !(txout.nValue == 0 && txout.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode != EVAL_NONE))
                     {
                         if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
                         {
@@ -5150,7 +5225,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
                     }
 
                     // Never create dust outputs; if we would, just
-                    // add the dust to the fee.
+                    // 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;
This page took 0.036185 seconds and 4 git commands to generate.