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 14118 : [[nodiscard]] bool CoreWrite(const T& objToSave)
39 : {
40 : // LOCK(objToSave.cs);
41 :
42 14118 : const auto start{SteadyClock::now()};
43 :
44 : // serialize, checksum data up to that point, then append checksum
45 14118 : CDataStream ssObj(SER_DISK, CLIENT_VERSION);
46 14118 : ssObj << strMagicMessage; // specific magic message for this type of object
47 14118 : ssObj << Params().MessageStart(); // network specific magic number
48 14118 : ssObj << objToSave;
49 14118 : uint256 hash = Hash(ssObj);
50 14118 : ssObj << hash;
51 :
52 : // open output file, and associate with AutoFile
53 14118 : FILE *file = fsbridge::fopen(pathDB, "wb");
54 14118 : AutoFile fileout{file};
55 14118 : 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 14118 : fileout << ssObj;
62 14118 : }
63 : catch (std::exception &e) {
64 0 : return error("%s: Serialize or I/O error - %s", __func__, e.what());
65 0 : }
66 14118 : fileout.fclose();
67 :
68 14118 : LogPrintf("Written info to %s %dms\n", strFilename, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
69 14118 : LogPrintf(" %s\n", objToSave.ToString());
70 :
71 14118 : return true;
72 14118 : }
73 :
74 22886 : [[nodiscard]] ReadResult CoreRead(T& objToLoad)
75 : {
76 : //LOCK(objToLoad.cs);
77 :
78 22886 : const auto start{SteadyClock::now()};
79 : // open input file, and associate with AutoFile
80 22886 : FILE *file = fsbridge::fopen(pathDB, "rb");
81 22886 : AutoFile filein{file};
82 22886 : if (filein.IsNull()) {
83 : // It is not actually error, maybe it's a first initialization of core.
84 9848 : return ReadResult::FileError;
85 : }
86 :
87 : // use file size to size memory buffer
88 13038 : int fileSize = fs::file_size(pathDB);
89 13038 : int dataSize = fileSize - sizeof(uint256);
90 : // Don't try to resize to a negative number if file is small
91 13038 : if (dataSize < 0)
92 0 : dataSize = 0;
93 13038 : std::vector<unsigned char> vchData;
94 13038 : vchData.resize(dataSize);
95 13038 : uint256 hashIn;
96 :
97 : // read data and checksum from file
98 : try {
99 13038 : filein.read(MakeWritableByteSpan(vchData));
100 13038 : filein >> hashIn;
101 13038 : }
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 13038 : filein.fclose();
107 :
108 13038 : CDataStream ssObj(vchData, SER_DISK, CLIENT_VERSION);
109 :
110 : // verify stored checksum matches input data
111 13038 : uint256 hashTmp = Hash(ssObj);
112 13038 : 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 13038 : std::string strMagicMessageTmp;
122 : // de-serialize file header (file specific magic message) and ..
123 13038 : ssObj >> strMagicMessageTmp;
124 :
125 : // ... verify the message matches predefined one
126 13038 : 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 13038 : ssObj >> pchMsgTmp;
135 :
136 : // ... verify the network matches ours
137 13038 : 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 13038 : ssObj >> objToLoad;
145 13038 : }
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 13038 : LogPrintf("Loaded info from %s %dms\n", strFilename, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
153 13038 : LogPrintf(" %s\n", objToLoad.ToString());
154 :
155 13038 : return ReadResult::Ok;
156 22886 : }
157 :
158 22886 : [[nodiscard]] bool Read(T& objToLoad)
159 : {
160 22886 : ReadResult readResult = CoreRead(objToLoad);
161 22886 : if (readResult == ReadResult::FileError)
162 9848 : LogPrintf("Missing file %s, will try to recreate\n", strFilename);
163 13038 : 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 22886 : return true;
174 22886 : }
175 :
176 : public:
177 27444 : CFlatDB(std::string&& strFilenameIn, std::string&& strMagicMessageIn) :
178 13722 : pathDB{gArgs.GetDataDirNet() / fs::u8path(strFilenameIn)},
179 13722 : strFilename{strFilenameIn},
180 13722 : strMagicMessage{strMagicMessageIn}
181 13722 : {
182 27444 : }
183 :
184 8768 : [[nodiscard]] bool Load(T& objToLoad)
185 : {
186 8768 : LogPrintf("Reading info from %s...\n", strFilename);
187 8768 : return Read(objToLoad);
188 0 : }
189 :
190 14118 : bool Store(const T& objToSave)
191 : {
192 14118 : LogPrintf("Verifying %s format...\n", strFilename);
193 14118 : T tmpObjToLoad;
194 14118 : if (!Read(tmpObjToLoad)) return false;
195 :
196 14118 : const auto start{SteadyClock::now()};
197 :
198 14118 : LogPrintf("Writing info to %s...\n", strFilename);
199 14118 : const bool ret = CoreWrite(objToSave);
200 14118 : LogPrintf("%s dump finished %dms\n", strFilename, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
201 :
202 14118 : return ret;
203 14118 : }
204 : };
205 :
206 :
207 : #endif // BITCOIN_FLAT_DATABASE_H
|