Repository: bitcoin/bitcoin
File: src/node/blockstorage.cpp (C++)

Code before:
    AutoFile filein{OpenBlockFile({pos.nFile, pos.nPos - STORAGE_HEADER_BYTES}, /*fReadOnly=*/true)};
    if (filein.IsNull()) {
        LogError("OpenBlockFile failed for %s while reading raw block", pos.ToString());
        return false;
    }

    try {
        MessageStartChars blk_start;
        unsigned int blk_size;

        filein >> blk_start >> blk_size;

        if (blk_start != GetParams().MessageStart()) {
            LogError("Block magic mismatch for %s: %s versus expected %s while reading raw block",
                pos.ToString(), HexStr(blk_start), HexStr(GetParams().MessageStart()));
            return false;
        }

        if (blk_size > MAX_SIZE) {
            LogError("Block data is larger than maximum deserialization size for %s: %s versus %s while reading raw block",
                pos.ToString(), blk_size, MAX_SIZE);
            return false;
        }

        if (part_offset > blk_size) {
            LogError("Read from block file failed: invalid offset: %u", part_offset);
            return false;
        }

        size_t size = blk_size - part_offset;
        if (part_size.has_value()) {
            if (*part_size > size || *part_size == 0) {
                LogError("Read from block file failed: invalid size: %u", *part_size);
                return false;
            }
            size = *part_size;
        }


        data.resize(size); // Zeroing of memory is intentional here
        if (part_offset > 0) filein.seek(part_offset, SEEK_CUR);
        filein.read(data);
    } catch (const std::exception& e) {
        LogError("Read from block file failed: %s for %s while reading raw block", e.what(), pos.ToString());
        return false;
    }

    return true;
}

FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight)

Review comment (bug):
the rest interface should be fine to expose to remote untrusted parties, no? So i am not sure about allowing them to unconditionally log confusing errors to the debug log.

i think it would be better if the caller could decide if there really was a storage corruption and an error should be logged, or if the user just had a typo in the rest url.

One way to achieve this, would be by returning a failure value.

Unfortunately, `util::Result` does not yet allow this:  #25665. So maybe for now, the return value could be a `std::optional<ReadRawError>`?

```cpp
struct ReadRawError{
  std::string msg;
  bool invalid_range;
};
```

Code after:
    if (filein.IsNull()) {
        LogError("OpenBlockFile failed for %s while reading raw block", pos.ToString());
        return util::Unexpected{ReadRawError::IO};
    }

    try {
        MessageStartChars blk_start;
        unsigned int blk_size;

        filein >> blk_start >> blk_size;

        if (blk_start != GetParams().MessageStart()) {
            LogError("Block magic mismatch for %s: %s versus expected %s while reading raw block",
                pos.ToString(), HexStr(blk_start), HexStr(GetParams().MessageStart()));
            return util::Unexpected{ReadRawError::IO};
        }

        if (blk_size > MAX_SIZE) {
            LogError("Block data is larger than maximum deserialization size for %s: %s versus %s while reading raw block",
                pos.ToString(), blk_size, MAX_SIZE);
            return util::Unexpected{ReadRawError::IO};
        }

        if (block_part) {
            const auto [offset, size]{*block_part};
            if (size == 0 || offset >= blk_size || size > blk_size - offset) {
                return util::Unexpected{ReadRawError::BadPartRange}; // Avoid logging - offset/size come from untrusted REST input
            }
            filein.seek(offset, SEEK_CUR);
            blk_size = size;
        }

        std::vector<std::byte> data(blk_size); // Zeroing of memory is intentional here
        filein.read(data);
        return data;
    } catch (const std::exception& e) {
        LogError("Read from block file failed: %s for %s while reading raw block", e.what(), pos.ToString());
        return util::Unexpected{ReadRawError::IO};
    }
}

FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight)
{
    const unsigned int block_size{static_cast<unsigned int>(GetSerializeSize(TX_WITH_WITNESS(block)))};
    FlatFilePos pos{FindNextBlockPos(block_size + STORAGE_HEADER_BYTES, nHeight, block.GetBlockTime())};
    if (pos.IsNull()) {
        LogError("FindNextBlockPos failed for %s while writing block", pos.ToString());
        return FlatFilePos();
    }
    AutoFile file{OpenBlockFile(pos, /*fReadOnly=*/false)};
    if (file.IsNull()) {