Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1001}1002{1003BufferedWriter fileout{file};1005// Write index header1006fileout << GetParams().MessageStart() << blockundo_size;1007pos.nPos += STORAGE_HEADER_BYTES;1008{1009// Calculate checksum1010HashWriter hasher{};1011hasher << block.pprev->GetBlockHash() << blockundo;1012// Write undo data & checksum1013fileout << blockundo << hasher.GetHash();1014}1015// BufferedWriter will flush pending data to file when fileout goes out of scope.1016}1018// Make sure that the file is closed before we call `FlushUndoFile`.1019if (file.fclose() != 0) {1020LogError("Failed to close block undo file %s: %s", pos.ToString(), SysErrorString(errno));1021return FatalError(m_opts.notifications, state, _("Failed to close block undo file."));1022}1024// rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)1025// we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height1026// in the block file info as below; note that this does not catch the case where the undo writes are keeping up1027// with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in1028// the FindNextBlockPos function1029if (pos.nFile < cursor.file_num && static_cast<uint32_t>(block.nHeight) == m_blockfile_info[pos.nFile].nHeightLast) {1030// Do not propagate the return code, a failed flush here should not1031// be an indication for a failed write. If it were propagated here,1032// the caller would assume the undo data not to be written, when in1033// fact it is. Note though, that a failed flush might leave the data1034// file untrimmed.1035if (!FlushUndoFile(pos.nFile, true)) {1036LogWarning("Failed to flush undo file %05i\n", pos.nFile);1037}1038} else if (pos.nFile == cursor.file_num && block.nHeight > cursor.undo_height) {1039cursor.undo_height = block.nHeight;1040}1041// update nUndoPos in block index1042block.nUndoPos = pos.nPos;1043block.nStatus |= BLOCK_HAVE_UNDO;1044m_dirty_blockindex.insert(&block);1045}1047return true;1048}1050bool BlockManager::ReadBlock(CBlock& block, const FlatFilePos& pos, const std::optional<uint256>& expected_hash) const1051{1052block.SetNull();1054// Open history file to read1055const auto block_data{ReadRawBlock(pos)};1056if (!block_data) {1057return false;1058}1060try {1061// Read block1062SpanReader{*block_data} >> TX_WITH_WITNESS(block);1063} catch (const std::exception& e) {1064LogError("Deserialize or I/O error - %s at %s while reading block", e.what(), pos.ToString());1065return false;1066}1068const auto block_hash{block.GetHash()};1070// Check the header1071if (!CheckProofOfWork(block_hash, block.nBits, GetConsensus())) {1072LogError("Errors in block header at %s while reading block", pos.ToString());1073return false;1074}1076// Signet only: check block solution1077if (GetConsensus().signet_blocks && !CheckSignetBlockSolution(block, GetConsensus())) {1078LogError("Errors in block solution at %s while reading block", pos.ToString());1079return false;1080}1082if (expected_hash && block_hash != *expected_hash) {1083LogError("GetHash() doesn't match index at %s while reading block (%s != %s)",1084pos.ToString(), block_hash.ToString(), expected_hash->ToString());1085return false;1086}1088return true;1089}1091bool BlockManager::ReadBlock(CBlock& block, const CBlockIndex& index) const1092{1093const FlatFilePos block_pos{WITH_LOCK(cs_main, return index.GetBlockPos())};1094return ReadBlock(block, block_pos, index.GetBlockHash());1095}1097BlockManager::ReadRawBlockResult BlockManager::ReadRawBlock(const FlatFilePos& pos, std::optional<std::pair<size_t, size_t>> block_part) const1098{1099if (pos.nPos < STORAGE_HEADER_BYTES) {1100// If nPos is less than STORAGE_HEADER_BYTES, we can't read the header that precedes the block data1101// This would cause an unsigned integer underflow when trying to position the file cursor1102// This can happen after pruning or default constructed positions1103LogError("Failed for %s while reading raw block storage header", pos.ToString());1104return util::Unexpected{ReadRawError::IO};1105}1106AutoFile filein{OpenBlockFile({pos.nFile, pos.nPos - STORAGE_HEADER_BYTES}, /*fReadOnly=*/true)};1107if (filein.IsNull()) {1108LogError("OpenBlockFile failed for %s while reading raw block", pos.ToString());1109return util::Unexpected{ReadRawError::IO};1110}1112try {1113MessageStartChars blk_start;1114unsigned int blk_size;1116filein >> blk_start >> blk_size;1118if (blk_start != GetParams().MessageStart()) {1119LogError("Block magic mismatch for %s: %s versus expected %s while reading raw block",1120pos.ToString(), HexStr(blk_start), HexStr(GetParams().MessageStart()));1121return util::Unexpected{ReadRawError::IO};1122}1124if (blk_size > MAX_SIZE) {1125LogError("Block data is larger than maximum deserialization size for %s: %s versus %s while reading raw block",1126pos.ToString(), blk_size, MAX_SIZE);1127return util::Unexpected{ReadRawError::IO};1128}1130if (block_part) {1131const auto [offset, size]{*block_part};1132if (size == 0 || SaturatingAdd(offset, size) > blk_size) {1133return util::Unexpected{ReadRawError::BadPartRange}; // Avoid logging - offset/size come from untrusted REST input1134}1135filein.seek(offset, SEEK_CUR);1136blk_size = size;1137}1139std::vector<std::byte> data(blk_size); // Zeroing of memory is intentional here1140filein.read(data);1141return data;1142} catch (const std::exception& e) {1143LogError("Read from block file failed: %s for %s while reading raw block", e.what(), pos.ToString());1144return util::Unexpected{ReadRawError::IO};1145}1146}1148FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight)1149{1150AssertLockHeld(::cs_main);1151const unsigned int block_size{static_cast<unsigned int>(GetSerializeSize(TX_WITH_WITNESS(block)))};1152FlatFilePos pos{FindNextBlockPos(block_size + STORAGE_HEADER_BYTES, nHeight, block.GetBlockTime())};1153if (pos.IsNull()) {1154LogError("FindNextBlockPos failed for %s while writing block", pos.ToString());1155return FlatFilePos();1156}1157AutoFile file{OpenBlockFile(pos, /*fReadOnly=*/false)};1158if (file.IsNull()) {1159LogError("OpenBlockFile failed for %s while writing block", pos.ToString());1160m_opts.notifications.fatalError(_("Failed to write block."));1161return FlatFilePos();1162}1163{1164BufferedWriter fileout{file};1166// Write index header1167fileout << GetParams().MessageStart() << block_size;1168pos.nPos += STORAGE_HEADER_BYTES;1169// Write block1170fileout << TX_WITH_WITNESS(block);1171}1173if (file.fclose() != 0) {1174LogError("Failed to close block file %s: %s", pos.ToString(), SysErrorString(errno));1175m_opts.notifications.fatalError(_("Failed to close file when writing block."));1176return FlatFilePos();1177}1179return pos;1180}1182static auto InitBlocksdirXorKey(const BlockManager::Options& opts)1183{1184// Bytes are serialized without length indicator, so this is also the exact1185// size of the XOR-key file.1186std::array<std::byte, Obfuscation::KEY_SIZE> obfuscation{};1188// Consider this to be the first run if the blocksdir contains only hidden1189// files (those which start with a .). Checking for a fully-empty dir would1190// be too aggressive as a .lock file may have already been written.1191bool first_run = true;1192for (const auto& entry : fs::directory_iterator(opts.blocks_dir)) {1193const std::string path = fs::PathToString(entry.path().filename());1194if (!entry.is_regular_file() || !path.starts_with('.')) {1195first_run = false;1196break;1197}1198}1200if (opts.use_xor && first_run) {1201// Only use random fresh key when the boolean option is set and on the1202// very first start of the program.1203FastRandomContext{}.fillrand(obfuscation);1204}1206const fs::path xor_key_path{opts.blocks_dir / "xor.dat"};1207if (fs::exists(xor_key_path)) {1208// A pre-existing xor key file has priority.1209AutoFile xor_key_file{fsbridge::fopen(xor_key_path, "rb")};1210xor_key_file >> obfuscation;1211} else {1212// Create initial or missing xor key file1213AutoFile xor_key_file{fsbridge::fopen(xor_key_path,1214#ifdef __MINGW64__1215"wb" // Temporary workaround for https://github.com/bitcoin/bitcoin/issues/302101216#else1217"wbx"1218#endif1219)};1220xor_key_file << obfuscation;1221if (xor_key_file.fclose() != 0) {1222throw std::runtime_error{strprintf("Error closing XOR key file %s: %s",1223fs::PathToString(xor_key_path),1224SysErrorString(errno))};1225}1226}1227// If the user disabled the key, it must be zero.1228if (!opts.use_xor && obfuscation != decltype(obfuscation){}) {1229throw std::runtime_error{1230strprintf("The blocksdir XOR-key can not be disabled when a random key was already stored! "1231"Stored key: '%s', stored path: '%s'.",1232HexStr(obfuscation), fs::PathToString(xor_key_path)),1233};1234}1235LogInfo("Using obfuscation key for blocksdir *.dat files (%s): '%s'\n", fs::PathToString(opts.blocks_dir), HexStr(obfuscation));1236return Obfuscation{obfuscation};1237}1239BlockManager::BlockManager(const util::SignalInterrupt& interrupt, Options opts)1240: m_prune_mode{opts.prune_target > 0},1241m_obfuscation{InitBlocksdirXorKey(opts)},1242m_opts{std::move(opts)},1243m_block_file_seq{FlatFileSeq{m_opts.blocks_dir, "blk", m_opts.fast_prune ? 0x4000 /* 16kB */ : BLOCKFILE_CHUNK_SIZE}},1244m_undo_file_seq{FlatFileSeq{m_opts.blocks_dir, "rev", UNDOFILE_CHUNK_SIZE}},1245m_interrupt{interrupt}1246{1247m_block_tree_db = std::make_unique<BlockTreeDB>(m_opts.block_tree_db_params);1249if (m_opts.block_tree_db_params.wipe_data) {1250m_block_tree_db->WriteReindexing(true);