refactor: Use SpanReader over DataStream #34483

pull maflcko wants to merge 6 commits into bitcoin:master from maflcko:2602-span-reader changing 30 files +49 −71
  1. maflcko commented at 3:54 PM on February 2, 2026: member

    This changes all places, where possible, to use SpanReader over DataStream. This makes the code easier to read and reason about, because SpanReader can never write data. Also, the code should be minimally faster, because it avoids a full redundant copy of the whole vector of bytes.

  2. test: Read debug log for self-checking comment
    The DataStream comment was a bit stale, because it was using
    CDataStream.
    
    Fix it by using assert_debug_log for a self-documenting and
    self-checking test code.
    fa879db735
  3. DrahtBot renamed this:
    refactor: Use SpanReader over DataStream
    refactor: Use SpanReader over DataStream
    on Feb 2, 2026
  4. DrahtBot added the label Refactoring on Feb 2, 2026
  5. DrahtBot commented at 3:55 PM on February 2, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK janb84, sipa, stickies-v, achow101
    Concept ACK davidgumberg
    Stale ACK darosior

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #34132 (coins: fail fast on database read deserialization errors by l0rinc)
    • #32554 (bench: replace embedded raw block with configurable block generator by l0rinc)
    • #31682 ([IBD] specialize CheckBlock's input & coinbase checks by l0rinc)
    • #31449 (coins,refactor: Reduce getblockstats RPC UTXO overhead estimation by l0rinc)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  6. in src/streams.h:103 in fa498e4512 outdated
      99 | @@ -100,6 +100,7 @@ class SpanReader
     100 |  
     101 |      size_t size() const { return m_data.size(); }
     102 |      bool empty() const { return m_data.empty(); }
     103 | +    bool eof() const { return m_data.empty(); }
    


    darosior commented at 4:06 PM on February 2, 2026:

    Is it not less confusing to change the one call site (that you are already touching) to use empty() rather than introducing an eof method to a class that is not reading from a file?


    maflcko commented at 4:14 PM on February 2, 2026:

    Then, I'd have to add empty() to the "streams interface", at which point the eof() alias can be removed. Let me push that ...


    maflcko commented at 6:36 PM on February 2, 2026:

    (done)

  7. darosior commented at 4:07 PM on February 2, 2026: member

    Code looks good to me, just got one question.

  8. stickies-v commented at 4:38 PM on February 2, 2026: contributor

    Concept ACK

  9. refactor: Use empty() over eof() in the streams interface
    End-of-file does not really make sense for streams that wrap buffers. So
    replace it by the equivalent empty() checks.
    fa20bc2ec2
  10. DrahtBot added the label CI failed on Feb 2, 2026
  11. DrahtBot commented at 5:09 PM on February 2, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task fuzzer,address,undefined,integer: https://github.com/bitcoin/bitcoin/actions/runs/21597138806/job/62232912845</sub> <sub>LLM reason (✨ experimental): AddressSanitizer heap-buffer-overflow in fuzz target during seed corpus processing.</sub>

    <details><summary>Hints</summary>

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

    </details>

  12. refactor: Avoid UB in SpanReader::ignore
    Currently std::span::subspan is called without checking the size first.
    
    This is UB, unless the std lib is hardened.
    
    With a hardened stdlib, the program aborts:
    
    > include/c++/v1/span:512: libc++ Hardening assertion __offset <= size()
    > failed: span<T>::subspan(offset, count): offset out of range
    
    Fix the UB and the abort by using the implementation from DataStream,
    which throws when hitting end-of-data.
    
    This commit should not change any behavior, because the UB is currently
    unreachable. Also, the newly added throw should properly be caught by
    any code that calls any streams function.
    fabd4d2e2e
  13. refactor: [qt] Use SpanReader to avoid two vector copies fa06e26764
  14. refactor: Use SpanReader over DataStream
    The mutable temporary strValue can be re-used to apply the obfuscation,
    which allows to avoid a redundant copy of the value.
    fad3eb3956
  15. maflcko force-pushed on Feb 2, 2026
  16. maflcko force-pushed on Feb 2, 2026
  17. maflcko commented at 5:51 PM on February 2, 2026: member

    Hmm, I guess there is some unrelated pre-existing UB. This isn't remote-triggerable, so I fixed it in a "refactor" commit.

    introduced in commit 82a379eca82, which I reviewed :(

    godbolt: https://godbolt.org/z/YKsavqGqx

  18. janb84 commented at 7:54 PM on February 2, 2026: contributor

    Concept ACK fa52b210e03879386e5582114cfefa51ec61396d

    Did an extensive search, most are converted. Listed below are some leftovers, maybe intentional not converted?

    Possible NIT;

    /src/rpc/txoutproof.cpp : L147-149

    from:

    DataStream ssMB{ParseHexV(request.params[0], "proof")};
    CMerkleBlock merkleBlock;
    ssMB >> merkleBlock;
    
    CMerkleBlock merkleBlock;
    SpanReader{ParseHexV(request.params[0], "proof")} >> merkleBlock;
    

    or src/wallet/rpc/backup.cpp:L60-62

    from:

        DataStream ssMB{ParseHexV(request.params[1], "proof")};
        CMerkleBlock merkleBlock;
        ssMB >> merkleBlock;
    

    to:

        CMerkleBlock merkleBlock;
        SpanReader{ParseHexV(request.params[1], "proof")} >> merkleBlock;
    
  19. DrahtBot removed the label CI failed on Feb 2, 2026
  20. maflcko force-pushed on Feb 2, 2026
  21. sipa commented at 8:19 PM on February 2, 2026: member

    Concept ACK

  22. DrahtBot added the label CI failed on Feb 2, 2026
  23. maflcko closed this on Feb 3, 2026

  24. maflcko reopened this on Feb 3, 2026

  25. DrahtBot removed the label CI failed on Feb 3, 2026
  26. davidgumberg commented at 8:19 AM on February 3, 2026: contributor

    Concept ACK. I think this should bring some measurable performance improvements because SpanReader does not use the zero after free allocator that DataStream does, which zero's memory and prevents compiler optimizations in order to guarantee that memory is zeroed. I came to the conclusion in #30987 at the time that there was nowhere DataStream was used at that time where one should be worried about data being left in memory after destruction.

  27. maflcko commented at 8:32 AM on February 3, 2026: member

    Just to clarify: This should have no impact on IBD time, because only the dbwrapper is changed, but I don't expect that to give any end-to-end performance improvement.

    When i tested this on btck_block_create last month, it gave a 1%-5% speed-up. I guess for all fuzz targets, the speedup is also shadowed by the remainder of the fuzz target. That is I don't expect the block fuzz target to be end-to-end faster at all.

    This is mostly a style cleanup and the UB fix doesn't seem too bad either.

  28. l0rinc commented at 11:58 AM on February 3, 2026: contributor

    this should bring some measurable performance improvements because SpanReader does not use the zero after free allocator that DataStream does

    I have measured something similar in https://github.com/l0rinc/bitcoin/pull/31 but couldn't find any difference in higher level benchmarks. The difference is only really measurable in debug mode and in the narrow micro-bench of not zeroing DataStream bytes on destruction, but it's probably optimized away on higher level.

  29. darosior approved
  30. darosior commented at 10:17 PM on February 3, 2026: member

    utACK fae650c2950f3b6d99eb7c062389de0bf6a64ac9

  31. DrahtBot requested review from stickies-v on Feb 3, 2026
  32. DrahtBot requested review from davidgumberg on Feb 3, 2026
  33. DrahtBot requested review from janb84 on Feb 3, 2026
  34. DrahtBot requested review from sipa on Feb 3, 2026
  35. in src/rpc/blockchain.cpp:835 in fae650c295
     831 | @@ -832,7 +832,7 @@ static RPCHelpMan getblock()
     832 |          return HexStr(block_data);
     833 |      }
     834 |  
     835 | -    DataStream block_stream{block_data};
     836 | +    SpanReader block_stream{block_data};
    


    stickies-v commented at 1:37 PM on February 5, 2026:

    nit: a bunch more of these can be inlined, avoiding the variables named "stream" etc:

    <details> <summary>git diff on fae650c295</summary>

    diff --git a/src/bench/readwriteblock.cpp b/src/bench/readwriteblock.cpp
    index 690818f659..b8e226c6eb 100644
    --- a/src/bench/readwriteblock.cpp
    +++ b/src/bench/readwriteblock.cpp
    @@ -21,9 +21,8 @@
     
     static CBlock CreateTestBlock()
     {
    -    SpanReader stream{benchmark::data::block413567};
         CBlock block;
    -    stream >> TX_WITH_WITNESS(block);
    +    SpanReader{benchmark::data::block413567} >> TX_WITH_WITNESS(block);
         return block;
     }
     
    diff --git a/src/core_io.cpp b/src/core_io.cpp
    index 094f1ec8cc..ec446debee 100644
    --- a/src/core_io.cpp
    +++ b/src/core_io.cpp
    @@ -239,9 +239,8 @@ bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header)
         if (!IsHex(hex_header)) return false;
     
         const std::vector<unsigned char> header_data{ParseHex(hex_header)};
    -    SpanReader ser_header{header_data};
         try {
    -        ser_header >> header;
    +        SpanReader{header_data} >> header;
         } catch (const std::exception&) {
             return false;
         }
    @@ -254,9 +253,8 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
             return false;
     
         std::vector<unsigned char> blockData(ParseHex(strHexBlk));
    -    SpanReader ssBlock{blockData};
         try {
    -        ssBlock >> TX_WITH_WITNESS(block);
    +        SpanReader{blockData} >> TX_WITH_WITNESS(block);
         }
         catch (const std::exception&) {
             return false;
    diff --git a/src/rest.cpp b/src/rest.cpp
    index 4ecd0f451e..9d73c1e604 100644
    --- a/src/rest.cpp
    +++ b/src/rest.cpp
    @@ -451,8 +451,7 @@ static bool rest_block(const std::any& context,
         case RESTResponseFormat::JSON: {
             if (tx_verbosity) {
                 CBlock block{};
    -            SpanReader block_stream{*block_data};
    -            block_stream >> TX_WITH_WITNESS(block);
    +            SpanReader{*block_data} >> TX_WITH_WITNESS(block);
                 UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, *tx_verbosity, chainman.GetConsensus().powLimit);
                 std::string strJSON = objBlock.write() + "\n";
                 req->WriteHeader("Content-Type", "application/json");
    diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
    index 6d4ced7364..d5c9987697 100644
    --- a/src/rpc/blockchain.cpp
    +++ b/src/rpc/blockchain.cpp
    @@ -832,9 +832,8 @@ static RPCHelpMan getblock()
             return HexStr(block_data);
         }
     
    -    SpanReader block_stream{block_data};
         CBlock block{};
    -    block_stream >> TX_WITH_WITNESS(block);
    +    SpanReader{block_data} >> TX_WITH_WITNESS(block);
     
         TxVerbosity tx_verbosity;
         if (verbosity == 1) {
    diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp
    index 6e45cc251f..7b87442985 100644
    --- a/src/test/fuzz/block.cpp
    +++ b/src/test/fuzz/block.cpp
    @@ -24,10 +24,9 @@ void initialize_block()
     
     FUZZ_TARGET(block, .init = initialize_block)
     {
    -    SpanReader ds{buffer};
         CBlock block;
         try {
    -        ds >> TX_WITH_WITNESS(block);
    +        SpanReader{buffer} >> TX_WITH_WITNESS(block);
         } catch (const std::ios_base::failure&) {
             return;
         }
    diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
    index 820538a54b..70cad07d69 100644
    --- a/src/test/fuzz/deserialize.cpp
    +++ b/src/test/fuzz/deserialize.cpp
    @@ -81,9 +81,8 @@ T Deserialize(DataStream&& ds, const P& params)
     template <typename T, typename P>
     void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj, const P& params)
     {
    -    SpanReader ds{buffer};
         try {
    -        ds >> params(obj);
    +        SpanReader{buffer} >> params(obj);
         } catch (const std::ios_base::failure&) {
             throw invalid_fuzzing_input_exception();
         }
    @@ -109,9 +108,8 @@ T Deserialize(DataStream ds)
     template <typename T>
     void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj)
     {
    -    SpanReader ds{buffer};
         try {
    -        ds >> obj;
    +        SpanReader{buffer} >> obj;
         } catch (const std::ios_base::failure&) {
             throw invalid_fuzzing_input_exception();
         }
    diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
    index 224cd9d9b7..8f9c942666 100644
    --- a/src/test/fuzz/transaction.cpp
    +++ b/src/test/fuzz/transaction.cpp
    @@ -41,10 +41,9 @@ FUZZ_TARGET(transaction, .init = initialize_transaction)
             }
         }();
         bool valid_mutable_tx = true;
    -    SpanReader ds_mtx{buffer};
         CMutableTransaction mutable_tx;
         try {
    -        ds_mtx >> TX_WITH_WITNESS(mutable_tx);
    +        SpanReader{buffer} >> TX_WITH_WITNESS(mutable_tx);
         } catch (const std::ios_base::failure&) {
             valid_mutable_tx = false;
         }
    diff --git a/src/test/fuzz/tx_in.cpp b/src/test/fuzz/tx_in.cpp
    index 4146d96162..10618d60e0 100644
    --- a/src/test/fuzz/tx_in.cpp
    +++ b/src/test/fuzz/tx_in.cpp
    @@ -13,10 +13,9 @@
     
     FUZZ_TARGET(tx_in)
     {
    -    SpanReader ds{buffer};
         CTxIn tx_in;
         try {
    -        ds >> tx_in;
    +        SpanReader{buffer} >> tx_in;
         } catch (const std::ios_base::failure&) {
             return;
         }
    diff --git a/src/test/fuzz/tx_out.cpp b/src/test/fuzz/tx_out.cpp
    index 1c1016eb92..ce39d14f1a 100644
    --- a/src/test/fuzz/tx_out.cpp
    +++ b/src/test/fuzz/tx_out.cpp
    @@ -12,10 +12,9 @@
     
     FUZZ_TARGET(tx_out)
     {
    -    SpanReader ds{buffer};
         CTxOut tx_out;
         try {
    -        ds >> tx_out;
    +        SpanReader{buffer} >> tx_out;
         } catch (const std::ios_base::failure&) {
             return;
         }
    diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
    index e55e1e6f03..136728a043 100644
    --- a/src/test/net_tests.cpp
    +++ b/src/test/net_tests.cpp
    @@ -872,10 +872,9 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
                                             std::span<const unsigned char> data,
                                             bool is_incoming) -> void {
             if (!is_incoming && msg_type == "addr") {
    -            SpanReader s{data};
                 std::vector<CAddress> addresses;
     
    -            s >> CAddress::V1_NETWORK(addresses);
    +            SpanReader{data} >> CAddress::V1_NETWORK(addresses);
     
                 for (const auto& addr : addresses) {
                     if (addr == expected) {
    
    

    </details>


    maflcko commented at 6:23 PM on February 5, 2026:

    sure, and thx, pushed your diff as-is

  36. stickies-v approved
  37. stickies-v commented at 2:53 PM on February 5, 2026: contributor

    ACK fae650c2950f3b6d99eb7c062389de0bf6a64ac9

    Found a few more instances that could be replaced:

    <details> <summary>git diff on fae650c295</summary>

    diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
    index 938aa233ed..8e02cfd08c 100644
    --- a/src/test/bloom_tests.cpp
    +++ b/src/test/bloom_tests.cpp
    @@ -110,7 +110,7 @@ BOOST_AUTO_TEST_CASE(bloom_match)
         // and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
         unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
         std::vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
    -    DataStream spendStream{vch};
    +    SpanReader spendStream{vch};
         CTransaction spendingTx(deserialize, TX_WITH_WITNESS, spendStream);
     
         CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
    diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
    index 4d6ee6613e..8c0756d852 100644
    --- a/src/test/coins_tests.cpp
    +++ b/src/test/coins_tests.cpp
    @@ -517,37 +517,33 @@ BOOST_FIXTURE_TEST_CASE(updatecoins_simulation_test, UpdateTest)
     BOOST_AUTO_TEST_CASE(ccoins_serialization)
     {
         // Good example
    -    DataStream ss1{"97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35"_hex};
         Coin cc1;
    -    ss1 >> cc1;
    +    SpanReader{"97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35"_hex} >> cc1;
         BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
         BOOST_CHECK_EQUAL(cc1.nHeight, 203998U);
         BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000});
         BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160("816115944e077fe7c803cfa57f29b36bf87c1d35"_hex_u8)))));
     
         // Good example
    -    DataStream ss2{"8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"_hex};
         Coin cc2;
    -    ss2 >> cc2;
    +    SpanReader{"8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"_hex} >> cc2;
         BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
         BOOST_CHECK_EQUAL(cc2.nHeight, 120891U);
         BOOST_CHECK_EQUAL(cc2.out.nValue, 110397);
         BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"_hex_u8)))));
     
         // Smallest possible example
    -    DataStream ss3{"000006"_hex};
         Coin cc3;
    -    ss3 >> cc3;
    +    SpanReader{"000006"_hex} >> cc3;
         BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
         BOOST_CHECK_EQUAL(cc3.nHeight, 0U);
         BOOST_CHECK_EQUAL(cc3.out.nValue, 0);
         BOOST_CHECK_EQUAL(cc3.out.scriptPubKey.size(), 0U);
     
         // scriptPubKey that ends beyond the end of the stream
    -    DataStream ss4{"000007"_hex};
         try {
             Coin cc4;
    -        ss4 >> cc4;
    +        SpanReader{"000007"_hex} >> cc4;
             BOOST_CHECK_MESSAGE(false, "We should have thrown");
         } catch (const std::ios_base::failure&) {
         }
    @@ -557,10 +553,9 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
         uint64_t x = 3000000000ULL;
         tmp << VARINT(x);
         BOOST_CHECK_EQUAL(HexStr(tmp), "8a95c0bb00");
    -    DataStream ss5{"00008a95c0bb00"_hex};
         try {
             Coin cc5;
    -        ss5 >> cc5;
    +        SpanReader{"00008a95c0bb00"_hex} >> cc5;
             BOOST_CHECK_MESSAGE(false, "We should have thrown");
         } catch (const std::ios_base::failure&) {
         }
    diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
    index 88260b272f..c9ab49078a 100644
    --- a/src/test/fuzz/addrman.cpp
    +++ b/src/test/fuzz/addrman.cpp
    @@ -9,6 +9,7 @@
     #include <common/args.h>
     #include <merkleblock.h>
     #include <random.h>
    +#include <streams.h>
     #include <test/fuzz/FuzzedDataProvider.h>
     #include <test/fuzz/fuzz.h>
     #include <test/fuzz/util.h>
    @@ -121,9 +122,8 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
         auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio());
         if (fuzzed_data_provider.ConsumeBool()) {
             const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
    -        DataStream ds{serialized_data};
             try {
    -            ds >> *addr_man_ptr;
    +            SpanReader{serialized_data} >> *addr_man_ptr;
             } catch (const std::ios_base::failure&) {
                 addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio());
             }
    diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
    index 4a8b7b2155..933baa4810 100644
    --- a/src/test/fuzz/connman.cpp
    +++ b/src/test/fuzz/connman.cpp
    @@ -9,6 +9,7 @@
     #include <net_processing.h>
     #include <netaddress.h>
     #include <protocol.h>
    +#include <streams.h>
     #include <test/fuzz/FuzzedDataProvider.h>
     #include <test/fuzz/fuzz.h>
     #include <test/fuzz/util.h>
    @@ -45,9 +46,8 @@ FUZZ_TARGET(connman, .init = initialize_connman)
         auto addr_man_ptr{std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio())};
         if (fuzzed_data_provider.ConsumeBool()) {
             const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
    -        DataStream ds{serialized_data};
             try {
    -            ds >> *addr_man_ptr;
    +            SpanReader{serialized_data} >> *addr_man_ptr;
             } catch (const std::ios_base::failure&) {
                 addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio());
             }
    diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
    index 722ba2eb69..f765c57f04 100644
    --- a/src/test/netbase_tests.cpp
    +++ b/src/test/netbase_tests.cpp
    @@ -575,10 +575,9 @@ BOOST_AUTO_TEST_CASE(caddress_serialize_v1)
     
     BOOST_AUTO_TEST_CASE(caddress_unserialize_v1)
     {
    -    DataStream s{ParseHex(stream_addrv1_hex)};
         std::vector<CAddress> addresses_unserialized;
     
    -    s >> CAddress::V1_NETWORK(addresses_unserialized);
    +    SpanReader{ParseHex(stream_addrv1_hex)} >> CAddress::V1_NETWORK(addresses_unserialized);
         BOOST_CHECK(fixture_addresses == addresses_unserialized);
     }
     
    @@ -592,10 +591,9 @@ BOOST_AUTO_TEST_CASE(caddress_serialize_v2)
     
     BOOST_AUTO_TEST_CASE(caddress_unserialize_v2)
     {
    -    DataStream s{ParseHex(stream_addrv2_hex)};
         std::vector<CAddress> addresses_unserialized;
     
    -    s >> CAddress::V2_NETWORK(addresses_unserialized);
    +    SpanReader{ParseHex(stream_addrv2_hex)} >> CAddress::V2_NETWORK(addresses_unserialized);
         BOOST_CHECK(fixture_addresses == addresses_unserialized);
     }
     
    diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
    index 4272821640..5d65c35a8c 100644
    --- a/src/test/sighash_tests.cpp
    +++ b/src/test/sighash_tests.cpp
    @@ -189,8 +189,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
               nHashType = test[3].getInt<int>();
               sigHashHex = test[4].get_str();
     
    -          DataStream stream(ParseHex(raw_tx));
    -          stream >> TX_WITH_WITNESS(tx);
    +          SpanReader{ParseHex(raw_tx)} >> TX_WITH_WITNESS(tx);
     
               TxValidationState state;
               BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest);
    diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
    index 3919f226d6..d0be15a00d 100644
    --- a/src/test/transaction_tests.cpp
    +++ b/src/test/transaction_tests.cpp
    @@ -377,9 +377,8 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
         // Random real transaction (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
         unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
         std::vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
    -    DataStream stream(vch);
         CMutableTransaction tx;
    -    stream >> TX_WITH_WITNESS(tx);
    +    SpanReader{vch} >> TX_WITH_WITNESS(tx);
         TxValidationState state;
         BOOST_CHECK_MESSAGE(CheckTransaction(CTransaction(tx), state) && state.IsValid(), "Simple deserialized transaction should be valid.");
     
    
    

    </details>

  38. maflcko force-pushed on Feb 5, 2026
  39. stickies-v commented at 8:38 PM on February 5, 2026: contributor

    re-ACK faaf01fbd87644d597cb691b535ba5c24544efa5

  40. DrahtBot requested review from darosior on Feb 5, 2026
  41. refactor: Use SpanReader over DataStream
    This refactor does not change behavior. However, it avoids a vector
    copy, which can lead to a minimal speed-up of 1%-5%, depending on the
    call-site. This is mostly relevant for the fuzz tests and utils that
    read large blobs of data (like a full block).
    fa0677d131
  42. maflcko force-pushed on Feb 6, 2026
  43. maflcko commented at 7:30 AM on February 6, 2026: member

    sry, didn't realize there were two diffs. I pushed the other one as well. However addrman has DataStream in it's interface, so this replacement is more involved and this diff does not compile as-is:

    diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
    index 88260b272f..0d77ddfb3a 100644
    --- a/src/test/fuzz/addrman.cpp
    +++ b/src/test/fuzz/addrman.cpp
    @@ -11,2 +11,3 @@
     #include <random.h>
    +#include <streams.h>
     #include <test/fuzz/FuzzedDataProvider.h>
    @@ -123,5 +124,4 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
             const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
    -        DataStream ds{serialized_data};
             try {
    -            ds >> *addr_man_ptr;
    +            DataStream{serialized_data} >> *addr_man_ptr;
             } catch (const std::ios_base::failure&) {
    diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
    index 4a8b7b2155..90a798501f 100644
    --- a/src/test/fuzz/connman.cpp
    +++ b/src/test/fuzz/connman.cpp
    @@ -11,2 +11,3 @@
     #include <protocol.h>
    +#include <streams.h>
     #include <test/fuzz/FuzzedDataProvider.h>
    @@ -47,5 +48,4 @@ FUZZ_TARGET(connman, .init = initialize_connman)
             const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
    -        DataStream ds{serialized_data};
             try {
    -            ds >> *addr_man_ptr;
    +            DataStream{serialized_data} >> *addr_man_ptr;
             } catch (const std::ios_base::failure&) {
    
  44. janb84 commented at 4:01 PM on February 6, 2026: contributor

    re ACK fa0677d131191d7db9868c4c1b3d780cb6991226

  45. DrahtBot requested review from stickies-v on Feb 6, 2026
  46. sipa commented at 4:42 PM on February 6, 2026: member

    crACK fa0677d131191d7db9868c4c1b3d780cb6991226

  47. stickies-v commented at 11:50 PM on February 6, 2026: contributor

    re-ACK fa0677d131191d7db9868c4c1b3d780cb6991226

    However addrman has DataStream in it's interface, so this replacement is more involved and this diff does not compile as-is:

    Apologies, wasn't compiling the fuzz binary so I missed this, you're right.

  48. achow101 commented at 1:48 AM on February 7, 2026: member

    ACK fa0677d131191d7db9868c4c1b3d780cb6991226

  49. achow101 merged this on Feb 7, 2026
  50. achow101 closed this on Feb 7, 2026

  51. maflcko deleted the branch on Feb 9, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-22 06:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me