diff --git a/src/init.cpp b/src/init.cpp index 242be406398f..dfc9811e7b47 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -246,8 +246,9 @@ void PrepareShutdown() privateSendClient.fPrivateSendRunning = false; privateSendClient.ResetPool(); } - if (pwalletMain) - pwalletMain->Flush(false); + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(false); + } #endif MapPort(false); UnregisterValidationInterface(peerLogic.get()); @@ -306,8 +307,9 @@ void PrepareShutdown() evoDb = NULL; } #ifdef ENABLE_WALLET - if (pwalletMain) - pwalletMain->Flush(true); + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(true); + } #endif #if ENABLE_ZMQ @@ -359,8 +361,10 @@ void Shutdown() // Shutdown part 2: Stop TOR thread and delete wallet instance StopTorControl(); #ifdef ENABLE_WALLET - delete pwalletMain; - pwalletMain = NULL; + for (CWalletRef pwallet : vpwallets) { + delete pwallet; + } + vpwallets.clear(); #endif globalVerifyHandle.reset(); ECC_Stop(); @@ -834,8 +838,8 @@ void ThreadImport(std::vector vImportFiles) #ifdef ENABLE_WALLET // we can't do this before DIP3 is fully initialized - if (pwalletMain) { - pwalletMain->AutoLockMasternodeCollaterals(); + for (CWalletRef pwallet : vpwallets) { + pwallet->AutoLockMasternodeCollaterals(); } #endif @@ -1998,11 +2002,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) nMaxRounds = std::numeric_limits::max(); } - if (pwalletMain == nullptr) { + if (vpwallets.empty()) { privateSendClient.fEnablePrivateSend = privateSendClient.fPrivateSendRunning = false; } else { privateSendClient.fEnablePrivateSend = gArgs.GetBoolArg("-enableprivatesend", !fLiteMode); - privateSendClient.fPrivateSendRunning = pwalletMain->IsLocked() ? false : gArgs.GetBoolArg("-privatesendautostart", DEFAULT_PRIVATESEND_AUTOSTART); + privateSendClient.fPrivateSendRunning = vpwallets[0]->IsLocked() ? false : gArgs.GetBoolArg("-privatesendautostart", DEFAULT_PRIVATESEND_AUTOSTART); } 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); @@ -2151,8 +2155,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.InitMessage(_("Done loading")); #ifdef ENABLE_WALLET - if (pwalletMain) - pwalletMain->postInitProcess(scheduler); + for (CWalletRef pwallet : vpwallets) { + pwallet->postInitProcess(scheduler); + } #endif return true; diff --git a/src/privatesend/privatesend-client.cpp b/src/privatesend/privatesend-client.cpp index 2e6a08ddc855..7cf50df5877a 100644 --- a/src/privatesend/privatesend-client.cpp +++ b/src/privatesend/privatesend-client.cpp @@ -268,13 +268,13 @@ void CPrivateSendClientSession::UnlockCoins() if (!privateSendClient.fEnablePrivateSend) return; while (true) { - TRY_LOCK(pwalletMain->cs_wallet, lockWallet); + TRY_LOCK(vpwallets[0]->cs_wallet, lockWallet); if (!lockWallet) { MilliSleep(50); continue; } for (const auto& outpoint : vecOutPointLocked) - pwalletMain->UnlockCoin(outpoint); + vpwallets[0]->UnlockCoin(outpoint); break; } @@ -616,7 +616,7 @@ bool CPrivateSendClientSession::SignFinalTransaction(const CTransaction& finalTr return false; } - const CKeyStore& keystore = *pwalletMain; + const CKeyStore& keystore = *vpwallets[0]; LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::SignFinalTransaction -- Signing my input %i\n", nMyInputIndex); if (!SignSignature(keystore, prevPubKey, finalMutableTransaction, nMyInputIndex, int(SIGHASH_ALL | SIGHASH_ANYONECANPAY))) { // changes scriptSig @@ -706,7 +706,7 @@ bool CPrivateSendClientManager::CheckAutomaticBackup() LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Automatic backups disabled, no mixing available.\n"); strAutoDenomResult = _("Automatic backups disabled") + ", " + _("no mixing available."); fPrivateSendRunning = false; // stop mixing - pwalletMain->nKeysLeftSinceAutoBackup = 0; // no backup, no "keys since last backup" + vpwallets[0]->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. @@ -724,24 +724,24 @@ bool CPrivateSendClientManager::CheckAutomaticBackup() return false; } - if (pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_STOP) { + if (vpwallets[0]->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_STOP) { // We should never get here via mixing itself but probably smth else is still actively using keypool - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Very low number of keys left: %d, no mixing available.\n", pwalletMain->nKeysLeftSinceAutoBackup); - strAutoDenomResult = strprintf(_("Very low number of keys left: %d") + ", " + _("no mixing available."), pwalletMain->nKeysLeftSinceAutoBackup); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Very low number of keys left: %d, no mixing available.\n", vpwallets[0]->nKeysLeftSinceAutoBackup); + strAutoDenomResult = strprintf(_("Very low number of keys left: %d") + ", " + _("no mixing available."), vpwallets[0]->nKeysLeftSinceAutoBackup); // It's getting really dangerous, stop mixing fPrivateSendRunning = false; return false; - } else if (pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { + } else if (vpwallets[0]->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", pwalletMain->nKeysLeftSinceAutoBackup); - strAutoDenomResult = strprintf(_("Very low number of keys left: %d"), pwalletMain->nKeysLeftSinceAutoBackup); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Very low number of keys left: %d\n", vpwallets[0]->nKeysLeftSinceAutoBackup); + strAutoDenomResult = strprintf(_("Very low number of keys left: %d"), vpwallets[0]->nKeysLeftSinceAutoBackup); if (fCreateAutoBackups) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Trying to create new backup.\n"); std::string warningString; std::string errorString; - if (!AutoBackupWallet(pwalletMain, "", warningString, errorString)) { + if (!AutoBackupWallet(vpwallets[0], "", warningString, errorString)) { if (!warningString.empty()) { // There were some issues saving backup but yet more or less safe to continue LogPrintf("CPrivateSendClientManager::CheckAutomaticBackup -- WARNING! Something went wrong on automatic backup: %s\n", warningString); @@ -759,7 +759,7 @@ bool CPrivateSendClientManager::CheckAutomaticBackup() } } - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Keys left since latest backup: %d\n", pwalletMain->nKeysLeftSinceAutoBackup); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientManager::CheckAutomaticBackup -- Keys left since latest backup: %d\n", vpwallets[0]->nKeysLeftSinceAutoBackup); return true; } @@ -782,9 +782,9 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool CAmount nBalanceNeedsAnonymized; { - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, vpwallets[0]->cs_wallet); - if (!fDryRun && pwalletMain->IsLocked(true)) { + if (!fDryRun && vpwallets[0]->IsLocked(true)) { strAutoDenomResult = _("Wallet is locked."); return false; } @@ -807,7 +807,7 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool } // check if there is anything left to do - CAmount nBalanceAnonymized = pwalletMain->GetAnonymizedBalance(); + CAmount nBalanceAnonymized = vpwallets[0]->GetAnonymizedBalance(); nBalanceNeedsAnonymized = privateSendClient.nPrivateSendAmount*COIN - nBalanceAnonymized; if (nBalanceNeedsAnonymized < 0) { @@ -819,13 +819,13 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool CAmount nValueMin = CPrivateSend::GetSmallestDenomination(); // if there are no confirmed DS collateral inputs yet - if (!pwalletMain->HasCollateralInputs()) { + if (!vpwallets[0]->HasCollateralInputs()) { // should have some additional amount for them nValueMin += CPrivateSend::GetMaxCollateralAmount(); } // including denoms but applying some restrictions - CAmount nBalanceAnonymizable = pwalletMain->GetAnonymizableBalance(); + CAmount nBalanceAnonymizable = vpwallets[0]->GetAnonymizableBalance(); // anonymizable balance is way too small if (nBalanceAnonymizable < nValueMin) { @@ -835,10 +835,10 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool } // excluding denoms - CAmount nBalanceAnonimizableNonDenom = pwalletMain->GetAnonymizableBalance(true); + CAmount nBalanceAnonimizableNonDenom = vpwallets[0]->GetAnonymizableBalance(true); // denoms - CAmount nBalanceDenominatedConf = pwalletMain->GetDenominatedBalance(); - CAmount nBalanceDenominatedUnconf = pwalletMain->GetDenominatedBalance(true); + CAmount nBalanceDenominatedConf = vpwallets[0]->GetDenominatedBalance(); + CAmount nBalanceDenominatedUnconf = vpwallets[0]->GetDenominatedBalance(true); CAmount nBalanceDenominated = nBalanceDenominatedConf + nBalanceDenominatedUnconf; CAmount nBalanceToDenominate = privateSendClient.nPrivateSendAmount * COIN - nBalanceDenominated; @@ -887,8 +887,8 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool } //check if we have the collateral sized inputs - if (!pwalletMain->HasCollateralInputs()) { - return !pwalletMain->HasCollateralInputs(false) && MakeCollateralAmounts(connman); + if (!vpwallets[0]->HasCollateralInputs()) { + return !vpwallets[0]->HasCollateralInputs(false) && MakeCollateralAmounts(connman); } if (nSessionID) { @@ -912,20 +912,20 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool //check our collateral and create new if needed std::string strReason; if (txMyCollateral == CMutableTransaction()) { - if (!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { + if (!vpwallets[0]->CreateCollateralTransaction(txMyCollateral, strReason)) { LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- create collateral error:%s\n", strReason); return false; } } else { if (!CPrivateSend::IsCollateralValid(txMyCollateral)) { LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- invalid collateral, recreating...\n"); - if (!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { + if (!vpwallets[0]->CreateCollateralTransaction(txMyCollateral, strReason)) { LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- create collateral error: %s\n", strReason); return false; } } } - } // LOCK2(cs_main, pwalletMain->cs_wallet); + } // LOCK2(cs_main, vpwallets[0]->cs_wallet); bool fUseQueue = GetRandInt(100) > 33; // don't use the queues all of the time for mixing unless we are a liquidity provider @@ -952,7 +952,7 @@ bool CPrivateSendClientManager::DoAutomaticDenominating(CConnman& connman, bool return false; } - if (!fDryRun && pwalletMain->IsLocked(true)) { + if (!fDryRun && vpwallets[0]->IsLocked(true)) { strAutoDenomResult = _("Wallet is locked."); return false; } @@ -1073,7 +1073,7 @@ bool CPrivateSendClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymize CAmount nMaxAmount = nBalanceNeedsAnonymized; // Try to match their denominations if possible, select exact number of denominations - if (!pwalletMain->SelectPSInOutPairsByDenominations(dsq.nDenom, nMinAmount, nMaxAmount, vecPSInOutPairsTmp)) { + if (!vpwallets[0]->SelectPSInOutPairsByDenominations(dsq.nDenom, nMinAmount, nMaxAmount, vecPSInOutPairsTmp)) { LogPrintf("CPrivateSendClientSession::JoinExistingQueue -- Couldn't match %d denominations %d (%s)\n", vecBits.front(), dsq.nDenom, CPrivateSend::GetDenominationsToString(dsq.nDenom)); continue; } @@ -1112,7 +1112,7 @@ bool CPrivateSendClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, C // ** find the coins we'll use std::vector vecTxIn; CAmount nValueInTmp = 0; - if (!pwalletMain->SelectPrivateCoins(CPrivateSend::GetSmallestDenomination(), nBalanceNeedsAnonymized, vecTxIn, nValueInTmp, 0, privateSendClient.nPrivateSendRounds - 1)) { + if (!vpwallets[0]->SelectPrivateCoins(CPrivateSend::GetSmallestDenomination(), nBalanceNeedsAnonymized, vecTxIn, nValueInTmp, 0, privateSendClient.nPrivateSendRounds - 1)) { // this should never happen LogPrintf("CPrivateSendClientSession::StartNewQueue -- Can't mix: no compatible inputs found!\n"); strAutoDenomResult = _("Can't mix: no compatible inputs found!"); @@ -1157,7 +1157,7 @@ bool CPrivateSendClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, C LogPrintf("CPrivateSendClientSession::StartNewQueue -- attempt %d connection to Masternode %s\n", nTries, dmn->pdmnState->addr.ToString()); std::vector vecAmounts; - pwalletMain->ConvertList(vecTxIn, vecAmounts); + vpwallets[0]->ConvertList(vecTxIn, vecAmounts); // try to get a single random denom out of vecAmounts while (nSessionDenom == 0) { nSessionDenom = CPrivateSend::GetDenominationsByAmounts(vecAmounts); @@ -1214,7 +1214,7 @@ void CPrivateSendClientManager::ProcessPendingDsaRequest(CConnman& connman) bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) { - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, vpwallets[0]->cs_wallet); std::string strError; std::vector > vecPSInOutPairs, vecPSInOutPairsTmp; @@ -1272,7 +1272,7 @@ bool CPrivateSendClientSession::SelectDenominate(std::string& strErrorRet, std:: { if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; - if (pwalletMain->IsLocked(true)) { + if (vpwallets[0]->IsLocked(true)) { strErrorRet = "Wallet locked, unable to create transaction!"; return false; } @@ -1291,7 +1291,7 @@ bool CPrivateSendClientSession::SelectDenominate(std::string& strErrorRet, std:: } std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); - bool fSelected = pwalletMain->SelectPSInOutPairsByDenominations(nSessionDenom, vecStandardDenoms[vecBits.front()], CPrivateSend::GetMaxPoolAmount(), vecPSInOutPairsRet); + bool fSelected = vpwallets[0]->SelectPSInOutPairsByDenominations(nSessionDenom, vecStandardDenoms[vecBits.front()], CPrivateSend::GetMaxPoolAmount(), vecPSInOutPairsRet); if (!fSelected) { strErrorRet = "Can't select current denominated inputs"; return false; @@ -1309,7 +1309,7 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds } for (const auto& pair : vecPSInOutPairsIn) { - pwalletMain->LockCoin(pair.first.prevout); + vpwallets[0]->LockCoin(pair.first.prevout); } // NOTE: No need to randomize order of inputs because they were @@ -1324,7 +1324,7 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds for (const auto& pair : vecPSInOutPairsIn) { if (pair.second.nRounds < nMinRounds || pair.second.nRounds > nMaxRounds) { // unlock unused coins - pwalletMain->UnlockCoin(pair.first.prevout); + vpwallets[0]->UnlockCoin(pair.first.prevout); continue; } bool fFound = false; @@ -1344,7 +1344,7 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds ++vecSteps[nBit]; break; } - scriptDenom = keyHolderStorage.AddKey(pwalletMain); + scriptDenom = keyHolderStorage.AddKey(vpwallets[0]); } vecPSInOutPairsRet.emplace_back(pair.first, CTxOut(nValueDenom, scriptDenom)); fFound = true; @@ -1356,14 +1356,14 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds } if (!fFound || fDryRun) { // unlock unused coins and if we are not going to mix right away - pwalletMain->UnlockCoin(pair.first.prevout); + vpwallets[0]->UnlockCoin(pair.first.prevout); } } if (nDenomResult != nSessionDenom) { // unlock used coins on failure for (const auto& pair : vecPSInOutPairsRet) { - pwalletMain->UnlockCoin(pair.first.prevout); + vpwallets[0]->UnlockCoin(pair.first.prevout); } keyHolderStorage.ReturnAll(); strErrorRet = "Can't prepare current denominated outputs"; @@ -1379,7 +1379,7 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(CConnman& connman) if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; std::vector vecTally; - if (!pwalletMain->SelectCoinsGroupedByAddresses(vecTally, false, false)) { + if (!vpwallets[0]->SelectCoinsGroupedByAddresses(vecTally, false, false)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::MakeCollateralAmounts -- SelectCoinsGroupedByAddresses can't find any inputs!\n"); return false; } @@ -1411,7 +1411,7 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta { if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, vpwallets[0]->cs_wallet); // denominated input is always a single one, so we can check its amount directly and return early if (!fTryDenominated && tallyItem.vecOutPoints.size() == 1 && CPrivateSend::IsDenominatedAmount(tallyItem.nAmount)) { @@ -1425,9 +1425,9 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta std::vector vecSend; // make our collateral address - CReserveKey reservekeyCollateral(pwalletMain); + CReserveKey reservekeyCollateral(vpwallets[0]); // make our change address - CReserveKey reservekeyChange(pwalletMain); + CReserveKey reservekeyChange(vpwallets[0]); CScript scriptCollateral; CPubKey vchPubKey; @@ -1446,14 +1446,14 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta coinControl.Select(outpoint); } - bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, + bool fSuccess = vpwallets[0]->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED); if (!fSuccess) { LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- ONLY_NONDENOMINATED: %s\n", strFail); // If we failed then most likely there are not enough funds on this address. if (fTryDenominated) { // Try to also use denominated coins (we can't mix denominated without collaterals anyway). - if (!pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, + if (!vpwallets[0]->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ALL_COINS)) { LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- ALL_COINS Error: %s\n", strFail); reservekeyCollateral.ReturnKey(); @@ -1472,7 +1472,7 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta // use the same nCachedLastSuccessBlock as for DS mixing to prevent race CValidationState state; - if (!pwalletMain->CommitTransaction(wtx, reservekeyChange, &connman, state)) { + if (!vpwallets[0]->CommitTransaction(wtx, reservekeyChange, &connman, state)) { LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); return false; } @@ -1487,14 +1487,14 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, { if (!privateSendClient.fEnablePrivateSend || !privateSendClient.fPrivateSendRunning) return false; - LOCK2(cs_main, pwalletMain->cs_wallet); + LOCK2(cs_main, vpwallets[0]->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 (!pwalletMain->SelectCoinsGroupedByAddresses(vecTally, true, true, true, 400)) { + if (!vpwallets[0]->SelectCoinsGroupedByAddresses(vecTally, true, true, true, 400)) { LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- SelectCoinsGroupedByAddresses can't find any inputs!\n"); return false; } @@ -1504,7 +1504,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, return a.nAmount > b.nAmount; }); - bool fCreateMixingCollaterals = !pwalletMain->HasCollateralInputs(); + bool fCreateMixingCollaterals = !vpwallets[0]->HasCollateralInputs(); for (const auto& item : vecTally) { if (!CreateDenominated(nBalanceToDenominate, item, fCreateMixingCollaterals, connman)) continue; @@ -1531,7 +1531,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, // ****** Add an output for mixing collaterals ************ / if (fCreateMixingCollaterals) { - CScript scriptCollateral = keyHolderStorageDenom.AddKey(pwalletMain); + CScript scriptCollateral = keyHolderStorageDenom.AddKey(vpwallets[0]); vecSend.push_back((CRecipient){scriptCollateral, CPrivateSend::GetMaxCollateralAmount(), false}); nValueLeft -= CPrivateSend::GetMaxCollateralAmount(); } @@ -1556,7 +1556,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, } // find new denoms to skip if any (ignore the largest one) - if (nDenomValue != vecStandardDenoms.front() && pwalletMain->CountInputsWithAmount(nDenomValue) > privateSendClient.nPrivateSendDenoms) { + if (nDenomValue != vecStandardDenoms.front() && vpwallets[0]->CountInputsWithAmount(nDenomValue) > privateSendClient.nPrivateSendDenoms) { strAutoDenomResult = strprintf(_("Too many %f denominations, removing."), (float)nDenomValue / COIN); LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- %s\n", strAutoDenomResult); privateSendClient.AddSkippedDenom(nDenomValue); @@ -1577,7 +1577,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, // add each output up to 11 times until it can't be added again while (needMoreOutputs() && nOutputs <= 10) { - CScript scriptDenom = keyHolderStorageDenom.AddKey(pwalletMain); + CScript scriptDenom = keyHolderStorageDenom.AddKey(vpwallets[0]); vecSend.push_back((CRecipient){scriptDenom, nDenomValue, false}); @@ -1612,9 +1612,9 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, int nChangePosRet = -1; std::string strFail = ""; // make our change address - CReserveKey reservekeyChange(pwalletMain); + CReserveKey reservekeyChange(vpwallets[0]); - bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, + bool fSuccess = vpwallets[0]->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED); if (!fSuccess) { LogPrintf("CPrivateSendClientSession::CreateDenominated -- Error: %s\n", strFail); @@ -1625,7 +1625,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, keyHolderStorageDenom.KeepAll(); CValidationState state; - if (!pwalletMain->CommitTransaction(wtx, reservekeyChange, &connman, state)) { + if (!vpwallets[0]->CommitTransaction(wtx, reservekeyChange, &connman, state)) { LogPrintf("CPrivateSendClientSession::CreateDenominated -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); return false; } diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index f6c47e025fdc..8c7f9430199f 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -431,7 +431,7 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); else { coinControl->Select(outpt); - int nRounds = pwalletMain->GetRealOutpointPrivateSendRounds(outpt); + int nRounds = vpwallets[0]->GetRealOutpointPrivateSendRounds(outpt); if (coinControl->fUsePrivateSend && nRounds < privateSendClient.nPrivateSendRounds) { QMessageBox::warning(this, windowTitle(), tr("Non-anonymized input selected. PrivateSend will be disabled.

If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again."), @@ -751,7 +751,7 @@ void CoinControlDialog::updateView() // PrivateSend rounds COutPoint outpoint = COutPoint(out.tx->tx->GetHash(), out.i); - int nRounds = pwalletMain->GetRealOutpointPrivateSendRounds(outpoint); + int nRounds = vpwallets[0]->GetRealOutpointPrivateSendRounds(outpoint); if (nRounds >= 0 || LogAcceptCategory(BCLog::PRIVATESEND)) itemOutput->setText(COLUMN_PRIVATESEND_ROUNDS, QString::number(nRounds)); else itemOutput->setText(COLUMN_PRIVATESEND_ROUNDS, tr("n/a")); diff --git a/src/qt/dash.cpp b/src/qt/dash.cpp index c4353b301b5c..b538367fb7c3 100644 --- a/src/qt/dash.cpp +++ b/src/qt/dash.cpp @@ -507,9 +507,10 @@ void BitcoinApplication::initializeResult(bool success) window->setClientModel(clientModel); #ifdef ENABLE_WALLET - if(pwalletMain) + // TODO: Expose secondary wallets + if (!vpwallets.empty()) { - walletModel = new WalletModel(platformStyle, pwalletMain, optionsModel); + walletModel = new WalletModel(platformStyle, vpwallets[0], optionsModel); window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel); window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET); diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index da3789742064..cfb70c41b2df 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -31,8 +31,9 @@ #include #ifdef ENABLE_WALLET -extern CWallet* pwalletMain; -#endif // ENABLE_WALLET +typedef CWallet* CWalletRef; +extern std::vector vpwallets; +#endif //ENABLE_WALLET OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : QDialog(parent), @@ -272,8 +273,8 @@ void OptionsDialog::on_okButton_clicked() mapper->submit(); #ifdef ENABLE_WALLET privateSendClient.nCachedNumBlocks = std::numeric_limits::max(); - if(pwalletMain) - pwalletMain->MarkDirty(); + if(!vpwallets.empty()) + vpwallets[0]->MarkDirty(); #endif // ENABLE_WALLET accept(); updateDefaultProxyNets(); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 95c70218fb3e..cb79034e7dc9 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -341,7 +341,7 @@ void OverviewPage::updatePrivateSendProgress() { if(!masternodeSync.IsBlockchainSynced() || ShutdownRequested()) return; - if(!pwalletMain) return; + if(vpwallets.empty()) return; QString strAmountAndRounds; QString strPrivateSendAmount = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, privateSendClient.nPrivateSendAmount * COIN, false, BitcoinUnits::separatorAlways); @@ -360,7 +360,7 @@ void OverviewPage::updatePrivateSendProgress() return; } - CAmount nAnonymizableBalance = pwalletMain->GetAnonymizableBalance(false, false); + CAmount nAnonymizableBalance = vpwallets[0]->GetAnonymizableBalance(false, false); CAmount nMaxToAnonymize = nAnonymizableBalance + currentAnonymizedBalance; @@ -394,10 +394,10 @@ void OverviewPage::updatePrivateSendProgress() CAmount nNormalizedAnonymizedBalance; float nAverageAnonymizedRounds; - nDenominatedConfirmedBalance = pwalletMain->GetDenominatedBalance(); - nDenominatedUnconfirmedBalance = pwalletMain->GetDenominatedBalance(true); - nNormalizedAnonymizedBalance = pwalletMain->GetNormalizedAnonymizedBalance(); - nAverageAnonymizedRounds = pwalletMain->GetAverageAnonymizedRounds(); + nDenominatedConfirmedBalance = vpwallets[0]->GetDenominatedBalance(); + nDenominatedUnconfirmedBalance = vpwallets[0]->GetDenominatedBalance(true); + nNormalizedAnonymizedBalance = vpwallets[0]->GetNormalizedAnonymizedBalance(); + nAverageAnonymizedRounds = vpwallets[0]->GetAverageAnonymizedRounds(); // calculate parts of the progress, each of them shouldn't be higher than 1 // progress of denominating @@ -473,8 +473,8 @@ void OverviewPage::privateSendStatus() if(((nBestHeight - privateSendClient.nCachedNumBlocks) / (GetTimeMillis() - nLastDSProgressBlockTime + 1) > 1)) return; nLastDSProgressBlockTime = GetTimeMillis(); - QString strKeysLeftText(tr("keys left: %1").arg(pwalletMain->nKeysLeftSinceAutoBackup)); - if(pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { + QString strKeysLeftText(tr("keys left: %1").arg(vpwallets[0]->nKeysLeftSinceAutoBackup)); + if(vpwallets[0]->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { strKeysLeftText = "" + strKeysLeftText + ""; } ui->labelPrivateSendEnabled->setToolTip(strKeysLeftText); @@ -498,7 +498,7 @@ void OverviewPage::privateSendStatus() // Warn user that wallet is running out of keys // NOTE: we do NOT warn user and do NOT create autobackups if mixing is not running - if (nWalletBackups > 0 && pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { + if (nWalletBackups > 0 && vpwallets[0]->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { QSettings settings; if(settings.value("fLowKeysWarning").toBool()) { QString strWarn = tr("Very low number of keys left since last automatic backup!") + "

" + @@ -515,7 +515,7 @@ void OverviewPage::privateSendStatus() std::string strBackupWarning; std::string strBackupError; - if(!AutoBackupWallet(pwalletMain, "", strBackupWarning, strBackupError)) { + if(!AutoBackupWallet(vpwallets[0], "", strBackupWarning, strBackupError)) { if (!strBackupWarning.empty()) { // It's still more or less safe to continue but warn user anyway LogPrintf("OverviewPage::privateSendStatus -- WARNING! Something went wrong on automatic backup: %s\n", strBackupWarning); diff --git a/src/util.cpp b/src/util.cpp index 581b1faf48d2..7531b6799e71 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -568,6 +568,7 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV { LOCK(cs_args); mapArgs[strArg] = strValue; + mapMultiArgs[strArg].clear(); mapMultiArgs[strArg].push_back(strValue); } diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 67f9f223f01d..3f96eee572b4 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -142,7 +142,7 @@ void CDBEnv::MakeMock() fMockDb = true; } -CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile)) +CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename) { LOCK(cs_db); assert(mapFileUseCount.count(strFile) == 0); @@ -155,21 +155,21 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFu return RECOVER_FAIL; // Try to recover: - bool fRecovered = (*recoverFunc)(strFile); + bool fRecovered = (*recoverFunc)(strFile, out_backup_filename); return (fRecovered ? RECOVER_OK : RECOVER_FAIL); } -bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue)) +bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename) { // Recovery procedure: - // move wallet file to wallet.timestamp.bak + // move wallet file to walletfilename.timestamp.bak // Call Salvage with fAggressive=true to // get as much data as possible. // Rewrite salvaged data to fresh wallet file // Set -rescan so any missing transactions will be // found. int64_t now = GetTime(); - std::string newFilename = strprintf("wallet.%d.bak", now); + newFilename = strprintf("%s.%d.bak", filename, now); int result = bitdb.dbenv->dbrename(NULL, filename.c_str(), NULL, newFilename.c_str(), DB_AUTO_COMMIT); @@ -259,18 +259,19 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataD return true; } -bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile)) +bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc) { if (fs::exists(dataDir / walletFile)) { - CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc); + std::string backup_filename; + CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc, backup_filename); if (r == CDBEnv::RECOVER_OK) { warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!" " Original %s saved as %s in %s; if" " your balance or transactions are incorrect you should" " restore from a backup."), - walletFile, "wallet.{timestamp}.bak", dataDir); + walletFile, backup_filename, dataDir); } if (r == CDBEnv::RECOVER_FAIL) { @@ -432,6 +433,11 @@ void CDB::Flush() env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0); } +void CWalletDBWrapper::IncrementUpdateCounter() +{ + ++nUpdateCounter; +} + void CDB::Close() { if (!pdb) diff --git a/src/wallet/db.h b/src/wallet/db.h index e4d8a5154d13..b220b0667698 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -55,7 +55,8 @@ class CDBEnv enum VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL }; - VerifyResult Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile)); + typedef bool (*recoverFunc_type)(const std::string& strFile, std::string& out_backup_filename); + VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename); /** * Salvage data from a file that Verify says is bad. * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation). @@ -93,13 +94,13 @@ class CWalletDBWrapper friend class CDB; public: /** Create dummy DB handle */ - CWalletDBWrapper(): env(nullptr) + CWalletDBWrapper() : nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr) { } /** Create DB handle to real database */ - CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in): - env(env_in), strFile(strFile_in) + CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in) : + nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in) { } @@ -119,6 +120,13 @@ class CWalletDBWrapper */ void Flush(bool shutdown); + void IncrementUpdateCounter(); + + std::atomic nUpdateCounter; + unsigned int nLastSeen; + unsigned int nLastFlushed; + int64_t nLastWalletUpdate; + private: /** BerkeleyDB specific */ CDBEnv *env; @@ -149,7 +157,7 @@ class CDB void Flush(); void Close(); - static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue)); + static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); /* flush the wallet passively (TRY_LOCK) ideal to be called periodically */ @@ -157,7 +165,7 @@ class CDB /* verifies the database environment */ static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr); /* verifies the database file */ - static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile)); + static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc); private: CDB(const CDB&); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 39a8267c81e0..d0ad5351fe8c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -32,7 +32,8 @@ CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request) { - return pwalletMain; + // TODO: Some way to access secondary wallets + return vpwallets.empty() ? nullptr : vpwallets[0]; } std::string HelpRequiringPassphrase(CWallet * const pwallet) diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 1989bf8d9b96..922fcc8e898d 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -8,6 +8,8 @@ #include "wallet/db.h" #include "wallet/wallet.h" +CWallet *pwalletMain; + WalletTestingSetup::WalletTestingSetup(const std::string& chainName): TestingSetup(chainName) { diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 76e322c9cf46..2181a95f373e 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -17,6 +17,8 @@ #include #include +extern CWallet* pwalletMain; + extern UniValue importmulti(const JSONRPCRequest& request); extern UniValue dumpwallet(const JSONRPCRequest& request); extern UniValue importwallet(const JSONRPCRequest& request); @@ -400,8 +402,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // after. { CWallet wallet; - CWallet *backup = ::pwalletMain; - ::pwalletMain = &wallet; + vpwallets.insert(vpwallets.begin(), &wallet); UniValue keys; keys.setArray(); UniValue key; @@ -432,7 +433,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) "downloading and rescanning the relevant blocks (see -reindex and -rescan " "options).\"}},{\"success\":true}]", 0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW)); - ::pwalletMain = backup; + vpwallets.erase(vpwallets.begin()); } } @@ -442,7 +443,6 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // than or equal to key birthday. BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) { - CWallet *pwalletMainBackup = ::pwalletMain; // Create two blocks with same timestamp to verify that importwallet rescan // will pick up both blocks, not just the first. const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5; @@ -468,7 +468,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) JSONRPCRequest request; request.params.setArray(); request.params.push_back("wallet.backup"); - ::pwalletMain = &wallet; + vpwallets.insert(vpwallets.begin(), &wallet); ::dumpwallet(request); } @@ -480,7 +480,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) JSONRPCRequest request; request.params.setArray(); request.params.push_back("wallet.backup"); - ::pwalletMain = &wallet; + vpwallets[0] = &wallet; ::importwallet(request); BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3); @@ -493,7 +493,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) } SetMockTime(0); - ::pwalletMain = pwalletMainBackup; + vpwallets.erase(vpwallets.begin()); } // Check that GetImmatureCredit() returns a newly calculated value instead of diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 976d55f7b5c0..1089d4557dbf 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -45,7 +45,7 @@ #include #include -CWallet* pwalletMain = NULL; +std::vector vpwallets; /** Transaction fee set by the user */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; @@ -606,30 +606,40 @@ bool CWallet::Verify() if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return true; - uiInterface.InitMessage(_("Verifying wallet...")); - std::string walletFile = gArgs.GetArg("-wallet", DEFAULT_WALLET_DAT); + uiInterface.InitMessage(_("Verifying wallet(s)...")); - std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) - return InitError(strError); + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + if (boost::filesystem::path(walletFile).filename() != walletFile) { + return InitError(_("-wallet parameter must only specify a filename (not a path)")); + } else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { + return InitError(_("Invalid characters in -wallet filename")); + } - if (gArgs.GetBoolArg("-salvagewallet", false)) - { - // Recover readable keypairs: - CWallet dummyWallet; - if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter)) + std::string strError; + if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { + return InitError(strError); + } + + if (gArgs.GetBoolArg("-salvagewallet", false)) { + // Recover readable keypairs: + CWallet dummyWallet; + std::string backup_filename; + if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { + return false; + } + } + + std::string strWarning; + bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); + if (!strWarning.empty()) { + InitWarning(strWarning); + } + if (!dbV) { + InitError(strError); return false; + } } - std::string strWarning; - bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); - if (!strWarning.empty()) - InitWarning(strWarning); - if (!dbV) - { - InitError(strError); - return false; - } return true; } @@ -3817,8 +3827,9 @@ bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry) bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwalletdb) { - if (!pwalletdb->WriteAccountingEntry_Backend(acentry)) + if (!pwalletdb->WriteAccountingEntry(++nAccountingEntryNumber, acentry)) { return false; + } laccentries.push_back(acentry); CAccountingEntry & entry = laccentries.back(); @@ -4927,7 +4938,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) walletInstance->ScanForWalletTransactions(pindexRescan, true); LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); walletInstance->SetBestChain(chainActive.GetLocator()); - CWalletDB::IncrementUpdateCounter(); + walletInstance->dbw->IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2") @@ -4970,24 +4981,17 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) bool CWallet::InitLoadWallet() { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - pwalletMain = NULL; LogPrintf("Wallet disabled!\n"); return true; } - std::string walletFile = gArgs.GetArg("-wallet", DEFAULT_WALLET_DAT); - - if (boost::filesystem::path(walletFile).filename() != walletFile) { - return InitError(_("-wallet parameter must only specify a filename (not a path)")); - } else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { - return InitError(_("Invalid characters in -wallet filename")); - } - - CWallet * const pwallet = CreateWalletFromFile(walletFile); - if (!pwallet) { - return false; + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + CWallet * const pwallet = CreateWalletFromFile(walletFile); + if (!pwallet) { + return false; + } + vpwallets.push_back(pwallet); } - pwalletMain = pwallet; return true; } @@ -5008,6 +5012,9 @@ void CWallet::postInitProcess(CScheduler& scheduler) bool CWallet::ParameterInteraction() { + gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return true; @@ -5016,15 +5023,27 @@ bool CWallet::ParameterInteraction() } if (gArgs.GetBoolArg("-salvagewallet", false) && gArgs.SoftSetBoolArg("-rescan", true)) { + if (is_multiwallet) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); + } // Rewrite just private keys: rescan to find transactions LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); } // -zapwallettx implies a rescan if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.SoftSetBoolArg("-rescan", true)) { + if (is_multiwallet) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); + } LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting -rescan=1\n", __func__); } + if (is_multiwallet) { + if (gArgs.GetBoolArg("-upgradewallet", false)) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); + } + } + if (gArgs.GetBoolArg("-sysperms", false)) return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false)) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 283ead09236d..a8fbaf723013 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -34,7 +34,8 @@ #include #include -extern CWallet* pwalletMain; +typedef CWallet* CWalletRef; +extern std::vector vpwallets; /** * Settings @@ -841,6 +842,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface nMasterKeyMaxID = 0; pwalletdbEncryption = NULL; nOrderPosNext = 0; + nAccountingEntryNumber = 0; nNextResend = 0; nLastResend = 0; nTimeFirstKey = 0; @@ -862,6 +864,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface TxItems wtxOrdered; int64_t nOrderPosNext; + uint64_t nAccountingEntryNumber; std::map mapRequestCount; std::map mapAddressBook; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index ec6871036470..627089e737d5 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -22,59 +22,47 @@ #include #include -static uint64_t nAccountingEntryNumber = 0; - -static std::atomic nWalletDBUpdateCounter; - // // CWalletDB // bool CWalletDB::WriteName(const std::string& strAddress, const std::string& strName) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string("name"), strAddress), strName); + return WriteIC(std::make_pair(std::string("name"), strAddress), strName); } bool CWalletDB::EraseName(const std::string& strAddress) { // This should only be used for sending addresses, never for receiving addresses, // receiving addresses must always have an address book entry if they're not change return. - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string("name"), strAddress)); + return EraseIC(std::make_pair(std::string("name"), strAddress)); } bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& strPurpose) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string("purpose"), strAddress), strPurpose); + return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose); } bool CWalletDB::ErasePurpose(const std::string& strPurpose) { - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string("purpose"), strPurpose)); + return EraseIC(std::make_pair(std::string("purpose"), strPurpose)); } bool CWalletDB::WriteTx(const CWalletTx& wtx) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx); + return WriteIC(std::make_pair(std::string("tx"), wtx.GetHash()), wtx); } bool CWalletDB::EraseTx(uint256 hash) { - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string("tx"), hash)); + return EraseIC(std::make_pair(std::string("tx"), hash)); } bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) { - nWalletDBUpdateCounter++; - - if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey), - keyMeta, false)) + if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) { return false; + } // hash pubkey/privkey to accelerate wallet load std::vector vchKey; @@ -82,7 +70,7 @@ bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, c vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); - return batch.Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); + return WriteIC(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); } bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, @@ -90,55 +78,53 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, const CKeyMetadata &keyMeta) { const bool fEraseUnencryptedKey = true; - nWalletDBUpdateCounter++; - if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey), - keyMeta)) + if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) { return false; + } - if (!batch.Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) + if (!WriteIC(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) { return false; + } if (fEraseUnencryptedKey) { - batch.Erase(std::make_pair(std::string("key"), vchPubKey)); - batch.Erase(std::make_pair(std::string("wkey"), vchPubKey)); + EraseIC(std::make_pair(std::string("key"), vchPubKey)); + EraseIC(std::make_pair(std::string("wkey"), vchPubKey)); } + return true; } bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); + return WriteIC(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false); + return WriteIC(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false); } bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta) { - nWalletDBUpdateCounter++; - if (!batch.Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta)) + if (!WriteIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta)) { return false; - return batch.Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); + } + return WriteIC(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); } bool CWalletDB::EraseWatchOnly(const CScript &dest) { - nWalletDBUpdateCounter++; - if (!batch.Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)))) + if (!EraseIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)))) { return false; - return batch.Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest))); + } + return EraseIC(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest))); } bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { - nWalletDBUpdateCounter++; - batch.Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan - return batch.Write(std::string("bestblock_nomerkle"), locator); + WriteIC(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan + return WriteIC(std::string("bestblock_nomerkle"), locator); } bool CWalletDB::ReadBestBlock(CBlockLocator& locator) @@ -149,14 +135,12 @@ bool CWalletDB::ReadBestBlock(CBlockLocator& locator) bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) { - nWalletDBUpdateCounter++; - return batch.Write(std::string("orderposnext"), nOrderPosNext); + return WriteIC(std::string("orderposnext"), nOrderPosNext); } bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) { - nWalletDBUpdateCounter++; - return batch.Write(std::string("defaultkey"), vchPubKey); + return WriteIC(std::string("defaultkey"), vchPubKey); } bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) @@ -166,19 +150,17 @@ bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string("pool"), nPool), keypool); + return WriteIC(std::make_pair(std::string("pool"), nPool), keypool); } bool CWalletDB::ErasePool(int64_t nPool) { - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string("pool"), nPool)); + return EraseIC(std::make_pair(std::string("pool"), nPool)); } bool CWalletDB::WriteMinVersion(int nVersion) { - return batch.Write(std::string("minversion"), nVersion); + return WriteIC(std::string("minversion"), nVersion); } bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account) @@ -189,17 +171,12 @@ bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account) bool CWalletDB::WriteAccount(const std::string& strAccount, const CAccount& account) { - return batch.Write(std::make_pair(std::string("acc"), strAccount), account); + return WriteIC(std::make_pair(std::string("acc"), strAccount), account); } bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) { - return batch.Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); -} - -bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry) -{ - return WriteAccountingEntry(++nAccountingEntryNumber, acentry); + return WriteIC(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); } CAmount CWalletDB::GetAccountCreditDebit(const std::string& strAccount) @@ -338,8 +315,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssKey >> strAccount; uint64_t nNumber; ssKey >> nNumber; - if (nNumber > nAccountingEntryNumber) - nAccountingEntryNumber = nNumber; + if (nNumber > pwallet->nAccountingEntryNumber) { + pwallet->nAccountingEntryNumber = nNumber; + } if (!wss.fAnyUnordered) { @@ -818,38 +796,39 @@ void MaybeCompactWalletDB() return; } - static unsigned int nLastSeen = CWalletDB::GetUpdateCounter(); - static unsigned int nLastFlushed = CWalletDB::GetUpdateCounter(); - static int64_t nLastWalletUpdate = GetTime(); + for (CWalletRef pwallet : vpwallets) { + CWalletDBWrapper& dbh = pwallet->GetDBHandle(); - if (nLastSeen != CWalletDB::GetUpdateCounter()) - { - nLastSeen = CWalletDB::GetUpdateCounter(); - nLastWalletUpdate = GetTime(); - } + unsigned int nUpdateCounter = dbh.nUpdateCounter; - if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2) - { - if (CDB::PeriodicFlush(pwalletMain->GetDBHandle())) { - nLastFlushed = CWalletDB::GetUpdateCounter(); + if (dbh.nLastSeen != nUpdateCounter) { + dbh.nLastSeen = nUpdateCounter; + dbh.nLastWalletUpdate = GetTime(); + } + + if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) { + if (CDB::PeriodicFlush(dbh)) { + dbh.nLastFlushed = nUpdateCounter; + } } } + fOneThread = false; } // // Try to (very carefully!) recover wallet file if there is a problem. // -bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue)) +bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename) { - return CDB::Recover(filename, callbackDataIn, recoverKVcallback); + return CDB::Recover(filename, callbackDataIn, recoverKVcallback, out_backup_filename); } -bool CWalletDB::Recover(const std::string& filename) +bool CWalletDB::Recover(const std::string& filename, std::string& out_backup_filename) { // recover without a key filter callback // results in recovering all record types - return CWalletDB::Recover(filename, NULL, NULL); + return CWalletDB::Recover(filename, NULL, NULL, out_backup_filename); } bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue) @@ -882,57 +861,40 @@ bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr) { - return CDB::VerifyDatabaseFile(walletFile, dataDir, errorStr, warningStr, CWalletDB::Recover); + return CDB::VerifyDatabaseFile(walletFile, dataDir, warningStr, errorStr, CWalletDB::Recover); } bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) { - nWalletDBUpdateCounter++; - return batch.Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); + return WriteIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); } bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) { - nWalletDBUpdateCounter++; - return batch.Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); + return EraseIC(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } bool CWalletDB::WriteHDChain(const CHDChain& chain) { - nWalletDBUpdateCounter++; - return batch.Write(std::string("hdchain"), chain); + return WriteIC(std::string("hdchain"), chain); } bool CWalletDB::WriteCryptedHDChain(const CHDChain& chain) { - nWalletDBUpdateCounter++; - - if (!batch.Write(std::string("chdchain"), chain)) + if (!WriteIC(std::string("chdchain"), chain)) return false; - batch.Erase(std::string("hdchain")); + EraseIC(std::string("hdchain")); return true; } bool CWalletDB::WriteHDPubKey(const CHDPubKey& hdPubKey, const CKeyMetadata& keyMeta) { - nWalletDBUpdateCounter++; - - if (!batch.Write(std::make_pair(std::string("keymeta"), hdPubKey.extPubKey.pubkey), keyMeta, false)) + if (!WriteIC(std::make_pair(std::string("keymeta"), hdPubKey.extPubKey.pubkey), keyMeta, false)) return false; - return batch.Write(std::make_pair(std::string("hdpubkey"), hdPubKey.extPubKey.pubkey), hdPubKey, false); -} - -void CWalletDB::IncrementUpdateCounter() -{ - nWalletDBUpdateCounter++; -} - -unsigned int CWalletDB::GetUpdateCounter() -{ - return nWalletDBUpdateCounter; + return WriteIC(std::make_pair(std::string("hdpubkey"), hdPubKey.extPubKey.pubkey), hdPubKey, false); } bool CWalletDB::TxnBegin() diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index fd9da46be559..520ee56e6fac 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -95,9 +95,31 @@ class CKeyMetadata */ class CWalletDB { +private: + template + bool WriteIC(const K& key, const T& value, bool fOverwrite = true) + { + if (!batch.Write(key, value, fOverwrite)) { + return false; + } + m_dbw.IncrementUpdateCounter(); + return true; + } + + template + bool EraseIC(const K& key) + { + if (!batch.Erase(key)) { + return false; + } + m_dbw.IncrementUpdateCounter(); + return true; + } + public: CWalletDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool _fFlushOnClose = true) : - batch(dbw, pszMode, _fFlushOnClose) + batch(dbw, pszMode, _fFlushOnClose), + m_dbw(dbw) { } @@ -135,7 +157,6 @@ class CWalletDB /// This writes directly to the database, and will not update the CWallet's cached accounting entries! /// Use wallet.AddAccountingEntry instead, to write *and* update its caches. bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry); - bool WriteAccountingEntry_Backend(const CAccountingEntry& acentry); bool ReadAccount(const std::string& strAccount, CAccount& account); bool WriteAccount(const std::string& strAccount, const CAccount& account); @@ -152,9 +173,9 @@ class CWalletDB DBErrors ZapWalletTx(std::vector& vWtx); DBErrors ZapSelectTx(std::vector& vHashIn, std::vector& vHashOut); /* Try to (very carefully!) recover wallet database (with a possible key type filter) */ - static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue)); + static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); /* Recover convenience-function to bypass the key filter callback, called when verify fails, recovers everything */ - static bool Recover(const std::string& filename); + static bool Recover(const std::string& filename, std::string& out_backup_filename); /* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */ static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue); /* Function to determine if a certain KV/key-type is a key (cryptographical key) type */ @@ -169,9 +190,6 @@ class CWalletDB bool WriteCryptedHDChain(const CHDChain& chain); bool WriteHDPubKey(const CHDPubKey& hdPubKey, const CKeyMetadata& keyMeta); - static void IncrementUpdateCounter(); - static unsigned int GetUpdateCounter(); - //! Begin a new transaction bool TxnBegin(); //! Commit current transaction @@ -184,6 +202,7 @@ class CWalletDB bool WriteVersion(int nVersion); private: CDB batch; + CWalletDBWrapper& m_dbw; CWalletDB(const CWalletDB&); void operator=(const CWalletDB&);