66
77#include < chain.h>
88#include < chainparams.h>
9+ #include < clientversion.h>
10+ #include < consensus/validation.h>
911#include < flatfile.h>
1012#include < fs.h>
13+ #include < hash.h>
1114#include < pow.h>
1215#include < shutdown.h>
1316#include < signet.h>
1417#include < streams.h>
18+ #include < undo.h>
1519#include < util/system.h>
1620#include < validation.h>
1721
@@ -38,6 +42,10 @@ std::set<CBlockIndex*> setDirtyBlockIndex;
3842std::set<int > setDirtyFileInfo;
3943// } // namespace
4044
45+ static FILE* OpenUndoFile (const FlatFilePos &pos, bool fReadOnly = false );
46+ static FlatFileSeq BlockFileSeq ();
47+ static FlatFileSeq UndoFileSeq ();
48+
4149bool IsBlockPruned (const CBlockIndex* pblockindex)
4250{
4351 return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0 );
@@ -84,8 +92,217 @@ void CleanupBlockRevFiles()
8492 }
8593}
8694
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+ }
89306
90307static bool WriteBlockToDisk (const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart)
91308{
@@ -110,6 +327,33 @@ static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessa
110327 return true ;
111328}
112329
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+
113357bool ReadBlockFromDisk (CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams)
114358{
115359 block.SetNull ();
0 commit comments