Skip to content

Commit

Permalink
merge #882: Autobackup enhancements + support autobackup in PS
Browse files Browse the repository at this point in the history
1c860ce Autobackup refactoring and improvements:
- make nWalletBackups globally accessable
- move autobackup code from init.cpp to walletdb.cpp, see AutoBackupWallet function
- refactor autobackup code to warn user if autobackup failed instead of silently ignoring this fact
- refactor autobackup code to be able to backup fresh new wallet right after it was created, add this functionality to init sequence
- add new cmd-line option "-walletbackupsdir" to specify full path to directory for automatic wallet backups, see GetBackupsDir function

0ba1548 autobackup in PS:
- add nKeysLeftSinceAutoBackup to have some idea how many keys in keypool are more or less safe, show it in advanced PS UI mode and in rpc output for privatesend and getwalletinfo commands
- add autobackups support in PrivateSend mixing both in daemon and QT mode, warn user if number of keys left since last autobackup is very low or even stop mixing completely if it's too low

f3a2494 Warn about a special case - less than 60 seconds between restarts i.e. backup file name is the same as previos one. Continue and do not disable automatic backups in this case
.

e7b56bd Refactor to address locked wallets issue, replenish keypool and re-initialize autobackup on unlock (only if was disabled due to keypool issue)
Adjust few message strings.
  • Loading branch information
UdjinM6 authored and schinzelh committed Jun 15, 2016
1 parent 0ee1bb5 commit 496a0c3
Show file tree
Hide file tree
Showing 14 changed files with 368 additions and 97 deletions.
63 changes: 61 additions & 2 deletions src/darksend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1351,16 +1351,75 @@ void CDarksendPool::ClearLastMessage()
//
// Passively run Darksend in the background to anonymize funds based on the given configuration.
//
// This does NOT run by default for daemons, only for QT.
//
bool CDarksendPool::DoAutomaticDenominating(bool fDryRun)
{
if(!fEnablePrivateSend) return false;
if(fMasterNode) return false;

if(!pCurrentBlockIndex) return false;
if(!pwalletMain || pwalletMain->IsLocked()) return false;

if(state == POOL_STATUS_ERROR || state == POOL_STATUS_SUCCESS) return false;

if (nWalletBackups == 0) {
LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating - Automatic backups disabled, no mixing available.\n");
strAutoDenomResult = _("Automatic backups disabled") + ", " + _("no mixing available.");
fEnablePrivateSend = false; // stop mixing
pwalletMain->nKeysLeftSinceAutoBackup = 0; // no backup, no "keys since last backup"
return false;
} else if (nWalletBackups == -1) {
// Automatic backup failed, nothing else we can do until user fixes the issue manually.
// There is no way to bring user attention in daemon mode so we just update status and
// keep spaming if debug is on.
LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating - ERROR! Failed to create automatic backup.\n");
strAutoDenomResult = _("ERROR! Failed to create automatic backup") + ", " + _("see debug.log for details.");
return false;
} else if (nWalletBackups == -2) {
// We were able to create automatic backup but keypool was not replenished because wallet is locked.
// There is no way to bring user attention in daemon mode so we just update status and
// keep spaming if debug is on.
LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating - WARNING! Failed to create replenish keypool, please unlock your wallet to do so.\n");
strAutoDenomResult = _("WARNING! Failed to replenish keypool, please unlock your wallet to do so.") + ", " + _("see debug.log for details.");
return false;
}

if (pwalletMain->nKeysLeftSinceAutoBackup < PS_KEYS_THRESHOLD_STOP) {
// We should never get here via mixing itself but probably smth else is still actively using keypool
LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating - 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);
// It's getting really dangerous, stop mixing
fEnablePrivateSend = false;
return false;
} else if (pwalletMain->nKeysLeftSinceAutoBackup < PS_KEYS_THRESHOLD_WARNING) {
// Low number of keys left but it's still more or less safe to continue
LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating - Very low number of keys left: %d\n", pwalletMain->nKeysLeftSinceAutoBackup);
strAutoDenomResult = strprintf(_("Very low number of keys left: %d"), pwalletMain->nKeysLeftSinceAutoBackup);

if (fCreateAutoBackups) {
LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating - Trying to create new backup.\n");
std::string warningString;
std::string errorString;

if(!AutoBackupWallet(pwalletMain, "", warningString, errorString)) {
if (!warningString.empty()) {
// There were some issues saving backup but yet more or less safe to continue
LogPrintf("CDarksendPool::DoAutomaticDenominating - WARNING! Something went wrong on automatic backup: %s\n", warningString);
}
if (!errorString.empty()) {
// Things are really broken
LogPrintf("CDarksendPool::DoAutomaticDenominating - ERROR! Failed to create automatic backup: %s\n", errorString);
strAutoDenomResult = strprintf(_("ERROR! Failed to create automatic backup") + ": %s", errorString);
return false;
}
}
} else {
// Wait for someone else (e.g. GUI action) to create automatic backup for us
return false;
}
}

LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating - Keys left since latest backup: %d\n", pwalletMain->nKeysLeftSinceAutoBackup);

if(GetEntriesCount() > 0) {
strAutoDenomResult = _("Mixing in progress...");
return false;
Expand Down
9 changes: 8 additions & 1 deletion src/darksend.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ class CActiveMasternode;
static const CAmount DARKSEND_COLLATERAL = (0.01*COIN);
static const CAmount DARKSEND_POOL_MAX = (999.99*COIN);
static const CAmount DENOMS_COUNT_MAX = 100;
// Warn user if mixing in gui or try to create backup if mixing in daemon mode
// when we have only this many keys left
static const int PS_KEYS_THRESHOLD_WARNING = 100;
// Stop mixing completely, it's too dangerous to continue when we have only this many keys left
static const int PS_KEYS_THRESHOLD_STOP = 50;

extern CDarksendPool darkSendPool;
extern CDarkSendSigner darkSendSigner;
Expand Down Expand Up @@ -326,6 +331,7 @@ class CDarksendPool
CMasternode* pSubmittedToMasternode;
int sessionDenom; //Users must submit an denom matching this
int cachedNumBlocks; //used for the overview screen
bool fCreateAutoBackups; //builtin support for automatic backups

CDarksendPool()
{
Expand All @@ -338,6 +344,7 @@ class CDarksendPool
txCollateral = CMutableTransaction();
minBlockSpacing = 0;
lastNewBlock = 0;
fCreateAutoBackups = true;

SetNull();
}
Expand Down Expand Up @@ -466,7 +473,7 @@ class CDarksendPool
/// Is this amount compatible with other client in the pool?
bool IsCompatibleWithSession(CAmount nAmount, CTransaction txCollateral, int &errorID);

/// Passively run Darksend in the background according to the configuration in settings (only for QT)
/// Passively run Darksend in the background according to the configuration in settings
bool DoAutomaticDenominating(bool fDryRun=false);
bool PrepareDarksendDenominate();

Expand Down
95 changes: 24 additions & 71 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ using namespace std;

#ifdef ENABLE_WALLET
CWallet* pwalletMain = NULL;
int nWalletBackups = 10;
#endif
bool fFeeEstimatesInitialized = false;
bool fRestartRequested = false; // true: restart false: shutdown
Expand Down Expand Up @@ -467,7 +466,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"));
strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") +
" " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)"));
strUsage += HelpMessageOpt("-createwalletbackups=<n>", _("Number of automatic wallet backups (default: 10)"));
strUsage += HelpMessageOpt("-createwalletbackups=<n>", strprintf(_("Number of automatic wallet backups (default: %u)"), nWalletBackups));
strUsage += HelpMessageOpt("-walletbackupsdir=<dir>", _("Specify full path to directory for automatic wallet backups (must exist)"));
strUsage += HelpMessageOpt("-keepass", strprintf(_("Use KeePass 2 integration using KeePassHttp plugin (default: %u)"), 0));
strUsage += HelpMessageOpt("-keepassport=<port>", strprintf(_("Connect to KeePassHttp on port <port> (default: %u)"), 19455));
strUsage += HelpMessageOpt("-keepasskey=<key>", _("KeePassHttp key for AES encrypted communication with KeePass"));
Expand Down Expand Up @@ -1230,82 +1230,24 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 5: Backup wallet and verify wallet database integrity
#ifdef ENABLE_WALLET
if (!fDisableWallet) {

filesystem::path backupDir = GetDataDir() / "backups";
if (!filesystem::exists(backupDir))
{
// Always create backup folder to not confuse the operating system's file browser
filesystem::create_directories(backupDir);
}
std::string warningString;
std::string errorString;

nWalletBackups = GetArg("-createwalletbackups", 10);
nWalletBackups = std::max(0, std::min(10, nWalletBackups));
if(nWalletBackups > 0)
{
if (filesystem::exists(backupDir))
{
// Create backup of the wallet
std::string dateTimeStr = DateTimeStrFormat(".%Y-%m-%d-%H-%M", GetTime());
std::string backupPathStr = backupDir.string();
backupPathStr += "/" + strWalletFile;
std::string sourcePathStr = GetDataDir().string();
sourcePathStr += "/" + strWalletFile;
boost::filesystem::path sourceFile = sourcePathStr;
boost::filesystem::path backupFile = backupPathStr + dateTimeStr;
sourceFile.make_preferred();
backupFile.make_preferred();
if(boost::filesystem::exists(sourceFile)) {
try {
boost::filesystem::copy_file(sourceFile, backupFile);
LogPrintf("Creating backup of %s -> %s\n", sourceFile, backupFile);
} catch(boost::filesystem::filesystem_error &error) {
LogPrintf("Failed to create backup %s\n", error.what());
}
}
// Keep only the last 10 backups, including the new one of course
typedef std::multimap<std::time_t, boost::filesystem::path> folder_set_t;
folder_set_t folder_set;
boost::filesystem::directory_iterator end_iter;
boost::filesystem::path backupFolder = backupDir.string();
backupFolder.make_preferred();
// Build map of backup files for current(!) wallet sorted by last write time
boost::filesystem::path currentFile;
for (boost::filesystem::directory_iterator dir_iter(backupFolder); dir_iter != end_iter; ++dir_iter)
{
// Only check regular files
if ( boost::filesystem::is_regular_file(dir_iter->status()))
{
currentFile = dir_iter->path().filename();
// Only add the backups for the current wallet, e.g. wallet.dat.*
if(dir_iter->path().stem().string() == strWalletFile)
{
folder_set.insert(folder_set_t::value_type(boost::filesystem::last_write_time(dir_iter->path()), *dir_iter));
}
}
}
// Loop backward through backup files and keep the N newest ones (1 <= N <= 10)
int counter = 0;
BOOST_REVERSE_FOREACH(PAIRTYPE(const std::time_t, boost::filesystem::path) file, folder_set)
{
counter++;
if (counter > nWalletBackups)
{
// More than nWalletBackups backups: delete oldest one(s)
try {
boost::filesystem::remove(file.second);
LogPrintf("Old backup deleted: %s\n", file.second);
} catch(boost::filesystem::filesystem_error &error) {
LogPrintf("Failed to delete backup %s\n", error.what());
}
}
}
}

if(!AutoBackupWallet(NULL, strWalletFile, warningString, errorString)) {
if (!warningString.empty())
InitWarning(warningString);
if (!errorString.empty())
return InitError(errorString);
}

LogPrintf("Using wallet %s\n", strWalletFile);
uiInterface.InitMessage(_("Verifying wallet..."));

std::string warningString;
std::string errorString;
// reset warning string
warningString = "";

if (!CWallet::Verify(strWalletFile, warningString, errorString))
return false;
Expand Down Expand Up @@ -1706,6 +1648,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}

pwalletMain->SetBestChain(chainActive.GetLocator());

// Try to create wallet backup right after new wallet was created
std::string warningString;
std::string errorString;
if(!AutoBackupWallet(pwalletMain, "", warningString, errorString)) {
if (!warningString.empty())
InitWarning(warningString);
if (!errorString.empty())
return InitError(errorString);
}

}

LogPrintf("%s", strErrors.str());
Expand Down
6 changes: 3 additions & 3 deletions src/qt/guiutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,11 @@ void openMNConfigfile()

void showBackups()
{
boost::filesystem::path pathBackups = GetDataDir() / "backups";
boost::filesystem::path backupsDir = GetBackupsDir();

/* Open folder with default browser */
if (boost::filesystem::exists(pathBackups))
QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathBackups)));
if (boost::filesystem::exists(backupsDir))
QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(backupsDir)));
}

void SubstituteFonts(const QString& language)
Expand Down
Loading

0 comments on commit 496a0c3

Please sign in to comment.