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

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

    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 <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

    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.

  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

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

    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.

  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:

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

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

    from:

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

    to:

    0    CMerkleBlock merkleBlock;
    1    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:

      0diff --git a/src/bench/readwriteblock.cpp b/src/bench/readwriteblock.cpp
      1index 690818f659..b8e226c6eb 100644
      2--- a/src/bench/readwriteblock.cpp
      3+++ b/src/bench/readwriteblock.cpp
      4@@ -21,9 +21,8 @@
      5 
      6 static CBlock CreateTestBlock()
      7 {
      8-    SpanReader stream{benchmark::data::block413567};
      9     CBlock block;
     10-    stream >> TX_WITH_WITNESS(block);
     11+    SpanReader{benchmark::data::block413567} >> TX_WITH_WITNESS(block);
     12     return block;
     13 }
     14 
     15diff --git a/src/core_io.cpp b/src/core_io.cpp
     16index 094f1ec8cc..ec446debee 100644
     17--- a/src/core_io.cpp
     18+++ b/src/core_io.cpp
     19@@ -239,9 +239,8 @@ bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header)
     20     if (!IsHex(hex_header)) return false;
     21 
     22     const std::vector<unsigned char> header_data{ParseHex(hex_header)};
     23-    SpanReader ser_header{header_data};
     24     try {
     25-        ser_header >> header;
     26+        SpanReader{header_data} >> header;
     27     } catch (const std::exception&) {
     28         return false;
     29     }
     30@@ -254,9 +253,8 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
     31         return false;
     32 
     33     std::vector<unsigned char> blockData(ParseHex(strHexBlk));
     34-    SpanReader ssBlock{blockData};
     35     try {
     36-        ssBlock >> TX_WITH_WITNESS(block);
     37+        SpanReader{blockData} >> TX_WITH_WITNESS(block);
     38     }
     39     catch (const std::exception&) {
     40         return false;
     41diff --git a/src/rest.cpp b/src/rest.cpp
     42index 4ecd0f451e..9d73c1e604 100644
     43--- a/src/rest.cpp
     44+++ b/src/rest.cpp
     45@@ -451,8 +451,7 @@ static bool rest_block(const std::any& context,
     46     case RESTResponseFormat::JSON: {
     47         if (tx_verbosity) {
     48             CBlock block{};
     49-            SpanReader block_stream{*block_data};
     50-            block_stream >> TX_WITH_WITNESS(block);
     51+            SpanReader{*block_data} >> TX_WITH_WITNESS(block);
     52             UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, *tx_verbosity, chainman.GetConsensus().powLimit);
     53             std::string strJSON = objBlock.write() + "\n";
     54             req->WriteHeader("Content-Type", "application/json");
     55diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
     56index 6d4ced7364..d5c9987697 100644
     57--- a/src/rpc/blockchain.cpp
     58+++ b/src/rpc/blockchain.cpp
     59@@ -832,9 +832,8 @@ static RPCHelpMan getblock()
     60         return HexStr(block_data);
     61     }
     62 
     63-    SpanReader block_stream{block_data};
     64     CBlock block{};
     65-    block_stream >> TX_WITH_WITNESS(block);
     66+    SpanReader{block_data} >> TX_WITH_WITNESS(block);
     67 
     68     TxVerbosity tx_verbosity;
     69     if (verbosity == 1) {
     70diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp
     71index 6e45cc251f..7b87442985 100644
     72--- a/src/test/fuzz/block.cpp
     73+++ b/src/test/fuzz/block.cpp
     74@@ -24,10 +24,9 @@ void initialize_block()
     75 
     76 FUZZ_TARGET(block, .init = initialize_block)
     77 {
     78-    SpanReader ds{buffer};
     79     CBlock block;
     80     try {
     81-        ds >> TX_WITH_WITNESS(block);
     82+        SpanReader{buffer} >> TX_WITH_WITNESS(block);
     83     } catch (const std::ios_base::failure&) {
     84         return;
     85     }
     86diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
     87index 820538a54b..70cad07d69 100644
     88--- a/src/test/fuzz/deserialize.cpp
     89+++ b/src/test/fuzz/deserialize.cpp
     90@@ -81,9 +81,8 @@ T Deserialize(DataStream&& ds, const P& params)
     91 template <typename T, typename P>
     92 void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj, const P& params)
     93 {
     94-    SpanReader ds{buffer};
     95     try {
     96-        ds >> params(obj);
     97+        SpanReader{buffer} >> params(obj);
     98     } catch (const std::ios_base::failure&) {
     99         throw invalid_fuzzing_input_exception();
    100     }
    101@@ -109,9 +108,8 @@ T Deserialize(DataStream ds)
    102 template <typename T>
    103 void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj)
    104 {
    105-    SpanReader ds{buffer};
    106     try {
    107-        ds >> obj;
    108+        SpanReader{buffer} >> obj;
    109     } catch (const std::ios_base::failure&) {
    110         throw invalid_fuzzing_input_exception();
    111     }
    112diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
    113index 224cd9d9b7..8f9c942666 100644
    114--- a/src/test/fuzz/transaction.cpp
    115+++ b/src/test/fuzz/transaction.cpp
    116@@ -41,10 +41,9 @@ FUZZ_TARGET(transaction, .init = initialize_transaction)
    117         }
    118     }();
    119     bool valid_mutable_tx = true;
    120-    SpanReader ds_mtx{buffer};
    121     CMutableTransaction mutable_tx;
    122     try {
    123-        ds_mtx >> TX_WITH_WITNESS(mutable_tx);
    124+        SpanReader{buffer} >> TX_WITH_WITNESS(mutable_tx);
    125     } catch (const std::ios_base::failure&) {
    126         valid_mutable_tx = false;
    127     }
    128diff --git a/src/test/fuzz/tx_in.cpp b/src/test/fuzz/tx_in.cpp
    129index 4146d96162..10618d60e0 100644
    130--- a/src/test/fuzz/tx_in.cpp
    131+++ b/src/test/fuzz/tx_in.cpp
    132@@ -13,10 +13,9 @@
    133 
    134 FUZZ_TARGET(tx_in)
    135 {
    136-    SpanReader ds{buffer};
    137     CTxIn tx_in;
    138     try {
    139-        ds >> tx_in;
    140+        SpanReader{buffer} >> tx_in;
    141     } catch (const std::ios_base::failure&) {
    142         return;
    143     }
    144diff --git a/src/test/fuzz/tx_out.cpp b/src/test/fuzz/tx_out.cpp
    145index 1c1016eb92..ce39d14f1a 100644
    146--- a/src/test/fuzz/tx_out.cpp
    147+++ b/src/test/fuzz/tx_out.cpp
    148@@ -12,10 +12,9 @@
    149 
    150 FUZZ_TARGET(tx_out)
    151 {
    152-    SpanReader ds{buffer};
    153     CTxOut tx_out;
    154     try {
    155-        ds >> tx_out;
    156+        SpanReader{buffer} >> tx_out;
    157     } catch (const std::ios_base::failure&) {
    158         return;
    159     }
    160diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
    161index e55e1e6f03..136728a043 100644
    162--- a/src/test/net_tests.cpp
    163+++ b/src/test/net_tests.cpp
    164@@ -872,10 +872,9 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
    165                                         std::span<const unsigned char> data,
    166                                         bool is_incoming) -> void {
    167         if (!is_incoming && msg_type == "addr") {
    168-            SpanReader s{data};
    169             std::vector<CAddress> addresses;
    170 
    171-            s >> CAddress::V1_NETWORK(addresses);
    172+            SpanReader{data} >> CAddress::V1_NETWORK(addresses);
    173 
    174             for (const auto& addr : addresses) {
    175                 if (addr == expected) {
    

    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:

      0diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
      1index 938aa233ed..8e02cfd08c 100644
      2--- a/src/test/bloom_tests.cpp
      3+++ b/src/test/bloom_tests.cpp
      4@@ -110,7 +110,7 @@ BOOST_AUTO_TEST_CASE(bloom_match)
      5     // and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
      6     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};
      7     std::vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
      8-    DataStream spendStream{vch};
      9+    SpanReader spendStream{vch};
     10     CTransaction spendingTx(deserialize, TX_WITH_WITNESS, spendStream);
     11 
     12     CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
     13diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
     14index 4d6ee6613e..8c0756d852 100644
     15--- a/src/test/coins_tests.cpp
     16+++ b/src/test/coins_tests.cpp
     17@@ -517,37 +517,33 @@ BOOST_FIXTURE_TEST_CASE(updatecoins_simulation_test, UpdateTest)
     18 BOOST_AUTO_TEST_CASE(ccoins_serialization)
     19 {
     20     // Good example
     21-    DataStream ss1{"97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35"_hex};
     22     Coin cc1;
     23-    ss1 >> cc1;
     24+    SpanReader{"97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35"_hex} >> cc1;
     25     BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
     26     BOOST_CHECK_EQUAL(cc1.nHeight, 203998U);
     27     BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000});
     28     BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160("816115944e077fe7c803cfa57f29b36bf87c1d35"_hex_u8)))));
     29 
     30     // Good example
     31-    DataStream ss2{"8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"_hex};
     32     Coin cc2;
     33-    ss2 >> cc2;
     34+    SpanReader{"8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"_hex} >> cc2;
     35     BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
     36     BOOST_CHECK_EQUAL(cc2.nHeight, 120891U);
     37     BOOST_CHECK_EQUAL(cc2.out.nValue, 110397);
     38     BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"_hex_u8)))));
     39 
     40     // Smallest possible example
     41-    DataStream ss3{"000006"_hex};
     42     Coin cc3;
     43-    ss3 >> cc3;
     44+    SpanReader{"000006"_hex} >> cc3;
     45     BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
     46     BOOST_CHECK_EQUAL(cc3.nHeight, 0U);
     47     BOOST_CHECK_EQUAL(cc3.out.nValue, 0);
     48     BOOST_CHECK_EQUAL(cc3.out.scriptPubKey.size(), 0U);
     49 
     50     // scriptPubKey that ends beyond the end of the stream
     51-    DataStream ss4{"000007"_hex};
     52     try {
     53         Coin cc4;
     54-        ss4 >> cc4;
     55+        SpanReader{"000007"_hex} >> cc4;
     56         BOOST_CHECK_MESSAGE(false, "We should have thrown");
     57     } catch (const std::ios_base::failure&) {
     58     }
     59@@ -557,10 +553,9 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
     60     uint64_t x = 3000000000ULL;
     61     tmp << VARINT(x);
     62     BOOST_CHECK_EQUAL(HexStr(tmp), "8a95c0bb00");
     63-    DataStream ss5{"00008a95c0bb00"_hex};
     64     try {
     65         Coin cc5;
     66-        ss5 >> cc5;
     67+        SpanReader{"00008a95c0bb00"_hex} >> cc5;
     68         BOOST_CHECK_MESSAGE(false, "We should have thrown");
     69     } catch (const std::ios_base::failure&) {
     70     }
     71diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
     72index 88260b272f..c9ab49078a 100644
     73--- a/src/test/fuzz/addrman.cpp
     74+++ b/src/test/fuzz/addrman.cpp
     75@@ -9,6 +9,7 @@
     76 #include <common/args.h>
     77 #include <merkleblock.h>
     78 #include <random.h>
     79+#include <streams.h>
     80 #include <test/fuzz/FuzzedDataProvider.h>
     81 #include <test/fuzz/fuzz.h>
     82 #include <test/fuzz/util.h>
     83@@ -121,9 +122,8 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
     84     auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio());
     85     if (fuzzed_data_provider.ConsumeBool()) {
     86         const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
     87-        DataStream ds{serialized_data};
     88         try {
     89-            ds >> *addr_man_ptr;
     90+            SpanReader{serialized_data} >> *addr_man_ptr;
     91         } catch (const std::ios_base::failure&) {
     92             addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio());
     93         }
     94diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
     95index 4a8b7b2155..933baa4810 100644
     96--- a/src/test/fuzz/connman.cpp
     97+++ b/src/test/fuzz/connman.cpp
     98@@ -9,6 +9,7 @@
     99 #include <net_processing.h>
    100 #include <netaddress.h>
    101 #include <protocol.h>
    102+#include <streams.h>
    103 #include <test/fuzz/FuzzedDataProvider.h>
    104 #include <test/fuzz/fuzz.h>
    105 #include <test/fuzz/util.h>
    106@@ -45,9 +46,8 @@ FUZZ_TARGET(connman, .init = initialize_connman)
    107     auto addr_man_ptr{std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio())};
    108     if (fuzzed_data_provider.ConsumeBool()) {
    109         const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
    110-        DataStream ds{serialized_data};
    111         try {
    112-            ds >> *addr_man_ptr;
    113+            SpanReader{serialized_data} >> *addr_man_ptr;
    114         } catch (const std::ios_base::failure&) {
    115             addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio());
    116         }
    117diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
    118index 722ba2eb69..f765c57f04 100644
    119--- a/src/test/netbase_tests.cpp
    120+++ b/src/test/netbase_tests.cpp
    121@@ -575,10 +575,9 @@ BOOST_AUTO_TEST_CASE(caddress_serialize_v1)
    122 
    123 BOOST_AUTO_TEST_CASE(caddress_unserialize_v1)
    124 {
    125-    DataStream s{ParseHex(stream_addrv1_hex)};
    126     std::vector<CAddress> addresses_unserialized;
    127 
    128-    s >> CAddress::V1_NETWORK(addresses_unserialized);
    129+    SpanReader{ParseHex(stream_addrv1_hex)} >> CAddress::V1_NETWORK(addresses_unserialized);
    130     BOOST_CHECK(fixture_addresses == addresses_unserialized);
    131 }
    132 
    133@@ -592,10 +591,9 @@ BOOST_AUTO_TEST_CASE(caddress_serialize_v2)
    134 
    135 BOOST_AUTO_TEST_CASE(caddress_unserialize_v2)
    136 {
    137-    DataStream s{ParseHex(stream_addrv2_hex)};
    138     std::vector<CAddress> addresses_unserialized;
    139 
    140-    s >> CAddress::V2_NETWORK(addresses_unserialized);
    141+    SpanReader{ParseHex(stream_addrv2_hex)} >> CAddress::V2_NETWORK(addresses_unserialized);
    142     BOOST_CHECK(fixture_addresses == addresses_unserialized);
    143 }
    144 
    145diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
    146index 4272821640..5d65c35a8c 100644
    147--- a/src/test/sighash_tests.cpp
    148+++ b/src/test/sighash_tests.cpp
    149@@ -189,8 +189,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
    150           nHashType = test[3].getInt<int>();
    151           sigHashHex = test[4].get_str();
    152 
    153-          DataStream stream(ParseHex(raw_tx));
    154-          stream >> TX_WITH_WITNESS(tx);
    155+          SpanReader{ParseHex(raw_tx)} >> TX_WITH_WITNESS(tx);
    156 
    157           TxValidationState state;
    158           BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest);
    159diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
    160index 3919f226d6..d0be15a00d 100644
    161--- a/src/test/transaction_tests.cpp
    162+++ b/src/test/transaction_tests.cpp
    163@@ -377,9 +377,8 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
    164     // Random real transaction (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
    165     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};
    166     std::vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
    167-    DataStream stream(vch);
    168     CMutableTransaction tx;
    169-    stream >> TX_WITH_WITNESS(tx);
    170+    SpanReader{vch} >> TX_WITH_WITNESS(tx);
    171     TxValidationState state;
    172     BOOST_CHECK_MESSAGE(CheckTransaction(CTransaction(tx), state) && state.IsValid(), "Simple deserialized transaction should be valid.");
    173 
    
  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:

     0diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
     1index 88260b272f..0d77ddfb3a 100644
     2--- a/src/test/fuzz/addrman.cpp
     3+++ b/src/test/fuzz/addrman.cpp
     4@@ -11,2 +11,3 @@
     5 #include <random.h>
     6+#include <streams.h>
     7 #include <test/fuzz/FuzzedDataProvider.h>
     8@@ -123,5 +124,4 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
     9         const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
    10-        DataStream ds{serialized_data};
    11         try {
    12-            ds >> *addr_man_ptr;
    13+            DataStream{serialized_data} >> *addr_man_ptr;
    14         } catch (const std::ios_base::failure&) {
    15diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
    16index 4a8b7b2155..90a798501f 100644
    17--- a/src/test/fuzz/connman.cpp
    18+++ b/src/test/fuzz/connman.cpp
    19@@ -11,2 +11,3 @@
    20 #include <protocol.h>
    21+#include <streams.h>
    22 #include <test/fuzz/FuzzedDataProvider.h>
    23@@ -47,5 +48,4 @@ FUZZ_TARGET(connman, .init = initialize_connman)
    24         const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
    25-        DataStream ds{serialized_data};
    26         try {
    27-            ds >> *addr_man_ptr;
    28+            DataStream{serialized_data} >> *addr_man_ptr;
    29         } 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-02-11 21:13 UTC

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