]> Git Repo - VerusCoin.git/blame - src/db.cpp
Merge pull request #2012 from luke-jr/invblk_errs
[VerusCoin.git] / src / db.cpp
CommitLineData
0a61b0df 1// Copyright (c) 2009-2010 Satoshi Nakamoto
88216419 2// Copyright (c) 2009-2012 The Bitcoin developers
0a61b0df 3// Distributed under the MIT/X11 software license, see the accompanying
3a25a2b9 4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
0a61b0df 5
1512d5ce 6#include "db.h"
ed6d0b5f
PW
7#include "util.h"
8#include "main.h"
4efbda3f 9#include <boost/version.hpp>
926e14b3 10#include <boost/filesystem.hpp>
31f29312 11#include <boost/filesystem/fstream.hpp>
0a61b0df 12
ed6d0b5f
PW
13#ifndef WIN32
14#include "sys/stat.h"
15#endif
16
223b6f1b
WL
17using namespace std;
18using namespace boost;
19
0a61b0df 20
21unsigned int nWalletDBUpdated;
0a61b0df 22
23
24
25//
26// CDB
27//
28
cd9696fc 29CDBEnv bitdb;
0a61b0df 30
cd9696fc 31void CDBEnv::EnvShutdown()
d764d916
GA
32{
33 if (!fDbEnvInit)
34 return;
35
36 fDbEnvInit = false;
8d5f461c
GA
37 int ret = dbenv.close(0);
38 if (ret != 0)
39 printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret);
148e107d
LD
40 if (!fMockDb)
41 DbEnv(0).remove(GetDataDir().string().c_str(), 0);
d764d916
GA
42}
43
8d5f461c 44CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS)
0a61b0df 45{
0a61b0df 46}
0a61b0df 47
cd9696fc
JG
48CDBEnv::~CDBEnv()
49{
50 EnvShutdown();
51}
52
53void CDBEnv::Close()
54{
55 EnvShutdown();
56}
0a61b0df 57
c74bae0f 58bool CDBEnv::Open(const boost::filesystem::path& path)
cd9696fc
JG
59{
60 if (fDbEnvInit)
61 return true;
62
63 if (fShutdown)
64 return false;
65
c74bae0f 66 filesystem::path pathLogDir = path / "database";
cd9696fc 67 filesystem::create_directory(pathLogDir);
c74bae0f 68 filesystem::path pathErrorFile = path / "db.log";
cd9696fc
JG
69 printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str());
70
0134a1c0
JG
71 unsigned int nEnvFlags = 0;
72 if (GetBoolArg("-privdb", true))
73 nEnvFlags |= DB_PRIVATE;
74
c74bae0f 75 unsigned int nDbCache = GetArg("-dbcache", 25);
cd9696fc
JG
76 dbenv.set_lg_dir(pathLogDir.string().c_str());
77 dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
78 dbenv.set_lg_bsize(1048576);
79 dbenv.set_lg_max(10485760);
ae8bfd12
PW
80 dbenv.set_lk_max_locks(40000);
81 dbenv.set_lk_max_objects(40000);
cd9696fc
JG
82 dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
83 dbenv.set_flags(DB_AUTO_COMMIT, 1);
84 dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
85 dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1);
c74bae0f 86 int ret = dbenv.open(path.string().c_str(),
cd9696fc
JG
87 DB_CREATE |
88 DB_INIT_LOCK |
89 DB_INIT_LOG |
90 DB_INIT_MPOOL |
91 DB_INIT_TXN |
92 DB_THREAD |
0134a1c0
JG
93 DB_RECOVER |
94 nEnvFlags,
cd9696fc 95 S_IRUSR | S_IWUSR);
8d5f461c
GA
96 if (ret != 0)
97 return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret);
cd9696fc
JG
98
99 fDbEnvInit = true;
148e107d 100 fMockDb = false;
cd9696fc
JG
101 return true;
102}
103
148e107d
LD
104void CDBEnv::MakeMock()
105{
106 if (fDbEnvInit)
107 throw runtime_error("CDBEnv::MakeMock(): already initialized");
108
109 if (fShutdown)
110 throw runtime_error("CDBEnv::MakeMock(): during shutdown");
111
112 printf("CDBEnv::MakeMock()\n");
113
114 dbenv.set_cachesize(1, 0, 1);
115 dbenv.set_lg_bsize(10485760*4);
116 dbenv.set_lg_max(10485760);
117 dbenv.set_lk_max_locks(10000);
118 dbenv.set_lk_max_objects(10000);
119 dbenv.set_flags(DB_AUTO_COMMIT, 1);
120 dbenv.log_set_config(DB_LOG_IN_MEMORY, 1);
121 int ret = dbenv.open(NULL,
122 DB_CREATE |
123 DB_INIT_LOCK |
124 DB_INIT_LOG |
125 DB_INIT_MPOOL |
126 DB_INIT_TXN |
127 DB_THREAD |
128 DB_PRIVATE,
129 S_IRUSR | S_IWUSR);
130 if (ret > 0)
131 throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret));
132
133 fDbEnvInit = true;
134 fMockDb = true;
135}
136
eed1785f
GA
137CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
138{
139 LOCK(cs_db);
140 assert(mapFileUseCount.count(strFile) == 0);
141
142 Db db(&dbenv, 0);
143 int result = db.verify(strFile.c_str(), NULL, NULL, 0);
144 if (result == 0)
145 return VERIFY_OK;
146 else if (recoverFunc == NULL)
147 return RECOVER_FAIL;
148
149 // Try to recover:
150 bool fRecovered = (*recoverFunc)(*this, strFile);
151 return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
152}
153
154bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
155 std::vector<CDBEnv::KeyValPair >& vResult)
156{
157 LOCK(cs_db);
158 assert(mapFileUseCount.count(strFile) == 0);
159
160 u_int32_t flags = DB_SALVAGE;
161 if (fAggressive) flags |= DB_AGGRESSIVE;
162
163 stringstream strDump;
164
165 Db db(&dbenv, 0);
166 int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
167 if (result != 0)
168 {
169 printf("ERROR: db salvage failed\n");
170 return false;
171 }
172
173 // Format of bdb dump is ascii lines:
174 // header lines...
175 // HEADER=END
176 // hexadecimal key
177 // hexadecimal value
178 // ... repeated
179 // DATA=END
180
181 string strLine;
182 while (!strDump.eof() && strLine != "HEADER=END")
183 getline(strDump, strLine); // Skip past header
184
185 std::string keyHex, valueHex;
186 while (!strDump.eof() && keyHex != "DATA=END")
187 {
188 getline(strDump, keyHex);
189 if (keyHex != "DATA_END")
190 {
191 getline(strDump, valueHex);
192 vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
193 }
194 }
195
196 return (result == 0);
197}
198
199
cd9696fc
JG
200void CDBEnv::CheckpointLSN(std::string strFile)
201{
202 dbenv.txn_checkpoint(0, 0, 0);
148e107d
LD
203 if (fMockDb)
204 return;
cd9696fc
JG
205 dbenv.lsn_reset(strFile.c_str(), 0);
206}
0a61b0df 207
208
94e34fa0
JG
209CDB::CDB(const char *pszFile, const char* pszMode) :
210 pdb(NULL), activeTxn(NULL)
0a61b0df 211{
212 int ret;
213 if (pszFile == NULL)
214 return;
215
216 fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
217 bool fCreate = strchr(pszMode, 'c');
218 unsigned int nFlags = DB_THREAD;
219 if (fCreate)
220 nFlags |= DB_CREATE;
221
0a61b0df 222 {
cd9696fc
JG
223 LOCK(bitdb.cs_db);
224 if (!bitdb.Open(GetDataDir()))
225 throw runtime_error("env open failed");
0a61b0df 226
227 strFile = pszFile;
ffe8b77a
JG
228 ++bitdb.mapFileUseCount[strFile];
229 pdb = bitdb.mapDb[strFile];
0a61b0df 230 if (pdb == NULL)
231 {
cd9696fc 232 pdb = new Db(&bitdb.dbenv, 0);
0a61b0df 233
148e107d
LD
234 bool fMockDb = bitdb.IsMock();
235 if (fMockDb)
236 {
237 DbMpoolFile*mpf = pdb->get_mpf();
238 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
239 if (ret != 0)
240 throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile));
241 }
242
0a61b0df 243 ret = pdb->open(NULL, // Txn pointer
148e107d 244 fMockDb ? NULL : pszFile, // Filename
450cbb09 245 fMockDb ? pszFile : "main", // Logical db name
0a61b0df 246 DB_BTREE, // Database type
247 nFlags, // Flags
248 0);
249
8d5f461c 250 if (ret != 0)
0a61b0df 251 {
252 delete pdb;
253 pdb = NULL;
028ec224 254 --bitdb.mapFileUseCount[strFile];
0a61b0df 255 strFile = "";
d743f035 256 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
0a61b0df 257 }
258
259 if (fCreate && !Exists(string("version")))
260 {
261 bool fTmp = fReadOnly;
262 fReadOnly = false;
f8ded588 263 WriteVersion(CLIENT_VERSION);
0a61b0df 264 fReadOnly = fTmp;
265 }
266
ffe8b77a 267 bitdb.mapDb[strFile] = pdb;
0a61b0df 268 }
269 }
270}
271
ae8bfd12 272void CDB::Flush()
0a61b0df 273{
8b1202c5 274 if (activeTxn)
ae8bfd12 275 return;
0a61b0df 276
277 // Flush database activity from memory pool to disk log
278 unsigned int nMinutes = 0;
f03304a9 279 if (fReadOnly)
280 nMinutes = 1;
25c5eca8 281
cd9696fc 282 bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0);
ae8bfd12
PW
283}
284
285void CDB::Close()
286{
287 if (!pdb)
288 return;
289 if (activeTxn)
290 activeTxn->abort();
291 activeTxn = NULL;
292 pdb = NULL;
293
294 Flush();
0a61b0df 295
f8dcd5ca 296 {
cd9696fc 297 LOCK(bitdb.cs_db);
ffe8b77a 298 --bitdb.mapFileUseCount[strFile];
f8dcd5ca 299 }
0a61b0df 300}
301
ffe8b77a 302void CDBEnv::CloseDb(const string& strFile)
0a61b0df 303{
0a61b0df 304 {
f8dcd5ca 305 LOCK(cs_db);
0a61b0df 306 if (mapDb[strFile] != NULL)
307 {
308 // Close the database handle
309 Db* pdb = mapDb[strFile];
310 pdb->close(0);
311 delete pdb;
312 mapDb[strFile] = NULL;
313 }
314 }
315}
316
eed1785f
GA
317bool CDBEnv::RemoveDb(const string& strFile)
318{
319 this->CloseDb(strFile);
320
321 LOCK(cs_db);
322 int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
323 return (rc == 0);
324}
325
d764d916 326bool CDB::Rewrite(const string& strFile, const char* pszSkip)
9e9869d0
PW
327{
328 while (!fShutdown)
329 {
9e9869d0 330 {
cd9696fc 331 LOCK(bitdb.cs_db);
ffe8b77a 332 if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
9e9869d0
PW
333 {
334 // Flush log data to the dat file
ffe8b77a 335 bitdb.CloseDb(strFile);
cd9696fc 336 bitdb.CheckpointLSN(strFile);
ffe8b77a 337 bitdb.mapFileUseCount.erase(strFile);
9e9869d0
PW
338
339 bool fSuccess = true;
d764d916
GA
340 printf("Rewriting %s...\n", strFile.c_str());
341 string strFileRes = strFile + ".rewrite";
f53c5ede
PW
342 { // surround usage of db with extra {}
343 CDB db(strFile.c_str(), "r");
cd9696fc 344 Db* pdbCopy = new Db(&bitdb.dbenv, 0);
ea0796bd 345
f53c5ede
PW
346 int ret = pdbCopy->open(NULL, // Txn pointer
347 strFileRes.c_str(), // Filename
348 "main", // Logical db name
349 DB_BTREE, // Database type
350 DB_CREATE, // Flags
351 0);
352 if (ret > 0)
9e9869d0 353 {
f53c5ede
PW
354 printf("Cannot create database file %s\n", strFileRes.c_str());
355 fSuccess = false;
356 }
ea0796bd 357
f53c5ede
PW
358 Dbc* pcursor = db.GetCursor();
359 if (pcursor)
360 while (fSuccess)
d764d916 361 {
6b6aaa16
PW
362 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
363 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
f53c5ede
PW
364 int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
365 if (ret == DB_NOTFOUND)
366 {
367 pcursor->close();
368 break;
369 }
370 else if (ret != 0)
371 {
372 pcursor->close();
373 fSuccess = false;
374 break;
375 }
376 if (pszSkip &&
377 strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
378 continue;
379 if (strncmp(&ssKey[0], "\x07version", 8) == 0)
380 {
381 // Update version:
382 ssValue.clear();
f8ded588 383 ssValue << CLIENT_VERSION;
f53c5ede
PW
384 }
385 Dbt datKey(&ssKey[0], ssKey.size());
386 Dbt datValue(&ssValue[0], ssValue.size());
387 int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
388 if (ret2 > 0)
389 fSuccess = false;
d764d916 390 }
f53c5ede
PW
391 if (fSuccess)
392 {
393 db.Close();
ffe8b77a 394 bitdb.CloseDb(strFile);
f53c5ede 395 if (pdbCopy->close(0))
9e9869d0 396 fSuccess = false;
f53c5ede 397 delete pdbCopy;
9e9869d0 398 }
9e9869d0
PW
399 }
400 if (fSuccess)
401 {
cd9696fc 402 Db dbA(&bitdb.dbenv, 0);
9e9869d0
PW
403 if (dbA.remove(strFile.c_str(), NULL, 0))
404 fSuccess = false;
cd9696fc 405 Db dbB(&bitdb.dbenv, 0);
9e9869d0
PW
406 if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
407 fSuccess = false;
408 }
409 if (!fSuccess)
d764d916 410 printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
9e9869d0
PW
411 return fSuccess;
412 }
413 }
414 Sleep(100);
415 }
416 return false;
417}
418
419
cd9696fc 420void CDBEnv::Flush(bool fShutdown)
0a61b0df 421{
53d0e6dd 422 int64 nStart = GetTimeMillis();
0a61b0df 423 // Flush log data to the actual data file
424 // on all files that are not in use
cd9696fc 425 printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
0a61b0df 426 if (!fDbEnvInit)
427 return;
0a61b0df 428 {
f8dcd5ca 429 LOCK(cs_db);
0a61b0df 430 map<string, int>::iterator mi = mapFileUseCount.begin();
431 while (mi != mapFileUseCount.end())
432 {
433 string strFile = (*mi).first;
434 int nRefCount = (*mi).second;
435 printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
436 if (nRefCount == 0)
437 {
438 // Move log data to the dat file
439 CloseDb(strFile);
83743ed6 440 printf("%s checkpoint\n", strFile.c_str());
0a61b0df 441 dbenv.txn_checkpoint(0, 0, 0);
92467073
PW
442 printf("%s detach\n", strFile.c_str());
443 if (!fMockDb)
444 dbenv.lsn_reset(strFile.c_str(), 0);
83743ed6 445 printf("%s closed\n", strFile.c_str());
0a61b0df 446 mapFileUseCount.erase(mi++);
447 }
448 else
449 mi++;
450 }
53d0e6dd 451 printf("DBFlush(%s)%s ended %15"PRI64d"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart);
0a61b0df 452 if (fShutdown)
453 {
454 char** listp;
455 if (mapFileUseCount.empty())
d764d916 456 {
0a61b0df 457 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
cd9696fc 458 Close();
d764d916 459 }
0a61b0df 460 }
461 }
462}
463
464
465
466
467
468
9ac282ca
JG
469
470
471
0a61b0df 472
473
474//
475// CAddrDB
476//
477
928d3a01
JG
478
479CAddrDB::CAddrDB()
5fee401f 480{
928d3a01 481 pathAddr = GetDataDir() / "peers.dat";
5fee401f
PW
482}
483
928d3a01 484bool CAddrDB::Write(const CAddrMan& addr)
0a61b0df 485{
928d3a01
JG
486 // Generate random temporary filename
487 unsigned short randv = 0;
488 RAND_bytes((unsigned char *)&randv, sizeof(randv));
489 std::string tmpfn = strprintf("peers.dat.%04x", randv);
490
491 // serialize addresses, checksum data up to that point, then append csum
492 CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
493 ssPeers << FLATDATA(pchMessageStart);
494 ssPeers << addr;
495 uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
496 ssPeers << hash;
497
498 // open temp output file, and associate with CAutoFile
499 boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
500 FILE *file = fopen(pathTmp.string().c_str(), "wb");
501 CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION);
502 if (!fileout)
503 return error("CAddrman::Write() : open failed");
504
505 // Write and commit header, data
506 try {
507 fileout << ssPeers;
5fee401f 508 }
928d3a01
JG
509 catch (std::exception &e) {
510 return error("CAddrman::Write() : I/O error");
511 }
512 FileCommit(fileout);
513 fileout.fclose();
5fee401f 514
928d3a01
JG
515 // replace existing peers.dat, if any, with new peers.dat.XXXX
516 if (!RenameOver(pathTmp, pathAddr))
517 return error("CAddrman::Write() : Rename-into-place failed");
5fee401f 518
928d3a01
JG
519 return true;
520}
0a61b0df 521
928d3a01
JG
522bool CAddrDB::Read(CAddrMan& addr)
523{
524 // open input file, and associate with CAutoFile
525 FILE *file = fopen(pathAddr.string().c_str(), "rb");
526 CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION);
527 if (!filein)
528 return error("CAddrman::Read() : open failed");
529
530 // use file size to size memory buffer
531 int fileSize = GetFilesize(filein);
532 int dataSize = fileSize - sizeof(uint256);
533 vector<unsigned char> vchData;
534 vchData.resize(dataSize);
535 uint256 hashIn;
536
537 // read data and checksum from file
538 try {
539 filein.read((char *)&vchData[0], dataSize);
540 filein >> hashIn;
5fee401f 541 }
928d3a01
JG
542 catch (std::exception &e) {
543 return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
544 }
545 filein.fclose();
0a61b0df 546
928d3a01 547 CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
53cb1a49 548
928d3a01
JG
549 // verify stored checksum matches input data
550 uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
551 if (hashIn != hashTmp)
552 return error("CAddrman::Read() : checksum mismatch; data corrupted");
0a61b0df 553
928d3a01
JG
554 unsigned char pchMsgTmp[4];
555 try {
bd2e1405 556 // de-serialize file header (pchMessageStart magic number) and
928d3a01 557 ssPeers >> FLATDATA(pchMsgTmp);
bd2e1405
JG
558
559 // verify the network matches ours
560 if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp)))
561 return error("CAddrman::Read() : invalid network magic number");
562
563 // de-serialize address data into one CAddrMan object
928d3a01
JG
564 ssPeers >> addr;
565 }
566 catch (std::exception &e) {
567 return error("CAddrman::Read() : I/O error or stream data corrupted");
568 }
0a61b0df 569
928d3a01
JG
570 return true;
571}
0a61b0df 572
This page took 0.193914 seconds and 4 git commands to generate.