test: Add Compact Block Encoding test ReceiveWithExtraTransactions covering non-empty extra_txn #30237

pull AngusP wants to merge 3 commits into bitcoin:master from AngusP:2024-06-cmpctblk-extra-txn-test changing 5 files +91 −34
  1. AngusP commented at 11:17 am on June 6, 2024: contributor

    This test uses the extra_txn (vExtraTxnForCompact) vector of optional orphan/conflicted/etc. transactions to provide transactions to a PartiallyDownloadedBlock that are not otherwise present in the mempool, and check that they are used.

    This also covers a former nullptr deref bug that was fixed in #29752 (bf031a517c79cec5b43420bcd40291ab0e9f68a8) where the extra_txn vec/circular-buffer was null-initialized and not yet filled when dereferenced in PartiallyDownloadedBlock::InitData.

  2. test: refactor: Rename extra_txn to const empty_extra_txn as it is empty in all test cases 4621e7cc8f
  3. DrahtBot commented at 11:17 am on June 6, 2024: contributor

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

    Code Coverage

    For detailed information about the code coverage, see the test coverage report.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK marcofleon, dergoegge, glozow
    Concept ACK instagibbs

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #29625 (Several randomness improvements by sipa)

    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.

  4. DrahtBot added the label Tests on Jun 6, 2024
  5. test: Add ReceiveWithExtraTransactions Compact Block receive test.
    This new test uses the `vExtraTxnForCompact` (`extra_txn`) vector of
    optional orphan/conflicted/etc. transactions to provide a transaction
    in a compact block that was not otherwise present in our mempool.
    
    This also covers an improbable nullptr deref bug addressed in
    bf031a517c79cec5b43420bcd40291ab0e9f68a8 (#29752) where the
    `extra_txn` vec/circular-buffer was sometimes null-initialized and
    not yet filled when dereferenced in `PartiallyDownloadedBlock::InitData`.
    4c99301220
  6. AngusP force-pushed on Jun 6, 2024
  7. DrahtBot added the label CI failed on Jun 6, 2024
  8. DrahtBot commented at 12:08 pm on June 6, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is 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.

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

    Debug: https://github.com/bitcoin/bitcoin/runs/25888431395

  9. DrahtBot removed the label CI failed on Jun 6, 2024
  10. in src/test/blockencodings_tests.cpp:341 in 4c99301220 outdated
    336+        BOOST_CHECK( partial_block.IsTxAvailable(2));
    337+
    338+        // Add an unrelated tx to extra_txn:
    339+        extra_txn[0] = non_block_tx;
    340+        // and a tx from the block that's not in the mempool:
    341+        extra_txn[1] = block.vtx[1];
    


    instagibbs commented at 1:40 pm on June 6, 2024:
    I think another useful case is a transaction that’s also in your mempool

    AngusP commented at 2:05 pm on June 6, 2024:

    L325 has pool.addUnchecked(entry.FromTx(block.vtx[2])); which adds a tx from the block to the mempool, giving .IsTxAvailable(2) (.IsTxAvailable(0) should always be true as that’s the coinbase which is in the compact block header, IIUC).

    SimpleRoundTripTest and a couple others in here also already covered the add to mempool case.

  11. instagibbs commented at 1:40 pm on June 6, 2024: member
    concept ACK!
  12. dergoegge approved
  13. dergoegge commented at 3:18 pm on June 6, 2024: member

    Code review ACK 4c99301220ab44e98d0d0e1cc8d774d96a25b7aa

    I’ve verified that this can reproduce the nullptr deref bug by reverting a8203e94123b6ea6e4f4a6320e3ad20457f44a28 and making short id collisions more likely (see top commits on: https://github.com/dergoegge/bitcoin/commits/2024-06-cmpctblk-extra-txn-test/).

    0$ n=0; while (( n++ < 100 )); do src/test/test_bitcoin --run_test=blockencodings_tests/ReceiveWithExtraTransactions ; done
    1...
    2Running 1 test case...
    3unknown location(0): fatal error: in "blockencodings_tests/ReceiveWithExtraTransactions": memory access violation at address: 0x00000059: no mapping at fault address
    4../../src/test/blockencodings_tests.cpp(333): last checkpoint
    5
    6*** 1 failure is detected in the test module "Bitcoin Core Test Suite"
    7...
    

    While it might be extremely rare, I believe this test is flaky due to possible short id collisions. Flakes can be observed by cherry picking just https://github.com/dergoegge/bitcoin/commit/540fb4ed526839c10ea8b746505276d04e92fb6f (i.e. increasing the likelyhood of collisions):

     0$ n=0; while (( n++ < 100 )); do src/test/test_bitcoin --run_test=blockencodings_tests/ReceiveWithExtraTransactions ; done
     1...
     2Running 1 test case...
     3../../src/test/blockencodings_tests.cpp(347): error: in "blockencodings_tests/ReceiveWithExtraTransactions": check partial_block_with_extra.IsTxAvailable(2) has failed
     4
     5*** 1 failure is detected in the test module "Bitcoin Core Test Suite"
     6...
     7Running 1 test case...
     8../../src/test/blockencodings_tests.cpp(333): error: in "blockencodings_tests/ReceiveWithExtraTransactions": check partial_block.InitData(cmpctblock, extra_txn) == READ_STATUS_OK has failed
     9../../src/test/blockencodings_tests.cpp(336): error: in "blockencodings_tests/ReceiveWithExtraTransactions": check partial_block.IsTxAvailable(2) has failed
    10../../src/test/blockencodings_tests.cpp(343): error: in "blockencodings_tests/ReceiveWithExtraTransactions": check partial_block_with_extra.InitData(cmpctblock, extra_txn) == READ_STATUS_OK has failed
    11../../src/test/blockencodings_tests.cpp(346): error: in "blockencodings_tests/ReceiveWithExtraTransactions": check partial_block_with_extra.IsTxAvailable(1) has failed
    12../../src/test/blockencodings_tests.cpp(347): error: in "blockencodings_tests/ReceiveWithExtraTransactions": check partial_block_with_extra.IsTxAvailable(2) has failed
    13
    14*** 5 failures are detected in the test module "Bitcoin Core Test Suite"
    

    Not sure if that is worth addressing since it’s so rare but perhaps simply adding a comment that the test might fail every 2^48 runs is good?

  14. DrahtBot requested review from instagibbs on Jun 6, 2024
  15. AngusP commented at 7:51 am on June 7, 2024: contributor

    Not sure if that is worth addressing since it’s so rare but perhaps simply adding a comment that the test might fail every 2^48 runs is good?

    I think it’s worth avoiding, I don’t really like the notion of flaky tests maybe failing for a random reason, even if it is very improbable. I’d guess short-ID collisions would also affect some of the other existing tests in here so there’s additional benefit in them not being flaky too.

  16. glozow commented at 10:42 am on June 7, 2024: member

    Concept ACK

    Not sure if that is worth addressing since it’s so rare but perhaps simply adding a comment that the test might fail every 2^48 runs is good?

    I think it’s worth avoiding, I don’t really like the notion of flaky tests maybe failing for a random reason, even if it is very improbable. I’d guess short-ID collisions would also affect some of the other existing tests in here so there’s additional benefit in them not being flaky too.

    Not sure exactly how flaky it is and don’t want to blow this out of proportion. Comment sounds good to save a future debugger from headache. If we might hit it, you can knock it down closer to 0 by running it e.g. 5 times and asserting that it doesn’t fail every time, like in coinselector_tests: https://github.com/bitcoin/bitcoin/blob/4a020ca443ba370bf41583962d16aa8551876f53/src/wallet/test/coinselector_tests.cpp#L752-L777

  17. maflcko commented at 1:24 pm on June 7, 2024: member
    Would it be difficult to make the test deterministic?
  18. AngusP commented at 8:54 am on June 12, 2024: contributor

    Would it be difficult to make the test deterministic?

    Yes, the straightforward way would be to replace the InsecureRand256() with fixed hashes that don’t result in transaction short IDs that collide. Less-straightforward but maybe better would be to keep using random hashes but check for a short ID collision and try again with a different random hash until the collision is gone – or start with one random hash and change it in a way that guarantees the short ID (siphash) will be different, but I’m not sure if siphash is easily ‘attackable’ in that sense.

  19. maflcko commented at 9:01 am on June 12, 2024: member

    InsecureRand256

    I’d say longer them those global non-deterministic functions should go away anyway. Not something you need to do here, but if you want you can create a commit to accept a FastRandomContext reference as param to BuildBlockTestCase, then in each unit test, create a local FastRandomContext and bind it to a lambda named BuildBlockTestCase.

  20. AngusP commented at 9:36 pm on June 12, 2024: contributor

    Pushed 09b90c6 which makes the tests deterministic by using predictable ‘random’ numbers. I’ve jsut picked them, there’s nothing particularly special about their values.

    Using this to reproduce the issue @dergoegge pointed out:

     0diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
     1index 695e8d806a..64d635a97a 100644
     2--- a/src/blockencodings.cpp
     3+++ b/src/blockencodings.cpp
     4@@ -44,7 +44,8 @@ void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const {
     5
     6 uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const {
     7     static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids");
     8-    return SipHashUint256(shorttxidk0, shorttxidk1, wtxid) & 0xffffffffffffL;
     9+    // return SipHashUint256(shorttxidk0, shorttxidk1, wtxid) & 0xffffffffffffL;
    10+    return SipHashUint256(shorttxidk0, shorttxidk1, wtxid) & 0x0f;
    11 }
    

    and then running

    0set -e;
    1n=0;
    2while (( n++ < 5000 )); do
    3    src/test/test_bitcoin --run_test=blockencodings_tests;
    4done
    

    the failures were either READ_STATUS_FAILED from InitData, or IsTxAvailable(...) == false when we were expecting true.

    On the above-patched 09b90c6, I’ve not seen any failures with 0x0f short IDs and ~10k re-runs of all the blockencodings tests.

    • Use a new FastRandomContext with a fixed seed in each test, to ensure ‘random’ uint256s used as fake prevouts are deterministic, so in-turn test txids and short IDs are deterministic and don’t collide causing very rare but flaky test failures.
    • Add new test-only/internal initializer for CBlockHeaderAndShortTxIDs that takes a specified nonce to further ensure determinism and avoid rare but undesireable short ID collisions. In a test context this nonce is set to a fixed known-good value. Normally it is random, as previously.
  21. AngusP requested review from dergoegge on Jun 12, 2024
  22. dergoegge approved
  23. dergoegge commented at 10:35 am on June 17, 2024: member
    tACK 09b90c69bc17ce43dce3881a208d9066a729c67d
  24. DrahtBot requested review from glozow on Jun 17, 2024
  25. in src/blockencodings.h:115 in 09b90c69bc outdated
    108@@ -109,6 +109,9 @@ class CBlockHeaderAndShortTxIDs {
    109     // Dummy for deserialization
    110     CBlockHeaderAndShortTxIDs() {}
    111 
    112+    // Unsafe init with fixed nonce for testing/internal-use only
    113+    CBlockHeaderAndShortTxIDs(const CBlock& block, const uint64_t nonce);
    114+
    115     CBlockHeaderAndShortTxIDs(const CBlock& block);
    


    glozow commented at 11:53 am on June 17, 2024:
    What about always requiring a FastRandomContext and getting a rand64() for the nonce that way? peerman would pass in its own `m_rng. Making the rng a param feels preferable to adding a test-only version of the ctor.

    AngusP commented at 8:20 pm on June 17, 2024:
    Sure, seems sensible to me

    AngusP commented at 10:10 pm on June 18, 2024:
    Addressed in bba77a2 – though I’ve kept it as passing in a uint64_t nonce, as then it doesn’t “impose” how you got the nonce; in peerman, m_rng has locks guarding it, so using GetRand<uint64_t>() is easier (and claims to be threadsafe), plus this matches the previous behaviour where the CBlockHeaderAndShortTxIDs constructor would just call GetRand itself, rather than switching to a FastRandomContext
  26. in src/test/blockencodings_tests.cpp:289 in 09b90c69bc outdated
    285@@ -282,7 +286,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
    286 
    287     // Test simple header round-trip with only coinbase
    288     {
    289-        CBlockHeaderAndShortTxIDs shortIDs{block};
    290+        CBlockHeaderAndShortTxIDs shortIDs{block, 1341};
    


    glozow commented at 11:56 am on June 17, 2024:
    really not a fan of these magic numbers, would prefer rand_ctx.rand64()

    AngusP commented at 8:19 pm on June 17, 2024:
    I think that should be OK, assuming it doesn’t happen to hit a case where there’s a short-ID collision

    AngusP commented at 10:10 pm on June 18, 2024:
    Addressed in bba77a2
  27. DrahtBot requested review from glozow on Jun 17, 2024
  28. AngusP force-pushed on Jun 18, 2024
  29. AngusP force-pushed on Jun 18, 2024
  30. DrahtBot added the label CI failed on Jun 18, 2024
  31. DrahtBot commented at 10:15 pm on June 18, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is 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.

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

    Debug: https://github.com/bitcoin/bitcoin/runs/26393192395

  32. DrahtBot removed the label CI failed on Jun 19, 2024
  33. in src/blockencodings.h:117 in bba77a2b54 outdated
    108@@ -109,7 +109,7 @@ class CBlockHeaderAndShortTxIDs {
    109     // Dummy for deserialization
    110     CBlockHeaderAndShortTxIDs() {}
    111 
    112-    CBlockHeaderAndShortTxIDs(const CBlock& block);
    113+    CBlockHeaderAndShortTxIDs(const CBlock& block, const uint64_t nonce);
    


    glozow commented at 11:53 am on June 19, 2024:
    Needs documentation, i.e. mentioning the nonce should be randomly generated
  34. in src/test/fuzz/partially_downloaded_block.cpp:55 in bba77a2b54 outdated
    50@@ -50,7 +51,8 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
    51         return;
    52     }
    53 
    54-    CBlockHeaderAndShortTxIDs cmpctblock{*block};
    55+    FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
    56+    CBlockHeaderAndShortTxIDs cmpctblock{*block, fast_random_context.rand64()};
    


    AngusP commented at 12:28 pm on June 19, 2024:
    I’m not familiar with the fuzz tests so I hope this change isn’t stupid/wrong?

    dergoegge commented at 3:00 pm on June 19, 2024:

    No need for the actual rng, we can just consume the nonce from the fuzz data.

    0    CBlockHeaderAndShortTxIDs cmpctblock{*block, fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
    
  35. AngusP requested review from dergoegge on Jun 19, 2024
  36. AngusP force-pushed on Jun 19, 2024
  37. AngusP force-pushed on Jun 19, 2024
  38. test: Make blockencodings_tests deterministic
    refactor: CBlockHeaderAndShortTxIDs constructor now always takes an explicit nonce.
    test: Make blockencodings_tests deterministic using fixed seed providing deterministic
    CBlockHeaderAndShortTxID nonces and dummy transaction IDs.
    
    Fixes very rare flaky test failures, where the ShortIDs of test transactions collide, leading to
    `READ_STATUS_FAILED` from PartiallyDownloadedBlock::InitData and/or `IsTxAvailable` giving `false`
    when the transaction should actually be available.
    
     * Use a new `FastRandomContext` with a fixed seed in each test, to ensure 'random' uint256s
       used as fake prevouts are deterministic, so in-turn test txids and short IDs are deterministic
       and don't collide causing very rare but flaky test failures.
     * Add new test-only/internal initializer for `CBlockHeaderAndShortTxIDs` that takes a specified
       nonce to further ensure determinism and avoid rare but undesireable short ID collisions.
       In a test context this nonce is set to a fixed known-good value. Normally it is random, as
       previously.
    
    Flaky test failures can be reproduced with:
    
    ```patch
    diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
    index 695e8d806a..64d635a97a 100644
    --- a/src/blockencodings.cpp
    +++ b/src/blockencodings.cpp
    @@ -44,7 +44,8 @@ void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const {
    
     uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const {
         static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids");
    -    return SipHashUint256(shorttxidk0, shorttxidk1, wtxid) & 0xffffffffffffL;
    +    // return SipHashUint256(shorttxidk0, shorttxidk1, wtxid) & 0xffffffffffffL;
    +    return SipHashUint256(shorttxidk0, shorttxidk1, wtxid) & 0x0f;
     }
    
    ```
    
    to increase the likelihood of a short ID collision; and running
    
    ```shell
    set -e;
    n=0;
    while (( n++ < 5000 )); do
        src/test/test_bitcoin --run_test=blockencodings_tests;
    done
    ```
    55eea003af
  39. AngusP force-pushed on Jun 19, 2024
  40. AngusP requested review from glozow on Jun 20, 2024
  41. AngusP requested review from dergoegge on Jun 20, 2024
  42. marcofleon commented at 1:29 pm on June 28, 2024: contributor
    Code review ACK 55eea003af24169c883e1761beb997e151845225. I ran the blockencodings unit test and no issues with the new test case.
  43. dergoegge approved
  44. dergoegge commented at 8:36 am on July 1, 2024: member
    Code review ACK 55eea003af24169c883e1761beb997e151845225
  45. in src/net_processing.cpp:2552 in 55eea003af
    2548@@ -2549,7 +2549,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
    2549                 if (a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
    2550                     MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, *a_recent_compact_block);
    2551                 } else {
    2552-                    CBlockHeaderAndShortTxIDs cmpctblock{*pblock};
    2553+                    CBlockHeaderAndShortTxIDs cmpctblock{*pblock, GetRand<uint64_t>()};
    


    glozow commented at 12:30 pm on July 1, 2024:
    Another PR could change these calls to use PeerManagerImpl::m_rng though it would require taking off the g_msgproc_mutex guard.
  46. glozow commented at 12:31 pm on July 1, 2024: member
    ACK 55eea003af24169c883e1761beb997e151845225
  47. glozow merged this on Jul 1, 2024
  48. glozow closed this on Jul 1, 2024


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: 2024-09-28 22:12 UTC

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