Skip to content

Conversation

maflcko
Copy link
Member

@maflcko maflcko commented Aug 2, 2024

This is mostly a style-cleanup for the tests' random generation:

  1. g_insecure_rand_ctx in the tests is problematic, because the name is a leftover when the generator was indeed insecure. However, now the generator is deterministic, because the seed is either passed in or printed (c.f. RANDOM_CTX_SEED). Stating that deterministic randomness is insecure in the tests seems redundant at best. Fix it by just using m_rng for the name.

  2. The global random context has many one-line aliases, such as InsecureRand32. This is problematic, because the same line of code may use the context directly and through a wrapper at the same time. For example in net_tests (see below). This inconsistency is harmless, but confusing. Fix it by just removing the one-line aliases.

src/test/net_tests.cpp:        auto msg_data_1 = g_insecure_rand_ctx.randbytes<uint8_t>(InsecureRandRange(100000));
  1. The wrapper for randmoney has the same problem that the same unit test uses the context directly and through a wrapper at the same time. Also, it has a single type of Rng hardcoded. Fix it by accepting any type.

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 2, 2024

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 hodlinator, ryanofsky, marcofleon
Concept ACK ismaelsadeeq

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:

  • #30737 (test: Fix RANDOM_CTX_SEED use with parallel tests by hodlinator)
  • #30673 (coins: remove logic for spent-and-FRESH cache entries and writing non-DIRTY entries by andrewtoth)
  • #30377 (refactor: Replace ParseHex with consteval ""_hex literals by hodlinator)
  • #29039 (versionbits refactoring by ajtowns)
  • #26812 (test: add end-to-end tests for CConnman and PeerManager by vasild)

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.

@DrahtBot DrahtBot added the Tests label Aug 2, 2024
Copy link
Member

@ismaelsadeeq ismaelsadeeq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK

Copy link
Contributor

@marcofleon marcofleon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm ACK fa29521. Reviewed the code and ran make check. Also ran the suggested test:
RANDOM_CTX_SEED=z ./src/test/test_bitcoin --log_level=all --run_test=timeoffsets_tests/timeoffsets_warning -- -printtoconsole=1 | grep RANDOM_CTX_SEED

@DrahtBot DrahtBot requested a review from ismaelsadeeq August 5, 2024 12:54
Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK fa29521 but I think it would be nicer to add FastRandomContext& m_rng{g_rand_ctx} to BasicTestingSetup and have most test code use m_rng instead of g_rand_ctx directly.

I think this would be nicer mostly as a matter of style since most test code now doesn't reference global variables directly, while this PR is adding hundreds of references. But it could also help down the road, for example if we wanted to remove the global, or run test cases deterministically in parallel. Current change looks ok to me though if this suggestion is too much work.

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 7, 2024

🚧 At least one of the CI tasks failed.
Debug: https://github.com/bitcoin/bitcoin/runs/28259682485

Hints

Make sure to run all tests locally, according to the documentation.

The failure may 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.

@maflcko
Copy link
Member Author

maflcko commented Aug 7, 2024

But it could also help down the road, for example if we wanted to remove the global, or run test cases deterministically in parallel.

I looked into it. However, it seems more work. My findings:

  • There are ~300 lines of code touched, which can use the member alias or the global
  • 108 lines can not use the member alias as-is, because the compiler does not see it in their scope, so they'll need to be fixed up manually one-by-one
  • Excluding them (and only them) automatically leaves some unit tests in an odd state where one function of the unit test is using the global and another function is using the member alias (Something to avoid, I'd say)
  • Excluding the files with compile errors completely, which makes each file self-consistent is possible, but it is only touching 80 lines of code at this point.

So I did that, and will leave the the remaining 220 lines to be fixed by someone else, because they need to be done file-by-file manually. (I am happy to review a pull request doing that)

@maflcko maflcko changed the title test: [refactor] Use g_rand_ctx directly test: [refactor] Use g_rng/m_rng directly Aug 7, 2024
@maflcko
Copy link
Member Author

maflcko commented Aug 7, 2024

Dropped one commit, because a conflicting pull has already picked it up.

@ryanofsky
Copy link
Contributor

ryanofsky commented Aug 14, 2024

Code review ACK facc85b. But I think we can eliminate the global now, and I think would be good to do it in this PR to avoid changing affected tests more than once. I posted a branch https://github.com/ryanofsky/bitcoin/commits/pr/rng which I hope you can steal. It has the following commits:

  • a334a9b test: Add m_rng alias for the global random context
  • c38390f test: refactor: Make unsigned promotion explicit
  • 51712d8 test: refactor: Give unit test functions access to test state
  • ce7ef02 test: refactor: Pass rng parameters to test functions
  • 678c3df test: refactor: Accept any RandomNumberGenerator in RandMoney
  • c1c384e scripted-diff: [test] Use g_rng/m_rng directly
  • 8468112 test: Remove FastRandomContext global

Copy link
Member Author

@maflcko maflcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, pushed your branch. A skim looks good. I left a nit and will do a real review later.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK f1c1b37. Current version of this looks identical to the branch I pushed, except it is rebased and whitespace in the scripted diff was adjusted.

@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed.
Debug: https://github.com/bitcoin/bitcoin/runs/29081320281

Hints

Make sure to run all tests locally, according to the documentation.

The failure may 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.

@maflcko
Copy link
Member Author

maflcko commented Aug 22, 2024

Force pushed to fix another (years old) bug in the first commit

Copy link
Contributor

@hodlinator hodlinator left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 38b03bc (still aiming to do some more testing).

Thanks for checking the includes in the tests @hodlinator! I think I'll leave this as-is for now, because having leftover unused includes in tests should be harmless, apart from possibly being confusing, or needing more compile time. I have a follow-up to use iwyu (and enforce it), so that reviewers can use the re-review cycles to clean up includes for reviewing the code itself. However, this will be a follow-up, so I'll leave this pull request as-is for now.

Agree it's low-value cycles to check #includes/forward declarations, but until we start using iwyu everywhere, it felt fitting as you are doing major surgery in & around test/util/random.h in this case.

Great that my discovery helped spawn your additional fixes to prevector_tests.cpp!

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK 38b03bc. Just another random seed bug in the prevector tests fixed since last review!


~prevector_tester() {
BOOST_CHECK_MESSAGE(passed, "insecure_rand: " + rand_seed.ToString());
BOOST_CHECK_MESSAGE(passed, "The random seed was logged at the start of the test");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: #30571 (comment)

Starting with commit fae43a9, the seed is static const per process

Wow, I didn't notice ctx_seed was statically caching a random value before this so I've been misunderstanding what the SeedRandomForTest function has been doing the whole time. I think this could be made clearer by:

  • Renaming SeedRand::SEED to SeedRand::FIXED_SEED so it is clearer SeedRandomForTest sets a fixed value, not one that can change between calls.
  • Dropping the SeedRandomForTest default argument value so if a test resets to the fixed seed the calls look like SeedRandomForTest(SeedRand::FIXED_SEED) which tells you a fixed value is being set, as opposed to SeedRandomForTest() which makes it look like it could be setting a new random seed.
  • Adding comments that better document what the seed functions and enum values do.

Would suggest:

--- a/src/test/util/random.h
+++ b/src/test/util/random.h
@@ -13,10 +13,13 @@
 
 enum class SeedRand {
     ZEROS, //!< Seed with a compile time constant of zeros
-    SEED,  //!< Use (and report) random seed from environment, or a (truly) random one.
+    FIXED_SEED, //!< Seed with a fixed value that never changes over the
+                //!< lifetime of this process. The seed is read from the
+                //!< RANDOM_CTX_SEED environment variable if set, otherwise
+                //!< generated randomly once, saved, and reused.
 };
 
-/** Seed the internal global RNG state for testing. This affects all randomness, except GetStrongRandBytes(). */
+/** Seed the global RNG state for testing and log the seed value. This affects all randomness, except GetStrongRandBytes(). */
 void SeedRandomStateForTest(SeedRand seed);
 
 template <RandomNumberGenerator Rng>
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -67,8 +67,8 @@ struct BasicTestingSetup {
     node::NodeContext m_node; // keep as first member to be destructed last
 
     FastRandomContext m_rng;
-    /** Seed the internal global RNG state and m_rng for testing. This affects all randomness, except GetStrongRandBytes(). */
-    void SeedRandomForTest(SeedRand seed = SeedRand::SEED)
+    /** Seed the global RNG state and m_rng for testing and log the seed value. This affects all randomness, except GetStrongRandBytes(). */
+    void SeedRandomForTest(SeedRand seed)
     {
         SeedRandomStateForTest(seed);
         m_rng.Reseed(GetRandHash());

MarcoFalke and others added 5 commits August 26, 2024 11:19
…t calls MakeRandDeterministicDANGEROUS

The global g_insecure_rand_ctx will be removed in the future, so
removing it from this helper is useful.

Also, tying the two concepts of the global internal RNGState and the
global test-only rng context is a bit confusing, because tests can
simply use the m_rng, if it exists. Also, tests may seed more than one
random context, or none at all, or a random context of a different type.

Fix all issues by moving the Reseed call to the two places where it is
used.
Add FastRandomContext parameter to the utility function
AddTestCoin(), and a few local test functions and classes.
Accepting any Rng in RandMoney makes tests more flexible to use a
different Rng. Also, passing in the Rng clarifies the call sites, so
that they all use g_rand_ctx explicitly and consistently.
-BEGIN VERIFY SCRIPT-

 # Use m_rng in unit test files
 ren() { sed -i "s:\<$1\>:$2:g" $( git grep -l "$1" src/test/*.cpp src/wallet/test/*.cpp src/test/util/setup_common.cpp ) ; }
 ren InsecureRand32                m_rng.rand32
 ren InsecureRand256               m_rng.rand256
 ren InsecureRandBits              m_rng.randbits
 ren InsecureRandRange             m_rng.randrange
 ren InsecureRandBool              m_rng.randbool
 ren g_insecure_rand_ctx           m_rng
 ren g_insecure_rand_ctx_temp_path g_rng_temp_path

-END VERIFY SCRIPT-
Drop g_insecure_rand_ctx
@maflcko
Copy link
Member Author

maflcko commented Aug 26, 2024

Pushed trivial doc-only update. Should be easy to re-review.

Copy link
Contributor

@hodlinator hodlinator left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 948238a

Gets rid of g_insecure_rand_ctx-global state along with global functions using it, in favor of local m_rng contexts.

Tested adding..

BOOST_CHECK_EQUAL(m_rng.rand64(), 0);

..to one of the tests and verified that the random number emitted varied when re-running the test, and became constant when called repeatedly with RANDOM_CTX_SEED env var set, and different constant when modifying RANDOM_CTX_SEED value.

(Also uncovered #30696 while testing).

fae7e37 "test: Correct the random seed log on a prevector test failure"

Good addition to the PR!

nit: The tense in the commit message feels off,
"rand_cache is unused since" -> "rand_cache was unused since"
"rand_seed is wrong" -> "rand_seed was wrong"
etc


~prevector_tester() {
BOOST_CHECK_MESSAGE(passed, "insecure_rand: " + rand_seed.ToString());
BOOST_CHECK_MESSAGE(passed, "The random seed was logged at the start of the test");
Copy link
Contributor

@hodlinator hodlinator Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Thread #30571 (comment))

One could argue ZEROS is also "fixed", but I still agree FIXED_SEED with the documentation would be a step a good direction.

@DrahtBot DrahtBot requested a review from ryanofsky August 26, 2024 12:08
Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK 948238a. Only changes since last review were changing a comments a little bit.

@maflcko
Copy link
Member Author

maflcko commented Aug 28, 2024

Is this test-only refactor (and small bugfix) with two acks rfm, or does it need more review?

@marcofleon
Copy link
Contributor

In the process of reviewing now.

Copy link
Contributor

@marcofleon marcofleon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK 948238a. Only changes since my last review are the improvements in prevector_tests.

@fanquake fanquake merged commit d184fc3 into bitcoin:master Aug 28, 2024
@jonatack
Copy link
Member

Concept ACK. Nice to remove the global state and methods.

@maflcko maflcko deleted the 2408-test-rng branch August 29, 2024 06:50
@Sjors Sjors mentioned this pull request Aug 29, 2024
@bitcoin bitcoin locked and limited conversation to collaborators Aug 29, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants