upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-14 11:08:34 -0300
committerNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-14 11:08:34 -0300
commitdd9db5ba5407b9c30b4f41fc1153db39f0770154 (patch)
treef39a99147d9265ed9bb98bb830cdf0919c3ba56d
parentbf62d480ae5d76c04d1a8b27750f51596473cc85 (diff)
wallet: install backups via temporary file
-rw-r--r--docs/storage.md3
-rw-r--r--src/db.cpp40
-rw-r--r--src/selftest.cpp7
3 files changed, 43 insertions, 7 deletions
diff --git a/docs/storage.md b/docs/storage.md
index e4a66d2..c1cda45 100644
--- a/docs/storage.md
+++ b/docs/storage.md
@@ -77,7 +77,8 @@ A future wallet backend must keep these properties:
773. A locked encrypted wallet may expose public keys and balances, but not 773. A locked encrypted wallet may expose public keys and balances, but not
78 private keys or decrypted HD seed material. 78 private keys or decrypted HD seed material.
794. `-backupwallet` must produce a wallet file that opens in a different datadir 794. `-backupwallet` must produce a wallet file that opens in a different datadir
80 without copying `database/`. 80 without copying `database/`, and must not leave a half-written final backup
81 path if the copy or install step fails.
815. `-recoveryaudit` must distinguish: 825. `-recoveryaudit` must distinguish:
82 - no recovery phrase installed; 83 - no recovery phrase installed;
83 - recovery phrase present; 84 - recovery phrase present;
diff --git a/src/db.cpp b/src/db.cpp
index b90219e..0ff58e7 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -1030,6 +1030,13 @@ bool BackupWallet(const string& strDest)
1030 if (!FileExists(strSrc.c_str())) 1030 if (!FileExists(strSrc.c_str()))
1031 return error("BackupWallet() : %s does not exist\n", strSrc.c_str()); 1031 return error("BackupWallet() : %s does not exist\n", strSrc.c_str());
1032 1032
1033 string strTemp = strprintf("%s.tmp.%lld", strDest.c_str(), GetTime());
1034 for (int i = 1; FileExists(strTemp.c_str()); i++)
1035 strTemp = strprintf("%s.tmp.%lld.%d", strDest.c_str(), GetTime(), i);
1036 string strOld = strprintf("%s.old.%lld", strDest.c_str(), GetTime());
1037 for (int i = 1; FileExists(strOld.c_str()); i++)
1038 strOld = strprintf("%s.old.%lld.%d", strDest.c_str(), GetTime(), i);
1039
1033 CRITICAL_BLOCK(cs_db) 1040 CRITICAL_BLOCK(cs_db)
1034 { 1041 {
1035 // Committing first, so the copy is not missing the newest records. 1042 // Committing first, so the copy is not missing the newest records.
@@ -1044,11 +1051,11 @@ bool BackupWallet(const string& strDest)
1044 FILE* pfIn = fopen(strSrc.c_str(), "rb"); 1051 FILE* pfIn = fopen(strSrc.c_str(), "rb");
1045 if (!pfIn) 1052 if (!pfIn)
1046 return error("BackupWallet() : cannot read %s\n", strSrc.c_str()); 1053 return error("BackupWallet() : cannot read %s\n", strSrc.c_str());
1047 FILE* pfOut = fopen(strDest.c_str(), "wb"); 1054 FILE* pfOut = fopen(strTemp.c_str(), "wb");
1048 if (!pfOut) 1055 if (!pfOut)
1049 { 1056 {
1050 fclose(pfIn); 1057 fclose(pfIn);
1051 return error("BackupWallet() : cannot write %s\n", strDest.c_str()); 1058 return error("BackupWallet() : cannot write %s\n", strTemp.c_str());
1052 } 1059 }
1053 1060
1054 char buf[65536]; 1061 char buf[65536];
@@ -1065,18 +1072,39 @@ bool BackupWallet(const string& strDest)
1065 1072
1066 if (!fOk) 1073 if (!fOk)
1067 { 1074 {
1068 remove(strDest.c_str()); // half a wallet is worse than none 1075 remove(strTemp.c_str()); // half a wallet is worse than none
1069 return error("BackupWallet() : copy to %s failed\n", strDest.c_str()); 1076 return error("BackupWallet() : copy to %s failed\n", strTemp.c_str());
1070 } 1077 }
1071 1078
1072 // Without this the copy is welded to this node's database/ dir. 1079 // Without this the copy is welded to this node's database/ dir.
1073 int ret = dbenv.lsn_reset(strDest.c_str(), 0); 1080 int ret = dbenv.lsn_reset(strTemp.c_str(), 0);
1074 if (ret != 0) 1081 if (ret != 0)
1075 printf("BackupWallet() : warning -- lsn_reset returned %d; the copy " 1082 printf("BackupWallet() : warning -- lsn_reset returned %d; the copy "
1076 "may only open beside this node's database/ directory\n", ret); 1083 "may only open beside this node's database/ directory\n", ret);
1084
1085 bool fHadOldDest = FileExists(strDest.c_str());
1086 if (fHadOldDest && rename(strDest.c_str(), strOld.c_str()) != 0)
1087 {
1088 remove(strTemp.c_str());
1089 return error("BackupWallet() : could not move old %s aside\n", strDest.c_str());
1090 }
1091 if (rename(strTemp.c_str(), strDest.c_str()) != 0)
1092 {
1093 remove(strTemp.c_str());
1094 if (fHadOldDest)
1095 rename(strOld.c_str(), strDest.c_str());
1096 return error("BackupWallet() : could not install %s\n", strDest.c_str());
1097 }
1098 if (fHadOldDest)
1099 remove(strOld.c_str());
1077 } 1100 }
1078 catch (const std::exception& e) 1101 catch (const std::exception& e)
1079 { return error("BackupWallet() : %s\n", e.what()); } 1102 {
1103 remove(strTemp.c_str());
1104 if (FileExists(strOld.c_str()) && !FileExists(strDest.c_str()))
1105 rename(strOld.c_str(), strDest.c_str());
1106 return error("BackupWallet() : %s\n", e.what());
1107 }
1080 } 1108 }
1081 1109
1082 // Recorded here rather than by whoever called, so that a backup taken with 1110 // Recorded here rather than by whoever called, so that a backup taken with
diff --git a/src/selftest.cpp b/src/selftest.cpp
index 638aa66..81c4439 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -1391,6 +1391,13 @@ static int RunWalletPortabilitySelfTest()
1391 nFail += Check(nBackupRet == 0 && FileExists(strPortableWallet.c_str()), 1391 nFail += Check(nBackupRet == 0 && FileExists(strPortableWallet.c_str()),
1392 "/backupwallet writes a portable wallet.dat") ? 0 : 1; 1392 "/backupwallet writes a portable wallet.dat") ? 0 : 1;
1393 1393
1394 string strBackupOut2 = tmp + "/backupwallet-overwrite.txt";
1395 int nBackupRet2 = RunBitflashChild(strExe, vBackupArgs, NULL, &strBackupOut2);
1396 nFail += Check(nBackupRet2 == 0 && FileExists(strPortableWallet.c_str()) &&
1397 !DirectoryHasFileWithPrefix(tmp, "portable-wallet.dat.tmp.") &&
1398 !DirectoryHasFileWithPrefix(tmp, "portable-wallet.dat.old."),
1399 "/backupwallet replaces an existing backup without leaving temp files") ? 0 : 1;
1400
1394 nFail += Check(CopyFileLocal(strOriginal + "/wallet.dat", 1401 nFail += Check(CopyFileLocal(strOriginal + "/wallet.dat",
1395 strRawCopy + "/wallet.dat"), 1402 strRawCopy + "/wallet.dat"),
1396 "a raw wallet.dat can be copied without database logs") ? 0 : 1; 1403 "a raw wallet.dat can be copied without database logs") ? 0 : 1;