upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/db.cpp
diff options
context:
space:
mode:
authorBitflash-sh <btf01@proton.me>2026-07-24 11:18:11 -0300
committerBitflash-sh <btf01@proton.me>2026-07-24 11:18:11 -0300
commit51d617e70f90a972185917ea0096c1ecef2bfa8b (patch)
tree9fbbcd867de9de9d0776afdd02f594ba312c46eb /src/db.cpp
Bitflash: fair-launch release
Satoshi's bitcoin-0.1.0 reborn with RandomX CPU proof of work, anonymous .btf addressing over Nostr with CGNAT rendezvous, automatic peer discovery, a headless Linux node, and Windows GUI wallet. Fair launch, no premine, 21M cap.
Diffstat (limited to 'src/db.cpp')
-rw-r--r--src/db.cpp604
1 files changed, 604 insertions, 0 deletions
diff --git a/src/db.cpp b/src/db.cpp
new file mode 100644
index 0000000..0df68fc
--- /dev/null
+++ b/src/db.cpp
@@ -0,0 +1,604 @@
1// Copyright (c) 2009 Satoshi Nakamoto
2// Distributed under the MIT/X11 software license, see the accompanying
3// file license.txt or http://www.opensource.org/licenses/mit-license.php.
4
5#include "headers.h"
6
7
8
9
10
11
12//
13// CDB
14//
15
16static CCriticalSection cs_db;
17static bool fDbEnvInit = false;
18DbEnv dbenv(0u);
19static map<string, int> mapFileUseCount;
20
21class CDBInit
22{
23public:
24 CDBInit()
25 {
26 }
27 ~CDBInit()
28 {
29 if (fDbEnvInit)
30 {
31 dbenv.close(0);
32 fDbEnvInit = false;
33 }
34 }
35}
36instance_of_cdbinit;
37
38
39CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL)
40{
41 int ret;
42 if (pszFile == NULL)
43 return;
44
45 bool fCreate = strchr(pszMode, 'c');
46 bool fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
47 unsigned int nFlags = DB_THREAD;
48 if (fCreate)
49 nFlags |= DB_CREATE;
50 else if (fReadOnly)
51 nFlags |= DB_RDONLY;
52 if (!fReadOnly || fTxn)
53 nFlags |= DB_AUTO_COMMIT;
54
55 CRITICAL_BLOCK(cs_db)
56 {
57 if (!fDbEnvInit)
58 {
59 string strAppDir = GetAppDir();
60 string strLogDir = strAppDir + "/database";
61 _mkdir(strLogDir.c_str());
62 printf("dbenv.open strAppDir=%s\n", strAppDir.c_str());
63
64 dbenv.set_lg_dir(strLogDir.c_str());
65 dbenv.set_lg_max(10000000);
66 dbenv.set_lk_max_locks(10000);
67 dbenv.set_lk_max_objects(10000);
68 dbenv.set_errfile(fopen("db.log", "a")); /// debug
69 ///dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption
70 ret = dbenv.open(strAppDir.c_str(),
71 DB_CREATE |
72 DB_INIT_LOCK |
73 DB_INIT_LOG |
74 DB_INIT_MPOOL |
75 DB_INIT_TXN |
76 DB_THREAD |
77 DB_PRIVATE |
78 DB_RECOVER,
79 0);
80 if (ret > 0)
81 throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret));
82 fDbEnvInit = true;
83 }
84
85 strFile = pszFile;
86 ++mapFileUseCount[strFile];
87 }
88
89 pdb = new Db(&dbenv, 0);
90
91 ret = pdb->open(NULL, // Txn pointer
92 pszFile, // Filename
93 "main", // Logical db name
94 DB_BTREE, // Database type
95 nFlags, // Flags
96 0);
97
98 if (ret > 0)
99 {
100 delete pdb;
101 pdb = NULL;
102 CRITICAL_BLOCK(cs_db)
103 --mapFileUseCount[strFile];
104 strFile = "";
105 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d\n", pszFile, ret));
106 }
107
108 if (fCreate && !Exists(string("version")))
109 WriteVersion(VERSION);
110
111 RandAddSeed();
112}
113
114void CDB::Close()
115{
116 if (!pdb)
117 return;
118 if (!vTxn.empty())
119 vTxn.front()->abort();
120 vTxn.clear();
121 pdb->close(0);
122 delete pdb;
123 pdb = NULL;
124 dbenv.txn_checkpoint(0, 0, 0);
125
126 CRITICAL_BLOCK(cs_db)
127 --mapFileUseCount[strFile];
128
129 RandAddSeed();
130}
131
132void DBFlush(bool fShutdown)
133{
134 // Flush log data to the actual data file
135 // on all files that are not in use
136 printf("DBFlush(%s)\n", fShutdown ? "true" : "false");
137 CRITICAL_BLOCK(cs_db)
138 {
139 dbenv.txn_checkpoint(0, 0, 0);
140 map<string, int>::iterator mi = mapFileUseCount.begin();
141 while (mi != mapFileUseCount.end())
142 {
143 string strFile = (*mi).first;
144 int nRefCount = (*mi).second;
145 if (nRefCount == 0)
146 {
147 dbenv.lsn_reset(strFile.c_str(), 0);
148 mapFileUseCount.erase(mi++);
149 }
150 else
151 mi++;
152 }
153 if (fShutdown)
154 {
155 char** listp;
156 if (mapFileUseCount.empty())
157 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
158 dbenv.close(0);
159 fDbEnvInit = false;
160 }
161 }
162}
163
164
165
166
167
168
169//
170// CTxDB
171//
172
173bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)
174{
175 assert(!fClient);
176 txindex.SetNull();
177 return Read(make_pair(string("tx"), hash), txindex);
178}
179
180bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)
181{
182 assert(!fClient);
183 return Write(make_pair(string("tx"), hash), txindex);
184}
185
186bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)
187{
188 assert(!fClient);
189
190 // Add to tx index
191 uint256 hash = tx.GetHash();
192 CTxIndex txindex(pos, tx.vout.size());
193 return Write(make_pair(string("tx"), hash), txindex);
194}
195
196bool CTxDB::EraseTxIndex(const CTransaction& tx)
197{
198 assert(!fClient);
199 uint256 hash = tx.GetHash();
200
201 return Erase(make_pair(string("tx"), hash));
202}
203
204bool CTxDB::ContainsTx(uint256 hash)
205{
206 assert(!fClient);
207 return Exists(make_pair(string("tx"), hash));
208}
209
210bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)
211{
212 assert(!fClient);
213 vtx.clear();
214
215 // Get cursor
216 Dbc* pcursor = GetCursor();
217 if (!pcursor)
218 return false;
219
220 unsigned int fFlags = DB_SET_RANGE;
221 loop
222 {
223 // Read next record
224 CDataStream ssKey;
225 if (fFlags == DB_SET_RANGE)
226 ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);
227 CDataStream ssValue;
228 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
229 fFlags = DB_NEXT;
230 if (ret == DB_NOTFOUND)
231 break;
232 else if (ret != 0)
233 return false;
234
235 // Unserialize
236 string strType;
237 uint160 hashItem;
238 CDiskTxPos pos;
239 ssKey >> strType >> hashItem >> pos;
240 int nItemHeight;
241 ssValue >> nItemHeight;
242
243 // Read transaction
244 if (strType != "owner" || hashItem != hash160)
245 break;
246 if (nItemHeight >= nMinHeight)
247 {
248 vtx.resize(vtx.size()+1);
249 if (!vtx.back().ReadFromDisk(pos))
250 return false;
251 }
252 }
253 return true;
254}
255
256bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)
257{
258 assert(!fClient);
259 tx.SetNull();
260 if (!ReadTxIndex(hash, txindex))
261 return false;
262 return (tx.ReadFromDisk(txindex.pos));
263}
264
265bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)
266{
267 CTxIndex txindex;
268 return ReadDiskTx(hash, tx, txindex);
269}
270
271bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)
272{
273 return ReadDiskTx(outpoint.hash, tx, txindex);
274}
275
276bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)
277{
278 CTxIndex txindex;
279 return ReadDiskTx(outpoint.hash, tx, txindex);
280}
281
282bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
283{
284 return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
285}
286
287bool CTxDB::EraseBlockIndex(uint256 hash)
288{
289 return Erase(make_pair(string("blockindex"), hash));
290}
291
292bool CTxDB::ReadHashBestChain(uint256& hashBestChain)
293{
294 return Read(string("hashBestChain"), hashBestChain);
295}
296
297bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
298{
299 return Write(string("hashBestChain"), hashBestChain);
300}
301
302CBlockIndex* InsertBlockIndex(uint256 hash)
303{
304 if (hash == 0)
305 return NULL;
306
307 // Return existing
308 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
309 if (mi != mapBlockIndex.end())
310 return (*mi).second;
311
312 // Create new
313 CBlockIndex* pindexNew = new CBlockIndex();
314 if (!pindexNew)
315 throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
316 mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
317 pindexNew->phashBlock = &((*mi).first);
318
319 return pindexNew;
320}
321
322bool CTxDB::LoadBlockIndex()
323{
324 // Get cursor
325 Dbc* pcursor = GetCursor();
326 if (!pcursor)
327 return false;
328
329 unsigned int fFlags = DB_SET_RANGE;
330 loop
331 {
332 // Read next record
333 CDataStream ssKey;
334 if (fFlags == DB_SET_RANGE)
335 ssKey << make_pair(string("blockindex"), uint256(0));
336 CDataStream ssValue;
337 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
338 fFlags = DB_NEXT;
339 if (ret == DB_NOTFOUND)
340 break;
341 else if (ret != 0)
342 return false;
343
344 // Unserialize
345 string strType;
346 ssKey >> strType;
347 if (strType == "blockindex")
348 {
349 CDiskBlockIndex diskindex;
350 ssValue >> diskindex;
351
352 // Construct block index object
353 CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
354 pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
355 pindexNew->pnext = InsertBlockIndex(diskindex.hashNext);
356 pindexNew->nFile = diskindex.nFile;
357 pindexNew->nBlockPos = diskindex.nBlockPos;
358 pindexNew->nHeight = diskindex.nHeight;
359 pindexNew->nVersion = diskindex.nVersion;
360 pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
361 pindexNew->nTime = diskindex.nTime;
362 pindexNew->nBits = diskindex.nBits;
363 pindexNew->nNonce = diskindex.nNonce;
364
365 // Watch for genesis block and best block
366 if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)
367 pindexGenesisBlock = pindexNew;
368 }
369 else
370 {
371 break;
372 }
373 }
374
375 if (!ReadHashBestChain(hashBestChain))
376 {
377 if (pindexGenesisBlock == NULL)
378 return true;
379 return error("CTxDB::LoadBlockIndex() : hashBestChain not found\n");
380 }
381
382 if (!mapBlockIndex.count(hashBestChain))
383 return error("CTxDB::LoadBlockIndex() : blockindex for hashBestChain not found\n");
384 pindexBest = mapBlockIndex[hashBestChain];
385 nBestHeight = pindexBest->nHeight;
386 printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight);
387
388 return true;
389}
390
391
392
393
394
395//
396// CAddrDB
397//
398
399bool CAddrDB::WriteAddress(const CAddress& addr)
400{
401 return Write(make_pair(string("addr"), addr.GetKey()), addr);
402}
403
404bool CAddrDB::LoadAddresses()
405{
406 CRITICAL_BLOCK(cs_mapAddresses)
407 {
408 // Load user provided addresses
409 CAutoFile filein = fopen("addr.txt", "rt");
410 if (filein)
411 {
412 try
413 {
414 char psz[1000];
415 while (fgets(psz, sizeof(psz), filein))
416 {
417 CAddress addr(psz, NODE_NETWORK);
418 if (addr.ip != 0)
419 AddAddress(*this, addr);
420 }
421 }
422 catch (...) { }
423 }
424
425 // Get cursor
426 Dbc* pcursor = GetCursor();
427 if (!pcursor)
428 return false;
429
430 loop
431 {
432 // Read next record
433 CDataStream ssKey;
434 CDataStream ssValue;
435 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
436 if (ret == DB_NOTFOUND)
437 break;
438 else if (ret != 0)
439 return false;
440
441 // Unserialize
442 string strType;
443 ssKey >> strType;
444 if (strType == "addr")
445 {
446 CAddress addr;
447 ssValue >> addr;
448 mapAddresses.insert(make_pair(addr.GetKey(), addr));
449 }
450 }
451
452 //// debug print
453 printf("mapAddresses:\n");
454 foreach(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
455 item.second.print();
456 printf("-----\n");
457 }
458
459 return true;
460}
461
462bool LoadAddresses()
463{
464 return CAddrDB("cr+").LoadAddresses();
465}
466
467
468
469
470//
471// CReviewDB
472//
473
474bool CReviewDB::ReadReviews(uint256 hash, vector<CReview>& vReviews)
475{
476 vReviews.size(); // msvc workaround, just need to do anything with vReviews
477 return Read(make_pair(string("reviews"), hash), vReviews);
478}
479
480bool CReviewDB::WriteReviews(uint256 hash, const vector<CReview>& vReviews)
481{
482 return Write(make_pair(string("reviews"), hash), vReviews);
483}
484
485
486
487
488
489
490
491//
492// CWalletDB
493//
494
495bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
496{
497 vchDefaultKeyRet.clear();
498
499 //// todo: shouldn't we catch exceptions and try to recover and continue?
500 CRITICAL_BLOCK(cs_mapKeys)
501 CRITICAL_BLOCK(cs_mapWallet)
502 {
503 // Get cursor
504 Dbc* pcursor = GetCursor();
505 if (!pcursor)
506 return false;
507
508 loop
509 {
510 // Read next record
511 CDataStream ssKey;
512 CDataStream ssValue;
513 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
514 if (ret == DB_NOTFOUND)
515 break;
516 else if (ret != 0)
517 return false;
518
519 // Unserialize
520 // Taking advantage of the fact that pair serialization
521 // is just the two items serialized one after the other
522 string strType;
523 ssKey >> strType;
524 if (strType == "name")
525 {
526 string strAddress;
527 ssKey >> strAddress;
528 ssValue >> mapAddressBook[strAddress];
529 }
530 else if (strType == "tx")
531 {
532 uint256 hash;
533 ssKey >> hash;
534 CWalletTx& wtx = mapWallet[hash];
535 ssValue >> wtx;
536
537 if (wtx.GetHash() != hash)
538 printf("Error in wallet.dat, hash mismatch\n");
539
540 //// debug print
541 //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
542 //printf(" %12I64d %s %s %s\n",
543 // wtx.vout[0].nValue,
544 // DateTimeStr(wtx.nTime).c_str(),
545 // wtx.hashBlock.ToString().substr(0,14).c_str(),
546 // wtx.mapValue["message"].c_str());
547 }
548 else if (strType == "key")
549 {
550 vector<unsigned char> vchPubKey;
551 ssKey >> vchPubKey;
552 CPrivKey vchPrivKey;
553 ssValue >> vchPrivKey;
554
555 mapKeys[vchPubKey] = vchPrivKey;
556 mapPubKeys[Hash160(vchPubKey)] = vchPubKey;
557 }
558 else if (strType == "defaultkey")
559 {
560 ssValue >> vchDefaultKeyRet;
561 }
562 else if (strType == "setting") /// or settings or option or options or config?
563 {
564 string strKey;
565 ssKey >> strKey;
566 if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins;
567 if (strKey == "nTransactionFee") ssValue >> nTransactionFee;
568 if (strKey == "addrIncoming") ssValue >> addrIncoming;
569 }
570 }
571 }
572
573 printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
574 printf("nTransactionFee = %I64d\n", nTransactionFee);
575 printf("addrIncoming = %s\n", addrIncoming.ToString().c_str());
576
577 return true;
578}
579
580bool LoadWallet()
581{
582 vector<unsigned char> vchDefaultKey;
583 if (!CWalletDB("cr").LoadWallet(vchDefaultKey))
584 return false;
585
586 if (mapKeys.count(vchDefaultKey))
587 {
588 // Set keyUser
589 keyUser.SetPubKey(vchDefaultKey);
590 keyUser.SetPrivKey(mapKeys[vchDefaultKey]);
591 }
592 else
593 {
594 // Create new keyUser and set as default key
595 keyUser.MakeNewKey();
596 if (!AddKey(keyUser))
597 return false;
598 if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address"))
599 return false;
600 CWalletDB().WriteDefaultKey(keyUser.GetPubKey());
601 }
602
603 return true;
604}