X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fdb.cpp;h=60dba3b35339bfd20aea119b3d03784c9653b202;hb=ed6d0b5f852dc5f1c9407abecb5a9c6a7e42b4b2;hp=b3fa3e1731aa9724de49c7e8c679c60ea5e36a2b;hpb=7c3002bf272c56dcc92b463db6d0b793221bfa8a;p=novacoin.git diff --git a/src/db.cpp b/src/db.cpp index b3fa3e1..60dba3b 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -1,13 +1,20 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. -#include "headers.h" #include "db.h" -#include "net.h" +#include "util.h" +#include "main.h" +#include "wallet.h" +#include #include #include +#ifndef WIN32 +#include "sys/stat.h" +#endif + using namespace std; using namespace boost; @@ -27,6 +34,23 @@ DbEnv dbenv(0); static map mapFileUseCount; static map mapDb; +static void EnvShutdown() +{ + if (!fDbEnvInit) + return; + + fDbEnvInit = false; + try + { + dbenv.close(0); + } + catch (const DbException& e) + { + printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno()); + } + DbEnv(0).remove(GetDataDir().string().c_str(), 0); +} + class CDBInit { public: @@ -35,17 +59,13 @@ public: } ~CDBInit() { - if (fDbEnvInit) - { - dbenv.close(0); - fDbEnvInit = false; - } + EnvShutdown(); } } instance_of_cdbinit; -CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) +CDB::CDB(const char *pszFile, const char* pszMode) : pdb(NULL) { int ret; if (pszFile == NULL) @@ -57,25 +77,29 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) if (fCreate) nFlags |= DB_CREATE; - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (!fDbEnvInit) { if (fShutdown) return; - string strDataDir = GetDataDir(); - string strLogDir = strDataDir + "/database"; - filesystem::create_directory(strLogDir.c_str()); - string strErrorFile = strDataDir + "/db.log"; - printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str()); - - dbenv.set_lg_dir(strLogDir.c_str()); - dbenv.set_lg_max(10000000); + filesystem::path pathDataDir = GetDataDir(); + filesystem::path pathLogDir = pathDataDir / "database"; + filesystem::create_directory(pathLogDir); + filesystem::path pathErrorFile = pathDataDir / "db.log"; + printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str()); + + int nDbCache = GetArg("-dbcache", 25); + dbenv.set_lg_dir(pathLogDir.string().c_str()); + dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); + dbenv.set_lg_bsize(1048576); + dbenv.set_lg_max(10485760); dbenv.set_lk_max_locks(10000); dbenv.set_lk_max_objects(10000); - dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug + dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug dbenv.set_flags(DB_AUTO_COMMIT, 1); - ret = dbenv.open(strDataDir.c_str(), + dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); + ret = dbenv.open(pathDataDir.string().c_str(), DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -107,8 +131,10 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) { delete pdb; pdb = NULL; - CRITICAL_BLOCK(cs_db) + { + LOCK(cs_db); --mapFileUseCount[strFile]; + } strFile = ""; throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret)); } @@ -117,7 +143,7 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) { bool fTmp = fReadOnly; fReadOnly = false; - WriteVersion(VERSION); + WriteVersion(CLIENT_VERSION); fReadOnly = fTmp; } @@ -141,18 +167,21 @@ void CDB::Close() nMinutes = 1; if (strFile == "addr.dat") nMinutes = 2; - if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0) - nMinutes = 1; - dbenv.txn_checkpoint(0, nMinutes, 0); + if (strFile == "blkindex.dat" && IsInitialBlockDownload()) + nMinutes = 5; - CRITICAL_BLOCK(cs_db) + dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); + + { + LOCK(cs_db); --mapFileUseCount[strFile]; + } } void static CloseDb(const string& strFile) { - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (mapDb[strFile] != NULL) { // Close the database handle @@ -164,6 +193,101 @@ void static CloseDb(const string& strFile) } } +bool CDB::Rewrite(const string& strFile, const char* pszSkip) +{ + while (!fShutdown) + { + { + LOCK(cs_db); + if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) + { + // Flush log data to the dat file + CloseDb(strFile); + dbenv.txn_checkpoint(0, 0, 0); + dbenv.lsn_reset(strFile.c_str(), 0); + mapFileUseCount.erase(strFile); + + bool fSuccess = true; + printf("Rewriting %s...\n", strFile.c_str()); + string strFileRes = strFile + ".rewrite"; + { // surround usage of db with extra {} + CDB db(strFile.c_str(), "r"); + Db* pdbCopy = new Db(&dbenv, 0); + + int ret = pdbCopy->open(NULL, // Txn pointer + strFileRes.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) + { + printf("Cannot create database file %s\n", strFileRes.c_str()); + fSuccess = false; + } + + Dbc* pcursor = db.GetCursor(); + if (pcursor) + while (fSuccess) + { + CDataStream ssKey; + CDataStream ssValue; + int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); + if (ret == DB_NOTFOUND) + { + pcursor->close(); + break; + } + else if (ret != 0) + { + pcursor->close(); + fSuccess = false; + break; + } + if (pszSkip && + strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0) + continue; + if (strncmp(&ssKey[0], "\x07version", 8) == 0) + { + // Update version: + ssValue.clear(); + ssValue << CLIENT_VERSION; + } + Dbt datKey(&ssKey[0], ssKey.size()); + Dbt datValue(&ssValue[0], ssValue.size()); + int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + if (fSuccess) + { + db.Close(); + CloseDb(strFile); + if (pdbCopy->close(0)) + fSuccess = false; + delete pdbCopy; + } + } + if (fSuccess) + { + Db dbA(&dbenv, 0); + if (dbA.remove(strFile.c_str(), NULL, 0)) + fSuccess = false; + Db dbB(&dbenv, 0); + if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) + fSuccess = false; + } + if (!fSuccess) + printf("Rewriting of %s FAILED!\n", strFileRes.c_str()); + return fSuccess; + } + } + Sleep(100); + } + return false; +} + + void DBFlush(bool fShutdown) { // Flush log data to the actual data file @@ -171,8 +295,8 @@ void DBFlush(bool fShutdown) printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); if (!fDbEnvInit) return; - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); map::iterator mi = mapFileUseCount.begin(); while (mi != mapFileUseCount.end()) { @@ -195,9 +319,10 @@ void DBFlush(bool fShutdown) { char** listp; if (mapFileUseCount.empty()) + { dbenv.log_archive(&listp, DB_ARCH_REMOVE); - dbenv.close(0); - fDbEnvInit = false; + EnvShutdown(); + } } } } @@ -469,19 +594,118 @@ bool CTxDB::LoadBlockIndex() ReadBestInvalidWork(bnBestInvalidWork); // Verify blocks in the best chain + int nCheckLevel = GetArg("-checklevel", 1); + int nCheckDepth = GetArg( "-checkblocks", 2500); + if (nCheckDepth == 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > nBestHeight) + nCheckDepth = nBestHeight; + printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CBlockIndex* pindexFork = NULL; + map, CBlockIndex*> mapBlockPos; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { - if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks")) + if (pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; if (!block.ReadFromDisk(pindex)) return error("LoadBlockIndex() : block.ReadFromDisk failed"); - if (!block.CheckBlock()) + // check level 1: verify block validity + if (nCheckLevel>0 && !block.CheckBlock()) { printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); pindexFork = pindex->pprev; } + // check level 2: verify transaction index validity + if (nCheckLevel>1) + { + pair pos = make_pair(pindex->nFile, pindex->nBlockPos); + mapBlockPos[pos] = pindex; + BOOST_FOREACH(const CTransaction &tx, block.vtx) + { + uint256 hashTx = tx.GetHash(); + CTxIndex txindex; + if (ReadTxIndex(hashTx, txindex)) + { + // check level 3: checker transaction hashes + if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos) + { + // either an error or a duplicate transaction + CTransaction txFound; + if (!txFound.ReadFromDisk(txindex.pos)) + { + printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + else + if (txFound.GetHash() != hashTx) // not a duplicate tx + { + printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + // check level 4: check whether spent txouts were spent within the main chain + int nOutput = 0; + if (nCheckLevel>3) + { + BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent) + { + if (!txpos.IsNull()) + { + pair posFind = make_pair(txpos.nFile, txpos.nBlockPos); + if (!mapBlockPos.count(posFind)) + { + printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + // check level 6: check whether spent txouts were spent by a valid transaction that consume them + if (nCheckLevel>5) + { + CTransaction txSpend; + if (!txSpend.ReadFromDisk(txpos)) + { + printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else if (!txSpend.CheckTransaction()) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else + { + bool fFound = false; + BOOST_FOREACH(const CTxIn &txin, txSpend.vin) + if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput) + fFound = true; + if (!fFound) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + } + } + } + nOutput++; + } + } + } + // check level 5: check whether all prevouts are marked spent + if (nCheckLevel>4) + { + BOOST_FOREACH(const CTxIn &txin, tx.vin) + { + CTxIndex txindex; + if (ReadTxIndex(txin.prevout.hash, txindex)) + if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull()) + { + printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + } + } + } } if (pindexFork) { @@ -505,68 +729,58 @@ bool CTxDB::LoadBlockIndex() // CAddrDB // -bool CAddrDB::WriteAddress(const CAddress& addr) +bool CAddrDB::WriteAddrman(const CAddrMan& addrman) { - return Write(make_pair(string("addr"), addr.GetKey()), addr); -} - -bool CAddrDB::EraseAddress(const CAddress& addr) -{ - return Erase(make_pair(string("addr"), addr.GetKey())); + return Write(string("addrman"), addrman); } bool CAddrDB::LoadAddresses() { - CRITICAL_BLOCK(cs_mapAddresses) + if (Read(string("addrman"), addrman)) { - // Load user provided addresses - CAutoFile filein = fopen((GetDataDir() + "/addr.txt").c_str(), "rt"); - if (filein) - { - try - { - char psz[1000]; - while (fgets(psz, sizeof(psz), filein)) - { - CAddress addr(psz, false, NODE_NETWORK); - addr.nTime = 0; // so it won't relay unless successfully connected - if (addr.IsValid()) - AddAddress(addr); - } - } - catch (...) { } - } + printf("Loaded %i addresses\n", addrman.size()); + return true; + } + + // Read pre-0.6 addr records - // Get cursor - Dbc* pcursor = GetCursor(); - if (!pcursor) + vector vAddr; + vector > vDelete; + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) + return false; + + loop + { + // Read next record + CDataStream ssKey; + CDataStream ssValue; + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) return false; - loop + // Unserialize + string strType; + ssKey >> strType; + if (strType == "addr") { - // Read next record - CDataStream ssKey; - CDataStream ssValue; - int ret = ReadAtCursor(pcursor, ssKey, ssValue); - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - return false; - - // Unserialize - string strType; - ssKey >> strType; - if (strType == "addr") - { - CAddress addr; - ssValue >> addr; - mapAddresses.insert(make_pair(addr.GetKey(), addr)); - } + CAddress addr; + ssValue >> addr; + vAddr.push_back(addr); } - pcursor->close(); - - printf("Loaded %d addresses\n", mapAddresses.size()); } + pcursor->close(); + + addrman.Add(vAddr, CNetAddr("0.0.0.0")); + printf("Loaded %i addresses\n", addrman.size()); + + // Note: old records left; we ran into hangs-on-startup + // bugs for some users who (we think) were running after + // an unclean shutdown. return true; } @@ -673,22 +887,26 @@ int CWalletDB::LoadWallet(CWallet* pwallet) pwallet->vchDefaultKey.clear(); int nFileVersion = 0; vector vWalletUpgrade; - - // Modify defaults -#ifndef __WXMSW__ - // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program - fMinimizeToTray = false; - fMinimizeOnClose = false; -#endif + bool fIsEncrypted = false; //// todo: shouldn't we catch exceptions and try to recover and continue? - CRITICAL_BLOCK(pwallet->cs_mapWallet) - CRITICAL_BLOCK(pwallet->cs_KeyStore) { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string)"minversion", nMinVersion)) + { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + // Get cursor Dbc* pcursor = GetCursor(); if (!pcursor) + { + printf("Error getting wallet database cursor\n"); return DB_CORRUPT; + } loop { @@ -699,7 +917,10 @@ int CWalletDB::LoadWallet(CWallet* pwallet) if (ret == DB_NOTFOUND) break; else if (ret != 0) + { + printf("Error reading next record from wallet database\n"); return DB_CORRUPT; + } // Unserialize // Taking advantage of the fact that pair serialization @@ -718,7 +939,7 @@ int CWalletDB::LoadWallet(CWallet* pwallet) ssKey >> hash; CWalletTx& wtx = pwallet->mapWallet[hash]; ssValue >> wtx; - wtx.pwallet = pwallet; + wtx.BindWallet(pwallet); if (wtx.GetHash() != hash) printf("Error in wallet.dat, hash mismatch\n"); @@ -768,16 +989,41 @@ int CWalletDB::LoadWallet(CWallet* pwallet) { CPrivKey pkey; ssValue >> pkey; + key.SetPubKey(vchPubKey); key.SetPrivKey(pkey); + if (key.GetPubKey() != vchPubKey) + { + printf("Error reading wallet database: CPrivKey pubkey inconsistency\n"); + return DB_CORRUPT; + } + if (!key.IsValid()) + { + printf("Error reading wallet database: invalid CPrivKey\n"); + return DB_CORRUPT; + } } else { CWalletKey wkey; ssValue >> wkey; + key.SetPubKey(vchPubKey); key.SetPrivKey(wkey.vchPrivKey); + if (key.GetPubKey() != vchPubKey) + { + printf("Error reading wallet database: CWalletKey pubkey inconsistency\n"); + return DB_CORRUPT; + } + if (!key.IsValid()) + { + printf("Error reading wallet database: invalid CWalletKey\n"); + return DB_CORRUPT; + } } if (!pwallet->LoadKey(key)) + { + printf("Error reading wallet database: LoadKey failed\n"); return DB_CORRUPT; + } } else if (strType == "mkey") { @@ -786,7 +1032,10 @@ int CWalletDB::LoadWallet(CWallet* pwallet) CMasterKey kMasterKey; ssValue >> kMasterKey; if(pwallet->mapMasterKeys.count(nID) != 0) + { + printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID); return DB_CORRUPT; + } pwallet->mapMasterKeys[nID] = kMasterKey; if (pwallet->nMasterKeyMaxID < nID) pwallet->nMasterKeyMaxID = nID; @@ -798,7 +1047,11 @@ int CWalletDB::LoadWallet(CWallet* pwallet) vector vchPrivKey; ssValue >> vchPrivKey; if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) + { + printf("Error reading wallet database: LoadCryptedKey failed\n"); return DB_CORRUPT; + } + fIsEncrypted = true; } else if (strType == "defaultkey") { @@ -816,30 +1069,17 @@ int CWalletDB::LoadWallet(CWallet* pwallet) if (nFileVersion == 10300) nFileVersion = 300; } - else if (strType == "setting") + else if (strType == "cscript") { - string strKey; - ssKey >> strKey; - - // Options -#ifndef GUI - if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins; -#endif - if (strKey == "nTransactionFee") ssValue >> nTransactionFee; - if (strKey == "fLimitProcessors") ssValue >> fLimitProcessors; - if (strKey == "nLimitProcessors") ssValue >> nLimitProcessors; - if (strKey == "fMinimizeToTray") ssValue >> fMinimizeToTray; - if (strKey == "fMinimizeOnClose") ssValue >> fMinimizeOnClose; - if (strKey == "fUseProxy") ssValue >> fUseProxy; - if (strKey == "addrProxy") ssValue >> addrProxy; - if (fHaveUPnP && strKey == "fUseUPnP") ssValue >> fUseUPnP; - } - else if (strType == "minversion") - { - int nMinVersion = 0; - ssValue >> nMinVersion; - if (nMinVersion > VERSION) - return DB_TOO_NEW; + uint160 hash; + ssKey >> hash; + CScript script; + ssValue >> script; + if (!pwallet->LoadCScript(script)) + { + printf("Error reading wallet database: LoadCScript failed\n"); + return DB_CORRUPT; + } } } pcursor->close(); @@ -849,26 +1089,14 @@ int CWalletDB::LoadWallet(CWallet* pwallet) WriteTx(hash, pwallet->mapWallet[hash]); printf("nFileVersion = %d\n", nFileVersion); - printf("fGenerateBitcoins = %d\n", fGenerateBitcoins); - printf("nTransactionFee = %"PRI64d"\n", nTransactionFee); - printf("fMinimizeToTray = %d\n", fMinimizeToTray); - printf("fMinimizeOnClose = %d\n", fMinimizeOnClose); - printf("fUseProxy = %d\n", fUseProxy); - printf("addrProxy = %s\n", addrProxy.ToString().c_str()); - if (fHaveUPnP) - printf("fUseUPnP = %d\n", fUseUPnP); - - - // Upgrade - if (nFileVersion < VERSION) - { - // Get rid of old debug.log file in current directory - if (nFileVersion <= 105 && !pszSetDataDir[0]) - unlink("debug.log"); - WriteVersion(VERSION); - } + // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: + if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000)) + return DB_NEED_REWRITE; + + if (nFileVersion < CLIENT_VERSION) // Update + WriteVersion(CLIENT_VERSION); return DB_LOAD_OK; } @@ -880,7 +1108,7 @@ void ThreadFlushWalletDB(void* parg) if (fOneThread) return; fOneThread = true; - if (mapArgs.count("-noflushwallet")) + if (!GetBoolArg("-flushwallet", true)) return; unsigned int nLastSeen = nWalletDBUpdated; @@ -898,7 +1126,8 @@ void ThreadFlushWalletDB(void* parg) if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) { - TRY_CRITICAL_BLOCK(cs_db) + TRY_LOCK(cs_db,lockDb); + if (lockDb) { // Don't do this if any databases are in use int nRefCount = 0; @@ -939,8 +1168,8 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) return false; while (!fShutdown) { - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0) { // Flush log data to the dat file @@ -950,18 +1179,23 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) mapFileUseCount.erase(wallet.strWalletFile); // Copy wallet.dat - filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile); + filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; filesystem::path pathDest(strDest); if (filesystem::is_directory(pathDest)) - pathDest = pathDest / wallet.strWalletFile; + pathDest /= wallet.strWalletFile; + + try { #if BOOST_VERSION >= 104000 - filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); + filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); #else - filesystem::copy_file(pathSrc, pathDest); + filesystem::copy_file(pathSrc, pathDest); #endif - printf("copied wallet.dat to %s\n", pathDest.string().c_str()); - - return true; + printf("copied wallet.dat to %s\n", pathDest.string().c_str()); + return true; + } catch(const filesystem::filesystem_error &e) { + printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what()); + return false; + } } } Sleep(100);