6
6
7
7
#include < chain.h>
8
8
#include < chainparams.h>
9
+ #include < clientversion.h>
10
+ #include < consensus/validation.h>
9
11
#include < flatfile.h>
10
12
#include < fs.h>
13
+ #include < hash.h>
11
14
#include < pow.h>
12
15
#include < shutdown.h>
13
16
#include < signet.h>
14
17
#include < streams.h>
18
+ #include < undo.h>
15
19
#include < util/system.h>
16
20
#include < validation.h>
17
21
@@ -38,6 +42,10 @@ std::set<CBlockIndex*> setDirtyBlockIndex;
38
42
std::set<int > setDirtyFileInfo;
39
43
// } // namespace
40
44
45
+ static FILE* OpenUndoFile (const FlatFilePos &pos, bool fReadOnly = false );
46
+ static FlatFileSeq BlockFileSeq ();
47
+ static FlatFileSeq UndoFileSeq ();
48
+
41
49
bool IsBlockPruned (const CBlockIndex* pblockindex)
42
50
{
43
51
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0 );
@@ -84,8 +92,217 @@ void CleanupBlockRevFiles()
84
92
}
85
93
}
86
94
87
- // From validation. TODO move here
88
- bool FindBlockPos (FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false );
95
+ std::string CBlockFileInfo::ToString () const
96
+ {
97
+ return strprintf (" CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)" , nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date (nTimeFirst), FormatISO8601Date (nTimeLast));
98
+ }
99
+
100
+ CBlockFileInfo* GetBlockFileInfo (size_t n)
101
+ {
102
+ LOCK (cs_LastBlockFile);
103
+
104
+ return &vinfoBlockFile.at (n);
105
+ }
106
+
107
+ static bool UndoWriteToDisk (const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
108
+ {
109
+ // Open history file to append
110
+ CAutoFile fileout (OpenUndoFile (pos), SER_DISK, CLIENT_VERSION);
111
+ if (fileout.IsNull ())
112
+ return error (" %s: OpenUndoFile failed" , __func__);
113
+
114
+ // Write index header
115
+ unsigned int nSize = GetSerializeSize (blockundo, fileout.GetVersion ());
116
+ fileout << messageStart << nSize;
117
+
118
+ // Write undo data
119
+ long fileOutPos = ftell (fileout.Get ());
120
+ if (fileOutPos < 0 )
121
+ return error (" %s: ftell failed" , __func__);
122
+ pos.nPos = (unsigned int )fileOutPos;
123
+ fileout << blockundo;
124
+
125
+ // calculate & write checksum
126
+ CHashWriter hasher (SER_GETHASH, PROTOCOL_VERSION);
127
+ hasher << hashBlock;
128
+ hasher << blockundo;
129
+ fileout << hasher.GetHash ();
130
+
131
+ return true ;
132
+ }
133
+
134
+ bool UndoReadFromDisk (CBlockUndo& blockundo, const CBlockIndex* pindex)
135
+ {
136
+ FlatFilePos pos = pindex->GetUndoPos ();
137
+ if (pos.IsNull ()) {
138
+ return error (" %s: no undo data available" , __func__);
139
+ }
140
+
141
+ // Open history file to read
142
+ CAutoFile filein (OpenUndoFile (pos, true ), SER_DISK, CLIENT_VERSION);
143
+ if (filein.IsNull ())
144
+ return error (" %s: OpenUndoFile failed" , __func__);
145
+
146
+ // Read block
147
+ uint256 hashChecksum;
148
+ CHashVerifier<CAutoFile> verifier (&filein); // We need a CHashVerifier as reserializing may lose data
149
+ try {
150
+ verifier << pindex->pprev ->GetBlockHash ();
151
+ verifier >> blockundo;
152
+ filein >> hashChecksum;
153
+ }
154
+ catch (const std::exception& e) {
155
+ return error (" %s: Deserialize or I/O error - %s" , __func__, e.what ());
156
+ }
157
+
158
+ // Verify checksum
159
+ if (hashChecksum != verifier.GetHash ())
160
+ return error (" %s: Checksum mismatch" , __func__);
161
+
162
+ return true ;
163
+ }
164
+
165
+ static void FlushUndoFile (int block_file, bool finalize = false )
166
+ {
167
+ FlatFilePos undo_pos_old (block_file, vinfoBlockFile[block_file].nUndoSize );
168
+ if (!UndoFileSeq ().Flush (undo_pos_old, finalize)) {
169
+ AbortNode (" Flushing undo file to disk failed. This is likely the result of an I/O error." );
170
+ }
171
+ }
172
+
173
+ void FlushBlockFile (bool fFinalize = false , bool finalize_undo = false )
174
+ {
175
+ LOCK (cs_LastBlockFile);
176
+ FlatFilePos block_pos_old (nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize );
177
+ if (!BlockFileSeq ().Flush (block_pos_old, fFinalize )) {
178
+ AbortNode (" Flushing block file to disk failed. This is likely the result of an I/O error." );
179
+ }
180
+ // we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
181
+ // e.g. during IBD or a sync after a node going offline
182
+ if (!fFinalize || finalize_undo) FlushUndoFile (nLastBlockFile, finalize_undo);
183
+ }
184
+
185
+ uint64_t CalculateCurrentUsage ()
186
+ {
187
+ LOCK (cs_LastBlockFile);
188
+
189
+ uint64_t retval = 0 ;
190
+ for (const CBlockFileInfo &file : vinfoBlockFile) {
191
+ retval += file.nSize + file.nUndoSize ;
192
+ }
193
+ return retval;
194
+ }
195
+
196
+ void UnlinkPrunedFiles (const std::set<int >& setFilesToPrune)
197
+ {
198
+ for (std::set<int >::iterator it = setFilesToPrune.begin (); it != setFilesToPrune.end (); ++it) {
199
+ FlatFilePos pos (*it, 0 );
200
+ fs::remove (BlockFileSeq ().FileName (pos));
201
+ fs::remove (UndoFileSeq ().FileName (pos));
202
+ LogPrintf (" Prune: %s deleted blk/rev (%05u)\n " , __func__, *it);
203
+ }
204
+ }
205
+
206
+ static FlatFileSeq BlockFileSeq ()
207
+ {
208
+ return FlatFileSeq (gArgs .GetBlocksDirPath (), " blk" , gArgs .GetBoolArg (" -fastprune" , false ) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE);
209
+ }
210
+
211
+ static FlatFileSeq UndoFileSeq ()
212
+ {
213
+ return FlatFileSeq (gArgs .GetBlocksDirPath (), " rev" , UNDOFILE_CHUNK_SIZE);
214
+ }
215
+
216
+ FILE* OpenBlockFile (const FlatFilePos &pos, bool fReadOnly ) {
217
+ return BlockFileSeq ().Open (pos, fReadOnly );
218
+ }
219
+
220
+ /* * Open an undo file (rev?????.dat) */
221
+ static FILE* OpenUndoFile (const FlatFilePos &pos, bool fReadOnly ) {
222
+ return UndoFileSeq ().Open (pos, fReadOnly );
223
+ }
224
+
225
+ fs::path GetBlockPosFilename (const FlatFilePos &pos)
226
+ {
227
+ return BlockFileSeq ().FileName (pos);
228
+ }
229
+
230
+ bool FindBlockPos (FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false )
231
+ {
232
+ LOCK (cs_LastBlockFile);
233
+
234
+ unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
235
+ if (vinfoBlockFile.size () <= nFile) {
236
+ vinfoBlockFile.resize (nFile + 1 );
237
+ }
238
+
239
+ bool finalize_undo = false ;
240
+ if (!fKnown ) {
241
+ while (vinfoBlockFile[nFile].nSize + nAddSize >= (gArgs .GetBoolArg (" -fastprune" , false ) ? 0x10000 /* 64kb */ : MAX_BLOCKFILE_SIZE)) {
242
+ // when the undo file is keeping up with the block file, we want to flush it explicitly
243
+ // when it is lagging behind (more blocks arrive than are being connected), we let the
244
+ // undo block write case handle it
245
+ assert (std::addressof (::ChainActive ()) == std::addressof (active_chain));
246
+ finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int )active_chain.Tip ()->nHeight );
247
+ nFile++;
248
+ if (vinfoBlockFile.size () <= nFile) {
249
+ vinfoBlockFile.resize (nFile + 1 );
250
+ }
251
+ }
252
+ pos.nFile = nFile;
253
+ pos.nPos = vinfoBlockFile[nFile].nSize ;
254
+ }
255
+
256
+ if ((int )nFile != nLastBlockFile) {
257
+ if (!fKnown ) {
258
+ LogPrint (BCLog::VALIDATION, " Leaving block file %i: %s\n " , nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString ());
259
+ }
260
+ FlushBlockFile (!fKnown , finalize_undo);
261
+ nLastBlockFile = nFile;
262
+ }
263
+
264
+ vinfoBlockFile[nFile].AddBlock (nHeight, nTime);
265
+ if (fKnown )
266
+ vinfoBlockFile[nFile].nSize = std::max (pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize );
267
+ else
268
+ vinfoBlockFile[nFile].nSize += nAddSize;
269
+
270
+ if (!fKnown ) {
271
+ bool out_of_space;
272
+ size_t bytes_allocated = BlockFileSeq ().Allocate (pos, nAddSize, out_of_space);
273
+ if (out_of_space) {
274
+ return AbortNode (" Disk space is too low!" , _ (" Disk space is too low!" ));
275
+ }
276
+ if (bytes_allocated != 0 && fPruneMode ) {
277
+ fCheckForPruning = true ;
278
+ }
279
+ }
280
+
281
+ setDirtyFileInfo.insert (nFile);
282
+ return true ;
283
+ }
284
+
285
+ static bool FindUndoPos (BlockValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize)
286
+ {
287
+ pos.nFile = nFile;
288
+
289
+ LOCK (cs_LastBlockFile);
290
+
291
+ pos.nPos = vinfoBlockFile[nFile].nUndoSize ;
292
+ vinfoBlockFile[nFile].nUndoSize += nAddSize;
293
+ setDirtyFileInfo.insert (nFile);
294
+
295
+ bool out_of_space;
296
+ size_t bytes_allocated = UndoFileSeq ().Allocate (pos, nAddSize, out_of_space);
297
+ if (out_of_space) {
298
+ return AbortNode (state, " Disk space is too low!" , _ (" Disk space is too low!" ));
299
+ }
300
+ if (bytes_allocated != 0 && fPruneMode ) {
301
+ fCheckForPruning = true ;
302
+ }
303
+
304
+ return true ;
305
+ }
89
306
90
307
static bool WriteBlockToDisk (const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart)
91
308
{
@@ -110,6 +327,33 @@ static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessa
110
327
return true ;
111
328
}
112
329
330
+ bool WriteUndoDataForBlock (const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
331
+ {
332
+ // Write undo information to disk
333
+ if (pindex->GetUndoPos ().IsNull ()) {
334
+ FlatFilePos _pos;
335
+ if (!FindUndoPos (state, pindex->nFile , _pos, ::GetSerializeSize (blockundo, CLIENT_VERSION) + 40 ))
336
+ return error (" ConnectBlock(): FindUndoPos failed" );
337
+ if (!UndoWriteToDisk (blockundo, _pos, pindex->pprev ->GetBlockHash (), chainparams.MessageStart ()))
338
+ return AbortNode (state, " Failed to write undo data" );
339
+ // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
340
+ // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
341
+ // in the block file info as below; note that this does not catch the case where the undo writes are keeping up
342
+ // with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
343
+ // the FindBlockPos function
344
+ if (_pos.nFile < nLastBlockFile && static_cast <uint32_t >(pindex->nHeight ) == vinfoBlockFile[_pos.nFile ].nHeightLast ) {
345
+ FlushUndoFile (_pos.nFile , true );
346
+ }
347
+
348
+ // update nUndoPos in block index
349
+ pindex->nUndoPos = _pos.nPos ;
350
+ pindex->nStatus |= BLOCK_HAVE_UNDO;
351
+ setDirtyBlockIndex.insert (pindex);
352
+ }
353
+
354
+ return true ;
355
+ }
356
+
113
357
bool ReadBlockFromDisk (CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams)
114
358
{
115
359
block.SetNull ();
0 commit comments