diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index a6d8cbd7a6b0..16e4eed4062a 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -62,7 +62,9 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con CPrivateSend::UpdatedBlockTip(pindexNew); #ifdef ENABLE_WALLET - privateSendClient.UpdatedBlockTip(pindexNew); + for (auto& pair : privateSendClientManagers) { + pair.second->UpdatedBlockTip(pindexNew); + } #endif // ENABLE_WALLET llmq::quorumInstantSendManager->UpdatedBlockTip(pindexNew); diff --git a/src/httprpc.cpp b/src/httprpc.cpp index c2b01b3c820f..7fc6fc0dc63a 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -258,6 +258,9 @@ void StopHTTPRPC() { LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n"); UnregisterHTTPHandler("/", true); +#ifdef ENABLE_WALLET + UnregisterHTTPHandler("/wallet/", false); +#endif if (httpRPCTimerInterface) { RPCUnsetTimerInterface(httpRPCTimerInterface.get()); httpRPCTimerInterface.reset(); diff --git a/src/masternode/masternode-utils.cpp b/src/masternode/masternode-utils.cpp index ae3ae41c9df5..ec7bc0f3ff3e 100644 --- a/src/masternode/masternode-utils.cpp +++ b/src/masternode/masternode-utils.cpp @@ -24,7 +24,9 @@ void CMasternodeUtils::ProcessMasternodeConnections(CConnman& connman) { std::vector vecDmns; // will be empty when no wallet #ifdef ENABLE_WALLET - privateSendClient.GetMixingMasternodesInfo(vecDmns); + for (const auto& pair : privateSendClientManagers) { + pair.second->GetMixingMasternodesInfo(vecDmns); + } #endif // ENABLE_WALLET // Don't disconnect masternode connections when we have less then the desired amount of outbound nodes diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 5b0ff964e759..5a9756863c15 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -3542,7 +3542,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { //probably one the extensions #ifdef ENABLE_WALLET - privateSendClient.ProcessMessage(pfrom, strCommand, vRecv, *connman, enable_bip61); + privateSendClientQueueManager.ProcessMessage(pfrom, strCommand, vRecv, *connman, enable_bip61); + for (auto& pair : privateSendClientManagers) { + pair.second->ProcessMessage(pfrom, strCommand, vRecv, *connman, enable_bip61); + } #endif // ENABLE_WALLET privateSendServer.ProcessMessage(pfrom, strCommand, vRecv, *connman, enable_bip61); sporkManager.ProcessSpork(pfrom, strCommand, vRecv, *connman); diff --git a/src/privatesend/privatesend-client.cpp b/src/privatesend/privatesend-client.cpp index 0ff222af023e..fe4cc044eac7 100644 --- a/src/privatesend/privatesend-client.cpp +++ b/src/privatesend/privatesend-client.cpp @@ -22,18 +22,20 @@ #include #include -CPrivateSendClientManager privateSendClient; +std::map privateSendClientManagers; +CPrivateSendClientQueueManager privateSendClientQueueManager; -void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, bool enable_bip61) +CPrivateSendClientOptions* CPrivateSendClientOptions::_instance{nullptr}; +std::once_flag CPrivateSendClientOptions::onceFlag; + +void CPrivateSendClientQueueManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, bool enable_bip61) { if (fMasternodeMode) return; - if (!fEnablePrivateSend) return; + if (!CPrivateSendClientOptions::IsEnabled()) return; if (!masternodeSync.IsBlockchainSynced()) return; if (!CheckDiskSpace()) { - ResetPool(); - fPrivateSendRunning = false; - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::ProcessMessage -- Not enough disk space, disabling PrivateSend.\n"); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientQueueManager::ProcessMessage -- Not enough disk space, disabling PrivateSend.\n"); return; } @@ -84,12 +86,9 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string& // if the queue is ready, submit if we can if (dsq.fReady) { - LOCK(cs_deqsessions); - for (auto& session : deqSessions) { - CDeterministicMNCPtr mnMixing; - if (session.GetMixingMasternodeInfo(mnMixing) && mnMixing->pdmnState->addr == dmn->pdmnState->addr && session.GetState() == POOL_STATE_QUEUE) { + for (auto& pair : privateSendClientManagers) { + if (pair.second->TrySubmitDenominate(dmn->pdmnState->addr, connman)) { LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- PrivateSend queue (%s) is ready on masternode %s\n", dsq.ToString(), dmn->pdmnState->addr.ToString()); - session.SubmitDenominate(connman); return; } } @@ -107,11 +106,9 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string& LogPrint(BCLog::PRIVATESEND, "DSQUEUE -- new PrivateSend queue (%s) from masternode %s\n", dsq.ToString(), dmn->pdmnState->addr.ToString()); - LOCK(cs_deqsessions); - for (auto& session : deqSessions) { - CDeterministicMNCPtr mnMixing; - if (session.GetMixingMasternodeInfo(mnMixing) && mnMixing->collateralOutpoint == dsq.masternodeOutpoint) { - dsq.fTried = true; + for (const auto& pair : privateSendClientManagers) { + if (pair.second->MarkAlreadyJoinedQueueAsTried(dsq)) { + break; } } @@ -121,7 +118,23 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string& dsq.Relay(connman); } - } else if (strCommand == NetMsgType::DSSTATUSUPDATE || + } +} + +void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, bool enable_bip61) +{ + if (fMasternodeMode) return; + if (!CPrivateSendClientOptions::IsEnabled()) return; + if (!masternodeSync.IsBlockchainSynced()) return; + + if (!CheckDiskSpace()) { + ResetPool(); + StopMixing(); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::ProcessMessage -- Not enough disk space, disabling PrivateSend.\n"); + return; + } + + if (strCommand == NetMsgType::DSSTATUSUPDATE || strCommand == NetMsgType::DSFINALTX || strCommand == NetMsgType::DSCOMPLETE) { LOCK(cs_deqsessions); @@ -134,7 +147,7 @@ void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string& void CPrivateSendClientSession::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, bool enable_bip61) { if (fMasternodeMode) return; - if (!privateSendClient.fEnablePrivateSend) return; + if (!CPrivateSendClientOptions::IsEnabled()) return; if (!masternodeSync.IsBlockchainSynced()) return; if (strCommand == NetMsgType::DSSTATUSUPDATE) { @@ -225,6 +238,24 @@ void CPrivateSendClientSession::ProcessMessage(CNode* pfrom, const std::string& } } +bool CPrivateSendClientManager::StartMixing(CWallet* pwallet) { + if (IsMixing()) { + return false; + } + assert(pwallet != nullptr); + mixingWallet = pwallet; + return true; +} + +void CPrivateSendClientManager::StopMixing() { + mixingWallet = nullptr; +} + +bool CPrivateSendClientManager::IsMixing() const +{ + return mixingWallet != nullptr; +} + void CPrivateSendClientSession::ResetPool() { txMyCollateral = CMutableTransaction(); @@ -258,16 +289,16 @@ void CPrivateSendClientSession::SetNull() // void CPrivateSendClientSession::UnlockCoins() { - if (!privateSendClient.fEnablePrivateSend) return; + if (!CPrivateSendClientOptions::IsEnabled()) return; while (true) { - TRY_LOCK(GetWallets()[0]->cs_wallet, lockWallet); + TRY_LOCK(mixingWallet->cs_wallet, lockWallet); if (!lockWallet) { MilliSleep(50); continue; } for (const auto& outpoint : vecOutPointLocked) - GetWallets()[0]->UnlockCoin(outpoint); + mixingWallet->UnlockCoin(outpoint); break; } @@ -398,9 +429,7 @@ void CPrivateSendClientManager::CheckTimeout() { if (fMasternodeMode) return; - CheckQueue(); - - if (!fEnablePrivateSend || !fPrivateSendRunning) return; + if (!CPrivateSendClientOptions::IsEnabled() || !IsMixing()) return; LOCK(cs_deqsessions); for (auto& session : deqSessions) { @@ -524,7 +553,7 @@ void CPrivateSendClientSession::ProcessPoolStateUpdate(CPrivateSendStatusUpdate // bool CPrivateSendClientSession::SignFinalTransaction(const CTransaction& finalTransactionNew, CNode* pnode, CConnman& connman) { - if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; if (fMasternodeMode || pnode == nullptr) return false; if (!mixingMasternode) return false; @@ -604,7 +633,7 @@ bool CPrivateSendClientSession::SignFinalTransaction(const CTransaction& finalTr return false; } - const CKeyStore& keystore = *GetWallets()[0]; + const CKeyStore& keystore = *mixingWallet; LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- Signing my input %i\n", __func__, nMyInputIndex); // TODO we're using amount=0 here but we should use the correct amount. This works because Dash ignores the amount while signing/verifying (only used in Bitcoin/Segwit) @@ -645,7 +674,7 @@ void CPrivateSendClientSession::CompletedTransaction(PoolMessage nMessageID) if (nMessageID == MSG_SUCCESS) { LogPrint(BCLog::PRIVATESEND, "CompletedTransaction -- success\n"); - privateSendClient.UpdatedSuccessBlock(); + privateSendClientManagers.at(mixingWallet->GetName())->UpdatedSuccessBlock(); keyHolderStorage.KeepAll(); } else { LogPrint(BCLog::PRIVATESEND, "CompletedTransaction -- error\n"); @@ -666,21 +695,21 @@ bool CPrivateSendClientManager::WaitForAnotherBlock() { if (!masternodeSync.IsBlockchainSynced()) return true; - if (fPrivateSendMultiSession) return false; + if (CPrivateSendClientOptions::IsMultiSessionEnabled()) return false; return nCachedBlockHeight - nCachedLastSuccessBlock < nMinBlocksToWait; } bool CPrivateSendClientManager::CheckAutomaticBackup() { - if (!fEnablePrivateSend || !fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !IsMixing()) return false; switch (nWalletBackups) { case 0: LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Automatic backups disabled, no mixing available.\n"); strAutoDenomResult = _("Automatic backups disabled") + ", " + _("no mixing available."); - fPrivateSendRunning = false; // stop mixing - GetWallets()[0]->nKeysLeftSinceAutoBackup = 0; // no backup, no "keys since last backup" + StopMixing(); + mixingWallet->nKeysLeftSinceAutoBackup = 0; // no backup, no "keys since last backup" return false; case -1: // Automatic backup failed, nothing else we can do until user fixes the issue manually. @@ -698,24 +727,24 @@ bool CPrivateSendClientManager::CheckAutomaticBackup() return false; } - if (GetWallets()[0]->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_STOP) { + if (mixingWallet->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_STOP) { // We should never get here via mixing itself but probably something else is still actively using keypool - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Very low number of keys left: %d, no mixing available.\n", GetWallets()[0]->nKeysLeftSinceAutoBackup); - strAutoDenomResult = strprintf(_("Very low number of keys left: %d") + ", " + _("no mixing available."), GetWallets()[0]->nKeysLeftSinceAutoBackup); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Very low number of keys left: %d, no mixing available.\n", mixingWallet->nKeysLeftSinceAutoBackup); + strAutoDenomResult = strprintf(_("Very low number of keys left: %d") + ", " + _("no mixing available."), mixingWallet->nKeysLeftSinceAutoBackup); // It's getting really dangerous, stop mixing - fPrivateSendRunning = false; + StopMixing(); return false; - } else if (GetWallets()[0]->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { + } else if (mixingWallet->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { // Low number of keys left but it's still more or less safe to continue - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Very low number of keys left: %d\n", GetWallets()[0]->nKeysLeftSinceAutoBackup); - strAutoDenomResult = strprintf(_("Very low number of keys left: %d"), GetWallets()[0]->nKeysLeftSinceAutoBackup); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Very low number of keys left: %d\n", mixingWallet->nKeysLeftSinceAutoBackup); + strAutoDenomResult = strprintf(_("Very low number of keys left: %d"), mixingWallet->nKeysLeftSinceAutoBackup); if (fCreateAutoBackups) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Trying to create new backup.\n"); std::string warningString; std::string errorString; - if (!GetWallets()[0]->AutoBackupWallet("", warningString, errorString)) { + if (!mixingWallet->AutoBackupWallet("", warningString, errorString)) { if (!warningString.empty()) { // There were some issues saving backup but yet more or less safe to continue LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- WARNING! Something went wrong on automatic backup: %s\n", warningString); @@ -733,7 +762,7 @@ bool CPrivateSendClientManager::CheckAutomaticBackup() } } - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Keys left since latest backup: %d\n", GetWallets()[0]->nKeysLeftSinceAutoBackup); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Keys left since latest backup: %d\n", mixingWallet->nKeysLeftSinceAutoBackup); return true; } @@ -751,15 +780,15 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool return false; } - if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; CAmount nBalanceNeedsAnonymized; { LOCK2(cs_main, mempool.cs); - LOCK(GetWallets()[0]->cs_wallet); + LOCK(mixingWallet->cs_wallet); - if (!fDryRun && GetWallets()[0]->IsLocked(true)) { + if (!fDryRun && mixingWallet->IsLocked(true)) { strAutoDenomResult = _("Wallet is locked."); return false; } @@ -783,8 +812,8 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool } // check if there is anything left to do - CAmount nBalanceAnonymized = GetWallets()[0]->GetAnonymizedBalance(); - nBalanceNeedsAnonymized = privateSendClient.nPrivateSendAmount*COIN - nBalanceAnonymized; + CAmount nBalanceAnonymized = mixingWallet->GetAnonymizedBalance(); + nBalanceNeedsAnonymized = CPrivateSendClientOptions::GetAmount() * COIN - nBalanceAnonymized; if (nBalanceNeedsAnonymized < 0) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::DoAutomaticDenominating -- Nothing to do\n"); @@ -795,13 +824,13 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool CAmount nValueMin = CPrivateSend::GetSmallestDenomination(); // if there are no confirmed DS collateral inputs yet - if (!GetWallets()[0]->HasCollateralInputs()) { + if (!mixingWallet->HasCollateralInputs()) { // should have some additional amount for them nValueMin += CPrivateSend::GetMaxCollateralAmount(); } // including denoms but applying some restrictions - CAmount nBalanceAnonymizable = GetWallets()[0]->GetAnonymizableBalance(); + CAmount nBalanceAnonymizable = mixingWallet->GetAnonymizableBalance(); // mixable balance is way too small if (nBalanceAnonymizable < nValueMin) { @@ -811,12 +840,12 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool } // excluding denoms - CAmount nBalanceAnonimizableNonDenom = GetWallets()[0]->GetAnonymizableBalance(true); + CAmount nBalanceAnonimizableNonDenom = mixingWallet->GetAnonymizableBalance(true); // denoms - CAmount nBalanceDenominatedConf = GetWallets()[0]->GetDenominatedBalance(); - CAmount nBalanceDenominatedUnconf = GetWallets()[0]->GetDenominatedBalance(true); + CAmount nBalanceDenominatedConf = mixingWallet->GetDenominatedBalance(); + CAmount nBalanceDenominatedUnconf = mixingWallet->GetDenominatedBalance(true); CAmount nBalanceDenominated = nBalanceDenominatedConf + nBalanceDenominatedUnconf; - CAmount nBalanceToDenominate = privateSendClient.nPrivateSendAmount * COIN - nBalanceDenominated; + CAmount nBalanceToDenominate = CPrivateSendClientOptions::GetAmount() * COIN - nBalanceDenominated; // adjust nBalanceNeedsAnonymized to consume final denom if (nBalanceDenominated - nBalanceAnonymized > nBalanceNeedsAnonymized) { @@ -863,8 +892,8 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool } //check if we have the collateral sized inputs - if (!GetWallets()[0]->HasCollateralInputs()) { - return !GetWallets()[0]->HasCollateralInputs(false) && MakeCollateralAmounts(connman); + if (!mixingWallet->HasCollateralInputs()) { + return !mixingWallet->HasCollateralInputs(false) && MakeCollateralAmounts(connman); } if (nSessionID) { @@ -879,7 +908,7 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool SetNull(); // should be no unconfirmed denoms in non-multi-session mode - if (!privateSendClient.fPrivateSendMultiSession && nBalanceDenominatedUnconf > 0) { + if (!CPrivateSendClientOptions::IsMultiSessionEnabled() && nBalanceDenominatedUnconf > 0) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::DoAutomaticDenominating -- Found unconfirmed denominated outputs, will wait till they confirm to continue.\n"); strAutoDenomResult = _("Found unconfirmed denominated outputs, will wait till they confirm to continue."); return false; @@ -888,14 +917,14 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool //check our collateral and create new if needed std::string strReason; if (txMyCollateral == CMutableTransaction()) { - if (!GetWallets()[0]->CreateCollateralTransaction(txMyCollateral, strReason)) { + if (!mixingWallet->CreateCollateralTransaction(txMyCollateral, strReason)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::DoAutomaticDenominating -- create collateral error:%s\n", strReason); return false; } } else { if (!CPrivateSend::IsCollateralValid(txMyCollateral)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::DoAutomaticDenominating -- invalid collateral, recreating...\n"); - if (!GetWallets()[0]->CreateCollateralTransaction(txMyCollateral, strReason)) { + if (!mixingWallet->CreateCollateralTransaction(txMyCollateral, strReason)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::DoAutomaticDenominating -- create collateral error: %s\n", strReason); return false; } @@ -903,10 +932,10 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool } // lock the funds we're going to use for our collateral for (const auto& txin : txMyCollateral.vin) { - GetWallets()[0]->LockCoin(txin.prevout); + mixingWallet->LockCoin(txin.prevout); vecOutPointLocked.push_back(txin.prevout); } - } // LOCK2(cs_main, GetWallets()[0]->cs_wallet); + } // LOCK2(cs_main, mixingWallet->cs_wallet); // Always attempt to join an existing queue if (JoinExistingQueue(nBalanceNeedsAnonymized, connman)) { @@ -923,14 +952,14 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool bool CPrivateSendClientManager::DoAutomaticDenominating(CConnman& connman, bool fDryRun) { if (fMasternodeMode) return false; // no client-side mixing on masternodes - if (!fEnablePrivateSend || !fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !IsMixing()) return false; if (!masternodeSync.IsBlockchainSynced()) { strAutoDenomResult = _("Can't mix while sync in progress."); return false; } - if (!fDryRun && GetWallets()[0]->IsLocked(true)) { + if (!fDryRun && mixingWallet->IsLocked(true)) { strAutoDenomResult = _("Wallet is locked."); return false; } @@ -949,8 +978,8 @@ bool CPrivateSendClientManager::DoAutomaticDenominating(CConnman& connman, bool LOCK(cs_deqsessions); bool fResult = true; - if ((int)deqSessions.size() < nPrivateSendSessions) { - deqSessions.emplace_back(); + if ((int)deqSessions.size() < CPrivateSendClientOptions::GetSessions()) { + deqSessions.emplace_back(mixingWallet); } for (auto& session : deqSessions) { if (!CheckAutomaticBackup()) return false; @@ -1013,13 +1042,13 @@ CDeterministicMNCPtr CPrivateSendClientManager::GetRandomNotUsedMasternode() bool CPrivateSendClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman) { - if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; auto mnList = deterministicMNManager->GetListAtChainTip(); // Look through the queues and see if anything matches CPrivateSendQueue dsq; - while (privateSendClient.GetQueueItemAndTry(dsq)) { + while (privateSendClientQueueManager.GetQueueItemAndTry(dsq)) { auto dmn = mnList.GetValidMNByCollateral(dsq.masternodeOutpoint); if (!dmn) { @@ -1042,12 +1071,12 @@ bool CPrivateSendClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymize std::vector > vecPSInOutPairsTmp; // Try to match their denominations if possible, select exact number of denominations - if (!GetWallets()[0]->SelectPSInOutPairsByDenominations(dsq.nDenom, nBalanceNeedsAnonymized, vecPSInOutPairsTmp)) { + if (!mixingWallet->SelectPSInOutPairsByDenominations(dsq.nDenom, nBalanceNeedsAnonymized, vecPSInOutPairsTmp)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::JoinExistingQueue -- Couldn't match denomination %d (%s)\n", dsq.nDenom, CPrivateSend::DenominationToString(dsq.nDenom)); continue; } - privateSendClient.AddUsedMasternode(dsq.masternodeOutpoint); + privateSendClientManagers.at(mixingWallet->GetName())->AddUsedMasternode(dsq.masternodeOutpoint); if (connman.IsMasternodeOrDisconnectRequested(dmn->pdmnState->addr)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::JoinExistingQueue -- skipping masternode connection, addr=%s\n", dmn->pdmnState->addr.ToString()); @@ -1072,7 +1101,7 @@ bool CPrivateSendClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymize bool CPrivateSendClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman) { - if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; if (nBalanceNeedsAnonymized <= 0) return false; int nTries = 0; @@ -1081,7 +1110,7 @@ bool CPrivateSendClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, C // find available denominated amounts std::set setAmounts; - if (!GetWallets()[0]->SelectDenominatedAmounts(nBalanceNeedsAnonymized, setAmounts)) { + if (!mixingWallet->SelectDenominatedAmounts(nBalanceNeedsAnonymized, setAmounts)) { // this should never happen LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::StartNewQueue -- Can't mix: no compatible inputs found!\n"); strAutoDenomResult = _("Can't mix: no compatible inputs found!"); @@ -1090,7 +1119,7 @@ bool CPrivateSendClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, C // otherwise, try one randomly while (nTries < 10) { - auto dmn = privateSendClient.GetRandomNotUsedMasternode(); + auto dmn = privateSendClientManagers.at(mixingWallet->GetName())->GetRandomNotUsedMasternode(); if (!dmn) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::StartNewQueue -- Can't find random masternode!\n"); @@ -1098,7 +1127,7 @@ bool CPrivateSendClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, C return false; } - privateSendClient.AddUsedMasternode(dmn->collateralOutpoint); + privateSendClientManagers.at(mixingWallet->GetName())->AddUsedMasternode(dmn->collateralOutpoint); // skip next mn payments winners if (dmn->pdmnState->nLastPaidHeight + nMnCount < mnList.GetHeight() + 8) { @@ -1184,10 +1213,36 @@ void CPrivateSendClientManager::ProcessPendingDsaRequest(CConnman& connman) } } +bool CPrivateSendClientManager::TrySubmitDenominate(const CService& mnAddr, CConnman& connman) +{ + LOCK(cs_deqsessions); + for (auto& session : deqSessions) { + CDeterministicMNCPtr mnMixing; + if (session.GetMixingMasternodeInfo(mnMixing) && mnMixing->pdmnState->addr == mnAddr && session.GetState() == POOL_STATE_QUEUE) { + session.SubmitDenominate(connman); + return true; + } + } + return false; +} + +bool CPrivateSendClientManager::MarkAlreadyJoinedQueueAsTried(CPrivateSendQueue& dsq) const +{ + LOCK(cs_deqsessions); + for (const auto& session : deqSessions) { + CDeterministicMNCPtr mnMixing; + if (session.GetMixingMasternodeInfo(mnMixing) && mnMixing->collateralOutpoint == dsq.masternodeOutpoint) { + dsq.fTried = true; + return true; + } + } + return false; +} + bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) { LOCK2(cs_main, mempool.cs); - LOCK(GetWallets()[0]->cs_wallet); + LOCK(mixingWallet->cs_wallet); std::string strError; std::vector > vecPSInOutPairs, vecPSInOutPairsTmp; @@ -1199,7 +1254,7 @@ bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) std::vector > vecInputsByRounds; - for (int i = 0; i < privateSendClient.nPrivateSendRounds; i++) { + for (int i = 0; i < CPrivateSendClientOptions::GetRounds(); i++) { if (PrepareDenominate(i, i, strError, vecPSInOutPairs, vecPSInOutPairsTmp, true)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); vecInputsByRounds.emplace_back(i, vecPSInOutPairsTmp.size()); @@ -1225,7 +1280,7 @@ bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) } // We failed? That's strange but let's just make final attempt and try to mix everything - if (PrepareDenominate(0, privateSendClient.nPrivateSendRounds - 1, strError, vecPSInOutPairs, vecPSInOutPairsTmp)) { + if (PrepareDenominate(0, CPrivateSendClientOptions::GetRounds() - 1, strError, vecPSInOutPairs, vecPSInOutPairsTmp)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for all rounds, success\n"); return SendDenominate(vecPSInOutPairsTmp, connman); } @@ -1238,9 +1293,9 @@ bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) bool CPrivateSendClientSession::SelectDenominate(std::string& strErrorRet, std::vector >& vecPSInOutPairsRet) { - if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; - if (GetWallets()[0]->IsLocked(true)) { + if (mixingWallet->IsLocked(true)) { strErrorRet = "Wallet locked, unable to create transaction!"; return false; } @@ -1252,7 +1307,7 @@ bool CPrivateSendClientSession::SelectDenominate(std::string& strErrorRet, std:: vecPSInOutPairsRet.clear(); - bool fSelected = GetWallets()[0]->SelectPSInOutPairsByDenominations(nSessionDenom, CPrivateSend::GetMaxPoolAmount(), vecPSInOutPairsRet); + bool fSelected = mixingWallet->SelectPSInOutPairsByDenominations(nSessionDenom, CPrivateSend::GetMaxPoolAmount(), vecPSInOutPairsRet); if (!fSelected) { strErrorRet = "Can't select current denominated inputs"; return false; @@ -1264,7 +1319,7 @@ bool CPrivateSendClientSession::SelectDenominate(std::string& strErrorRet, std:: bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector >& vecPSInOutPairsIn, std::vector >& vecPSInOutPairsRet, bool fDryRun) { AssertLockHeld(cs_main); - AssertLockHeld(GetWallets()[0]->cs_wallet); + AssertLockHeld(mixingWallet->cs_wallet); if (!CPrivateSend::IsValidDenomination(nSessionDenom)) { strErrorRet = "Incorrect session denom"; @@ -1295,7 +1350,7 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds ++nSteps; continue; } - scriptDenom = keyHolderStorage.AddKey(GetWallets()[0]); + scriptDenom = keyHolderStorage.AddKey(mixingWallet); } vecPSInOutPairsRet.emplace_back(pair.first, CTxOut(nDenomAmount, scriptDenom)); // step is complete @@ -1313,7 +1368,7 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds } for (const auto& pair : vecPSInOutPairsRet) { - GetWallets()[0]->LockCoin(pair.first.prevout); + mixingWallet->LockCoin(pair.first.prevout); vecOutPointLocked.push_back(pair.first.prevout); } @@ -1323,17 +1378,17 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds // Create collaterals by looping through inputs grouped by addresses bool CPrivateSendClientSession::MakeCollateralAmounts(CConnman& connman) { - if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; LOCK2(cs_main, mempool.cs); - LOCK(GetWallets()[0]->cs_wallet); + LOCK(mixingWallet->cs_wallet); // NOTE: We do not allow txes larger than 100kB, so we have to limit number of inputs here. // We still want to consume a lot of inputs to avoid creating only smaller denoms though. // Knowing that each CTxIn is at least 148b big, 400 inputs should take 400 x ~148b = ~60kB. // This still leaves more than enough room for another data of typical MakeCollateralAmounts tx. std::vector vecTally; - if (!GetWallets()[0]->SelectCoinsGroupedByAddresses(vecTally, false, false, true, 400)) { + if (!mixingWallet->SelectCoinsGroupedByAddresses(vecTally, false, false, true, 400)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::MakeCollateralAmounts -- SelectCoinsGroupedByAddresses can't find any inputs!\n"); return false; } @@ -1365,9 +1420,9 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta { AssertLockHeld(cs_main); AssertLockHeld(mempool.cs); - AssertLockHeld(GetWallets()[0]->cs_wallet); + AssertLockHeld(mixingWallet->cs_wallet); - if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; // Skip way too tiny amounts if (tallyItem.nAmount < CPrivateSend::GetCollateralAmount()) { @@ -1391,9 +1446,9 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta std::vector vecSend; // make our collateral address - CReserveKey reservekeyCollateral(GetWallets()[0]); + CReserveKey reservekeyCollateral(mixingWallet); // make our change address - CReserveKey reservekeyChange(GetWallets()[0]); + CReserveKey reservekeyChange(mixingWallet); CScript scriptCollateral; CPubKey vchPubKey; @@ -1422,7 +1477,7 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta coinControl.Select(outpoint); } - bool fSuccess = GetWallets()[0]->CreateTransaction(vecSend, wtx, reservekeyChange, + bool fSuccess = mixingWallet->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, coinControl); if (!fSuccess) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::MakeCollateralAmounts -- ONLY_NONDENOMINATED: %s\n", strFail); @@ -1430,7 +1485,7 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta if (fTryDenominated) { // Try to also use denominated coins (we can't mix denominated without collaterals anyway). coinControl.nCoinType = CoinType::ALL_COINS; - if (!GetWallets()[0]->CreateTransaction(vecSend, wtx, reservekeyChange, + if (!mixingWallet->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, coinControl)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::MakeCollateralAmounts -- ALL_COINS Error: %s\n", strFail); reservekeyCollateral.ReturnKey(); @@ -1449,12 +1504,12 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta // use the same nCachedLastSuccessBlock as for DS mixing to prevent race CValidationState state; - if (!GetWallets()[0]->CommitTransaction(wtx, reservekeyChange, &connman, state)) { + if (!mixingWallet->CommitTransaction(wtx, reservekeyChange, &connman, state)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::MakeCollateralAmounts -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); return false; } - privateSendClient.UpdatedSuccessBlock(); + privateSendClientManagers.at(mixingWallet->GetName())->UpdatedSuccessBlock(); return true; } @@ -1462,17 +1517,17 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta // Create denominations by looping through inputs grouped by addresses bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, CConnman& connman) { - if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; LOCK2(cs_main, mempool.cs); - LOCK(GetWallets()[0]->cs_wallet); + LOCK(mixingWallet->cs_wallet); // NOTE: We do not allow txes larger than 100kB, so we have to limit number of inputs here. // We still want to consume a lot of inputs to avoid creating only smaller denoms though. // Knowing that each CTxIn is at least 148b big, 400 inputs should take 400 x ~148b = ~60kB. // This still leaves more than enough room for another data of typical CreateDenominated tx. std::vector vecTally; - if (!GetWallets()[0]->SelectCoinsGroupedByAddresses(vecTally, true, true, true, 400)) { + if (!mixingWallet->SelectCoinsGroupedByAddresses(vecTally, true, true, true, 400)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- SelectCoinsGroupedByAddresses can't find any inputs!\n"); return false; } @@ -1482,7 +1537,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, return a.nAmount > b.nAmount; }); - bool fCreateMixingCollaterals = !GetWallets()[0]->HasCollateralInputs(); + bool fCreateMixingCollaterals = !mixingWallet->HasCollateralInputs(); for (const auto& item : vecTally) { if (!CreateDenominated(nBalanceToDenominate, item, fCreateMixingCollaterals, connman)) continue; @@ -1498,9 +1553,9 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, { AssertLockHeld(cs_main); AssertLockHeld(mempool.cs); - AssertLockHeld(GetWallets()[0]->cs_wallet); + AssertLockHeld(mixingWallet->cs_wallet); - if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; + if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; std::vector vecSend; CKeyHolderStorage keyHolderStorageDenom; @@ -1520,7 +1575,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, // ****** Add an output for mixing collaterals ************ / if (fCreateMixingCollaterals) { - CScript scriptCollateral = keyHolderStorageDenom.AddKey(GetWallets()[0]); + CScript scriptCollateral = keyHolderStorageDenom.AddKey(mixingWallet); vecSend.push_back((CRecipient){scriptCollateral, CPrivateSend::GetMaxCollateralAmount(), false}); nValueLeft -= CPrivateSend::GetMaxCollateralAmount() + nOutputFee; } @@ -1533,7 +1588,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, std::map mapDenomCount; for (auto nDenomValue : vecStandardDenoms) { - mapDenomCount.insert(std::pair(nDenomValue, GetWallets()[0]->CountInputsWithAmount(nDenomValue))); + mapDenomCount.insert(std::pair(nDenomValue, mixingWallet->CountInputsWithAmount(nDenomValue))); } // Will generate outputs for the createdenoms up to privatesendmaxdenoms per denom @@ -1570,8 +1625,8 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, }; // add each output up to 11 times or until it can't be added again or until we reach nPrivateSendDenomsGoal - while (needMoreOutputs() && nOutputs <= 10 && currentDenomIt->second < privateSendClient.nPrivateSendDenomsGoal) { - CScript scriptDenom = keyHolderStorageDenom.AddKey(GetWallets()[0]); + while (needMoreOutputs() && nOutputs <= 10 && currentDenomIt->second < CPrivateSendClientOptions::GetDenomsGoal()) { + CScript scriptDenom = keyHolderStorageDenom.AddKey(mixingWallet); vecSend.push_back((CRecipient) {scriptDenom, nDenomValue, false}); @@ -1593,7 +1648,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, for (const auto it : mapDenomCount) { // Check if this specific denom could use another loop, check that there aren't nPrivateSendDenomsGoal of this // denom and that our nValueLeft/nBalanceToDenominate is enough to create one of these denoms, if so, loop again. - if (it.second < privateSendClient.nPrivateSendDenomsGoal && (nValueLeft >= it.first + nOutputFee) && nBalanceToDenominate > 0) { + if (it.second < CPrivateSendClientOptions::GetDenomsGoal() && (nValueLeft >= it.first + nOutputFee) && nBalanceToDenominate > 0) { finished = false; LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- 1 - NOT finished - nDenomValue: %f, count: %d, nValueLeft: %f, nBalanceToDenominate: %f\n", @@ -1633,9 +1688,9 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, auto it = mapDenomCount.find(nDenomValue); for (int i = 0; i < denomsToCreate; i++) { // Never go above the cap unless it's the largest denom - if (nDenomValue != nLargestDenomValue && it->second >= privateSendClient.nPrivateSendDenomsHardCap) break; + if (nDenomValue != nLargestDenomValue && it->second >= CPrivateSendClientOptions::GetDenomsHardCap()) break; - CScript scriptDenom = keyHolderStorageDenom.AddKey(GetWallets()[0]); + CScript scriptDenom = keyHolderStorageDenom.AddKey(mixingWallet); vecSend.push_back((CRecipient) {scriptDenom, nDenomValue, false}); // increment outputs and subtract denomination amount @@ -1681,9 +1736,9 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, int nChangePosRet = -1; std::string strFail = ""; // make our change address - CReserveKey reservekeyChange(GetWallets()[0]); + CReserveKey reservekeyChange(mixingWallet); - bool fSuccess = GetWallets()[0]->CreateTransaction(vecSend, wtx, reservekeyChange, + bool fSuccess = mixingWallet->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, coinControl); if (!fSuccess) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- Error: %s\n", strFail); @@ -1694,13 +1749,13 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, keyHolderStorageDenom.KeepAll(); CValidationState state; - if (!GetWallets()[0]->CommitTransaction(wtx, reservekeyChange, &connman, state)) { + if (!mixingWallet->CommitTransaction(wtx, reservekeyChange, &connman, state)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); return false; } // use the same nCachedLastSuccessBlock as for DS mixing to prevent race - privateSendClient.UpdatedSuccessBlock(); + privateSendClientManagers.at(mixingWallet->GetName())->UpdatedSuccessBlock(); LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- txid=%s\n", wtx.GetHash().GetHex()); return true; @@ -1730,9 +1785,19 @@ void CPrivateSendClientManager::UpdatedBlockTip(const CBlockIndex* pindex) LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::UpdatedBlockTip -- nCachedBlockHeight: %d\n", nCachedBlockHeight); } +void CPrivateSendClientQueueManager::DoMaintenance() +{ + if (!CPrivateSendClientOptions::IsEnabled()) return; + if (fMasternodeMode) return; // no client-side mixing on masternodes + + if (!masternodeSync.IsBlockchainSynced() || ShutdownRequested()) return; + + CheckQueue(); +} + void CPrivateSendClientManager::DoMaintenance(CConnman& connman) { - if (!fEnablePrivateSend) return; + if (!CPrivateSendClientOptions::IsEnabled()) return; if (fMasternodeMode) return; // no client-side mixing on masternodes if (!masternodeSync.IsBlockchainSynced() || ShutdownRequested()) return; @@ -1751,8 +1816,7 @@ void CPrivateSendClientManager::DoMaintenance(CConnman& connman) void CPrivateSendClientSession::GetJsonInfo(UniValue& obj) const { - obj.clear(); - obj.setObject(); + assert(obj.isObject()); if (mixingMasternode != nullptr) { assert(mixingMasternode->pdmnState); obj.pushKV("protxhash", mixingMasternode->proTxHash.ToString()); @@ -1767,17 +1831,8 @@ void CPrivateSendClientSession::GetJsonInfo(UniValue& obj) const void CPrivateSendClientManager::GetJsonInfo(UniValue& obj) const { LOCK(cs_deqsessions); - obj.clear(); - obj.setObject(); - obj.pushKV("enabled", fEnablePrivateSend); - obj.pushKV("running", fPrivateSendRunning); - obj.pushKV("multisession", fPrivateSendMultiSession); - obj.pushKV("max_sessions", nPrivateSendSessions); - obj.pushKV("max_rounds", nPrivateSendRounds); - obj.pushKV("max_amount", nPrivateSendAmount); - obj.pushKV("denoms_goal", nPrivateSendDenomsGoal); - obj.pushKV("denoms_hardcap", nPrivateSendDenomsHardCap); - obj.pushKV("queue_size", GetQueueSize()); + assert(obj.isObject()); + obj.pushKV("running", IsMixing()); UniValue arrSessions(UniValue::VARR); for (const auto& session : deqSessions) { @@ -1789,3 +1844,73 @@ void CPrivateSendClientManager::GetJsonInfo(UniValue& obj) const } obj.pushKV("sessions", arrSessions); } + +void DoPrivateSendMaintenance(CConnman& connman) +{ + privateSendClientQueueManager.DoMaintenance(); + for (auto& pair : privateSendClientManagers) { + pair.second->DoMaintenance(connman); + } +} + +CPrivateSendClientOptions& CPrivateSendClientOptions::Get() +{ + std::call_once(onceFlag, CPrivateSendClientOptions::Init); + assert(CPrivateSendClientOptions::_instance); + return *CPrivateSendClientOptions::_instance; +} + +void CPrivateSendClientOptions::SetEnabled(bool fEnabled) +{ + CPrivateSendClientOptions& options = CPrivateSendClientOptions::Get(); + LOCK(options.cs_ps_options); + options.fEnablePrivateSend = fEnabled; +} + +void CPrivateSendClientOptions::SetMultiSessionEnabled(bool fEnabled) +{ + CPrivateSendClientOptions& options = CPrivateSendClientOptions::Get(); + LOCK(options.cs_ps_options); + options.fPrivateSendMultiSession = fEnabled; +} + +void CPrivateSendClientOptions::SetRounds(int nRounds) +{ + CPrivateSendClientOptions& options = CPrivateSendClientOptions::Get(); + LOCK(options.cs_ps_options); + options.nPrivateSendRounds = nRounds; +} + +void CPrivateSendClientOptions::SetAmount(CAmount amount) +{ + CPrivateSendClientOptions& options = CPrivateSendClientOptions::Get(); + LOCK(options.cs_ps_options); + options.nPrivateSendAmount = amount; +} + +void CPrivateSendClientOptions::Init() +{ + assert(!CPrivateSendClientOptions::_instance); + static CPrivateSendClientOptions instance; + LOCK(instance.cs_ps_options); + instance.fPrivateSendMultiSession = gArgs.GetBoolArg("-privatesendmultisession", DEFAULT_PRIVATESEND_MULTISESSION); + instance.nPrivateSendSessions = std::min(std::max((int)gArgs.GetArg("-privatesendsessions", DEFAULT_PRIVATESEND_SESSIONS), MIN_PRIVATESEND_SESSIONS), MAX_PRIVATESEND_SESSIONS); + instance.nPrivateSendRounds = std::min(std::max((int)gArgs.GetArg("-privatesendrounds", DEFAULT_PRIVATESEND_ROUNDS), MIN_PRIVATESEND_ROUNDS), MAX_PRIVATESEND_ROUNDS); + instance.nPrivateSendAmount = std::min(std::max((int)gArgs.GetArg("-privatesendamount", DEFAULT_PRIVATESEND_AMOUNT), MIN_PRIVATESEND_AMOUNT), MAX_PRIVATESEND_AMOUNT); + instance.nPrivateSendDenomsGoal = std::min(std::max((int)gArgs.GetArg("-privatesenddenomsgoal", DEFAULT_PRIVATESEND_DENOMS_GOAL), MIN_PRIVATESEND_DENOMS_GOAL), MAX_PRIVATESEND_DENOMS_GOAL); + instance.nPrivateSendDenomsHardCap = std::min(std::max((int)gArgs.GetArg("-privatesenddenomshardcap", DEFAULT_PRIVATESEND_DENOMS_HARDCAP), MIN_PRIVATESEND_DENOMS_HARDCAP), MAX_PRIVATESEND_DENOMS_HARDCAP); + CPrivateSendClientOptions::_instance = &instance; +} + +void CPrivateSendClientOptions::GetJsonInfo(UniValue& obj) +{ + assert(obj.isObject()); + CPrivateSendClientOptions& options = CPrivateSendClientOptions::Get(); + obj.pushKV("enabled", options.fEnablePrivateSend); + obj.pushKV("multisession", options.fPrivateSendMultiSession); + obj.pushKV("max_sessions", options.nPrivateSendSessions); + obj.pushKV("max_rounds", options.nPrivateSendRounds); + obj.pushKV("max_amount", options.nPrivateSendAmount); + obj.pushKV("denoms_goal", options.nPrivateSendDenomsGoal); + obj.pushKV("denoms_hardcap", options.nPrivateSendDenomsHardCap); +} diff --git a/src/privatesend/privatesend-client.h b/src/privatesend/privatesend-client.h index 52115d619cc4..6b979f94a745 100644 --- a/src/privatesend/privatesend-client.h +++ b/src/privatesend/privatesend-client.h @@ -11,9 +11,13 @@ #include +class CPrivateSendClientOptions; class CPrivateSendClientManager; +class CPrivateSendClientQueueManager; + class CConnman; class CNode; + class UniValue; static const int MIN_PRIVATESEND_SESSIONS = 1; @@ -52,7 +56,10 @@ static const int PRIVATESEND_KEYS_THRESHOLD_WARNING = 100; static const int PRIVATESEND_KEYS_THRESHOLD_STOP = 50; // The main object for accessing mixing -extern CPrivateSendClientManager privateSendClient; +extern std::map privateSendClientManagers; + +// The object to track mixing queues +extern CPrivateSendClientQueueManager privateSendClientQueueManager; class CPendingDsaRequest { @@ -110,6 +117,8 @@ class CPrivateSendClientSession : public CPrivateSendBaseSession CKeyHolderStorage keyHolderStorage; // storage for keys used in PrepareDenominate + CWallet* mixingWallet; + /// Create denominations bool CreateDenominated(CAmount nBalanceToDenominate, CConnman& connman); bool CreateDenominated(CAmount nBalanceToDenominate, const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals, CConnman& connman); @@ -143,14 +152,15 @@ class CPrivateSendClientSession : public CPrivateSendBaseSession void SetNull(); public: - CPrivateSendClientSession() : + CPrivateSendClientSession(CWallet* pwallet) : vecOutPointLocked(), strLastMessage(), strAutoDenomResult(), mixingMasternode(), txMyCollateral(), pendingDsaRequest(), - keyHolderStorage() + keyHolderStorage(), + mixingWallet(pwallet) { } @@ -177,9 +187,19 @@ class CPrivateSendClientSession : public CPrivateSendBaseSession void GetJsonInfo(UniValue& obj) const; }; +/** Used to keep track of mixing queues + */ +class CPrivateSendClientQueueManager : public CPrivateSendBaseManager +{ +public: + void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, bool enable_bip61); + + void DoMaintenance(); +}; + /** Used to keep track of current status of mixing pool */ -class CPrivateSendClientManager : public CPrivateSendBaseManager +class CPrivateSendClientManager { private: // Keep track of the used Masternodes @@ -193,6 +213,8 @@ class CPrivateSendClientManager : public CPrivateSendBaseManager int nMinBlocksToWait; // how many blocks to wait after one successful mixing tx in non-multisession mode std::string strAutoDenomResult; + CWallet* mixingWallet; + // Keep track of current block height int nCachedBlockHeight; @@ -202,15 +224,6 @@ class CPrivateSendClientManager : public CPrivateSendBaseManager bool CheckAutomaticBackup(); public: - int nPrivateSendSessions; - int nPrivateSendRounds; - int nPrivateSendAmount; - int nPrivateSendDenomsGoal; - int nPrivateSendDenomsHardCap; - bool fEnablePrivateSend; - bool fPrivateSendRunning; - bool fPrivateSendMultiSession; - int nCachedNumBlocks; //used for the overview screen bool fCreateAutoBackups; //builtin support for automatic backups @@ -221,20 +234,17 @@ class CPrivateSendClientManager : public CPrivateSendBaseManager nMinBlocksToWait(1), strAutoDenomResult(), nCachedBlockHeight(0), - nPrivateSendRounds(DEFAULT_PRIVATESEND_ROUNDS), - nPrivateSendAmount(DEFAULT_PRIVATESEND_AMOUNT), - nPrivateSendDenomsGoal(DEFAULT_PRIVATESEND_DENOMS_GOAL), - nPrivateSendDenomsHardCap(DEFAULT_PRIVATESEND_DENOMS_HARDCAP), - fEnablePrivateSend(false), - fPrivateSendRunning(false), - fPrivateSendMultiSession(DEFAULT_PRIVATESEND_MULTISESSION), nCachedNumBlocks(std::numeric_limits::max()), - fCreateAutoBackups(true) + fCreateAutoBackups(true), + mixingWallet(nullptr) { } void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman, bool enable_bip61); + bool StartMixing(CWallet* pwallet); + void StopMixing(); + bool IsMixing() const; void ResetPool(); std::string GetStatuses(); @@ -245,6 +255,9 @@ class CPrivateSendClientManager : public CPrivateSendBaseManager /// Passively run mixing in the background according to the configuration in settings bool DoAutomaticDenominating(CConnman& connman, bool fDryRun = false); + bool TrySubmitDenominate(const CService& mnAddr, CConnman& connman); + bool MarkAlreadyJoinedQueueAsTried(CPrivateSendQueue& dsq) const; + void CheckTimeout(); void ProcessPendingDsaRequest(CConnman& connman); @@ -261,4 +274,56 @@ class CPrivateSendClientManager : public CPrivateSendBaseManager void GetJsonInfo(UniValue& obj) const; }; +/* Application wide mixing options */ +class CPrivateSendClientOptions +{ +public: + static int GetSessions() { return CPrivateSendClientOptions::Get().nPrivateSendSessions; } + static int GetRounds() { return CPrivateSendClientOptions::Get().nPrivateSendRounds; } + static int GetAmount() { return CPrivateSendClientOptions::Get().nPrivateSendAmount; } + static int GetDenomsGoal() { return CPrivateSendClientOptions::Get().nPrivateSendDenomsGoal; } + static int GetDenomsHardCap() { return CPrivateSendClientOptions::Get().nPrivateSendDenomsHardCap; } + + static void SetEnabled(bool fEnabled); + static void SetMultiSessionEnabled(bool fEnabled); + static void SetRounds(int nRounds); + static void SetAmount(CAmount amount); + + static int IsEnabled() { return CPrivateSendClientOptions::Get().fEnablePrivateSend; } + static int IsMultiSessionEnabled() { return CPrivateSendClientOptions::Get().fPrivateSendMultiSession; } + + static void GetJsonInfo(UniValue& obj); + +private: + static CPrivateSendClientOptions* _instance; + static std::once_flag onceFlag; + + CCriticalSection cs_ps_options; + int nPrivateSendSessions; + int nPrivateSendRounds; + int nPrivateSendAmount; + int nPrivateSendDenomsGoal; + int nPrivateSendDenomsHardCap; + bool fEnablePrivateSend; + bool fPrivateSendMultiSession; + + CPrivateSendClientOptions() : + nPrivateSendRounds(DEFAULT_PRIVATESEND_ROUNDS), + nPrivateSendAmount(DEFAULT_PRIVATESEND_AMOUNT), + nPrivateSendDenomsGoal(DEFAULT_PRIVATESEND_DENOMS_GOAL), + nPrivateSendDenomsHardCap(DEFAULT_PRIVATESEND_DENOMS_HARDCAP), + fEnablePrivateSend(false), + fPrivateSendMultiSession(DEFAULT_PRIVATESEND_MULTISESSION) + { + } + + CPrivateSendClientOptions(const CPrivateSendClientOptions& other) = delete; + CPrivateSendClientOptions& operator=(const CPrivateSendClientOptions&) = delete; + + static CPrivateSendClientOptions& Get(); + static void Init(); +}; + +void DoPrivateSendMaintenance(CConnman& connman); + #endif // BITCOIN_PRIVATESEND_PRIVATESEND_CLIENT_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index ef71c6fc2e0a..b872b54fdad4 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #endif // ENABLE_WALLET #ifdef Q_OS_MAC @@ -41,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -70,10 +72,6 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM = #endif ; -/** Display name for default wallet name. Uses tilde to avoid name - * collisions in the future with additional wallets */ -const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; - BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) : QMainWindow(parent), enableWallet(false), @@ -88,6 +86,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * progressBar(0), progressDialog(0), appMenuBar(0), + appToolBar(0), + appToolBarLogoAction(0), overviewAction(0), historyAction(0), masternodeAction(0), @@ -588,6 +588,7 @@ void BitcoinGUI::createToolBars() if(walletFrame) { QToolBar *toolbar = new QToolBar(tr("Tabs toolbar")); + appToolBar = toolbar; toolbar->setContextMenuPolicy(Qt::PreventContextMenu); toolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolbar->setToolButtonStyle(Qt::ToolButtonTextOnly); @@ -612,11 +613,17 @@ void BitcoinGUI::createToolBars() toolbar->setMovable(false); // remove unused icon in upper left corner overviewAction->setChecked(true); +#ifdef ENABLE_WALLET + m_wallet_selector = new QComboBox(this); + m_wallet_selector->setHidden(true); + connect(m_wallet_selector, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(setCurrentWallet(const QString&))); +#endif + QLabel *logoLabel = new QLabel(); logoLabel->setObjectName("lblToolbarLogo"); logoLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - toolbar->addWidget(logoLabel); + appToolBarLogoAction = toolbar->addWidget(logoLabel); /** Create additional container for toolbar and walletFrame and make it the central widget. This is a workaround mostly for toolbar styling on Mac OS but should work fine for every other OSes too. @@ -729,12 +736,28 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel) } #ifdef ENABLE_WALLET -bool BitcoinGUI::addWallet(const QString& name, WalletModel *walletModel) +bool BitcoinGUI::addWallet(WalletModel *walletModel) { if(!walletFrame) return false; + const QString name = walletModel->getWalletName(); setWalletActionsEnabled(true); - return walletFrame->addWallet(name, walletModel); + m_wallet_selector->addItem(name); + if (m_wallet_selector->count() == 2) { + m_wallet_selector->setHidden(false); + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(m_wallet_selector); + layout->setSpacing(0); + layout->setMargin(0); + layout->setContentsMargins(5, 0, 5, 0); + QWidget* walletSelector = new QWidget(this); + walletSelector->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + walletSelector->setObjectName("walletSelector"); + walletSelector->setLayout(layout); + appToolBar->insertWidget(appToolBarLogoAction, walletSelector); + } + rpcConsole->addWallet(walletModel); + return walletFrame->addWallet(walletModel); } bool BitcoinGUI::setCurrentWallet(const QString& name) @@ -759,8 +782,8 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled) sendCoinsAction->setEnabled(enabled); sendCoinsMenuAction->setEnabled(enabled); #ifdef ENABLE_WALLET - privateSendCoinsAction->setEnabled(enabled && privateSendClient.fEnablePrivateSend); - privateSendCoinsMenuAction->setEnabled(enabled && privateSendClient.fEnablePrivateSend); + privateSendCoinsAction->setEnabled(enabled && CPrivateSendClientOptions::IsEnabled()); + privateSendCoinsMenuAction->setEnabled(enabled && CPrivateSendClientOptions::IsEnabled()); #else privateSendCoinsAction->setEnabled(enabled); privateSendCoinsMenuAction->setEnabled(enabled); @@ -1028,7 +1051,9 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer // Disabling macOS App Nap on initial sync, disk, reindex operations and mixing. bool disableAppNap = !masternodeSync.IsSynced(); #ifdef ENABLE_WALLET - disableAppNap |= privateSendClient.fPrivateSendRunning; + for (const auto& pair : privateSendClientManagers) { + disableAppNap |= pair.second->IsMixing(); + } #endif // ENABLE_WALLET if (disableAppNap) { m_app_nap_inhibitor->disableAppNap(); @@ -1317,10 +1342,10 @@ void BitcoinGUI::showEvent(QShowEvent *event) } #ifdef ENABLE_WALLET -void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label) +void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName) { IncomingTransactionMessage itx = { - date, unit, amount, type, address, label + date, unit, amount, type, address, label, walletName }; incomingTransactions.emplace_back(itx); @@ -1386,8 +1411,11 @@ void BitcoinGUI::showIncomingTransactions() for (auto& itx : txs) { // On new transaction, make an info balloon QString msg = tr("Date: %1\n").arg(itx.date) + - tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(itx.unit, itx.amount, true)) + - tr("Type: %1\n").arg(itx.type); + tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(itx.unit, itx.amount, true)); + if (WalletModel::isMultiwallet() && !itx.walletName.isEmpty()) { + msg += tr("Wallet: %1\n").arg(itx.walletName); + } + msg += tr("Type: %1\n").arg(itx.type); if (!itx.label.isEmpty()) msg += tr("Label: %1\n").arg(itx.label); else if (!itx.address.isEmpty()) @@ -1496,6 +1524,20 @@ void BitcoinGUI::setEncryptionStatus(int status) break; } } + +void BitcoinGUI::updateWalletStatus() +{ + if (!walletFrame) { + return; + } + WalletView * const walletView = walletFrame->currentWalletView(); + if (!walletView) { + return; + } + WalletModel * const walletModel = walletView->getWalletModel(); + setEncryptionStatus(walletModel->getEncryptionStatus()); + setHDStatus(walletModel->hdEnabled()); +} #endif // ENABLE_WALLET void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 4083cf6a654d..f86f1230546f 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -38,6 +38,7 @@ class ModalOverlay; QT_BEGIN_NAMESPACE class QAction; +class QComboBox; class QProgressBar; class QProgressDialog; class QToolButton; @@ -52,7 +53,6 @@ class BitcoinGUI : public QMainWindow Q_OBJECT public: - static const QString DEFAULT_WALLET; static const std::string DEFAULT_UIPLATFORM; explicit BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0); @@ -68,8 +68,7 @@ class BitcoinGUI : public QMainWindow The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending functionality. */ - bool addWallet(const QString& name, WalletModel *walletModel); - bool setCurrentWallet(const QString& name); + bool addWallet(WalletModel *walletModel); void removeAllWallets(); #endif // ENABLE_WALLET bool enableWallet; @@ -96,6 +95,8 @@ class BitcoinGUI : public QMainWindow QProgressDialog *progressDialog; QMenuBar *appMenuBar; + QToolBar *appToolBar; + QAction* appToolBarLogoAction; QToolButton *overviewAction; QToolButton *historyAction; QToolButton *masternodeAction; @@ -130,6 +131,9 @@ class BitcoinGUI : public QMainWindow QAction *showHelpMessageAction; QAction *showPrivateSendHelpAction; + QLabel *m_wallet_selector_label; + QComboBox *m_wallet_selector; + QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; QMenu *dockIconMenu; @@ -156,6 +160,7 @@ class BitcoinGUI : public QMainWindow QString type; QString address; QString label; + QString walletName; }; std::list incomingTransactions; QTimer* incomingTransactionsTimer; @@ -212,22 +217,29 @@ public Q_SLOTS: void message(const QString &title, const QString &message, unsigned int style, bool *ret = nullptr); #ifdef ENABLE_WALLET - /** Set the hd-enabled status as shown in the UI. - @param[in] status current hd enabled status - @see WalletModel::EncryptionStatus - */ - void setHDStatus(int hdEnabled); + bool setCurrentWallet(const QString& name); + /** Set the UI status indicators based on the currently selected wallet. + */ + void updateWalletStatus(); +private: /** Set the encryption status as shown in the UI. @param[in] status current encryption status @see WalletModel::EncryptionStatus */ void setEncryptionStatus(int status); + /** Set the hd-enabled status as shown in the UI. + @param[in] status current hd enabled status + @see WalletModel::EncryptionStatus + */ + void setHDStatus(int hdEnabled); + +public Q_SLOTS: bool handlePaymentRequest(const SendCoinsRecipient& recipient); /** Show incoming transaction notification for new transactions. */ - void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label); + void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); void showIncomingTransactions(); #endif // ENABLE_WALLET diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 0829ce7efb05..0779892a584e 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -501,7 +501,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) // unselect non-fully-mixed, this can happen when users switch from Send to PrivateSend if (coinControl()->IsUsingPrivateSend()) { int nRounds = model->getRealOutpointPrivateSendRounds(outpt); - if (nRounds < privateSendClient.nPrivateSendRounds) { + if (nRounds < CPrivateSendClientOptions::GetRounds()) { coinControl()->UnSelect(outpt); fUnselectedNonMixed = true; continue; @@ -707,7 +707,7 @@ void CoinControlDialog::updateView() COutPoint outpoint = COutPoint(out.tx->tx->GetHash(), out.i); int nRounds = model->getRealOutpointPrivateSendRounds(outpoint); - if ((coinControl()->IsUsingPrivateSend() && nRounds >= privateSendClient.nPrivateSendRounds) || !(coinControl()->IsUsingPrivateSend())) { + if ((coinControl()->IsUsingPrivateSend() && nRounds >= CPrivateSendClientOptions::GetRounds()) || !(coinControl()->IsUsingPrivateSend())) { nSum += out.tx->tx->vout[out.i].nValue; nChildren++; diff --git a/src/qt/dash.cpp b/src/qt/dash.cpp index 29d90bb47434..597483d8fb2e 100644 --- a/src/qt/dash.cpp +++ b/src/qt/dash.cpp @@ -232,7 +232,7 @@ public Q_SLOTS: QTimer *pollShutdownTimer; #ifdef ENABLE_WALLET PaymentServer* paymentServer; - WalletModel *walletModel; + std::vector m_wallet_models; #endif int returnValue; const PlatformStyle *platformStyle; @@ -334,7 +334,7 @@ BitcoinApplication::BitcoinApplication(int &argc, char **argv): pollShutdownTimer(0), #ifdef ENABLE_WALLET paymentServer(0), - walletModel(0), + m_wallet_models(), #endif returnValue(0) { @@ -461,8 +461,10 @@ void BitcoinApplication::requestShutdown() #ifdef ENABLE_WALLET window->removeAllWallets(); - delete walletModel; - walletModel = 0; + for (WalletModel *walletModel : m_wallet_models) { + delete walletModel; + } + m_wallet_models.clear(); #endif delete clientModel; clientModel = 0; @@ -491,16 +493,20 @@ void BitcoinApplication::initializeResult(bool success) window->setClientModel(clientModel); #ifdef ENABLE_WALLET - // TODO: Expose secondary wallets - if (HasWallets()) - { - walletModel = new WalletModel(platformStyle, GetWallets()[0], optionsModel); - - window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel); - window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET); + bool fFirstWallet = true; + for (CWallet* pwallet : GetWallets()) { + WalletModel * const walletModel = new WalletModel(platformStyle, pwallet, optionsModel); + + window->addWallet(walletModel); + if (fFirstWallet) { + window->setCurrentWallet(walletModel->getWalletName()); + fFirstWallet = false; + } connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)), paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray))); + + m_wallet_models.push_back(walletModel); } #endif diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 9df65c113916..e7992fdd3f3a 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -494,6 +494,22 @@ 4 + + + + Wallet: + + + + + + + + (none) + + + + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index d77498cfbbe5..ea246e9eafff 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -288,9 +288,10 @@ void OptionsDialog::on_okButton_clicked() { mapper->submit(); #ifdef ENABLE_WALLET - privateSendClient.nCachedNumBlocks = std::numeric_limits::max(); - if(HasWallets()) - GetWallets()[0]->MarkDirty(); + for (auto& pwallet : GetWallets()) { + privateSendClientManagers.at(pwallet->GetName())->nCachedNumBlocks = std::numeric_limits::max(); + pwallet->MarkDirty(); + } #endif // ENABLE_WALLET accept(); updateDefaultProxyNets(); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index efb95f56e80c..463269a8fa91 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -138,7 +138,7 @@ void OptionsModel::Init(bool resetSettings) settings.setValue("nPrivateSendRounds", DEFAULT_PRIVATESEND_ROUNDS); if (!gArgs.SoftSetArg("-privatesendrounds", settings.value("nPrivateSendRounds").toString().toStdString())) addOverriddenOption("-privatesendrounds"); - privateSendClient.nPrivateSendRounds = settings.value("nPrivateSendRounds").toInt(); + CPrivateSendClientOptions::SetRounds(settings.value("nPrivateSendRounds").toInt()); if (!settings.contains("nPrivateSendAmount")) { // for migration from old settings @@ -149,13 +149,13 @@ void OptionsModel::Init(bool resetSettings) } if (!gArgs.SoftSetArg("-privatesendamount", settings.value("nPrivateSendAmount").toString().toStdString())) addOverriddenOption("-privatesendamount"); - privateSendClient.nPrivateSendAmount = settings.value("nPrivateSendAmount").toInt(); + CPrivateSendClientOptions::SetAmount(settings.value("nPrivateSendAmount").toInt()); if (!settings.contains("fPrivateSendMultiSession")) settings.setValue("fPrivateSendMultiSession", DEFAULT_PRIVATESEND_MULTISESSION); if (!gArgs.SoftSetBoolArg("-privatesendmultisession", settings.value("fPrivateSendMultiSession").toBool())) addOverriddenOption("-privatesendmultisession"); - privateSendClient.fPrivateSendMultiSession = settings.value("fPrivateSendMultiSession").toBool(); + CPrivateSendClientOptions::SetMultiSessionEnabled(settings.value("fPrivateSendMultiSession").toBool()); #endif // Network @@ -470,24 +470,24 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in case PrivateSendRounds: if (settings.value("nPrivateSendRounds") != value) { - privateSendClient.nPrivateSendRounds = value.toInt(); - settings.setValue("nPrivateSendRounds", privateSendClient.nPrivateSendRounds); + CPrivateSendClientOptions::SetRounds(value.toInt()); + settings.setValue("nPrivateSendRounds", CPrivateSendClientOptions::GetRounds()); Q_EMIT privateSendRoundsChanged(); } break; case PrivateSendAmount: if (settings.value("nPrivateSendAmount") != value) { - privateSendClient.nPrivateSendAmount = value.toInt(); - settings.setValue("nPrivateSendAmount", privateSendClient.nPrivateSendAmount); + CPrivateSendClientOptions::SetAmount(value.toInt()); + settings.setValue("nPrivateSendAmount", CPrivateSendClientOptions::GetAmount()); Q_EMIT privateSentAmountChanged(); } break; case PrivateSendMultiSession: if (settings.value("fPrivateSendMultiSession") != value) { - privateSendClient.fPrivateSendMultiSession = value.toBool(); - settings.setValue("fPrivateSendMultiSession", privateSendClient.fPrivateSendMultiSession); + CPrivateSendClientOptions::SetMultiSessionEnabled(value.toBool()); + settings.setValue("fPrivateSendMultiSession", CPrivateSendClientOptions::IsMultiSessionEnabled()); } break; #endif diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 46e2fc69f619..53e25962bcb6 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -172,7 +172,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) // start with displaying the "out of sync" warnings showOutOfSyncWarning(true); - if(!privateSendClient.fEnablePrivateSend) return; + if (!CPrivateSendClientOptions::IsEnabled()) return; // Disable any PS UI for masternode or when autobackup is disabled or failed for whatever reason if(fMasternodeMode || nWalletBackups <= 0){ @@ -181,14 +181,16 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) ui->labelPrivateSendEnabled->setToolTip(tr("Automatic backups are disabled, no mixing available!")); } } else { - if(!privateSendClient.fPrivateSendRunning){ - ui->togglePrivateSend->setText(tr("Start Mixing")); - } else { - ui->togglePrivateSend->setText(tr("Stop Mixing")); - } // Disable privateSendClient builtin support for automatic backups while we are in GUI, // we'll handle automatic backups and user warnings in privateSendStatus() - privateSendClient.fCreateAutoBackups = false; + for (auto& pair : privateSendClientManagers) { + if (!pair.second->IsMixing()) { + ui->togglePrivateSend->setText(tr("Start Mixing")); + } else { + ui->togglePrivateSend->setText(tr("Stop Mixing")); + } + pair.second->fCreateAutoBackups = false; + } timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(privateSendStatus())); @@ -308,7 +310,7 @@ void OverviewPage::setWalletModel(WalletModel *model) // Initialize PS UI privateSendStatus(true); - if(!privateSendClient.fEnablePrivateSend) return; + if (!CPrivateSendClientOptions::IsEnabled()) return; connect(model->getOptionsModel(), SIGNAL(privateSendRoundsChanged()), this, SLOT(updatePrivateSendProgress())); connect(model->getOptionsModel(), SIGNAL(privateSentAmountChanged()), this, SLOT(updatePrivateSendProgress())); @@ -357,7 +359,7 @@ void OverviewPage::updatePrivateSendProgress() if(!walletModel) return; QString strAmountAndRounds; - QString strPrivateSendAmount = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, privateSendClient.nPrivateSendAmount * COIN, false, BitcoinUnits::separatorAlways); + QString strPrivateSendAmount = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, CPrivateSendClientOptions::GetAmount() * COIN, false, BitcoinUnits::separatorAlways); if(currentBalance == 0) { @@ -366,7 +368,7 @@ void OverviewPage::updatePrivateSendProgress() // when balance is zero just show info from settings strPrivateSendAmount = strPrivateSendAmount.remove(strPrivateSendAmount.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); - strAmountAndRounds = strPrivateSendAmount + " / " + tr("%n Rounds", "", privateSendClient.nPrivateSendRounds); + strAmountAndRounds = strPrivateSendAmount + " / " + tr("%n Rounds", "", CPrivateSendClientOptions::GetRounds()); ui->labelAmountRounds->setToolTip(tr("No inputs detected")); ui->labelAmountRounds->setText(strAmountAndRounds); @@ -378,15 +380,15 @@ void OverviewPage::updatePrivateSendProgress() CAmount nMaxToAnonymize = nAnonymizableBalance + currentAnonymizedBalance; // If it's more than the anon threshold, limit to that. - if(nMaxToAnonymize > privateSendClient.nPrivateSendAmount*COIN) nMaxToAnonymize = privateSendClient.nPrivateSendAmount*COIN; + if (nMaxToAnonymize > CPrivateSendClientOptions::GetAmount() * COIN) nMaxToAnonymize = CPrivateSendClientOptions::GetAmount() * COIN; if(nMaxToAnonymize == 0) return; - if(nMaxToAnonymize >= privateSendClient.nPrivateSendAmount * COIN) { + if (nMaxToAnonymize >= CPrivateSendClientOptions::GetAmount() * COIN) { ui->labelAmountRounds->setToolTip(tr("Found enough compatible inputs to mix %1") .arg(strPrivateSendAmount)); strPrivateSendAmount = strPrivateSendAmount.remove(strPrivateSendAmount.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); - strAmountAndRounds = strPrivateSendAmount + " / " + tr("%n Rounds", "", privateSendClient.nPrivateSendRounds); + strAmountAndRounds = strPrivateSendAmount + " / " + tr("%n Rounds", "", CPrivateSendClientOptions::GetRounds()); } else { QString strMaxToAnonymize = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, nMaxToAnonymize, false, BitcoinUnits::separatorAlways); ui->labelAmountRounds->setToolTip(tr("Not enough compatible inputs to mix %2,
" @@ -397,7 +399,7 @@ void OverviewPage::updatePrivateSendProgress() strMaxToAnonymize = strMaxToAnonymize.remove(strMaxToAnonymize.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); strAmountAndRounds = "" + QString(BitcoinUnits::factor(nDisplayUnit) == 1 ? "" : "~") + strMaxToAnonymize + - " / " + tr("%n Rounds", "", privateSendClient.nPrivateSendRounds) + ""; + " / " + tr("%n Rounds", "", CPrivateSendClientOptions::GetRounds()) + ""; } ui->labelAmountRounds->setText(strAmountAndRounds); @@ -436,7 +438,7 @@ void OverviewPage::updatePrivateSendProgress() // apply some weights to them ... float denomWeight = 1; - float anonNormWeight = privateSendClient.nPrivateSendRounds; + float anonNormWeight = CPrivateSendClientOptions::GetRounds(); float anonFullWeight = 2; float fullWeight = denomWeight + anonNormWeight + anonFullWeight; // ... and calculate the whole progress @@ -452,7 +454,7 @@ void OverviewPage::updatePrivateSendProgress() tr("Denominated") + ": %2%
" + tr("Partially mixed") + ": %3%
" + tr("Mixed") + ": %4%
" + - tr("Denominated inputs have %5 of %n rounds on average", "", privateSendClient.nPrivateSendRounds)) + tr("Denominated inputs have %5 of %n rounds on average", "", CPrivateSendClientOptions::GetRounds())) .arg(progress).arg(denomPart).arg(anonNormPart).arg(anonFullPart) .arg(nAverageAnonymizedRounds); ui->privateSendProgress->setToolTip(strToolPip); @@ -460,10 +462,10 @@ void OverviewPage::updatePrivateSendProgress() void OverviewPage::updateAdvancedPSUI(bool fShowAdvancedPSUI) { this->fShowAdvancedPSUI = fShowAdvancedPSUI; - int nNumItems = (!privateSendClient.fEnablePrivateSend || !fShowAdvancedPSUI) ? NUM_ITEMS : NUM_ITEMS_ADV; + int nNumItems = (!CPrivateSendClientOptions::IsEnabled() || !fShowAdvancedPSUI) ? NUM_ITEMS : NUM_ITEMS_ADV; SetupTransactionList(nNumItems); - if (!privateSendClient.fEnablePrivateSend) return; + if (!CPrivateSendClientOptions::IsEnabled()) return; ui->framePrivateSend->setVisible(true); ui->labelCompletitionText->setVisible(fShowAdvancedPSUI); @@ -490,8 +492,15 @@ void OverviewPage::privateSendStatus(bool fForce) static int64_t nLastDSProgressBlockTime = 0; int nBestHeight = clientModel->getNumBlocks(); + auto it = privateSendClientManagers.find(walletModel->getWallet()->GetName()); + if (it == privateSendClientManagers.end()) { + // nothing to do + return; + } + CPrivateSendClientManager* privateSendClientManager = it->second; + // We are processing more than 1 block per second, we'll just leave - if(nBestHeight > privateSendClient.nCachedNumBlocks && GetTime() - nLastDSProgressBlockTime <= 1) return; + if(nBestHeight > privateSendClientManager->nCachedNumBlocks && GetTime() - nLastDSProgressBlockTime <= 1) return; nLastDSProgressBlockTime = GetTime(); QString strKeysLeftText(tr("keys left: %1").arg(walletModel->getKeysLeftSinceAutoBackup())); @@ -500,9 +509,9 @@ void OverviewPage::privateSendStatus(bool fForce) } ui->labelPrivateSendEnabled->setToolTip(strKeysLeftText); - if (!privateSendClient.fPrivateSendRunning) { - if (nBestHeight != privateSendClient.nCachedNumBlocks) { - privateSendClient.nCachedNumBlocks = nBestHeight; + if (!privateSendClientManager->IsMixing()) { + if (nBestHeight != privateSendClientManager->nCachedNumBlocks) { + privateSendClientManager->nCachedNumBlocks = nBestHeight; updatePrivateSendProgress(); } @@ -557,7 +566,7 @@ void OverviewPage::privateSendStatus(bool fForce) } } - QString strEnabled = privateSendClient.fPrivateSendRunning ? tr("Enabled") : tr("Disabled"); + QString strEnabled = privateSendClientManager->IsMixing() ? tr("Enabled") : tr("Disabled"); // Show how many keys left in advanced PS UI mode only if(fShowAdvancedPSUI) strEnabled += ", " + strKeysLeftText; ui->labelPrivateSendEnabled->setText(strEnabled); @@ -579,15 +588,15 @@ void OverviewPage::privateSendStatus(bool fForce) } // check privatesend status and unlock if needed - if(nBestHeight != privateSendClient.nCachedNumBlocks) { + if(nBestHeight != privateSendClientManager->nCachedNumBlocks) { // Balance and number of transactions might have changed - privateSendClient.nCachedNumBlocks = nBestHeight; + privateSendClientManager->nCachedNumBlocks = nBestHeight; updatePrivateSendProgress(); } setWidgetsVisible(true); - ui->labelSubmittedDenom->setText(QString(privateSendClient.GetSessionDenoms().c_str())); + ui->labelSubmittedDenom->setText(QString(privateSendClientManager->GetSessionDenoms().c_str())); } void OverviewPage::togglePrivateSend(){ @@ -600,7 +609,15 @@ void OverviewPage::togglePrivateSend(){ QMessageBox::Ok, QMessageBox::Ok); settings.setValue("hasMixed", "hasMixed"); } - if(!privateSendClient.fPrivateSendRunning){ + + auto it = privateSendClientManagers.find(walletModel->getWallet()->GetName()); + if (it == privateSendClientManagers.end()) { + // nothing to do + return; + } + CPrivateSendClientManager* privateSendClientManager = it->second; + + if (!privateSendClientManager->IsMixing()) { const CAmount nMinAmount = CPrivateSend::GetSmallestDenomination() + CPrivateSend::GetMaxCollateralAmount(); if(currentBalance < nMinAmount){ QString strMinAmount(BitcoinUnits::formatWithUnit(nDisplayUnit, nMinAmount)); @@ -617,7 +634,7 @@ void OverviewPage::togglePrivateSend(){ if(!ctx.isValid()) { //unlock was cancelled - privateSendClient.nCachedNumBlocks = std::numeric_limits::max(); + privateSendClientManager->nCachedNumBlocks = std::numeric_limits::max(); QMessageBox::warning(this, tr("PrivateSend"), tr("Wallet is locked and user declined to unlock. Disabling PrivateSend."), QMessageBox::Ok, QMessageBox::Ok); @@ -628,14 +645,15 @@ void OverviewPage::togglePrivateSend(){ } - privateSendClient.fPrivateSendRunning = !privateSendClient.fPrivateSendRunning; - privateSendClient.nCachedNumBlocks = std::numeric_limits::max(); + privateSendClientManager->nCachedNumBlocks = std::numeric_limits::max(); - if(!privateSendClient.fPrivateSendRunning){ + if (privateSendClientManager->IsMixing()) { ui->togglePrivateSend->setText(tr("Start Mixing")); - privateSendClient.ResetPool(); + privateSendClientManager->ResetPool(); + privateSendClientManager->StopMixing(); } else { ui->togglePrivateSend->setText(tr("Stop Mixing")); + privateSendClientManager->StartMixing(walletModel->getWallet()); } } @@ -663,5 +681,10 @@ void OverviewPage::DisablePrivateSendCompletely() { if (nWalletBackups <= 0) { ui->labelPrivateSendEnabled->setText("(" + tr("Disabled") + ")"); } - privateSendClient.fPrivateSendRunning = false; + auto it = privateSendClientManagers.find(walletModel->getWallet()->GetName()); + if (it == privateSendClientManagers.end()) { + // nothing to do + return; + } + it->second->StopMixing(); } diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 155abc915b35..a393e7e19765 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -143,7 +143,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() ui->reqAmount->value(), ui->reqMessage->text()); ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->setModel(model->getOptionsModel()); + dialog->setModel(model); dialog->setInfo(info); dialog->show(); clear(); @@ -156,7 +156,7 @@ void ReceiveCoinsDialog::on_recentRequestsView_doubleClicked(const QModelIndex & { const RecentRequestsTableModel *submodel = model->getRecentRequestsTableModel(); ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); - dialog->setModel(model->getOptionsModel()); + dialog->setModel(model); dialog->setInfo(submodel->entry(index.row()).recipient); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index 441d3662da9a..6bb9c1238f1c 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -107,12 +107,12 @@ ReceiveRequestDialog::~ReceiveRequestDialog() delete ui; } -void ReceiveRequestDialog::setModel(OptionsModel *_model) +void ReceiveRequestDialog::setModel(WalletModel *_model) { this->model = _model; if (_model) - connect(_model, SIGNAL(displayUnitChanged(int)), this, SLOT(update())); + connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(update())); // update the display unit if necessary update(); @@ -143,11 +143,14 @@ void ReceiveRequestDialog::update() uri + "\">" + GUIUtil::HtmlEscape(uri) + "
"; html += ""+tr("Address")+": " + GUIUtil::HtmlEscape(info.address) + "
"; if(info.amount) - html += ""+tr("Amount")+": " + BitcoinUnits::formatHtmlWithUnit(model->getDisplayUnit(), info.amount) + "
"; + html += ""+tr("Amount")+": " + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), info.amount) + "
"; if(!info.label.isEmpty()) html += ""+tr("Label")+": " + GUIUtil::HtmlEscape(info.label) + "
"; if(!info.message.isEmpty()) html += ""+tr("Message")+": " + GUIUtil::HtmlEscape(info.message) + "
"; + if(model->isMultiwallet()) { + html += ""+tr("Wallet")+": " + GUIUtil::HtmlEscape(model->getWalletName()) + "
"; + } ui->outUri->setText(html); #ifdef USE_QRCODE diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h index 303db5bac698..71feb54f42f8 100644 --- a/src/qt/receiverequestdialog.h +++ b/src/qt/receiverequestdialog.h @@ -12,8 +12,6 @@ #include #include -class OptionsModel; - namespace Ui { class ReceiveRequestDialog; } @@ -53,7 +51,7 @@ class ReceiveRequestDialog : public QDialog explicit ReceiveRequestDialog(QWidget *parent = 0); ~ReceiveRequestDialog(); - void setModel(OptionsModel *model); + void setModel(WalletModel *model); void setInfo(const SendCoinsRecipient &info); private Q_SLOTS: @@ -64,7 +62,7 @@ private Q_SLOTS: private: Ui::ReceiveRequestDialog *ui; - OptionsModel *model; + WalletModel *model; SendCoinsRecipient info; }; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 35889bc9cb61..37402e3aa873 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -91,7 +92,7 @@ class RPCExecutor : public QObject Q_OBJECT public Q_SLOTS: - void request(const QString &command); + void request(const QString &command, const QString &walletID); Q_SIGNALS: void reply(int category, const QString &command); @@ -152,7 +153,7 @@ class QtRPCTimerInterface: public RPCTimerInterface * @param[out] pstrFilteredOut Command line, filtered to remove any sensitive data */ -bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut) +bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut, const std::string *walletID) { std::vector< std::vector > stack; stack.push_back(std::vector()); @@ -310,10 +311,8 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & req.params = RPCConvertValues(stack.back()[0], std::vector(stack.back().begin() + 1, stack.back().end())); req.strMethod = stack.back()[0]; #ifdef ENABLE_WALLET - // TODO: Move this logic to WalletModel - if (HasWallets()) { - // in Qt, use always the wallet with index 0 when running with multiple wallets - QByteArray encodedName = QUrl::toPercentEncoding(QString::fromStdString(GetWallets()[0]->GetName())); + if (walletID && !walletID->empty()) { + QByteArray encodedName = QUrl::toPercentEncoding(QString::fromStdString(*walletID)); req.URI = "/wallet/"+std::string(encodedName.constData(), encodedName.length()); } #endif @@ -392,7 +391,7 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & } } -void RPCExecutor::request(const QString &command) +void RPCExecutor::request(const QString &command, const QString &walletID) { try { @@ -423,7 +422,8 @@ void RPCExecutor::request(const QString &command) " example: getblock(getblockhash(0),true)[tx][0]\n\n"))); return; } - if(!RPCConsole::RPCExecuteCommandLine(result, executableCommand)) + std::string wallet_id = walletID.toStdString(); + if(!RPCConsole::RPCExecuteCommandLine(result, executableCommand, nullptr, &wallet_id)) { Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \"")); return; @@ -498,6 +498,10 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : connect(ui->fontSmallerButton, SIGNAL(clicked()), this, SLOT(fontSmaller())); connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph, SLOT(clear())); + // disable the wallet selector by default + ui->WalletSelector->setVisible(false); + ui->WalletSelectorLabel->setVisible(false); + // Wallet Repair Buttons // connect(ui->btn_salvagewallet, SIGNAL(clicked()), this, SLOT(walletSalvage())); // Disable salvage option in GUI, it's way too powerful and can lead to funds loss @@ -735,6 +739,23 @@ void RPCConsole::setClientModel(ClientModel *model) } } +#ifdef ENABLE_WALLET +void RPCConsole::addWallet(WalletModel * const walletModel) +{ + const QString name = walletModel->getWalletName(); + // use name for text and internal data object (to allow to move to a wallet id later) + ui->WalletSelector->addItem(name, name); + if (ui->WalletSelector->count() == 2 && !isVisible()) { + // First wallet added, set to default so long as the window isn't presently visible (and potentially in use) + ui->WalletSelector->setCurrentIndex(1); + } + if (ui->WalletSelector->count() > 2) { + ui->WalletSelector->setVisible(true); + ui->WalletSelectorLabel->setVisible(true); + } +} +#endif + static QString categoryClass(int category) { switch(category) @@ -1016,8 +1037,25 @@ void RPCConsole::on_lineEdit_returnPressed() cmdBeforeBrowsing = QString(); + QString walletID; +#ifdef ENABLE_WALLET + const int wallet_index = ui->WalletSelector->currentIndex(); + if (wallet_index > 0) { + walletID = (QString)ui->WalletSelector->itemData(wallet_index).value(); + } + + if (m_last_wallet_id != walletID) { + if (walletID.isEmpty()) { + message(CMD_REQUEST, tr("Executing command without any wallet")); + } else { + message(CMD_REQUEST, tr("Executing command using \"%1\" wallet").arg(walletID)); + } + m_last_wallet_id = walletID; + } +#endif + message(CMD_REQUEST, QString::fromStdString(strFilteredCmd)); - Q_EMIT cmdRequest(cmd); + Q_EMIT cmdRequest(cmd, walletID); cmd = QString::fromStdString(strFilteredCmd); @@ -1065,7 +1103,7 @@ void RPCConsole::startExecutor() // Replies from executor object must go to this object connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString))); // Requests from this object must go to executor - connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString))); + connect(this, SIGNAL(cmdRequest(QString, QString)), executor, SLOT(request(QString, QString))); // On stopExecutor signal // - quit the Qt event loop in the execution thread diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 1ade453af74f..11a99f86befd 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -19,6 +19,7 @@ class ClientModel; class PlatformStyle; class RPCTimerInterface; +class WalletModel; namespace Ui { class RPCConsole; @@ -38,12 +39,13 @@ class RPCConsole: public QWidget explicit RPCConsole(const PlatformStyle *platformStyle, QWidget *parent); ~RPCConsole(); - static bool RPCParseCommandLine(std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = nullptr); - static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = nullptr) { - return RPCParseCommandLine(strResult, strCommand, true, pstrFilteredOut); + static bool RPCParseCommandLine(std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr); + static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = nullptr, const std::string *walletID = nullptr) { + return RPCParseCommandLine(strResult, strCommand, true, pstrFilteredOut, walletID); } void setClientModel(ClientModel *model); + void addWallet(WalletModel * const walletModel); enum MessageClass { MC_ERROR, @@ -94,7 +96,7 @@ public Q_SLOTS: void fontBigger(); void fontSmaller(); void setFontSize(int newSize); - + /** Wallet repair options */ void walletSalvage(); void walletRescan(); @@ -102,7 +104,7 @@ public Q_SLOTS: void walletZaptxes2(); void walletUpgrade(); void walletReindex(); - + /** Append the message to the message widget */ void message(int category, const QString &message, bool html = false); /** Set number of connections shown in the UI */ @@ -139,7 +141,7 @@ public Q_SLOTS: Q_SIGNALS: // For RPC command executor void stopExecutor(); - void cmdRequest(const QString &command); + void cmdRequest(const QString &command, const QString &walletID); /** Get restart command-line parameters and handle restart */ void handleRestart(QStringList args); @@ -175,6 +177,7 @@ public Q_SLOTS: int consoleFontSize; QCompleter *autoCompleter; QThread thread; + QString m_last_wallet_id; /** Update UI with latest network info from model. */ void updateNetworkState(); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index fd699c97f9ca..36316e6abf75 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -331,10 +331,12 @@ void SendCoinsDialog::send(QList recipients) QStringList formatted; for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients()) { - // generate bold amount string + // generate bold amount string with wallet name in case of multiwallet QString amount = "" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); + if (model->isMultiwallet()) { + amount.append(" "+tr("from wallet %1").arg(GUIUtil::HtmlEscape(model->getWalletName()))+" "); + } amount.append(" "); - // generate monospace address string QString address = "" + rcp.address; address.append(""); diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 6f08ac82ea70..0b3806608014 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include @@ -39,10 +40,16 @@ void WalletFrame::setClientModel(ClientModel *_clientModel) this->clientModel = _clientModel; } -bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel) +bool WalletFrame::addWallet(WalletModel *walletModel) { - if (!gui || !clientModel || !walletModel || mapWalletViews.count(name) > 0) + if (!gui || !clientModel || !walletModel) { return false; + } + + const QString name = walletModel->getWalletName(); + if (mapWalletViews.count(name) > 0) { + return false; + } WalletView *walletView = new WalletView(platformStyle, this); walletView->setBitcoinGUI(gui); diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 55210b080a65..78aff0f166c5 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -36,7 +36,7 @@ class WalletFrame : public QFrame void setClientModel(ClientModel *clientModel); - bool addWallet(const QString& name, WalletModel *walletModel); + bool addWallet(WalletModel *walletModel); bool setCurrentWallet(const QString& name); bool removeWallet(const QString &name); void removeAllWallets(); @@ -59,6 +59,7 @@ class WalletFrame : public QFrame const PlatformStyle *platformStyle; +public: WalletView *currentWalletView(); public Q_SLOTS: diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 221218680929..61d03c10cf57 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -138,8 +138,9 @@ void WalletModel::updateStatus() { EncryptionStatus newEncryptionStatus = getEncryptionStatus(); - if(cachedEncryptionStatus != newEncryptionStatus) - Q_EMIT encryptionStatusChanged(newEncryptionStatus); + if(cachedEncryptionStatus != newEncryptionStatus) { + Q_EMIT encryptionStatusChanged(); + } } void WalletModel::pollBalanceChanged() @@ -154,13 +155,12 @@ void WalletModel::pollBalanceChanged() if(!lockWallet) return; - if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks || privateSendClient.nPrivateSendRounds != cachedPrivateSendRounds) - { + if (fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks || CPrivateSendClientOptions::GetRounds() != cachedPrivateSendRounds) { fForceCheckBalanceChanged = false; // Balance and number of transactions might have changed cachedNumBlocks = chainActive.Height(); - cachedPrivateSendRounds = privateSendClient.nPrivateSendRounds; + cachedPrivateSendRounds = CPrivateSendClientOptions::GetRounds(); checkBalanceChanged(); if(transactionTableModel) @@ -808,3 +808,18 @@ int WalletModel::getDefaultConfirmTarget() const { return nTxConfirmTarget; } + +QString WalletModel::getWalletName() const +{ + LOCK(wallet->cs_wallet); + QString walletName = QString::fromStdString(wallet->GetName()); + if (walletName.endsWith(".dat")) { + walletName.truncate(walletName.size() - 4); + } + return walletName; +} + +bool WalletModel::isMultiwallet() +{ + return gArgs.GetArgs("-wallet").size() > 1; +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 27a64a4d9e8c..518f0a4cf75b 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -133,6 +133,8 @@ class WalletModel : public QObject TransactionTableModel *getTransactionTableModel(); RecentRequestsTableModel *getRecentRequestsTableModel(); + CWallet *getWallet() const { return wallet; }; + CAmount getBalance(const CCoinControl *coinControl = nullptr) const; CAmount getUnconfirmedBalance() const; CAmount getImmatureBalance() const; @@ -232,6 +234,9 @@ class WalletModel : public QObject int getRealOutpointPrivateSendRounds(const COutPoint& outpoint) const; + QString getWalletName() const; + + static bool isMultiwallet(); private: CWallet *wallet; bool fHaveWatchOnly; @@ -270,7 +275,7 @@ class WalletModel : public QObject const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance); // Encryption status of wallet changed - void encryptionStatusChanged(int status); + void encryptionStatusChanged(); // Signal emitted when wallet needs to be unlocked // It is valid behaviour for listeners to keep the wallet locked after this signal; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 7383f31a47cf..6415198db6df 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -134,13 +134,13 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui) connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int))); // Pass through encryption status changed signals - connect(this, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int))); + connect(this, SIGNAL(encryptionStatusChanged()), gui, SLOT(updateWalletStatus())); // Pass through transaction notifications - connect(this, SIGNAL(incomingTransaction(QString,int,CAmount,QString,QString,QString)), gui, SLOT(incomingTransaction(QString,int,CAmount,QString,QString,QString))); + connect(this, SIGNAL(incomingTransaction(QString,int,CAmount,QString,QString,QString,QString)), gui, SLOT(incomingTransaction(QString,int,CAmount,QString,QString,QString,QString))); // Connect HD enabled state signal - connect(this, SIGNAL(hdEnabledStatusChanged(int)), gui, SLOT(setHDStatus(int))); + connect(this, SIGNAL(hdEnabledStatusChanged()), gui, SLOT(updateWalletStatus())); } } @@ -178,11 +178,11 @@ void WalletView::setWalletModel(WalletModel *_walletModel) connect(_walletModel, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); // Handle changes in encryption status - connect(_walletModel, SIGNAL(encryptionStatusChanged(int)), this, SIGNAL(encryptionStatusChanged(int))); + connect(_walletModel, SIGNAL(encryptionStatusChanged()), this, SIGNAL(encryptionStatusChanged())); updateEncryptionStatus(); // update HD status - Q_EMIT hdEnabledStatusChanged(_walletModel->hdEnabled()); + Q_EMIT hdEnabledStatusChanged(); // Balloon pop-up for new transaction connect(_walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), @@ -222,7 +222,7 @@ void WalletView::processNewTransaction(const QModelIndex& parent, int start, int QString address = ttm->data(index, TransactionTableModel::AddressRole).toString(); QString label = ttm->data(index, TransactionTableModel::LabelRole).toString(); - Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label); + Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, walletModel->getWalletName()); } void WalletView::gotoOverviewPage() @@ -303,7 +303,7 @@ void WalletView::showOutOfSyncWarning(bool fShow) void WalletView::updateEncryptionStatus() { - Q_EMIT encryptionStatusChanged(walletModel->getEncryptionStatus()); + Q_EMIT encryptionStatusChanged(); } void WalletView::encryptWallet(bool status) diff --git a/src/qt/walletview.h b/src/qt/walletview.h index ba92577bdfea..b40a186d9dfb 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -46,6 +46,7 @@ class WalletView : public QStackedWidget The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic. */ void setClientModel(ClientModel *clientModel); + WalletModel *getWalletModel() { return walletModel; } /** Set the wallet model. The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending functionality. @@ -132,11 +133,11 @@ public Q_SLOTS: /** Fired when a message should be reported to the user */ void message(const QString &title, const QString &message, unsigned int style); /** Encryption status of wallet changed */ - void encryptionStatusChanged(int status); + void encryptionStatusChanged(); /** HD-Enabled status of wallet changed (only possible during startup) */ - void hdEnabledStatusChanged(int hdEnabled); + void hdEnabledStatusChanged(); /** Notify that a new transaction appeared */ - void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label); + void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); /** Notify that the out of sync warning icon has been pressed */ void outOfSyncWarningClicked(); }; diff --git a/src/rpc/privatesend.cpp b/src/rpc/privatesend.cpp index 6447a8f7ca7d..f2dd6c011e77 100644 --- a/src/rpc/privatesend.cpp +++ b/src/rpc/privatesend.cpp @@ -32,7 +32,7 @@ UniValue privatesend(const JSONRPCRequest& request) if (fMasternodeMode) throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes"); - if (!privateSendClient.fEnablePrivateSend) { + if (!CPrivateSendClientOptions::IsEnabled()) { if (!gArgs.GetBoolArg("-enableprivatesend", true)) { // otherwise it's on by default, unless cmd line option says otherwise throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing is disabled via -enableprivatesend=0 command line option, remove it to enable mixing again"); @@ -43,6 +43,8 @@ UniValue privatesend(const JSONRPCRequest& request) } } + auto it = privateSendClientManagers.find(pwallet->GetName()); + if (request.params[0].get_str() == "start") { { LOCK(pwallet->cs_wallet); @@ -50,18 +52,21 @@ UniValue privatesend(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please unlock wallet for mixing with walletpassphrase first."); } - privateSendClient.fPrivateSendRunning = true; - bool result = privateSendClient.DoAutomaticDenominating(*g_connman); - return "Mixing " + (result ? "started successfully" : ("start failed: " + privateSendClient.GetStatuses() + ", will retry")); + if (!it->second->StartMixing(pwallet)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Mixing has been started already."); + } + + bool result = it->second->DoAutomaticDenominating(*g_connman); + return "Mixing " + (result ? "started successfully" : ("start failed: " + it->second->GetStatuses() + ", will retry")); } if (request.params[0].get_str() == "stop") { - privateSendClient.fPrivateSendRunning = false; + it->second->StopMixing(); return "Mixing was stopped"; } if (request.params[0].get_str() == "reset") { - privateSendClient.ResetPool(); + it->second->ResetPool(); return "Mixing was reset"; } @@ -86,13 +91,13 @@ UniValue getprivatesendinfo(const JSONRPCRequest& request) "\nResult (for regular nodes):\n" "{\n" " \"enabled\": true|false, (bool) Whether mixing functionality is enabled\n" - " \"running\": true|false, (bool) Whether mixing is currently running\n" " \"multisession\": true|false, (bool) Whether PrivateSend Multisession option is enabled\n" " \"max_sessions\": xxx, (numeric) How many parallel mixing sessions can there be at once\n" " \"max_rounds\": xxx, (numeric) How many rounds to mix\n" " \"max_amount\": xxx, (numeric) Target PrivateSend balance in " + CURRENCY_UNIT + "\n" " \"max_denoms\": xxx, (numeric) How many inputs of each denominated amount to create\n" " \"queue_size\": xxx, (numeric) How many queues there are currently on the network\n" + " \"running\": true|false, (bool) Whether mixing is currently running\n" " \"sessions\": (array of json objects)\n" " [\n" " {\n" @@ -130,13 +135,18 @@ UniValue getprivatesendinfo(const JSONRPCRequest& request) #ifdef ENABLE_WALLET - privateSendClient.GetJsonInfo(obj); + + CPrivateSendClientOptions::GetJsonInfo(obj); + + obj.pushKV("queue_size", privateSendClientQueueManager.GetQueueSize()); CWallet* const pwallet = GetWalletForJSONRPCRequest(request); if (!pwallet) { return obj; } + privateSendClientManagers.at(pwallet->GetName())->GetJsonInfo(obj); + obj.pushKV("keys_left", pwallet->nKeysLeftSinceAutoBackup); obj.push_back(Pair("warnings", pwallet->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING ? "WARNING: keypool is almost depleted!" : "")); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 8d96744ba641..891bd240f79a 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -364,20 +364,20 @@ void WalletInit::Start(CScheduler& scheduler) // Run a thread to flush wallet periodically scheduler.scheduleEvery(MaybeCompactWalletDB, 500); - if (!fMasternodeMode && privateSendClient.fEnablePrivateSend) { - scheduler.scheduleEvery(std::bind(&CPrivateSendClientManager::DoMaintenance, std::ref(privateSendClient), - std::ref(*g_connman)), 1 * 1000); + if (!fMasternodeMode && CPrivateSendClientOptions::IsEnabled()) { + scheduler.scheduleEvery(std::bind(&DoPrivateSendMaintenance, std::ref(*g_connman)), 1 * 1000); } } void WalletInit::Flush() { - if (privateSendClient.fEnablePrivateSend) { - // Stop PrivateSend, release keys - privateSendClient.fPrivateSendRunning = false; - privateSendClient.ResetPool(); - } for (CWallet* pwallet : GetWallets()) { + if (CPrivateSendClientOptions::IsEnabled()) { + // Stop PrivateSend, release keys + auto it = privateSendClientManagers.find(pwallet->GetName()); + it->second->ResetPool(); + it->second->StopMixing(); + } pwallet->Flush(false); } } @@ -407,28 +407,24 @@ void WalletInit::AutoLockMasternodeCollaterals() void WalletInit::InitPrivateSendSettings() { - if (!HasWallets()) { - privateSendClient.fEnablePrivateSend = privateSendClient.fPrivateSendRunning = false; - } else { - privateSendClient.fEnablePrivateSend = gArgs.GetBoolArg("-enableprivatesend", true); - privateSendClient.fPrivateSendRunning = GetWallets()[0]->IsLocked() ? false : gArgs.GetBoolArg("-privatesendautostart", DEFAULT_PRIVATESEND_AUTOSTART); + CPrivateSendClientOptions::SetEnabled(HasWallets() ? gArgs.GetBoolArg("-enableprivatesend", true) : false); + if (!CPrivateSendClientOptions::IsEnabled()) { + return; } - privateSendClient.fPrivateSendMultiSession = gArgs.GetBoolArg("-privatesendmultisession", DEFAULT_PRIVATESEND_MULTISESSION); - privateSendClient.nPrivateSendSessions = std::min(std::max((int)gArgs.GetArg("-privatesendsessions", DEFAULT_PRIVATESEND_SESSIONS), MIN_PRIVATESEND_SESSIONS), MAX_PRIVATESEND_SESSIONS); - privateSendClient.nPrivateSendRounds = std::min(std::max((int)gArgs.GetArg("-privatesendrounds", DEFAULT_PRIVATESEND_ROUNDS), MIN_PRIVATESEND_ROUNDS), MAX_PRIVATESEND_ROUNDS); - privateSendClient.nPrivateSendAmount = std::min(std::max((int)gArgs.GetArg("-privatesendamount", DEFAULT_PRIVATESEND_AMOUNT), MIN_PRIVATESEND_AMOUNT), MAX_PRIVATESEND_AMOUNT); - privateSendClient.nPrivateSendDenomsGoal = std::min(std::max((int)gArgs.GetArg("-privatesenddenomsgoal", DEFAULT_PRIVATESEND_DENOMS_GOAL), MIN_PRIVATESEND_DENOMS_GOAL), MAX_PRIVATESEND_DENOMS_GOAL); - privateSendClient.nPrivateSendDenomsHardCap = std::min(std::max((int)gArgs.GetArg("-privatesenddenomshardcap", DEFAULT_PRIVATESEND_DENOMS_HARDCAP), MIN_PRIVATESEND_DENOMS_HARDCAP), MAX_PRIVATESEND_DENOMS_HARDCAP); - - if (privateSendClient.fEnablePrivateSend) { - LogPrintf("PrivateSend: autostart=%d, multisession=%d," /* Continued */ - "sessions=%d, rounds=%d, amount=%d, denoms_goal=%d, denoms_hardcap=%d\n", - privateSendClient.fPrivateSendRunning, privateSendClient.fPrivateSendMultiSession, - privateSendClient.nPrivateSendSessions, privateSendClient.nPrivateSendRounds, - privateSendClient.nPrivateSendAmount, - privateSendClient.nPrivateSendDenomsGoal, privateSendClient.nPrivateSendDenomsHardCap); + bool fAutoStart = gArgs.GetBoolArg("-privatesendautostart", DEFAULT_PRIVATESEND_AUTOSTART); + for (auto& pwallet : GetWallets()) { + if (pwallet->IsLocked()) { + privateSendClientManagers.at(pwallet->GetName())->StopMixing(); + } else if (fAutoStart) { + privateSendClientManagers.at(pwallet->GetName())->StartMixing(pwallet); + } } - + LogPrintf("PrivateSend: autostart=%d, multisession=%d," /* Continued */ + "sessions=%d, rounds=%d, amount=%d, denoms_goal=%d, denoms_hardcap=%d\n", + fAutoStart, CPrivateSendClientOptions::IsMultiSessionEnabled(), + CPrivateSendClientOptions::GetSessions(), CPrivateSendClientOptions::GetRounds(), + CPrivateSendClientOptions::GetAmount(), CPrivateSendClientOptions::GetDenomsGoal(), + CPrivateSendClientOptions::GetDenomsHardCap()); } void WalletInit::InitKeePass() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e85f68360224..db4e100dd9e7 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2682,7 +2682,7 @@ UniValue setprivatesendrounds(const JSONRPCRequest& request) if (nRounds > MAX_PRIVATESEND_ROUNDS || nRounds < MIN_PRIVATESEND_ROUNDS) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid number of rounds"); - privateSendClient.nPrivateSendRounds = nRounds; + CPrivateSendClientOptions::SetRounds(nRounds); return NullUniValue; } @@ -2710,7 +2710,7 @@ UniValue setprivatesendamount(const JSONRPCRequest& request) if (nAmount > MAX_PRIVATESEND_AMOUNT || nAmount < MIN_PRIVATESEND_AMOUNT) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount of " + CURRENCY_UNIT + " as mixing goal amount"); - privateSendClient.nPrivateSendAmount = nAmount; + CPrivateSendClientOptions::SetAmount(nAmount); return NullUniValue; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 833965e8c2d8..470096ad9483 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -53,6 +53,7 @@ bool AddWallet(CWallet* wallet) std::vector::const_iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); if (i != vpwallets.end()) return false; vpwallets.push_back(wallet); + privateSendClientManagers.emplace(std::make_pair(wallet->GetName(), new CPrivateSendClientManager())); return true; } @@ -63,6 +64,10 @@ bool RemoveWallet(CWallet* wallet) std::vector::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); if (i == vpwallets.end()) return false; vpwallets.erase(i); + auto it = privateSendClientManagers.find(wallet->GetName()); + delete it->second; + it->second = nullptr; + privateSendClientManagers.erase(it); return true; } @@ -1657,7 +1662,7 @@ int CWallet::GetCappedOutpointPrivateSendRounds(const COutPoint& outpoint) const { LOCK(cs_wallet); int realPrivateSendRounds = GetRealOutpointPrivateSendRounds(outpoint); - return realPrivateSendRounds > privateSendClient.nPrivateSendRounds ? privateSendClient.nPrivateSendRounds : realPrivateSendRounds; + return realPrivateSendRounds > CPrivateSendClientOptions::GetRounds() ? CPrivateSendClientOptions::GetRounds() : realPrivateSendRounds; } bool CWallet::IsDenominated(const COutPoint& outpoint) const @@ -2359,7 +2364,7 @@ CAmount CWalletTx::GetAnonymizedCredit(bool fUseCache) const if (pwallet->IsSpent(hashTx, i) || !CPrivateSend::IsDenominatedAmount(txout.nValue)) continue; const int nRounds = pwallet->GetCappedOutpointPrivateSendRounds(outpoint); - if (nRounds >= privateSendClient.nPrivateSendRounds){ + if (nRounds >= CPrivateSendClientOptions::GetRounds()) { nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) throw std::runtime_error(std::string(__func__) + ": value out of range"); @@ -2568,7 +2573,7 @@ CAmount CWallet::GetBalance() const CAmount CWallet::GetAnonymizableBalance(bool fSkipDenominated, bool fSkipUnconfirmed) const { - if(!privateSendClient.fEnablePrivateSend) return 0; + if (!CPrivateSendClientOptions::IsEnabled()) return 0; std::vector vecTally; if(!SelectCoinsGroupedByAddresses(vecTally, fSkipDenominated, true, fSkipUnconfirmed)) return 0; @@ -2590,7 +2595,7 @@ CAmount CWallet::GetAnonymizableBalance(bool fSkipDenominated, bool fSkipUnconfi CAmount CWallet::GetAnonymizedBalance() const { - if(!privateSendClient.fEnablePrivateSend) return 0; + if (!CPrivateSendClientOptions::IsEnabled()) return 0; CAmount nTotal = 0; @@ -2607,7 +2612,7 @@ CAmount CWallet::GetAnonymizedBalance() const // that's ok as long as we use it for informational purposes only float CWallet::GetAverageAnonymizedRounds() const { - if(!privateSendClient.fEnablePrivateSend) return 0; + if (!CPrivateSendClientOptions::IsEnabled()) return 0; int nTotal = 0; int nCount = 0; @@ -2629,7 +2634,7 @@ float CWallet::GetAverageAnonymizedRounds() const // that's ok as long as we use it for informational purposes only CAmount CWallet::GetNormalizedAnonymizedBalance() const { - if(!privateSendClient.fEnablePrivateSend) return 0; + if (!CPrivateSendClientOptions::IsEnabled()) return 0; CAmount nTotal = 0; @@ -2643,7 +2648,7 @@ CAmount CWallet::GetNormalizedAnonymizedBalance() const if (it->second.GetDepthInMainChain() < 0) continue; int nRounds = GetCappedOutpointPrivateSendRounds(outpoint); - nTotal += nValue * nRounds / privateSendClient.nPrivateSendRounds; + nTotal += nValue * nRounds / CPrivateSendClientOptions::GetRounds(); } return nTotal; @@ -2651,7 +2656,7 @@ CAmount CWallet::GetNormalizedAnonymizedBalance() const CAmount CWallet::GetDenominatedBalance(bool unconfirmed) const { - if(!privateSendClient.fEnablePrivateSend) return 0; + if (!CPrivateSendClientOptions::IsEnabled()) return 0; CAmount nTotal = 0; @@ -2826,11 +2831,11 @@ void CWallet::AvailableCoins(std::vector &vCoins, bool fOnlySafe, const if (nCoinType == CoinType::ONLY_FULLY_MIXED) { if (!CPrivateSend::IsDenominatedAmount(pcoin->tx->vout[i].nValue)) continue; int nRounds = GetCappedOutpointPrivateSendRounds(COutPoint(wtxid, i)); - found = nRounds >= privateSendClient.nPrivateSendRounds; + found = nRounds >= CPrivateSendClientOptions::GetRounds(); } else if(nCoinType == CoinType::ONLY_READY_TO_MIX) { if (!CPrivateSend::IsDenominatedAmount(pcoin->tx->vout[i].nValue)) continue; int nRounds = GetCappedOutpointPrivateSendRounds(COutPoint(wtxid, i)); - found = nRounds < privateSendClient.nPrivateSendRounds; + found = nRounds < CPrivateSendClientOptions::GetRounds(); } else if(nCoinType == CoinType::ONLY_NONDENOMINATED) { if (CPrivateSend::IsCollateralAmount(pcoin->tx->vout[i].nValue)) continue; // do not use collateral amounts found = !CPrivateSend::IsDenominatedAmount(pcoin->tx->vout[i].nValue); @@ -3212,7 +3217,7 @@ bool CWallet::SelectCoins(const std::vector& vAvailableCoins, const CAm // Make sure to include mixed preset inputs only, // even if some non-mixed inputs were manually selected via CoinControl int nRounds = GetRealOutpointPrivateSendRounds(outpoint); - if (nRounds < privateSendClient.nPrivateSendRounds) continue; + if (nRounds < CPrivateSendClientOptions::GetRounds()) continue; } nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; setPresetCoins.insert(CInputCoin(pcoin, outpoint.n)); @@ -3415,7 +3420,7 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector& vecTa // otherwise they will just lead to higher fee / lower priority if(wtx.tx->vout[i].nValue <= nSmallestDenom/10) continue; // ignore mixed - if(GetCappedOutpointPrivateSendRounds(COutPoint(outpoint.hash, i)) >= privateSendClient.nPrivateSendRounds) continue; + if (GetCappedOutpointPrivateSendRounds(COutPoint(outpoint.hash, i)) >= CPrivateSendClientOptions::GetRounds()) continue; } if (itTallyItem == mapTally.end()) { @@ -4342,7 +4347,7 @@ bool CWallet::NewKeyPool() batch.ErasePool(nIndex); } setExternalKeyPool.clear(); - privateSendClient.fPrivateSendRunning = false; + privateSendClientManagers.at(GetName())->StopMixing(); nKeysLeftSinceAutoBackup = 0; m_pool_key_to_index.clear();