Line data Source code
1 : // Copyright (c) 2014-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #ifndef BITCOIN_FLAT_DATABASE_H
6 : #define BITCOIN_FLAT_DATABASE_H
7 :
8 : #include <clientversion.h>
9 : #include <chainparams.h>
10 : #include <fs.h>
11 : #include <hash.h>
12 : #include <streams.h>
13 : #include <util/system.h>
14 :
15 : /**
16 : * Generic Dumping and Loading
17 : * ---------------------------
18 : */
19 :
20 : template<typename T>
21 : class CFlatDB
22 : {
23 : private:
24 : enum class ReadResult {
25 : Ok,
26 : FileError,
27 : HashReadError,
28 : IncorrectHash,
29 : IncorrectMagicMessage,
30 : IncorrectMagicNumber,
31 : IncorrectFormat
32 : };
33 :
34 : fs::path pathDB;
35 : std::string strFilename;
36 : std::string strMagicMessage;
37 :
38 0 : [[nodiscard]] bool CoreWrite(const T& objToSave)
39 : {
40 : // LOCK(objToSave.cs);
41 :
42 0 : const auto start{SteadyClock::now()};
43 :
44 : // serialize, checksum data up to that point, then append checksum
45 0 : CDataStream ssObj(SER_DISK, CLIENT_VERSION);
46 0 : ssObj << strMagicMessage; // specific magic message for this type of object
47 0 : ssObj << Params().MessageStart(); // network specific magic number
48 0 : ssObj << objToSave;
49 0 : uint256 hash = Hash(ssObj);
50 0 : ssObj << hash;
51 :
52 : // open output file, and associate with AutoFile
53 0 : FILE *file = fsbridge::fopen(pathDB, "wb");
54 0 : AutoFile fileout{file};
55 0 : if (fileout.IsNull()) {
56 0 : return error("%s: Failed to open file %s", __func__, fs::PathToString(pathDB));
57 : }
58 :
59 : // Write and commit header, data
60 : try {
61 0 : fileout << ssObj;
62 0 : }
63 : catch (std::exception &e) {
64 0 : return error("%s: Serialize or I/O error - %s", __func__, e.what());
65 0 : }
66 0 : fileout.fclose();
67 :
68 0 : LogPrintf("Written info to %s %dms\n", strFilename, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
69 0 : LogPrintf(" %s\n", objToSave.ToString());
70 :
71 0 : return true;
72 0 : }
73 :
74 0 : [[nodiscard]] ReadResult CoreRead(T& objToLoad)
75 : {
76 : //LOCK(objToLoad.cs);
77 :
78 0 : const auto start{SteadyClock::now()};
79 : // open input file, and associate with AutoFile
80 0 : FILE *file = fsbridge::fopen(pathDB, "rb");
81 0 : AutoFile filein{file};
82 0 : if (filein.IsNull()) {
83 : // It is not actually error, maybe it's a first initialization of core.
84 0 : return ReadResult::FileError;
85 : }
86 :
87 : // use file size to size memory buffer
88 0 : int fileSize = fs::file_size(pathDB);
89 0 : int dataSize = fileSize - sizeof(uint256);
90 : // Don't try to resize to a negative number if file is small
91 0 : if (dataSize < 0)
92 0 : dataSize = 0;
93 0 : std::vector<unsigned char> vchData;
94 0 : vchData.resize(dataSize);
95 0 : uint256 hashIn;
96 :
97 : // read data and checksum from file
98 : try {
99 0 : filein.read(MakeWritableByteSpan(vchData));
100 0 : filein >> hashIn;
101 0 : }
102 : catch (std::exception &e) {
103 0 : error("%s: Deserialize or I/O error - %s", __func__, e.what());
104 0 : return ReadResult::HashReadError;
105 0 : }
106 0 : filein.fclose();
107 :
108 0 : CDataStream ssObj(vchData, SER_DISK, CLIENT_VERSION);
109 :
110 : // verify stored checksum matches input data
111 0 : uint256 hashTmp = Hash(ssObj);
112 0 : if (hashIn != hashTmp)
113 : {
114 0 : error("%s: Checksum mismatch, data corrupted", __func__);
115 0 : return ReadResult::IncorrectHash;
116 : }
117 :
118 :
119 : try {
120 : unsigned char pchMsgTmp[4];
121 0 : std::string strMagicMessageTmp;
122 : // de-serialize file header (file specific magic message) and ..
123 0 : ssObj >> strMagicMessageTmp;
124 :
125 : // ... verify the message matches predefined one
126 0 : if (strMagicMessage != strMagicMessageTmp)
127 : {
128 0 : error("%s: Invalid magic message", __func__);
129 0 : return ReadResult::IncorrectMagicMessage;
130 : }
131 :
132 :
133 : // de-serialize file header (network specific magic number) and ..
134 0 : ssObj >> pchMsgTmp;
135 :
136 : // ... verify the network matches ours
137 0 : if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
138 : {
139 0 : error("%s: Invalid network magic number", __func__);
140 0 : return ReadResult::IncorrectMagicNumber;
141 : }
142 :
143 : // de-serialize data into T object
144 0 : ssObj >> objToLoad;
145 0 : }
146 : catch (std::exception &e) {
147 0 : objToLoad.Clear();
148 0 : error("%s: Deserialize or I/O error - %s", __func__, e.what());
149 0 : return ReadResult::IncorrectFormat;
150 0 : }
151 :
152 0 : LogPrintf("Loaded info from %s %dms\n", strFilename, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
153 0 : LogPrintf(" %s\n", objToLoad.ToString());
154 :
155 0 : return ReadResult::Ok;
156 0 : }
157 :
158 0 : [[nodiscard]] bool Read(T& objToLoad)
159 : {
160 0 : ReadResult readResult = CoreRead(objToLoad);
161 0 : if (readResult == ReadResult::FileError)
162 0 : LogPrintf("Missing file %s, will try to recreate\n", strFilename);
163 0 : else if (readResult != ReadResult::Ok) {
164 0 : LogPrintf("ERROR: CFlatDB::Read Error reading %s: ", strFilename);
165 0 : if (readResult == ReadResult::IncorrectFormat) {
166 0 : LogPrintf("%s: Magic is ok but data has invalid format, will try to recreate\n", __func__);
167 0 : } else {
168 0 : LogPrintf("%s: File format is unknown or invalid, please fix it manually\n", __func__);
169 : // program should exit with an error
170 0 : return false;
171 : }
172 0 : }
173 0 : return true;
174 0 : }
175 :
176 : public:
177 4118 : CFlatDB(std::string&& strFilenameIn, std::string&& strMagicMessageIn) :
178 2059 : pathDB{gArgs.GetDataDirNet() / fs::u8path(strFilenameIn)},
179 2059 : strFilename{strFilenameIn},
180 2059 : strMagicMessage{strMagicMessageIn}
181 2059 : {
182 4118 : }
183 :
184 0 : [[nodiscard]] bool Load(T& objToLoad)
185 : {
186 0 : LogPrintf("Reading info from %s...\n", strFilename);
187 0 : return Read(objToLoad);
188 0 : }
189 :
190 0 : bool Store(const T& objToSave)
191 : {
192 0 : LogPrintf("Verifying %s format...\n", strFilename);
193 0 : T tmpObjToLoad;
194 0 : if (!Read(tmpObjToLoad)) return false;
195 :
196 0 : const auto start{SteadyClock::now()};
197 :
198 0 : LogPrintf("Writing info to %s...\n", strFilename);
199 0 : const bool ret = CoreWrite(objToSave);
200 0 : LogPrintf("%s dump finished %dms\n", strFilename, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
201 :
202 0 : return ret;
203 0 : }
204 : };
205 :
206 :
207 : #endif // BITCOIN_FLAT_DATABASE_H
|