diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 136a909e7..b636ca338 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,7 +6,9 @@ stages: ####START#### PROJECT LEVEL VARIABLES ####START#### ######################################################################################################################## variables: - VERSION: 0.7.3-4 + + VERSION: 0.7.3-5 + VERUS_CLI_ARM64_LINUX: Verus-CLI-Linux-v${VERSION}-arm64.tar.gz VERUS_CLI_LINUX_X86_64: Verus-CLI-Linux-v${VERSION}-x86_64.tar.gz VERUS_CLI_WINDOWS: Verus-CLI-Windows-v${VERSION}.zip diff --git a/README.md b/README.md index 335168241..f92170466 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -## VerusCoin version 0.7.3-4 + +## VerusCoin version 0.7.3-5 Arguably the world's most advanced technology, zero knowledge privacy-centric blockchain, Verus Coin brings Sapling performance and zero knowledge features to an intelligent system with interchain smart contracts and a completely original, combined proof of stake/proof of work consensus algorithm that solves the nothing at stake problem. With this and its approach towards CPU mining and ASICs, Verus Coin strives to be one of the most naturally decentralizing and attack resistant blockchains in existence. diff --git a/doc/man/verus-cli/linux/README.txt b/doc/man/verus-cli/linux/README.txt index 37df1af85..12d6a86ed 100644 --- a/doc/man/verus-cli/linux/README.txt +++ b/doc/man/verus-cli/linux/README.txt @@ -1,4 +1,5 @@ -VerusCoin Command Line Tools v0.7.3-4 + +VerusCoin Command Line Tools v0.7.3-5 Contents: verusd - VerusCoin daemon diff --git a/doc/man/verus-cli/mac/README.txt b/doc/man/verus-cli/mac/README.txt index 52eb1484d..84de5be26 100644 --- a/doc/man/verus-cli/mac/README.txt +++ b/doc/man/verus-cli/mac/README.txt @@ -1,4 +1,6 @@ -VerusCoin Command Line Tools v0.7.3-4 + +VerusCoin Command Line Tools v0.7.3-5 + Contents: verusd - VerusCoin daemon. verus - VerusCoin command line utility. diff --git a/doc/man/verus-cli/windows/README.txt b/doc/man/verus-cli/windows/README.txt index 7880906c7..a63795e7a 100644 --- a/doc/man/verus-cli/windows/README.txt +++ b/doc/man/verus-cli/windows/README.txt @@ -1,4 +1,6 @@ -VerusCoin Command Line Tools v0.7.3-4 + +VerusCoin Command Line Tools v0.7.3-5 + Contents: verusd.exe - VerusCoin daemon verus.exe - VerusCoin command line utility diff --git a/src/deprecation.h b/src/deprecation.h index 748bcb57a..31d8bcfb9 100644 --- a/src/deprecation.h +++ b/src/deprecation.h @@ -8,7 +8,9 @@ // Deprecation policy: // * Shut down 20 weeks' worth of blocks after the estimated release block height. // * A warning is shown during the 2 weeks' worth of blocks prior to shut down. -static const int APPROX_RELEASE_HEIGHT = 1520000; + +static const int APPROX_RELEASE_HEIGHT = 1537000; + static const int WEEKS_UNTIL_DEPRECATION = 20; static const int DEPRECATION_HEIGHT = APPROX_RELEASE_HEIGHT + (WEEKS_UNTIL_DEPRECATION * 7 * 60 * 24); diff --git a/src/komodo_utils.h b/src/komodo_utils.h index c29dea5a7..24a19fbdb 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1845,8 +1845,6 @@ void komodo_args(char *argv0) mapArgs["-ac_supply"] = "5000000000000000"; mapArgs["-ac_eras"] = "1"; mapArgs["-ac_reward"] = "1200000000"; - std::string halving = GetArg("-ac_halving", "2111115"); // this assignment is required for an ARM compiler workaround - mapArgs["-ac_halving"] = halving; // allow testing easily with different values here mapArgs["-ac_decay"] = "0"; mapArgs["-ac_options"] = "72"; // OPTION_ID_REFERRALS + OPTION_CANBERESERVE mapArgs["-ac_end"] = "0"; @@ -1860,6 +1858,9 @@ void komodo_args(char *argv0) { LogPrintf("Config file for %s not found.\n", name.c_str()); } + + std::string halving = GetArg("-ac_halving", mapArgs.count("-ac_halving") ? mapArgs["-ac_halving"] : "2111115"); // this assignment is required for an ARM compiler workaround + mapArgs["-ac_halving"] = halving; // allow testing easily with different values here } else { diff --git a/src/main.cpp b/src/main.cpp index 530bd7a17..ba7738680 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1824,7 +1824,7 @@ bool AcceptToMemoryPoolInt(CTxMemPool& pool, CValidationState &state, const CTra if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now - //fprintf(stderr,"pool.mapNextTx.count\n"); + //printf("%s: outpoint already spent in mempool by tx: %s\n", __func__, pool.mapNextTx[outpoint].ptx->GetHash().GetHex().c_str()); return state.Invalid(false, REJECT_INVALID, "bad-txns-inputs-spent"); } } diff --git a/src/miner.h b/src/miner.h index efd03ac87..5e5e94012 100644 --- a/src/miner.h +++ b/src/miner.h @@ -29,6 +29,7 @@ struct CBlockTemplate /** Generate a new block, without valid proof-of-work */ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn, bool isStake=false); +CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const std::vector &minerOutputs, bool isStake=false); #ifdef ENABLE_WALLET boost::optional GetMinerScriptPubKey(CReserveKey& reservekey); CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, bool isStake=false); diff --git a/src/net.cpp b/src/net.cpp index 8c23b892c..65bcbbd29 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1274,9 +1274,14 @@ void ThreadSocketHandler() void ThreadDNSAddressSeed() { - // goal: only query DNS seeds if address need is acute + // goal: only query DNS seeds if address need is acute and connect is not set if ((addrman.size() > 0) && - (!GetBoolArg("-forcednsseed", false))) { + (!GetBoolArg("-forcednsseed", false))) + { + if (!(mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0)) + { + return; + } MilliSleep(11 * 1000); LOCK(cs_vNodes); diff --git a/src/pbaas/identity.cpp b/src/pbaas/identity.cpp index 971f28494..02ce5d4b8 100644 --- a/src/pbaas/identity.cpp +++ b/src/pbaas/identity.cpp @@ -529,6 +529,7 @@ bool ValidateSpendingIdentityReservation(const CTransaction &tx, int32_t outNum, // referrer must be mined in when this transaction is put into the mem pool if (heightOut >= height || !firstReferralIdentity.IsValid() || firstReferralIdentity.parent != ASSETCHAINS_CHAINID) { + //printf("%s: cannot find first instance of: %s\n", __func__, EncodeDestination(CIdentityID(newName.referral)).c_str()); return state.Error("Invalid identity registration referral"); } diff --git a/src/pbaas/pbaas.cpp b/src/pbaas/pbaas.cpp index 7386c667b..3165d12ab 100644 --- a/src/pbaas/pbaas.cpp +++ b/src/pbaas/pbaas.cpp @@ -1611,7 +1611,9 @@ bool CConnectedChains::GetReserveDeposits(const uint160 ¤cyID, const CCoin } for (auto &oneConfirmed : confirmedUTXOs) { - if (view.GetCoins(oneConfirmed.first.txhash, coin) && coin.IsAvailable(oneConfirmed.first.index)) + if (!mempool.mapNextTx.count(COutPoint(oneConfirmed.first.txhash, oneConfirmed.first.index)) && + view.GetCoins(oneConfirmed.first.txhash, coin) && + coin.IsAvailable(oneConfirmed.first.index)) { reserveDeposits.push_back(CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis, CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index))); @@ -1624,6 +1626,7 @@ bool CConnectedChains::GetReserveDeposits(const uint160 ¤cyID, const CCoin { COptCCParams p; if (!oneUnconfirmed.first.spending && + !mempool.mapNextTx.count(COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index)) && view.GetCoins(oneUnconfirmed.first.txhash, coin) && coin.IsAvailable(oneUnconfirmed.first.index)) { @@ -2462,20 +2465,38 @@ bool CConnectedChains::CreateLatestImports(const CCurrencyDefinition &sourceSyst } { + std::set txesToShow; for (auto &oneIn : newImportTx.vin) { if (!view.HaveCoins(oneIn.prevout.hash)) { - /*UniValue jsonTx(UniValue::VOBJ); - uint256 hashBlk; - TxToUniv(newImportTx, hashBlk, jsonTx); - printf("%s\n", jsonTx.write(1,2).c_str()); //*/ printf("%s: cannot find input in view %s\n", __func__, oneIn.prevout.hash.GetHex().c_str()); } + else + { + txesToShow.insert(oneIn.prevout.hash); + } } + /* DEBUG output only + for (auto &oneTxId : txesToShow) + { + CTransaction inputTx; + uint256 inputBlkHash; + if (myGetTransaction(oneTxId, inputTx, inputBlkHash)) + { + UniValue uni(UniValue::VOBJ); + TxToUniv(inputTx, inputBlkHash, uni); + printf("%s: inputTx:\n%s\n", __func__, uni.write(1,2).c_str()); + } + else + { + printf("%s: unable to retrieve input transaction: %s\n", __func__, oneTxId.GetHex().c_str()); + } + } //*/ + // put our transaction in place of any others - std::list removed; + //std::list removed; //mempool.removeConflicts(newImportTx, removed); // add to mem pool and relay diff --git a/src/pbaas/reserves.cpp b/src/pbaas/reserves.cpp index 40bacade2..af6684d0e 100644 --- a/src/pbaas/reserves.cpp +++ b/src/pbaas/reserves.cpp @@ -2492,6 +2492,7 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre // convert fees to next destination native, if necessary/possible CCurrencyDefinition curNextDest = ConnectedChains.GetCachedCurrency(curTransfer.destination.gatewayID); uint160 nextDestSysID = curNextDest.IsGateway() ? curNextDest.gatewayID : curNextDest.systemID; + // if it's already in the correct currency, nothing to do, otherwise convert if we can if (curTransfer.feeCurrencyID != nextDestSysID) { @@ -2534,6 +2535,16 @@ bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurre AddReserveOutput(nextDestSysID, reserveFromFrac); AddReserveOutConverted(nextDestSysID, reserveFromFrac); + } else + { + if (curTransfer.feeCurrencyID == systemDestID) + { + nativeOut = curTransfer.destination.fees; + } + else + { + AddReserveOutput(nextDestSysID, curTransfer.destination.fees); + } } } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index c72e5f23d..f50cb198d 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -533,7 +533,7 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) UniValue chainNames(UniValue::VARR); for (auto chain : chains) { - chainNames.push_back(chain.name); + chainNames.push_back(EncodeDestination(CIdentityID(chain.GetID())) + " (" + chain.name + ")"); } obj.push_back(Pair("mergeminedchains", chainNames)); } @@ -605,9 +605,14 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) "\nArguments:\n" "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n" " {\n" - " \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n" - " \"capabilities\":[ (array, optional) A list of strings\n" - " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n" + " \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n" + " \"rewarddistribution\":{\n" + " \"(recipientaddress)\":n, (addressorid, relativeweight) key value to determine distribution\n" + " \"(recipientaddress)\":n,\n" + " \"...\n" + " \"}\n" + " \"capabilities\":[ (array, optional) A list of strings\n" + " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n" " ,...\n" " ]\n" " }\n" @@ -679,6 +684,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) { const UniValue& oparam = params[0].get_obj(); const UniValue& modeval = find_value(oparam, "mode"); + if (modeval.isStr()) strMode = modeval.get_str(); else if (modeval.isNull()) @@ -808,12 +814,46 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) delete pblocktemplate; pblocktemplate = NULL; } + + UniValue recipientWeights; + if (params.size() > 0 && + (recipientWeights = find_value(params[0], "recipientdistribution")).isArray() && + recipientWeights.size() && + recipientWeights[0].isObject()) + { + std::vector minerOutputs; + for (int i = 0; i < recipientWeights.size(); i++) + { + if (!recipientWeights[i].isObject()) + { + throw JSONRPCError(RPC_INVALID_PARAMS, "recipientdistribution must be an array of objects, each specifing a valid address as key and relative weight value"); + } + std::vector keys = recipientWeights[i].getKeys(); + std::vector values = recipientWeights[i].getValues(); + if (keys.size() != 1 || values.size() != 1) + { + throw JSONRPCError(RPC_INVALID_PARAMS, "Each object in recipientdistribution array must have one valid address as key and one relative weight value"); + } + CTxDestination oneDest = DecodeDestination(keys[0]); + CAmount relVal = 0; + if (oneDest.which() == COptCCParams::ADDRTYPE_INVALID || + !(relVal = uni_get_int64(values[0]))) + { + throw JSONRPCError(RPC_INVALID_PARAMS, "Invalid destination or zero weight specified in recipientdistribution array"); + } + minerOutputs.push_back(CTxOut(relVal, GetScriptForDestination(oneDest))); + } + pblocktemplate = CreateNewBlock(Params(), minerOutputs, false); + } + else + { #ifdef ENABLE_WALLET - CReserveKey reservekey(pwalletMain); - pblocktemplate = CreateNewBlockWithKey(reservekey,chainActive.LastTip()->GetHeight()+1); + CReserveKey reservekey(pwalletMain); + pblocktemplate = CreateNewBlockWithKey(reservekey, chainActive.LastTip()->GetHeight()+1); #else - pblocktemplate = CreateNewBlockWithKey(); + pblocktemplate = CreateNewBlockWithKey(); #endif + } /* keep Zcash script-based approach for reference boost::shared_ptr coinbaseScript; diff --git a/src/rpc/pbaasrpc.cpp b/src/rpc/pbaasrpc.cpp index 675511a51..764dfadc0 100644 --- a/src/rpc/pbaasrpc.cpp +++ b/src/rpc/pbaasrpc.cpp @@ -331,27 +331,54 @@ std::pair ValidateTransferDestination(const std:: // set default peer nodes in the current connected chains bool SetPeerNodes(const UniValue &nodes) { - if (!nodes.isArray() || nodes.size() == 0) + if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { - return false; + printf("%s: Ignoring seednodes due to nodes specified in \"-connect\" parameter\n", __func__); + LogPrintf("%s: Ignoring seednodes due to nodes specified in \"-connect\" parameter\n", __func__); + std::vector connectNodes = mapMultiArgs["-connect"]; + for (int i = 0; i < connectNodes.size(); i++) + { + CNodeData oneNode = CNodeData(connectNodes[i], ""); + if (oneNode.networkAddress != "") + { + ConnectedChains.defaultPeerNodes.push_back(oneNode); + } + } } + else + { + if (!nodes.isArray() || nodes.size() == 0) + { + return false; + } - LOCK(ConnectedChains.cs_mergemining); - ConnectedChains.defaultPeerNodes.clear(); + LOCK(ConnectedChains.cs_mergemining); + ConnectedChains.defaultPeerNodes.clear(); - for (int i = 0; i < nodes.size(); i++) - { - CNodeData oneNode(nodes[i]); - if (oneNode.networkAddress != "") + for (int i = 0; i < nodes.size(); i++) { - ConnectedChains.defaultPeerNodes.push_back(oneNode); + CNodeData oneNode(nodes[i]); + if (oneNode.networkAddress != "") + { + ConnectedChains.defaultPeerNodes.push_back(oneNode); + } + } + + std::vector seedNodes = mapMultiArgs["-seednode"]; + for (int i = 0; i < seedNodes.size(); i++) + { + CNodeData oneNode = CNodeData(seedNodes[i], ""); + if (oneNode.networkAddress != "") + { + ConnectedChains.defaultPeerNodes.push_back(oneNode); + } } } - std::vector seedNodes = mapMultiArgs["-seednode"]; - for (int i = 0; i < seedNodes.size(); i++) + std::vector addNodes = mapMultiArgs["-addnode"]; + for (int i = 0; i < addNodes.size(); i++) { - CNodeData oneNode = CNodeData(seedNodes[i], ""); + CNodeData oneNode = CNodeData(addNodes[i], ""); if (oneNode.networkAddress != "") { ConnectedChains.defaultPeerNodes.push_back(oneNode); @@ -360,16 +387,22 @@ bool SetPeerNodes(const UniValue &nodes) // set all command line parameters into mapArgs from chain definition vector nodeStrs; + for (auto node : ConnectedChains.defaultPeerNodes) { nodeStrs.push_back(node.networkAddress); } - mapMultiArgs["-seednode"] = nodeStrs; - for (auto &oneNode : seedNodes) + if (!(mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0)) + { + mapMultiArgs["-seednode"] = nodeStrs; + } + + for (auto &oneNode : nodeStrs) { AddOneShot(oneNode); } + if (int port = ConnectedChains.GetThisChainPort()) { mapArgs["-port"] = to_string(port); @@ -3154,7 +3187,7 @@ UniValue estimateconversion(const UniValue& params, bool fHelp) { throw runtime_error( "estimateconversion '{\"currency\":\"name\",\"convertto\":\"name\",\"amount\":n}'\n" - "\nThis estimates conversion from one currency to another, taking into account pending conversions and slippage.\n" + "\nThis estimates conversion from one currency to another, taking into account pending conversions, fees and slippage.\n" "\nArguments\n" "1. {\n" @@ -3360,11 +3393,15 @@ UniValue sendcurrency(const UniValue& params, bool fHelp) " \"currency\": \"name\" (string, required) Name of the source currency to send in this output, defaults to native of chain\n" " \"amount\":amount (numeric, required) The numeric amount of currency, denominated in source currency\n" " \"convertto\":\"name\", (string, optional) Valid currency to convert to, either a reserve of a fractional, or fractional\n" + " \"exportto\":\"name\", (string, optional) Valid chain or system name or ID to export to\n" + " \"feecurrency\":\"name\", (string, optional) Valid currency that should be pulled from the current wallet and used to pay fee\n" " \"via\":\"name\", (string, optional) If source and destination currency are reserves, via is a common fractional to convert through\n" " \"address\":\"dest\" (string, required) The address and optionally chain/system after the \"@\" as a system specific destination\n" " \"refundto\":\"dest\" (string, optional) For pre-conversions, this is where refunds will go, defaults to fromaddress\n" " \"memo\":memo (string, optional) If destination is a zaddr (not supported on testnet), a string message (not hexadecimal) to include.\n" " \"preconvert\":\"false\", (bool, optional) convert to currency at market price (default=false), only works if transaction is mined before start of currency\n" + " \"burn\":\"false\", (bool, optional) destroy the currency and subtract it from the supply. Currency must be a token.\n" + " \"mintnew\":\"false\", (bool, optional) if the transaction is sent from the currency ID of a centralized currency, this creates new currency to send\n" " }, ... ]\n" "3. \"feeamount\" (bool, optional) specific fee amount requested instead of default miner's fee\n" @@ -3491,9 +3528,10 @@ UniValue sendcurrency(const UniValue& params, bool fHelp) exportToStr.size() || burnCurrency || mintNew || - preConvert)) + preConvert || + sourceCurrencyID != ASSETCHAINS_CHAINID)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert, preconvert, mint, cross-chain send, or burn currency being sent to a z-address."); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot convert, preconvert, mint, cross-chain send, burn or send non-native currency when sending to a z-address."); } // re-encode destination, in case it is specified as the private address of an ID @@ -4078,7 +4116,7 @@ UniValue sendcurrency(const UniValue& params, bool fHelp) // if we're converting and then sending, we don't need an initial fee, so all // fees go into the final destination dest.type |= dest.FLAG_DEST_GATEWAY; - dest.gatewayID = destSystemID; + dest.gatewayID = exportSystemDef.GetID(); CChainNotarizationData cnd; if (!GetNotarizationData(convertToCurrencyID, cnd) || !cnd.IsConfirmed()) @@ -4912,9 +4950,16 @@ UniValue definecurrency(const UniValue& params, bool fHelp) "name and ID as the currency being defined.\n" "\nArguments\n" " {\n" - " \"options\" : n, (int, optional) bits:\n" - " 1 = FRACTIONAL, 2 = IDRESTRICTED, 4 = IDSTAKING, 8 = IDREFERRALS\n" - " 0x10 = IDREFERRALSREQUIRED, 0x20 = TOKEN, 0x40 = CANBERESERVE\n" + " \"options\" : n, (int, optional) bits (in hexadecimal):\n" + " 1 = FRACTIONAL\n" + " 2 = IDRESTRICTED\n" + " 4 = IDSTAKING\n" + " 8 = IDREFERRALS\n" + " 0x10 = IDREFERRALSREQUIRED\n" + " 0x20 = TOKEN\n" + " 0x40 = CANBERESERVE\n" + " 0x100 = IS_PBAAS_CHAIN\n" + "\n" " \"name\" : \"xxxx\", (string, required) name of existing identity with no active or pending blockchain\n" " \"idregistrationprice\" : \"xx.xx\", (value, required) price of an identity in native currency\n" " \"idreferrallevels\" : n, (int, required) how many levels ID referrals go back in reward\n" diff --git a/src/script/script.cpp b/src/script/script.cpp index 65bdfba89..c3171a6d8 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1232,7 +1232,9 @@ std::set COptCCParams::GetIndexKeys() const CNameReservation nameRes; if (vData.size() && (nameRes = CNameReservation(vData[0])).IsValid()) { - destinations.insert(CIndexID(CCrossChainRPCData::GetConditionID(nameRes.name, evalCode))); + uint160 parent = ASSETCHAINS_CHAINID; + uint160 ourID = CIdentity::GetID(nameRes.name, parent); + destinations.insert(CIndexID(CCrossChainRPCData::GetConditionID(ourID, evalCode))); } break; } diff --git a/src/version.h b/src/version.h index 179649629..b69bed3e7 100644 --- a/src/version.h +++ b/src/version.h @@ -35,6 +35,6 @@ static const int MEMPOOL_GD_VERSION = 60002; static const int NO_BLOOM_VERSION = 170004; #define KOMODO_VERSION "0.2.1" -#define VERUS_VERSION "0.7.3-4" +#define VERUS_VERSION "0.7.3-5" #endif // BITCOIN_VERSION_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a57a226ff..31a1d2b50 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2669,14 +2669,15 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl { if (unspentOutputs.size() > MAX_OUR_UTXOS_ID_RESCAN) { - continue; + unspentOutputs.clear(); } // the exception would currently be if all of the following are true: // 1) We have spending, not just signing power over the ID, // 2) the ID has no separate revoke and recover, so it cannot be pulled back, and // 3) the ID does not have an average of < 0.00001 in native outputs of a random sample // of its UTXOs - if (canSignCanSpend.second && + if (unspentOutputs.size() && + canSignCanSpend.second && identity.revocationAuthority == identity.recoveryAuthority && identity.revocationAuthority == idID) { @@ -2703,12 +2704,12 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl if (!counted.size() || (total / (CAmount)counted.size() < 10000)) { - continue; + unspentOutputs.clear(); } } - else + else if (unspentOutputs.size()) { - continue; + unspentOutputs.clear(); } } @@ -2752,6 +2753,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl { CAddressUnspentKey unspentKey(CScript::P2ID, idID, wtx.first, k); CBlockIndex *pIndex = mapBlockIndex[wtx.second.hashBlock]; + unspentTxSet.insert(wtx.first); unspentOutputs.push_back( make_pair(unspentKey, CAddressUnspentValue(oneOut.nValue, oneOut.scriptPubKey, pIndex->GetHeight()))); // if we add one on a tx, no need to check more here @@ -2808,13 +2810,12 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl continue; } wtx.SetMerkleBranch(block); + AddToWallet(wtx, false, &walletdb); } - - AddToWallet(wtx, false, &walletdb); pWtx = GetWalletTx(newOut.first.txhash); } } - else if (pWtx = GetWalletTx(newOut.first.txhash)) + else if (pWtx) { if (!(ExtractDestinations(newOut.second.script, newTypeRet, newAddressRet, newNRequired, this, &newCanSign, &newCanSpend, nHeight == 0 ? INT_MAX : nHeight + 1) && newCanSign)) { @@ -2823,7 +2824,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl } // if we were or are now in the wallet, we need to see if we should record new spends - if (pWtx != nullptr && newOut.second.script.IsPayToCryptoCondition()) + if (pWtx) { // while we know there is an unspent index to this ID on the new transaction output, we don't know // if there are other outputs to this ID on the transaction, which are already spent. @@ -2831,17 +2832,22 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl // not consider them spent. uint256 spendBlkHash; CTransaction spendTx; - std::vector checkIfSpent = pWtx->vout; + std::vector> checkIfSpent; + for (int counter = 0; counter < pWtx->vout.size(); counter++) + { + checkIfSpent.push_back(std::make_pair(pWtx->GetHash(), counter)); + } + const CWalletTx *txToCheck = pWtx; for (int i = 0; i < checkIfSpent.size(); i++) { - // if it really came from the unspent index and is the same output, don't bother looking for a spend - if (unspentTxSet.count(newOut.first.txhash) && newOut.first.index == i) + if (txToCheck->GetHash() != checkIfSpent[i].first) { - continue; + txToCheck = GetWalletTx(checkIfSpent[i].first); } // if we can't spend it, no need to check for spends - if (!(ExtractDestinations(checkIfSpent[i].scriptPubKey, + if (!(txToCheck && + ExtractDestinations(txToCheck->vout[checkIfSpent[i].second].scriptPubKey, newTypeRet, newAddressRet, newNRequired, @@ -2854,12 +2860,18 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl } CSpentIndexValue spentInfo; - CSpentIndexKey spentKey(newOut.first.txhash, i); - if (GetSpentIndex(spentKey, spentInfo)) + CSpentIndexKey spentKey(checkIfSpent[i].first, checkIfSpent[i].second); + // if it's spent, we need to put spender in the wallet + // if the spender has outputs that we can now spend due to the ID, + // we need to check for those being spent as well + if (GetSpentIndex(spentKey, spentInfo) && + !spentInfo.IsNull()) { - if (GetWalletTx(spentInfo.txid) == nullptr && + const CWalletTx *pSpendingTx = GetWalletTx(spentInfo.txid); + if (pSpendingTx == nullptr && spentInfo.blockHeight <= nHeight && - myGetTransaction(spentInfo.txid, spendTx, spendBlkHash) && !spendBlkHash.IsNull()) + myGetTransaction(spentInfo.txid, spendTx, spendBlkHash) && + !spendBlkHash.IsNull()) { CWalletTx spendWtx(this, spendTx); @@ -2876,7 +2888,19 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl // add these outputs to the outputs we need to check if spent // as long as we are adding spending transactions that are earlier // or up to this height, we follow the spends - checkIfSpent.insert(checkIfSpent.end(), spendTx.vout.begin(), spendTx.vout.end()); + for (int counter = 0; counter < spendTx.vout.size(); counter++) + { + checkIfSpent.push_back(std::make_pair(spendTx.GetHash(), counter)); + } + } + } + else if (pSpendingTx && + spentInfo.blockHeight <= nHeight) + { + // we may have outputs on a spending transaction that should be considered as well + for (int counter = 0; counter < pSpendingTx->vout.size(); counter++) + { + checkIfSpent.push_back(std::make_pair(pSpendingTx->GetHash(), counter)); } } }