db.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. // ECOin - Copyright (c) - 2014/2024 - GPLv3 - epsylon@riseup.net (https://03c8.net)
  2. #include "db.h"
  3. #include "net.h"
  4. #include "util.h"
  5. #include "main.h"
  6. #include "ui_interface.h"
  7. #include <boost/filesystem.hpp>
  8. #include <boost/filesystem/fstream.hpp>
  9. #ifndef WIN32
  10. #include "sys/stat.h"
  11. #endif
  12. using namespace std;
  13. using namespace boost;
  14. unsigned int nWalletDBUpdated;
  15. CDBEnv bitdb;
  16. void CDBEnv::EnvShutdown()
  17. {
  18. if (!fDbEnvInit)
  19. return;
  20. fDbEnvInit = false;
  21. int ret = dbenv.close(0);
  22. if (ret != 0)
  23. printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret);
  24. if (!fMockDb)
  25. DbEnv(0).remove(strPath.c_str(), 0);
  26. }
  27. CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS)
  28. {
  29. fDbEnvInit = false;
  30. fMockDb = false;
  31. }
  32. CDBEnv::~CDBEnv()
  33. {
  34. EnvShutdown();
  35. }
  36. void CDBEnv::Close()
  37. {
  38. EnvShutdown();
  39. }
  40. bool CDBEnv::Open(boost::filesystem::path pathEnv_)
  41. {
  42. if (fDbEnvInit)
  43. return true;
  44. if (fShutdown)
  45. return false;
  46. pathEnv = pathEnv_;
  47. boost::filesystem::path pathDataDir = pathEnv;
  48. strPath = pathDataDir.string();
  49. boost::filesystem::path pathLogDir = pathDataDir / "database";
  50. boost::filesystem::create_directory(pathLogDir);
  51. boost::filesystem::path pathErrorFile = pathDataDir / "db.log";
  52. printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str());
  53. unsigned int nEnvFlags = 0;
  54. if (GetBoolArg("-privdb", true))
  55. nEnvFlags |= DB_PRIVATE;
  56. int nDbCache = GetArg("-dbcache", 25);
  57. dbenv.set_lg_dir(pathLogDir.string().c_str());
  58. dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
  59. dbenv.set_lg_bsize(1048576);
  60. dbenv.set_lg_max(10485760);
  61. dbenv.set_lk_max_locks(537000);
  62. dbenv.set_lk_max_objects(10000);
  63. dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
  64. dbenv.set_flags(DB_AUTO_COMMIT, 1);
  65. dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
  66. #ifdef DB_LOG_AUTO_REMOVE
  67. dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1);
  68. #endif
  69. int ret = dbenv.open(strPath.c_str(),
  70. DB_CREATE |
  71. DB_INIT_LOCK |
  72. DB_INIT_LOG |
  73. DB_INIT_MPOOL |
  74. DB_INIT_TXN |
  75. DB_THREAD |
  76. DB_RECOVER |
  77. nEnvFlags,
  78. S_IRUSR | S_IWUSR);
  79. if (ret != 0)
  80. return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret);
  81. fDbEnvInit = true;
  82. fMockDb = false;
  83. return true;
  84. }
  85. void CDBEnv::MakeMock()
  86. {
  87. if (fDbEnvInit)
  88. throw runtime_error("CDBEnv::MakeMock(): already initialized");
  89. if (fShutdown)
  90. throw runtime_error("CDBEnv::MakeMock(): during shutdown");
  91. printf("CDBEnv::MakeMock()\n");
  92. dbenv.set_cachesize(1, 0, 1);
  93. dbenv.set_lg_bsize(10485760*4);
  94. dbenv.set_lg_max(10485760);
  95. dbenv.set_lk_max_locks(10000);
  96. dbenv.set_lk_max_objects(10000);
  97. dbenv.set_flags(DB_AUTO_COMMIT, 1);
  98. #ifdef DB_LOG_IN_MEMORY
  99. dbenv.log_set_config(DB_LOG_IN_MEMORY, 1);
  100. #endif
  101. int ret = dbenv.open(NULL,
  102. DB_CREATE |
  103. DB_INIT_LOCK |
  104. DB_INIT_LOG |
  105. DB_INIT_MPOOL |
  106. DB_INIT_TXN |
  107. DB_THREAD |
  108. DB_PRIVATE,
  109. S_IRUSR | S_IWUSR);
  110. if (ret > 0)
  111. throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret));
  112. fDbEnvInit = true;
  113. fMockDb = true;
  114. }
  115. CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
  116. {
  117. LOCK(cs_db);
  118. assert(mapFileUseCount.count(strFile) == 0);
  119. Db db(&dbenv, 0);
  120. int result = db.verify(strFile.c_str(), NULL, NULL, 0);
  121. if (result == 0)
  122. return VERIFY_OK;
  123. else if (recoverFunc == NULL)
  124. return RECOVER_FAIL;
  125. // Try to recover:
  126. bool fRecovered = (*recoverFunc)(*this, strFile);
  127. return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
  128. }
  129. bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
  130. std::vector<CDBEnv::KeyValPair >& vResult)
  131. {
  132. LOCK(cs_db);
  133. assert(mapFileUseCount.count(strFile) == 0);
  134. u_int32_t flags = DB_SALVAGE;
  135. if (fAggressive) flags |= DB_AGGRESSIVE;
  136. stringstream strDump;
  137. Db db(&dbenv, 0);
  138. int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
  139. if (result != 0)
  140. {
  141. printf("ERROR: db salvage failed\n");
  142. return false;
  143. }
  144. string strLine;
  145. while (!strDump.eof() && strLine != "HEADER=END")
  146. getline(strDump, strLine); // Skip past header
  147. std::string keyHex, valueHex;
  148. while (!strDump.eof() && keyHex != "DATA=END")
  149. {
  150. getline(strDump, keyHex);
  151. if (keyHex != "DATA_END")
  152. {
  153. getline(strDump, valueHex);
  154. vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
  155. }
  156. }
  157. return (result == 0);
  158. }
  159. void CDBEnv::CheckpointLSN(std::string strFile)
  160. {
  161. dbenv.txn_checkpoint(0, 0, 0);
  162. if (fMockDb)
  163. return;
  164. dbenv.lsn_reset(strFile.c_str(), 0);
  165. }
  166. CDB::CDB(const char *pszFile, const char* pszMode) :
  167. pdb(NULL), activeTxn(NULL)
  168. {
  169. int ret;
  170. if (pszFile == NULL)
  171. return;
  172. fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
  173. bool fCreate = strchr(pszMode, 'c');
  174. unsigned int nFlags = DB_THREAD;
  175. if (fCreate)
  176. nFlags |= DB_CREATE;
  177. {
  178. LOCK(bitdb.cs_db);
  179. if (!bitdb.Open(GetDataDir()))
  180. throw runtime_error("env open failed");
  181. strFile = pszFile;
  182. ++bitdb.mapFileUseCount[strFile];
  183. pdb = bitdb.mapDb[strFile];
  184. if (pdb == NULL)
  185. {
  186. pdb = new Db(&bitdb.dbenv, 0);
  187. bool fMockDb = bitdb.IsMock();
  188. if (fMockDb)
  189. {
  190. DbMpoolFile*mpf = pdb->get_mpf();
  191. ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
  192. if (ret != 0)
  193. throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile));
  194. }
  195. ret = pdb->open(NULL, // Txn pointer
  196. fMockDb ? NULL : pszFile, // Filename
  197. "main", // Logical db name
  198. DB_BTREE, // Database type
  199. nFlags, // Flags
  200. 0);
  201. if (ret != 0)
  202. {
  203. delete pdb;
  204. pdb = NULL;
  205. --bitdb.mapFileUseCount[strFile];
  206. strFile = "";
  207. throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
  208. }
  209. if (fCreate && !Exists(string("version")))
  210. {
  211. bool fTmp = fReadOnly;
  212. fReadOnly = false;
  213. WriteVersion(CLIENT_VERSION);
  214. fReadOnly = fTmp;
  215. }
  216. bitdb.mapDb[strFile] = pdb;
  217. }
  218. }
  219. }
  220. static bool IsChainFile(std::string strFile)
  221. {
  222. if (strFile == "blkindex.dat")
  223. return true;
  224. return false;
  225. }
  226. void CDB::Close()
  227. {
  228. if (!pdb)
  229. return;
  230. if (activeTxn)
  231. activeTxn->abort();
  232. activeTxn = NULL;
  233. pdb = NULL;
  234. unsigned int nMinutes = 0;
  235. if (fReadOnly)
  236. nMinutes = 1;
  237. if (IsChainFile(strFile))
  238. nMinutes = 2;
  239. if (IsChainFile(strFile) && IsInitialBlockDownload())
  240. nMinutes = 5;
  241. bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0);
  242. {
  243. LOCK(bitdb.cs_db);
  244. --bitdb.mapFileUseCount[strFile];
  245. }
  246. }
  247. void CDBEnv::CloseDb(const string& strFile)
  248. {
  249. {
  250. LOCK(cs_db);
  251. if (mapDb[strFile] != NULL)
  252. {
  253. Db* pdb = mapDb[strFile];
  254. pdb->close(0);
  255. delete pdb;
  256. mapDb[strFile] = NULL;
  257. }
  258. }
  259. }
  260. bool CDBEnv::RemoveDb(const string& strFile)
  261. {
  262. this->CloseDb(strFile);
  263. LOCK(cs_db);
  264. int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
  265. return (rc == 0);
  266. }
  267. bool CDB::Rewrite(const string& strFile, const char* pszSkip)
  268. {
  269. while (!fShutdown)
  270. {
  271. {
  272. LOCK(bitdb.cs_db);
  273. if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
  274. {
  275. bitdb.CloseDb(strFile);
  276. bitdb.CheckpointLSN(strFile);
  277. bitdb.mapFileUseCount.erase(strFile);
  278. bool fSuccess = true;
  279. printf("Rewriting %s...\n", strFile.c_str());
  280. string strFileRes = strFile + ".rewrite";
  281. { // surround usage of db with extra {}
  282. CDB db(strFile.c_str(), "r");
  283. Db* pdbCopy = new Db(&bitdb.dbenv, 0);
  284. int ret = pdbCopy->open(NULL, // Txn pointer
  285. strFileRes.c_str(), // Filename
  286. "main", // Logical db name
  287. DB_BTREE, // Database type
  288. DB_CREATE, // Flags
  289. 0);
  290. if (ret > 0)
  291. {
  292. printf("Cannot create database file %s\n", strFileRes.c_str());
  293. fSuccess = false;
  294. }
  295. Dbc* pcursor = db.GetCursor();
  296. if (pcursor)
  297. while (fSuccess)
  298. {
  299. CDataStream ssKey(SER_DISK, CLIENT_VERSION);
  300. CDataStream ssValue(SER_DISK, CLIENT_VERSION);
  301. int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
  302. if (ret == DB_NOTFOUND)
  303. {
  304. pcursor->close();
  305. break;
  306. }
  307. else if (ret != 0)
  308. {
  309. pcursor->close();
  310. fSuccess = false;
  311. break;
  312. }
  313. if (pszSkip &&
  314. strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
  315. continue;
  316. if (strncmp(&ssKey[0], "\x07version", 8) == 0)
  317. {
  318. // Update version:
  319. ssValue.clear();
  320. ssValue << CLIENT_VERSION;
  321. }
  322. Dbt datKey(&ssKey[0], ssKey.size());
  323. Dbt datValue(&ssValue[0], ssValue.size());
  324. int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
  325. if (ret2 > 0)
  326. fSuccess = false;
  327. }
  328. if (fSuccess)
  329. {
  330. db.Close();
  331. bitdb.CloseDb(strFile);
  332. if (pdbCopy->close(0))
  333. fSuccess = false;
  334. delete pdbCopy;
  335. }
  336. }
  337. if (fSuccess)
  338. {
  339. Db dbA(&bitdb.dbenv, 0);
  340. if (dbA.remove(strFile.c_str(), NULL, 0))
  341. fSuccess = false;
  342. Db dbB(&bitdb.dbenv, 0);
  343. if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
  344. fSuccess = false;
  345. }
  346. if (!fSuccess)
  347. printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
  348. return fSuccess;
  349. }
  350. }
  351. Sleep(100);
  352. }
  353. return false;
  354. }
  355. void CDBEnv::Flush(bool fShutdown)
  356. {
  357. int64 nStart = GetTimeMillis();
  358. printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
  359. if (!fDbEnvInit)
  360. return;
  361. {
  362. LOCK(cs_db);
  363. map<string, int>::iterator mi = mapFileUseCount.begin();
  364. while (mi != mapFileUseCount.end())
  365. {
  366. string strFile = (*mi).first;
  367. int nRefCount = (*mi).second;
  368. printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
  369. if (nRefCount == 0)
  370. {
  371. CloseDb(strFile);
  372. printf("%s checkpoint\n", strFile.c_str());
  373. dbenv.txn_checkpoint(0, 0, 0);
  374. if (!IsChainFile(strFile) || fDetachDB) {
  375. printf("%s detach\n", strFile.c_str());
  376. if (!fMockDb)
  377. dbenv.lsn_reset(strFile.c_str(), 0);
  378. }
  379. printf("%s closed\n", strFile.c_str());
  380. mapFileUseCount.erase(mi++);
  381. }
  382. else
  383. mi++;
  384. }
  385. printf("DBFlush(%s)%s ended %15" PRI64d"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart);
  386. if (fShutdown)
  387. {
  388. char** listp;
  389. if (mapFileUseCount.empty())
  390. {
  391. dbenv.log_archive(&listp, DB_ARCH_REMOVE);
  392. Close();
  393. }
  394. }
  395. }
  396. }
  397. CAddrDB::CAddrDB()
  398. {
  399. pathAddr = GetDataDir() / "peers.dat";
  400. }
  401. bool CAddrDB::Write(const CAddrMan& addr)
  402. {
  403. // generate random temporary filename
  404. unsigned short randv = 0;
  405. RAND_bytes((unsigned char *)&randv, sizeof(randv));
  406. std::string tmpfn = strprintf("peers.dat.%04x", randv);
  407. // serialize addresses, checksum data up to that point, then append csum
  408. CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
  409. ssPeers << FLATDATA(pchMessageStart);
  410. ssPeers << addr;
  411. uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
  412. ssPeers << hash;
  413. // open temp output file, and associate with CAutoFile
  414. boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
  415. FILE *file = fopen(pathTmp.string().c_str(), "wb");
  416. CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION);
  417. if (!fileout)
  418. return error("CAddrman::Write() : open failed");
  419. // write and commit header, data
  420. try {
  421. fileout << ssPeers;
  422. }
  423. catch (std::exception &e) {
  424. return error("CAddrman::Write() : I/O error");
  425. }
  426. FileCommit(fileout);
  427. fileout.fclose();
  428. // replace existing peers.dat, if any, with new peers.dat.XXXX
  429. if (!RenameOver(pathTmp, pathAddr))
  430. return error("CAddrman::Write() : Rename-into-place failed");
  431. return true;
  432. }
  433. bool CAddrDB::Read(CAddrMan& addr)
  434. {
  435. // open input file, and associate with CAutoFile
  436. FILE *file = fopen(pathAddr.string().c_str(), "rb");
  437. CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION);
  438. if (!filein)
  439. return error("CAddrman::Read() : open failed");
  440. // use file size to size memory buffer
  441. int fileSize = GetFilesize(filein);
  442. int dataSize = fileSize - sizeof(uint256);
  443. if ( dataSize < 0 ) dataSize = 0;
  444. vector<unsigned char> vchData;
  445. vchData.resize(dataSize);
  446. uint256 hashIn;
  447. // read data and checksum from file
  448. try {
  449. filein.read((char *)&vchData[0], dataSize);
  450. filein >> hashIn;
  451. }
  452. catch (std::exception &e) {
  453. return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
  454. }
  455. filein.fclose();
  456. CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
  457. // verify stored checksum matches input data
  458. uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
  459. if (hashIn != hashTmp)
  460. return error("CAddrman::Read() : checksum mismatch; data corrupted");
  461. unsigned char pchMsgTmp[4];
  462. try {
  463. // de-serialize file header (pchMessageStart magic number) and
  464. ssPeers >> FLATDATA(pchMsgTmp);
  465. // verify the network matches ours
  466. if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp)))
  467. return error("CAddrman::Read() : invalid network magic number");
  468. // de-serialize address data into one CAddrMan object
  469. ssPeers >> addr;
  470. }
  471. catch (std::exception &e) {
  472. return error("CAddrman::Read() : I/O error or stream data corrupted");
  473. }
  474. return true;
  475. }