feature: Use different datadirs for different signets #34566

pull ekzyis wants to merge 5 commits into bitcoin:master from ekzyis:signet-datadirs changing 12 files +193 −43
  1. ekzyis commented at 7:46 PM on February 11, 2026: contributor

    closes #27494

    This adds support for syncing multiple signets.

    Each custom signet is stored in a different datadir, using the network magic (message start) as the suffix.

    The default signet is always stored without a suffix for backward compatibility, even if the default challenge is provided explicitly via -signetchallenge.

    This builds upon #29838.

    For those already familiar with #29838, here are the differences between #29838 and this that are visible to the user:

    [^1]: could be something for a follow-up PR, or maybe it's intentional to not mix explicit with implicit options?

    Unlike #29838, this PR does not update tests that didn't break (rpc_bind.py, interface_bitcoin_cli.py). I have found other tests where datadir_path is used in combination with self.chain (regexp: datadir_path.*chain). I considered it inconsistent to update some tests but not all of them. I've decided to not update all of them to keep the scope of this PR low until feedback. I can go through all tests in a follow-up PR to make sure they don't break when they are ever used with (custom) signet, however unlikely that might be.

    I have done some manual testing like this:

    <details> <summary>manual testing</summary>

    1. 'signet' directory is used, IBD:
    $ bitcoind -signet
    
    1. 'signet' directory is used, no IBD without -addnode (see #29838 (comment)) or existing peers.dat:
    $ bitcoind -signet -signetchallenge=512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae
    
    1. 'signet' directory is used
    $ bitcoind -conf="signet_default.conf"
    
    # signet_default.conf
    signet=1
    [signet]
    signetchallenge=512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae
    
    1. 'signet_f6ac8550' directory is used:
    $ bitcoind -conf="signet_custom.conf"
    
    # signet_custom.conf
    signet=1
    [signet]
    signetchallenge=0014d4528367459d54e1545b0d0a677d2a7d71d648e0
    

    Tested RPC with bitcoin-cli getblockchaininfo with the same options (-signet, -conf, or -signet -signetchallenge)

    </details>

    I'm new to contributing to Bitcoin Core so it's entirely possible that I've missed something obvious.

  2. DrahtBot commented at 7:47 PM on February 11, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/34566.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept ACK RandyMcMillan
    Approach ACK ajtowns
    Stale ACK ViniciusCestarii, pinheadmz

    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:

    • #28690 (build: Introduce internal kernel library by sedited)

    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-->

    LLM Linter (✨ experimental)

    Possible places where comparison-specific test macros should replace generic comparisons:

    • [test/functional/tool_signet_miner.py] assert get_signet_commitment(get_segwit_commitment(node)) is None -> assert_equal(get_signet_commitment(get_segwit_commitment(node)), None)

    <sup>2026-08-14 14:23:53</sup>

  3. ekzyis marked this as a draft on Feb 11, 2026
  4. ekzyis commented at 8:04 PM on February 11, 2026: contributor

    Will keep this in draft until I fix why tool_signet_miner.py is failing in CI (but not locally for some reason)

    Update: Ah, it's because the CI runs it with --usecli

  5. DrahtBot added the label CI failed on Feb 11, 2026
  6. ekzyis force-pushed on Feb 11, 2026
  7. ekzyis force-pushed on Feb 12, 2026
  8. ekzyis force-pushed on Feb 12, 2026
  9. ekzyis renamed this:
    feature: Use separate datadirs for each signet
    feature: Use different datadirs for different signets
    on Feb 12, 2026
  10. ekzyis force-pushed on Feb 12, 2026
  11. ekzyis force-pushed on Feb 12, 2026
  12. DrahtBot removed the label CI failed on Feb 12, 2026
  13. ekzyis marked this as ready for review on Feb 12, 2026
  14. ekzyis force-pushed on Feb 12, 2026
  15. sedited commented at 5:09 PM on March 8, 2026: contributor

    This hasn't gotten any attention so far. @ajtowns do you want to take a look here since you opened the original issue?

  16. ekzyis force-pushed on Apr 6, 2026
  17. ekzyis commented at 11:12 PM on April 6, 2026: contributor

    rebased on master (b730dc3301f)

  18. in test/functional/feature_signet.py:125 in c4765b3c3a outdated
     120 | +        self.log.info("Test that the signet data directory with -signetchallenge=51 is 'signet_51'")
     121 | +        assert_node_datadir(self.nodes[0], "signet_51")
     122 | +
     123 | +        self.log.info("Test that the main signet data directory is 'signet'")
     124 | +        assert_node_datadir(self.nodes[3], "signet")
     125 | +
    


    ViniciusCestarii commented at 1:23 PM on April 8, 2026:

    Missing a test that verifies truncation to 16 chars for a longer challenge. The 522103ad5e... signet challenge would work. Just add an assert_node_datadir(self.nodes[4], "signet_522103ad5e0edad1") check.


    ekzyis commented at 1:43 PM on April 8, 2026:

    Oh, right, thanks! Done in a2d755132b4

  19. in src/chainparamsbase.cpp:40 in c4765b3c3a outdated
      32 | @@ -33,6 +33,18 @@ const CBaseChainParams& BaseParams()
      33 |      return *globalChainBaseParams;
      34 |  }
      35 |  
      36 | +std::string GetSignetDataDir()
      37 | +{
      38 | +    std::string base_data_dir = "signet";
      39 | +    const std::string signet_challenge = gArgs.GetArg("-signetchallenge", "");
      40 | +    const std::string default_signet_challenge = "512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae";
    


    ViniciusCestarii commented at 1:38 PM on April 8, 2026:

    The default signet challenge string is now duplicated in chainparamsbase.cpp and kernel/chainparams.cpp (and also in the Python test framework). I'm not sure if this is actually a problem, but could this be defined as a shared constant? I'm still learning the codebase, so I'd appreciate some input for better practices here.


    ekzyis commented at 1:46 PM on April 8, 2026:

    I had the same question, and since I’m also new, I was hoping for feedback from more experienced developers as well


    ekzyis commented at 2:19 PM on April 8, 2026:

    I fixed this in 63cf3340748.

    I added test/functional/test_framework/signet.py so test_node.py and feature_signet.py can import the constant from there.

    For the C++ code, I'm now using HexStr(CChainParams::SigNet({})->GetConsensus().signet_challenge).

    Thanks for making me look into it again!


    ekzyis commented at 2:21 PM on April 8, 2026:

    Oh, this introduced a circular dependency:

    A new circular dependency in the form of "chainparams -> chainparamsbase -> chainparams" appears to have been introduced.

    https://github.com/bitcoin/bitcoin/actions/runs/24139839220/job/70438028501?pr=34566


    ekzyis commented at 3:34 PM on April 8, 2026:

    Fixed in 364969087e0. I accidentally included <chainparams.h> in chainparamsbase.cpp instead of <kernel/chainparams.h>.

  20. ekzyis force-pushed on Apr 8, 2026
  21. ekzyis force-pushed on Apr 8, 2026
  22. DrahtBot added the label CI failed on Apr 8, 2026
  23. ekzyis marked this as a draft on Apr 8, 2026
  24. ekzyis force-pushed on Apr 8, 2026
  25. DrahtBot removed the label CI failed on Apr 8, 2026
  26. in test/functional/tool_signet_miner.py:131 in 364969087e outdated
     127 | @@ -121,33 +128,33 @@ def run_test(self):
     128 |          node = self.nodes[0]
     129 |          # import private key needed for signing block
     130 |          wallet_importprivkey(node, bytes_to_wif(CHALLENGE_PRIVATE_KEY), 0)
     131 | -        self.mine_block(node)
     132 | +        self.mine_block(node, self.extra_args[0])
    


    pinheadmz commented at 4:02 PM on April 8, 2026:

    what is the extra_args parameter for?


    ekzyis commented at 5:22 PM on April 8, 2026:

    This passes the signet challenge of the node to mine_block. It's required to pass -signetchallenge to the RPC. Without it, RPC authentication fails because the cookie isn't found. It will look in the default signet datadir:

    $ build/test/functional/test_runner.py tool_signet_miner.py
    Temporary test directory at /tmp/nix-shell-115046-4006075642/test_runner_₿_šŸƒ_20260408_191958
    Remaining jobs: [tool_signet_miner.py]
    1/1 - tool_signet_miner.py failed, Duration: 0 s
    
    stdout:
    2026-04-08T17:19:58.648068Z TestFramework (INFO): PRNG seed is: 8822581970357866232
    2026-04-08T17:19:58.698730Z TestFramework (INFO): Initializing test directory /tmp/nix-shell-115046-4006075642/test_runner_₿_šŸƒ_20260408_191958/tool_signet_miner_0
    2026-04-08T17:19:59.085274Z TestFramework (INFO): Signet node with single signature challenge
    error: Authorization failed: Failed to read cookie file and no rpcpassword was specified. Configuration file: (/tmp/nix-shell-115046-4006075642/test_runner_₿_šŸƒ_20260408_191958/tool_signet_miner_0/node0/bitcoin.conf)
    

    pinheadmz commented at 5:31 PM on April 17, 2026:

    I think it would make more sense if you just changed the name of the argument then:

    def mine_block(self, node, extra_args):
    

    Since this is how its being used (just appending args to a CLI command)


    ekzyis commented at 5:48 PM on April 21, 2026:

    Done in 8824d95819

  27. pinheadmz commented at 4:03 PM on April 8, 2026: member

    concept ACK, I use a lot of different signets for educational projects and we could really use this. Personally I feel like using the magic bytes (message start) makes more sense and is less likely to collide between signet challenges

  28. ekzyis marked this as ready for review on Apr 8, 2026
  29. ekzyis force-pushed on Apr 8, 2026
  30. ekzyis force-pushed on Apr 8, 2026
  31. DrahtBot added the label CI failed on Apr 8, 2026
  32. ekzyis force-pushed on Apr 8, 2026
  33. ekzyis commented at 8:09 PM on April 8, 2026: contributor

    concept ACK, I use a lot of different signets for educational projects and we could really use this. Personally I feel like using the magic bytes (message start) makes more sense and is less likely to collide between signet challenges

    Right! 77cd6c99306 uses magic bytes as the suffix. Additionally to what you mentioned, I also like this for these reasons:

    • makes directory names shorter (was also mentioned in #29383 (comment))
    • consistent suffix length, independent of challenge length

    Updated commit message and PR description.

  34. DrahtBot removed the label CI failed on Apr 8, 2026
  35. RandyMcMillan commented at 2:31 PM on April 9, 2026: contributor

    concept ACK

  36. ekzyis force-pushed on Apr 10, 2026
  37. ekzyis commented at 11:50 AM on April 10, 2026: contributor

    I think "notable new feature" and "visible change to the end-user experience" from developer-notes.md apply here, so I added release notes in f283eaa.

  38. in src/chainparamsbase.cpp:43 in f283eaac1d outdated
      37 | @@ -33,6 +38,28 @@ const CBaseChainParams& BaseParams()
      38 |      return *globalChainBaseParams;
      39 |  }
      40 |  
      41 | +std::string GetSignetDataDir()
      42 | +{
      43 | +    std::string base_data_dir = "signet";
    


    pinheadmz commented at 5:00 PM on April 17, 2026:

    could be constexpr std::string_view base_data_dir = "signet";


    ekzyis commented at 11:44 AM on April 21, 2026:

    Would it be worth it if I have to cast to std::string anyway on return?

    diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
    index 8dc0296649..addd1551b0 100644
    --- a/src/chainparamsbase.cpp
    +++ b/src/chainparamsbase.cpp
    @@ -40,10 +40,10 @@ const CBaseChainParams& BaseParams()
    
     std::string GetSignetDataDir()
     {
    -    std::string base_data_dir = "signet";
    +    constexpr std::string_view base_data_dir{"signet"};
         const std::string challenge_hex = gArgs.GetArg("-signetchallenge", "");
         if (challenge_hex.empty()) {
    -        return base_data_dir;
    +        return std::string(base_data_dir);
         }
    
         const std::vector<uint8_t> challenge_bytes = ParseHex<uint8_t>(challenge_hex);
    @@ -55,9 +55,9 @@ std::string GetSignetDataDir()
    
         const MessageStartChars default_msg_start = CChainParams::SigNet({})->MessageStart();
         if (msg_start == default_msg_start) {
    -        return base_data_dir;
    +        return std::string(base_data_dir);
         }
    -    return base_data_dir + "_" + HexStr(msg_start);
    +    return std::string(base_data_dir) + "_" + HexStr(msg_start);
     }
    
     /**
    
    

    CBaseChainParams expects std::string, so afaict, I have to use std::string as the return type of GetSignetDataDir().


    pinheadmz commented at 10:11 AM on July 28, 2026:

    Sorry you're right 😬 tidy didn't like this either

  39. in src/chainparamsbase.cpp:49 in f283eaac1d outdated
      44 | +    const std::string challenge_hex = gArgs.GetArg("-signetchallenge", "");
      45 | +    if (challenge_hex.empty()) {
      46 | +        return base_data_dir;
      47 | +    }
      48 | +
      49 | +    const std::vector<uint8_t> challenge_bytes = ParseHex<uint8_t>(challenge_hex);
    


    pinheadmz commented at 5:02 PM on April 17, 2026:

    is challenge_hex guaranteed to be valid hex at this point? Otherwise I think this is empty but that might be ok since I'm fairly certain we'd fail later on if the challenge isnt valid.


    ekzyis commented at 1:10 PM on April 21, 2026:

    is challenge_hex guaranteed to be valid hex at this point?

    Good question! No. This line in chainparamsbase.cpp gets hit before we fail because of invalid hex here in chainparams.cpp:

    https://github.com/bitcoin/bitcoin/blob/0c0f75eaaf173d31d31317ba396a6a94baac868a/src/chainparams.cpp#L36-L39

    I don't see this having any undesired side effect like creating a datadir that can't be used, though, so I also think it's okay.

    It just reminds me that I originally intended this functionality to be part of CChainParams instead of CBaseChainParams, but DataDir() is part of the CBaseChainParams interface. I explored conditionally adding -datadir with SoftSetArg like we do for other options, but then we would still create a "signet" folder inside it.

  40. in src/chainparamsbase.cpp:59 in f283eaac1d
      54 | +    std::copy_n(hash.begin(), 4, msg_start.begin());
      55 | +
      56 | +    const MessageStartChars default_msg_start = CChainParams::SigNet({})->MessageStart();
      57 | +    if (msg_start == default_msg_start) {
      58 | +        return base_data_dir;
      59 | +    }
    


    pinheadmz commented at 5:15 PM on April 17, 2026:

    What if the hard-coded default signet challenge was moved from kernel/chainparams.cpp to src/signet.h as a constexpr. Then you can just compare the actual challenge provided by the user string-to-string (although probably need ToLower for case sensitivity)

      // signet.h                                                                                                                                                                                                                                                                                                   
      constexpr std::string_view SIGNET_DEFAULT_CHALLENGE{                                                                                                                                                                                                                                                                      
          "512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"
      };                                                                                                                                                                                                                                                                                                                        
    
                  
      if (challenge_hex.empty() || challenge_hex == SIGNET_DEFAULT_CHALLENGE) {
          return std::string(base_data_dir);                                                                                                                                                                                                                                                                                    
      }
    

    pinheadmz commented at 5:25 PM on April 17, 2026:

    I wonder if the message start method could be de-duplicated into signet.h, cpp as well ?


    ekzyis commented at 5:49 PM on April 21, 2026:

    Done in 2fd5da15ea

    I had to update src/CMakeLists.txt, see commit message

    probably need ToLower for case sensitivity

    I considered to add a test for this, but I think it would be too granular

    edit: Okay, looking at CI, I think I need to update other CMakeLists.txt, too

  41. in test/functional/feature_signet.py:117 in f283eaac1d outdated
     112 | +            assert_equal(path.basename(datadir), expected_dirname)
     113 | +            # check if the directory exists
     114 | +            assert datadir.is_dir()
     115 | +            # check if the directory is being used
     116 | +            rpc_log_path = node.getrpcinfo()['logpath']
     117 | +            assert rpc_log_path.startswith(str(datadir))
    


    pinheadmz commented at 5:26 PM on April 17, 2026:

    Since you're using startswith here is there a risk of signet being confused with signet_XXXXXXXX ?


    ekzyis commented at 1:39 PM on April 21, 2026:

    No, this would be caught when checking the directory name in line 111.

    For example:

    diff --git a/test/functional/feature_signet.py b/test/functional/feature_signet.py
    index 9bc8353785..81f0246b22 100755
    --- a/test/functional/feature_signet.py
    +++ b/test/functional/feature_signet.py
    @@ -117,7 +117,7 @@ class SignetBasicTest(BitcoinTestFramework):
                 assert rpc_log_path.startswith(str(datadir) + "/")
    
             self.log.info("Test that the signet data directory with custom -signetchallenge uses network magic as suffix")
    -        assert_node_datadir(self.nodes[0], f"signet_{message_start(self.signets[0].challenge)}")
    +        assert_node_datadir(self.nodes[0], f"signet")
             assert_node_datadir(self.nodes[4], f"signet_{message_start(self.signets[2].challenge)}")
    
             self.log.info("Test that the main signet data directory is 'signet'")
    
    $ python feature_signet.py
    ...
    Traceback (most recent call last):
      File "/home/ekzyis/prog/bitcoin/test/functional/test_framework/test_framework.py", line 143, in main
        self.run_test()
        ~~~~~~~~~~~~~^^
      File "/home/ekzyis/prog/bitcoin/build/test/functional/feature_signet.py", line 120, in run_test
        assert_node_datadir(self.nodes[0], f"signet")
        ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/ekzyis/prog/bitcoin/build/test/functional/feature_signet.py", line 112, in assert_node_datadir
        assert_equal(path.basename(datadir), expected_dirname)
        ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/ekzyis/prog/bitcoin/test/functional/test_framework/util.py", line 83, in assert_equal
        raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args))
    AssertionError: not(signet_54d26fbd == signet)
    
  42. pinheadmz commented at 5:41 PM on April 17, 2026: member

    code review at f283eaac1dd41fb86c15eac4c734e1bc919134b7 some thoughts:

    • I think this is too much for one commit. See if you can pull out any refactors into the first commit, then make your change (with any tests that MUST be changed to stay passing), add more tests next, and finally the release note can even be on its own commit.
    • There are probably other doc files that could use notes about this change along with the release note (files.md for example)
    • I'm not sure how to address this but the bitcoin.conf file has these network sections like [regtest] and [signet]. But that section would include the signetchallenge so I don't think a user would need more than one signet section in their conf. But a user switching between signets might have hard-coded peers like connect= in the signet section of their conf, and that could be an issue when switching networks. Just wondering if you have any thoughts about that.

    Otherwise it's looking good! I built on macos/arm64 and ran the functional tests with and without --usecli. Played with the feature on default and custom signets.

  43. ekzyis force-pushed on Apr 21, 2026
  44. ekzyis marked this as a draft on Apr 21, 2026
  45. ekzyis commented at 6:34 PM on April 21, 2026: contributor

    I think this is too much for one commit. See if you can pull out any refactors into the first commit, then make your change (with any tests that MUST be changed to stay passing), add more tests next, and finally the release note can even be on its own commit.

    I split it into four commits as suggested

    There are probably other doc files that could use notes about this change along with the release note (files.md for example)

    I updated files.md but I haven't found other documentation about datadirs that need an update

    [...] Just wondering if you have any thoughts about that.

    I think different peers for each signet is an argument for supporting more than one signet section in the config (in a follow-up PR) as mentioned in #29838 (comment).


    Looks like my refactor in 2fd5da15ea broke all kinds of builds. I put this into draft until I fixed them.

  46. DrahtBot added the label CI failed on Apr 21, 2026
  47. DrahtBot commented at 6:35 PM on April 21, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task test ancestor commits: https://github.com/bitcoin/bitcoin/actions/runs/24737621566/job/72368627292</sub> <sub>LLM reason (✨ experimental): CTest failed because test_kernel (the only failing test) returned a non-zero status, causing the CI to exit with code 8.</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>

  48. ekzyis force-pushed on Apr 21, 2026
  49. ekzyis force-pushed on Apr 21, 2026
  50. ekzyis force-pushed on Apr 21, 2026
  51. DrahtBot removed the label CI failed on Apr 21, 2026
  52. ekzyis commented at 9:55 PM on April 21, 2026: contributor

    12d77c9d8a rebased on master (875faa29e1) to resolve a conflict in src/kernel/CMakeLists.txt

    5912565c81 I resolved the conflict by not adding a line to src/kernel/CMakeLists.txt

  53. ekzyis marked this as ready for review on Apr 21, 2026
  54. ekzyis force-pushed on Apr 21, 2026
  55. ekzyis force-pushed on Apr 21, 2026
  56. DrahtBot added the label CI failed on Apr 21, 2026
  57. DrahtBot commented at 11:20 PM on April 21, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task test ancestor commits: https://github.com/bitcoin/bitcoin/actions/runs/24751140594/job/72414298229</sub> <sub>LLM reason (✨ experimental): CI failed because the C++ build errored while compiling chainparamsbase.cpp (bitcoin_common / variant operator __OP), causing gmake to exit with code 1.</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>

  58. DrahtBot removed the label CI failed on Apr 22, 2026
  59. DrahtBot added the label Needs rebase on May 22, 2026
  60. ekzyis marked this as a draft on May 22, 2026
  61. ekzyis force-pushed on May 22, 2026
  62. DrahtBot added the label CI failed on May 22, 2026
  63. ekzyis force-pushed on May 22, 2026
  64. ekzyis commented at 9:29 PM on May 22, 2026: contributor

    a59dda147a9 rebased on master (9f7b08c61ca)

    Thanks to #35334, I was able to remove the custom SignetMinerTest.setup_nodes() and the manual assignment to node.cli.options in SignetBasicTest.setup_nodes(). Those changes may not have been necessary even before that PR (I might have changed the existing tests more than needed), but they are definitely not needed anymore.

  65. ekzyis marked this as ready for review on May 22, 2026
  66. DrahtBot removed the label Needs rebase on May 22, 2026
  67. DrahtBot removed the label CI failed on May 22, 2026
  68. DrahtBot added the label Needs rebase on May 29, 2026
  69. ekzyis force-pushed on May 29, 2026
  70. ekzyis commented at 11:52 AM on May 29, 2026: contributor

    ba97013d760 rebased on master (13b7fffc5e0)

    This was the only conflict:

    <<<<<<< HEAD
    #include <script/verify_flags.h>
    ||||||| parent of f6307691e65 (refactor: Import signet constant and helper from signet.h)
    =======
    #include <signet.h>
    >>>>>>> f6307691e65 (refactor: Import signet constant and helper from signet.h)
    
  71. DrahtBot added the label CI failed on May 29, 2026
  72. DrahtBot removed the label Needs rebase on May 29, 2026
  73. ekzyis force-pushed on May 29, 2026
  74. DrahtBot removed the label CI failed on May 29, 2026
  75. pinheadmz commented at 7:57 PM on June 1, 2026: member

    One thing that occured to me about this change is that the although the signet_challenge field has been added to RPCs like getmininginfo and getblockchaininfo the magic bytes and therefore the data directory path isn't obvious to the user. This might be an issue for some (automated) users of the software that expect a data directory to be, simply ~/.bitcoin/{network} (like warnet, probably)

    I wonder if datadir should be added to an RPC somewhere?

  76. ajtowns commented at 1:13 AM on June 2, 2026: contributor

    I wonder if datadir should be added to an RPC somewhere?

    I'd contend that a getnodeinfo that reported info like that would be a useful feature.

  77. pinheadmz commented at 9:42 AM on June 2, 2026: member

    Although, the user won't be able to call any RPC if they don't know where the cookie is...

    Maybe the approach should be, requiring -datadir is set if -signetchallenge is present? Forces the user to make their own nickname and path for every signet they use.

  78. ekzyis commented at 10:40 AM on June 2, 2026: contributor

    Although, the user won't be able to call any RPC if they don't know where the cookie is...

    I agree with the problem, this is confusing:

    $ bitcoin-cli -signet getblockchaininfo
    error: Authorization failed: Failed to read cookie file and no rpcpassword was specified. Configuration file: (/home/ekzyis/.bitcoin/bitcoin.conf)
    

    It is not obvious that -signetchallenge is needed:

    $ bitcoin-cli -signet -signetchallenge=51 getblockchaininfo
    {
      "chain": "signet",
      "blocks": 0,
      "headers": 0,
      "bestblockhash": "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6",
      "bits": "1e0377ae",
      "target": "00000377ae000000000000000000000000000000000000000000000000000000",
      "difficulty": 0.001126515290698186,
      "time": 1598918400,
      "mediantime": 1598918400,
      "verificationprogress": 1,
      "initialblockdownload": true,
      "chainwork": "000000000000000000000000000000000000000000000000000000000049d414",
      "size_on_disk": 293,
      "pruned": false,
      "signet_challenge": "51",
      "warnings": [
        "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"
      ]
    }
    

    This wasn't the case before.

    Maybe the approach should be, requiring -datadir is set if -signetchallenge is present? Forces the user to make their own nickname and path for every signet they use.

    Not sure I'm following. -datadir only changes the base path (~/.bitcoin by default). -signetchallenge would still be needed to know which signet folder to pick inside datadir (~/.bitcoin/signet or ~/.bitcoin/signet_XXXXXX).

    So I think if RPC auth fails and -signet was passed, we could add "did you forget -signetchallenge for custom signets?" to the error message. WDYT?

    I wonder if datadir should be added to an RPC somewhere?

    I'd contend that a getnodeinfo that reported info like that would be a useful feature.

    I think datadir (and network_magic) could be added to the output of getblockchaininfo above.

  79. pinheadmz commented at 11:06 AM on June 2, 2026: member

    I meant when starting bitcoind with -signetchallenge (that is not default) we should crash and throw an init error telling the user to ALSO set -datadir so they know where the cookie is. It's an entirely different approach than what you've implemented here (sorry). Just up for discussion.

  80. pinheadmz commented at 11:16 AM on June 2, 2026: member

    I also didn't realize -signetchallenge could be passed to cli and that works, but not super user friendly

  81. DrahtBot added the label Needs rebase on Jun 3, 2026
  82. ekzyis force-pushed on Jun 4, 2026
  83. DrahtBot removed the label Needs rebase on Jun 4, 2026
  84. DrahtBot added the label CI failed on Jun 4, 2026
  85. ekzyis commented at 12:31 PM on June 4, 2026: contributor

    7364f0c7a29 rebased on master (47da4f9b716)

    I meant when starting bitcoind with -signetchallenge (that is not default) we should crash and throw an init error telling the user to ALSO set -datadir so they know where the cookie is. It's an entirely different approach than what you've implemented here (sorry). Just up for discussion.

    Mhh, a strict requirement like this would be a simple fix to avoid conflicts between chain data, but I'm not sure it would make running multiple signets easier. Instead of handling different datadirs for different signets in the code, we make the user handle them.

    I also didn't realize -signetchallenge could be passed to cli and that works, but not super user friendly

    I think f53d611b176 would help. WDYT?

  86. pinheadmz commented at 12:58 PM on June 4, 2026: member

    I can't review the code for a few more days but I think the communication is right. This will be a niche developer use case anyway.

    Another idea I had is maybe adding a command to bitcoin-util that derives the magic bytes, or maybe the entire data dir path, given a signet challenge. That way in applications like warnet we know everything we need to know before starting bitcoind.

  87. DrahtBot removed the label CI failed on Jun 4, 2026
  88. ekzyis commented at 9:22 PM on June 26, 2026: contributor

    Another idea I had is maybe adding a command to bitcoin-util that derives the magic bytes

    Makes sense! Added this in #35610.

  89. sedited referenced this in commit 43d89bb6e1 on Jun 30, 2026
  90. Kino1994 referenced this in commit 9b5b2e43dc on Jul 10, 2026
  91. sedited requested review from ViniciusCestarii on Jul 24, 2026
  92. sedited requested review from pinheadmz on Jul 24, 2026
  93. in test/functional/test_framework/test_node.py:584 in 821b15875f
     580 | +            return self.chain
     581 | +        for arg in self.extra_args:
     582 | +            if not arg.startswith("-signetchallenge"):
     583 | +                continue
     584 | +            signetchallenge = arg.split('=')[1]
     585 | +            if signetchallenge == SIGNET_DEFAULT_CHALLENGE:
    


    ViniciusCestarii commented at 1:19 PM on July 24, 2026:

    In "Use different datadirs for different signets" 821b15875fec4fc0b211feaaaf5ef10794ff4795

                if signetchallenge.lower() == SIGNET_DEFAULT_CHALLENGE:
    

    nit: match the C++ code lowering signet challenge when comparing signet challenge with signet default challenge


    ekzyis commented at 11:40 PM on July 25, 2026:

    done in a6bde84a3b

  94. in test/functional/feature_signet.py:1 in 3875deab14 outdated


    ViniciusCestarii commented at 1:28 PM on July 24, 2026:

    In "test: Add signet datadir tests" 3875deab1410672d05325b8e9b561244e190f527

    nit: worth adding a test that checks if -signetchallenge=SIGNET_DEFAULT_CHALLENGE.upper() also uses 'signet' datadir


    ekzyis commented at 11:37 PM on July 25, 2026:

    I considered this in #34566 (review), but I didn't really explain my reasoning, so let me do that here:

    By "too granular", I meant that it would test behavior that is IMO very easy to verify: the test would verify whether a single line uses ToLower() or not.

    Additionally, based on how infrequently CreateBaseChainParams() has changed, I don't think it's likely to change again anytime soon, and I think this will apply to GetSignetDataDir() even more. Because of that, it also doesn't seem like a good candidate for a regression test:

    ba8fc7d7889 (TheCharlatan             2023-04-17 22:20:59 +0200 54) std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const ChainType chain)
    20e01b1a038 (Pieter Wuille            2014-09-19 19:21:46 +0200 55) {
    ba8fc7d7889 (TheCharlatan             2023-04-17 22:20:59 +0200 56)     switch (chain) {
    ba8fc7d7889 (TheCharlatan             2023-04-17 22:20:59 +0200 57)     case ChainType::MAIN:
    0e2b12b92a2 (Martin Zumsande          2024-11-04 18:24:45 -0500 58)         return std::make_unique<CBaseChainParams>("", 8332);
    ba8fc7d7889 (TheCharlatan             2023-04-17 22:20:59 +0200 59)     case ChainType::TESTNET:
    0e2b12b92a2 (Martin Zumsande          2024-11-04 18:24:45 -0500 60)         return std::make_unique<CBaseChainParams>("testnet3", 18332);
    74a04f9e7ad (Fabian Jahr              2024-03-31 23:50:42 +0200 61)     case ChainType::TESTNET4:
    0e2b12b92a2 (Martin Zumsande          2024-11-04 18:24:45 -0500 62)         return std::make_unique<CBaseChainParams>("testnet4", 48332);
    ba8fc7d7889 (TheCharlatan             2023-04-17 22:20:59 +0200 63)     case ChainType::SIGNET:
    a6bde84a3b8 (ekzyis                   2026-02-11 13:42:45 +0100 64)         return std::make_unique<CBaseChainParams>(GetSignetDataDir(), 38332);
    ba8fc7d7889 (TheCharlatan             2023-04-17 22:20:59 +0200 65)     case ChainType::REGTEST:
    0e2b12b92a2 (Martin Zumsande          2024-11-04 18:24:45 -0500 66)         return std::make_unique<CBaseChainParams>("regtest", 18443);
    e8990f12140 (Karl-Johan Alm           2020-03-05 15:58:30 +0900 67)     }
    e23088707be (TheCharlatan             2023-05-09 22:03:50 +0200 68)     assert(false);
    84ce18ca933 (Wladimir J. van der Laan 2014-06-19 15:10:04 +0200 69) }
    

    Given that, I don't think it's worth spinning up another node (or restarting one) just to cover this case in the tests.

    What do you, or others, think?


    ViniciusCestarii commented at 1:09 PM on July 27, 2026:

    I still think it's worth it, but this is still non blocking and up to you

    By "too granular", I meant that it would test behavior that is IMO very easy to verify: the test would verify whether a single line uses ToLower() or not.

    Test value comes from what happens if the invariant breaks (in this case it would start from a new probably empty datadir), even if it's a single line. It's very easy to verify but also trivial to silently break in a future refactor and this is why I think it's worth it.

    Given that, I don't think it's worth spinning up another node (or restarting one) just to cover this case in the tests.

    This test already spins 7 nodes, one more isn't a different order of magnitude.


    ekzyis commented at 11:25 AM on July 28, 2026:

    The code under test has been removed, see #34566 (review)


    ViniciusCestarii commented at 2:27 PM on July 28, 2026:

    In "Use different datadirs for different signets" 5418bf352a3dcc827739cafedf53a23aa527e334

    nit: These changes on test/functional/test_framework/test_node.py introduce a case (chain_dir reads self.extra_args, and start() never updates it) where if restarting a node with a new -signetchallenge it still returns the old chain_dir.

    But this is a non issue today and would be only if someone added a test where restart with a new -signetchallenge. I believe this is fine as is and not worth spending time on it. I'm just commenting this so it's on record.

    Found this with Opus 5.


    ekzyis commented at 8:45 PM on July 28, 2026:

    I believe this is fine as is and not worth spending time on it. I'm just commenting this so it's on record.

    Yes, I agree. Thanks for flagging it! Sounds similar to what I "found" with Claude (https://github.com/bitcoin/bitcoin/pull/34566#discussion_r3668945346), which turned out to test the test framework.

  95. in doc/release-notes-34566.md:8 in 7364f0c7a2 outdated
       0 | @@ -0,0 +1,8 @@
       1 | +Network changes
       2 | +---------------
       3 | +
       4 | +Custom signets now use separate data directories, with a suffix derived from the
       5 | +network magic (message start), so multiple signets can be synced. The default
       6 | +signet continues to use the unsuffixed directory for backward compatibility,
       7 | +including when the default challenge is set explicitly via `-signetchallenge`.
       8 | +(#34566)
    


    ViniciusCestarii commented at 1:51 PM on July 24, 2026:

    In "doc: Update documentation for signet datadir" 7364f0c7a29aafaeb73cdecbd86c76e9705e01a1

    I believe this release note should warn and explain how to upgrade for users that are running with a non default signet challenge. Something like:

    Users currently running a custom signet (a non-default -signetchallenge) will have their node select a new, differently-named data directory after upgrading, since previously all signets shared the unsuffixed signet/ directory. To avoid a resync, manually rename/move the existing signet/ directory to the new signet_XXXXXXXX/ path before starting the upgraded node (you can find the suffix in the field magic printed by bitcoin-util -signet -signetchallenge=<hex-script> getchainparams)


    pinheadmz commented at 11:49 PM on July 24, 2026:

    This makes me think there should also be a better error message when the user starts on the "wrong" signet:

    Error: Invalid or corrupt peers.dat (Invalid network magic number). If you believe this is a bug, please report it to https://github.com/bitcoin/bitcoin/issues. As a workaround, you can move the file ("/Users/matthewzipkin/Library/Application Support/Bitcoin/signet/peers.dat") out of the way (rename, move, or delete) to have a new one created on the next start.


    ekzyis commented at 11:43 PM on July 25, 2026:

    I believe this release note should warn and explain how to upgrade for users that are running with a non default signet challenge.

    Good point! Done in 1ae29e4f78. I shortened your suggestion to what I think is the essential information, but please check if you think there's something missing now.

    This makes me think there should also be a better error message when the user starts on the "wrong" signet:

    Makes sense, but this didn't make it into the current round of addressing feedback. I need to think more about how to improve it. I also don't think it's blocking, so it could be done in a different PR.


    ViniciusCestarii commented at 1:06 PM on July 27, 2026:

    I shortened your suggestion to what I think is the essential information, but please check if you think there's something missing now.

    Looks good!

  96. in src/signet.h:17 in 90c55d6528
      12 |  
      13 | +#include <cstdint>
      14 |  #include <optional>
      15 | +#include <vector>
      16 | +
      17 | +using namespace util::hex_literals;
    


    ViniciusCestarii commented at 2:30 PM on July 24, 2026:

    In "refactor: Import signet constant and helper from signet.h" 90c55d6528b52ffe91f6caa2fac490a4e05ff1c6

    This line and #include <util/strencodings.h> in signet.h means this is also passed to any .cpp that include or transitively include this header which leaks every _hex operator.

    This leak can be seen in src/kernel/chainparams.cpp where the #include <util/strencodings.h> was removed although it still uses the operator _hex and still works but it shouldn't rely on the transitive include coming from signet.h.

    Suggest moving SIGNET_DEFAULT_CHALLENGE definition into signet.cpp (with an extern const std::vector<uint8_t> SIGNET_DEFAULT_CHALLENGE; declaration in this signet.h), so signet.h doesn't export any hex-literal operators at all.


    ekzyis commented at 11:48 PM on July 25, 2026:

    Nice catch, thank you!! Fixed in 07b10574f1.

  97. ViniciusCestarii commented at 2:39 PM on July 24, 2026: contributor

    Concept ACK looks good. I left some comments

  98. ekzyis force-pushed on Jul 25, 2026
  99. ekzyis force-pushed on Jul 25, 2026
  100. DrahtBot added the label CI failed on Jul 25, 2026
  101. DrahtBot commented at 10:55 PM on July 25, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task test ancestor commits: https://github.com/bitcoin/bitcoin/actions/runs/30177629571/job/89728930764</sub> <sub>LLM reason (✨ experimental): CI failed because test_kernel crashed with a runtime symbol lookup error (libbitcoinkernel.so undefined symbol _Z11TryParseHex...).</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>

  102. DrahtBot removed the label CI failed on Jul 25, 2026
  103. ekzyis commented at 11:58 PM on July 25, 2026: contributor

    8894a6bc9c rebased on master (e34b8d5a7d) with #35736 merged (not that I need it; I just mention it in the release notes).

  104. ViniciusCestarii commented at 1:44 PM on July 27, 2026: contributor

    One issue that I think should be fixed before merge: the fuzz harness removal in 07b10574f1b3950d0d0ecbc15880061fa6e04bd8. Left a comment. Happy to ACK once that's addressed.

  105. in src/test/fuzz/CMakeLists.txt:121 in 07b10574f1
     117 | @@ -118,7 +118,6 @@ add_executable(fuzz
     118 |    secp256k1_ec_seckey_import_export_der.cpp
     119 |    secp256k1_ecdsa_signature_parse_der_lax.cpp
     120 |    signature_checker.cpp
     121 | -  signet.cpp
    


    ViniciusCestarii commented at 1:45 PM on July 27, 2026:

    In "refactor: Import signet constant and helper from signet.h" 07b10574f1b3950d0d0ecbc15880061fa6e04bd8

    I didn't realize before but signet.cpp in this list is for src/test/fuzz/signet.cpp and not for src/signet.cpp. Removing this line would remove the fuzz harness provided by src/test/fuzz/signet.cpp.

    This line should be reverted and the commit message updated too.


    ekzyis commented at 11:18 AM on July 28, 2026:

    Wow, that would almost have been my first contribution to fuzzing in Bitcoin Core, haha :sweat_smile:

    Thank you, fixed in b3d54514bf!!

  106. ViniciusCestarii commented at 1:46 PM on July 27, 2026: contributor

    One issue that I think should be fixed before merge: the fuzz harness removal in https://github.com/bitcoin/bitcoin/commit/07b10574f1b3950d0d0ecbc15880061fa6e04bd8. Left a comment. Happy to ACK once that's addressed.

  107. in src/signet.cpp:33 in 07b10574f1 outdated
      26 | @@ -25,6 +27,21 @@
      27 |  #include <utility>
      28 |  #include <vector>
      29 |  
      30 | +using namespace util::hex_literals;
      31 | +
      32 | +const std::vector<uint8_t> SIGNET_DEFAULT_CHALLENGE{
      33 | +    "512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"_hex_v_u8};
    


    pinheadmz commented at 7:39 PM on July 27, 2026:

    07b10574f1b3950d0d0ecbc15880061fa6e04bd8

    Do you have to duplicate the long hex string here? Why not import the hex from the header...

    diff --git a/src/signet.cpp b/src/signet.cpp
    index b55ce20911..fb77b4929a 100644
    --- a/src/signet.cpp
    +++ b/src/signet.cpp
    @@ -27,10 +27,7 @@
     #include <utility>
     #include <vector>
     
    -using namespace util::hex_literals;
    -
    -const std::vector<uint8_t> SIGNET_DEFAULT_CHALLENGE{
    -    "512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"_hex_v_u8};
    +const std::vector<uint8_t> SIGNET_DEFAULT_CHALLENGE{ParseHex<uint8_t>(SIGNET_DEFAULT_CHALLENGE_HEX)};
     
     MessageStartChars GetSignetMessageStart(const std::vector<uint8_t>& signet_challenge)
     {
    

    ekzyis commented at 11:15 AM on July 28, 2026:

    Sorry, was too lazy to mention in advance that I tried this, but it would require adding ../util/strencodings.cpp to the source list of the bitcoinkernel target, and I think we don't want to do that.

    However, @rustaceanrob looked at my code, and noticed that I don't even need SIGNET_DEFAULT_CHALLENGE_HEX. I can just parse -signetchallenge as bytes and then compare the bytes, instead of comparing them as (case-insensitive) strings. Then I also happen to not need the test mentioned by @ViniciusCestarii in #34566 (review).

    Thanks @rustaceanrob!!

    Done in 5418bf352a

  108. pinheadmz commented at 8:08 PM on July 27, 2026: member

    code review in progress at 8894a6bc9cc425e4834a6d2ae4d336f242f000c1

    Just wanted to post one comment I had for now

  109. ekzyis force-pushed on Jul 28, 2026
  110. DrahtBot added the label CI failed on Jul 28, 2026
  111. DrahtBot commented at 10:40 AM on July 28, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task tidy: https://github.com/bitcoin/bitcoin/actions/runs/30347606100/job/90237389580</sub> <sub>LLM reason (✨ experimental): CI failed because clang-tidy reported an error (performance-no-automatic-move) in chainparamsbase.cpp, treating warnings as errors.</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>

  112. ekzyis force-pushed on Jul 28, 2026
  113. ekzyis commented at 11:52 AM on July 28, 2026: contributor

    Overview of the changes (git range-diff e34b8d5a7d 8894a6bc9c 06460c7b6a):

    1. I rewrapped all commit messages
    2. b3d54514bf: fixed accidental removal of a fuzz harness + update commit message
    3. 5418bf352a:
      • refactored the code to no longer require SIGNET_DEFAULT_CHALLENGE_HEX
      • fixed missing usage of self.chain_dir instead of self.chain in test_node.py (Claude noticed that -rpcwait wouldn't check in the right directory for custom signets)
      • added a comment regarding hex validation timing of -signetchallenge
    4. 40a538ad83: added a test for what Claude found

    Edit: Looks like I broke the interface_http.py test, investigating

    Test passes locally with and without --usecli. Force-pushed ab56c78a99 to trigger a new CI run.

    Update: CI looks good now

  114. ekzyis force-pushed on Jul 28, 2026
  115. DrahtBot removed the label CI failed on Jul 28, 2026
  116. in test/functional/feature_signet.py:129 in 40a538ad83
     124 | +
     125 | +        self.log.info("Test that the signet data directory with -signetchallenge=SIGNET_DEFAULT_CHALLENGE is 'signet'")
     126 | +        assert_node_datadir(self.nodes[6], "signet")
     127 | +
     128 | +        self.log.info("Test that a custom signet node with -rpcwait will read the auth cookie correctly")
     129 | +        self.nodes[0].wait_for_cookie_credentials()
    


    ViniciusCestarii commented at 1:40 PM on July 28, 2026:

    In "test: Add signet datadir tests" 40a538ad83c15b1f7857e4f9144a2de9e8096be5

    nit: test log is a bit misleading because this is not testing the actual bitcoin-cli -rpcwait.


    ekzyis commented at 8:27 PM on July 28, 2026:

    Right. Looking at what Claude flagged again, it just found a dormant bug in wait_for_cookie_credentials() because I didn't replace self.chain with self.chain_dir. I fixed this in 5418bf352a:

             poll_per_s = 4
             for _ in range(poll_per_s * self.rpc_timeout):
                 try:
    -                get_auth_cookie(self.datadir_path, self.chain)
    +                get_auth_cookie(self.datadir_path, self.chain_dir)
                     self.log.debug("Cookie credentials successfully retrieved")
                     return
                 except ValueError:  # cookie file not found and no rpcuser or rpcpassword; bitcoind is still starting
    @
    

    (I originally intentionally didn't replace this because it broke no test, but now I changed my mind. I now replaced all occurrences of self.chain in test_node.py.)

    This was unrelated to actually calling bitcoin-cli -rpcwait. RPC authentication is already tested well enough in the previous lines afaict.

    Since we don't need to test the test framework, I removed this test in e84bcd07c3.

  117. ViniciusCestarii commented at 2:45 PM on July 28, 2026: contributor

    ACK ab56c78a9929a8e62d377de3dc64d8a81fd85284

    Reviewed and tested changes locally.

  118. DrahtBot requested review from pinheadmz on Jul 28, 2026
  119. in test/functional/test_framework/test_node.py:558 in 5418bf352a outdated
     551 | @@ -549,7 +552,21 @@ def replace_in_config(self, replacements):
     552 |  
     553 |      @property
     554 |      def chain_path(self) -> Path:
     555 | -        return self.datadir_path / self.chain
     556 | +        return self.datadir_path / self.chain_dir
     557 | +
     558 | +    @property
     559 | +    def chain_dir(self) -> str:
    


    pinheadmz commented at 5:01 PM on July 28, 2026:

    Not blocking, I just noticed this method won't return empty for main net, which bitcoin core actually would. Turns out we do have a few mainnet tests but they set chain = "" 🤷

    https://github.com/bitcoin/bitcoin/blob/ab56c78a9929a8e62d377de3dc64d8a81fd85284/test/functional/mining_mainnet.py#L47


    ekzyis commented at 9:18 PM on July 28, 2026:

    Interesting

    resolves conversation :smile:


    On a more serious note: I think my changes didn't change this (self.chain_dir resolves to self.chain except if a custom challenge is passed to -signetchallenge), so not sure if there's something to do here

  120. in src/chainparamsbase.cpp:47 in 5418bf352a outdated
      42 | +    const std::string challenge_hex = gArgs.GetArg("-signetchallenge", "");
      43 | +    if (challenge_hex.empty()) {
      44 | +        return base_data_dir;
      45 | +    }
      46 | +    // -signetchallenge can be invalid hex here (it's checked later in
      47 | +    // ReadSigNetArgs), but we don't mind to keep validation in one place.
    


    pinheadmz commented at 6:15 PM on July 28, 2026:

    5418bf352a3dcc827739cafedf53a23aa527e334

    Nit:

    ReadSigNetArgs() isn't called from bitcoin-cli so you could this slightly confusing behavior:

    --> bccli -signet -signetchallenge=z  getblockcount
    error: Authorization failed: Failed to read cookie file and no rpcpassword was specified. Configuration file: (/Users/matthewzipkin/Library/Application Support/Bitcoin/bitcoin.conf)
    Did you forget -signetchallenge for custom signets?
    

    ekzyis commented at 9:17 PM on July 28, 2026:

    Mhh, this looks like a good follow-up!

    Before my changes, -signetchallenge didn't matter to bitcoin-cli, so my changes exposed this behavior. However, I think we want separate Concept ACKs for fixing this, for example by calling SelectParams() in the RPC client instead of only SelectBaseParams() (might be too heavy-handed).

  121. pinheadmz approved
  122. pinheadmz commented at 6:17 PM on July 28, 2026: member

    ACK ab56c78a9929a8e62d377de3dc64d8a81fd85284

    Built and tested (with and without --cli) on macos/arm64. Reviewed all changes, everything looks good. Tested with various arguments, challenges and cli commands. Tried running several signets from same machine. Happy to re-ack if @ViniciusCestarii nits are addressed.

    A few non blocking notes below.

    <details><summary>Show Signature</summary>

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA256
    
    ACK ab56c78a9929a8e62d377de3dc64d8a81fd85284
    -----BEGIN PGP SIGNATURE-----
    
    iQJPBAEBCAA5FiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmpo5R4bFIAAAAAABAAO
    bWFudTIsMi41KzEuMTIsMCwzAAoJEOfimEtiick69L8P/1rJg++qSndTVfJs2obF
    /4pmz7CrgGMaJ6fZZRc62pjGUFHYNtYQOqt9EfAP7sGUwAWXemvAiYacrXk6Am/u
    OoxZC64wWcAOJgmHdxcAMK7UlErxe6oeUXzRKeLhbAlynzCjihiNtJFeeNGf3Mq4
    z7mqiyaeCeq3U2JlRPVgUyI9lQcbhUhOC1hg1MuxaDnEtsAT1RfMhRvPYk5qfh/U
    /wpsVQKowDL0CoxYbcuyHRvyRWJNfqDMWUJixEZwJvt4qL/56AGFArxvcYkR00ZJ
    r17sHl/263L9FYLCk+OAAPZKhRIyXvMOXePAe1JRAkFDGzlbdiiUrbVncu3dJ8cT
    kg2SMSsWMY05rBUj7GEsjfNQYz64+08yJaqdoouLZ/3uTL9WfHhKzC1LlUinQtUy
    x5+Zfw1QFf/WDxoTl7zudpmYgLZm/avfmyiuedyPSncCJ8kaG1VlBsZdejscTjmI
    2c5RG+bQSVp7Hg/8fYkRJr95wQF43Tk8uI2SOyaLvdiSZzHuFU/TqnMaOBPnmdon
    KMS8zY6NGp2Nrd1GMpg8f/6xp9bWSW0UG46eEG+cT/7tOkJoXnUTz1RT4q6zmLH6
    n1cXAaPbKDn7dXJIAFdRWbczxz1qt1zSCrAfV8IkXAAcPqyTaEIziqxxGNH+p56T
    ir2Uz/aHIDqVjmxjiHRNJ+c2
    =lFTf
    -----END PGP SIGNATURE-----
    

    pinheadmz's public key is on openpgp.org

    </details>

  123. ekzyis force-pushed on Jul 28, 2026
  124. ekzyis commented at 9:25 PM on July 28, 2026: contributor

    Thanks for all the reviewz :heart:

    40b712e80b removed unnecessary test testing the test framework, see #34566 (review)

    <details> <summary>git range-diff master ab56c78 40b712e80b</summary>

    $ git range-diff master ab56c78 40b712e80b
    1:  b3d54514bf = 1:  b3d54514bf refactor: Import signet constant and helper from signet.h
    2:  5418bf352a = 2:  5418bf352a Use different datadirs for different signets
    3:  40a538ad83 ! 3:  e84bcd07c3 test: Add signet datadir tests
        @@ test/functional/feature_signet.py: class SignetBasicTest(BitcoinTestFramework):
         +
         +        self.log.info("Test that the signet data directory with -signetchallenge=SIGNET_DEFAULT_CHALLENGE is 'signet'")
         +        assert_node_datadir(self.nodes[6], "signet")
        -+
        -+        self.log.info("Test that a custom signet node with -rpcwait will read the auth cookie correctly")
        -+        self.nodes[0].wait_for_cookie_credentials()
         +
                  self.log.info("test that signet logs the network magic on node start")
                  with self.nodes[0].assert_debug_log(["Signet derived magic (message start)"]):
    4:  e09e45c5c3 = 4:  4e3d41a68c doc: Update documentation for signet datadir
    5:  ab56c78a99 = 5:  40b712e80b cli: Add -signetchallenge info on RPC auth error
    

    </details>

  125. ViniciusCestarii commented at 1:07 PM on July 29, 2026: contributor

    re-ACK 40b712e80ba1930766892ac5da9dc50e1837f5fd

  126. DrahtBot requested review from pinheadmz on Jul 29, 2026
  127. pinheadmz approved
  128. pinheadmz commented at 2:17 PM on July 29, 2026: member

    re-ACK 40b712e80ba1930766892ac5da9dc50e1837f5fd

    Minimal change since last review, just remove an unnecessary functional test.

    <details><summary>Show Signature</summary>

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA256
    
    ACK 40b712e80ba1930766892ac5da9dc50e1837f5fd
    -----BEGIN PGP SIGNATURE-----
    
    iQJPBAEBCAA5FiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmpqC0gbFIAAAAAABAAO
    bWFudTIsMi41KzEuMTIsMCwzAAoJEOfimEtiick66Q4P/ims1GazlwLyVwwBSfZ3
    7+DaxyZxlRlskkAOfa7lrt+txC3MYpNktIZ1swZQ7w4DbYyE7pyp4SLpwkrNEhgd
    bZE4kWAvwBU2xrKT0SQx0SPKDc2mpkjOpBCqstZS0sR/t872/DkIh2gJE4JoxAOF
    Bmmi0C1oxJ8QHVJT6QoM1J7GQym0hplBcRSDEfhDdH2NtjqMGF42n1E1tamPjKSG
    LacGk1PxBhAF/BVn6Rg029GD8d6QzV+W6nSmpDSX6wP2VmguVn6heRbzDdZf20oE
    hElJvY6bY8okzJDJkt+xNDsGCQjfmXDfct/i7mtHmkwJYnsawhhVhtFDN1xsWNBN
    x9nRDjbXlEGJpz5JIJXX1W8ReK4q234jwaS0fXZUiUlLzA8jlfsJ6ZEe1VcJyX8l
    nIEtKSaL41v5x15JjXcxZcGwTIcZ5DQg2h8j8ZDOPXvO1CwABngYLteDGs9aEseQ
    iplcI+YgOR/jBqwrZmkA05M5I8BDZ0O/2Ruvgalok9nsgvrXoz2T53gNBJYzm8f8
    GeSkFmK/Ok+LeKLeq3fP5fyVkCFaqUyfgaOwrT5R9aeFKSgtXthSmG3mDP+v9SLW
    JI+oIPeHVLUpxs6CxP0UlNGlb3jQsZajhhmTbx0veqtSAslKwDqw7SusgE+gEEfi
    hX2XKSzmoSCNZujNY7LDepNB
    =sGEv
    -----END PGP SIGNATURE-----
    

    pinheadmz's public key is on openpgp.org

    </details>

  129. pinheadmz commented at 2:42 PM on July 29, 2026: member

    I also built and ran the fuzzer from this branch just to make sure nothing got bumped šŸ‘

  130. sedited requested review from ajtowns on Aug 4, 2026
  131. in src/CMakeLists.txt:147 in b3d54514bf
     143 | @@ -144,6 +144,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL
     144 |    script/sign.cpp
     145 |    script/signingprovider.cpp
     146 |    script/solver.cpp
     147 | +  signet.cpp
    


    ajtowns commented at 8:55 PM on August 13, 2026:

    I don't think this really makes sense -- signet.cpp is mostly validation logic, so shouldn't be added to common.

    I think just adding a dedicated header would be better, perhaps kernel/signet_default.h:

    #ifndef BITCOIN_KERNEL_SIGNET_DEFAULT_H
    #define BITCOIN_KERNEL_SIGNET_DEFAULT_H
    
    #include <hash.h>
    #include <kernel/messagestartchars.h>
    #include <uint256.h>
    #include <util/strencodings.h>
    
    #include <algorithm>
    #include <cstdint>
    #include <vector>
    
    namespace kernel {
    using namespace util::hex_literals;
    inline const std::vector<uint8_t> SIGNET_DEFAULT_CHALLENGE{
        "512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"_hex_v_u8
    };
    
    inline MessageStartChars GetSignetMessageStart(const std::vector<uint8_t>& signet_challenge)
    {
        HashWriter h{};
        h << signet_challenge;
        const uint256 hash = h.GetHash();
        MessageStartChars msg_start;
        std::copy_n(hash.begin(), 4, msg_start.begin());
        return msg_start;
    }
    } // kernel namespace
    
    #endif // BITCOIN_KERNEL_SIGNET_DEFAULT_H
    

    ekzyis commented at 2:19 PM on August 14, 2026:

    Ohh, this requires no changes to any CMakeLists.txt, nice!

    Done in 75501ddc54. I named the file kernel/signet.h instead of kernel/signet_default.h because GetSignetMessageStart is IMO about more than just the default signet and to be consistent with other headers (chainparams.h and kernel/chainparams.h).

  132. in src/chainparamsbase.cpp:80 in 5418bf352a
      74 | @@ -56,6 +75,9 @@ std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const ChainType chain)
      75 |  
      76 |  void SelectBaseParams(const ChainType chain)
      77 |  {
      78 | -    globalChainBaseParams = CreateBaseChainParams(chain);
      79 | +    // We need to call SelectConfigNetwork before CreateBaseChainParams since we
      80 | +    // check -signetchallenge in CreateBaseChainParams to determine the signet
      81 | +    // datadir.
    


    ajtowns commented at 9:10 PM on August 13, 2026:

    Might be clearer if gArgs were passed as an argument to CreateBaseChainParams and GetSignetDataDir rather than be a global with ordering dependencies?


    ekzyis commented at 2:19 PM on August 14, 2026:

    Makes sense! Done in 67a057eb91 and removed the comment. I think the order is now clear based on what CreateBaseChainParams accepts. But it's not enforced because you could still pass gArgs before calling gArgs.SelectConfigNetwork(chainType). Lmk if you think the comment still makes sense.

  133. in src/chainparamsbase.cpp:42 in 5418bf352a
      35 | @@ -33,6 +36,22 @@ const CBaseChainParams& BaseParams()
      36 |      return *globalChainBaseParams;
      37 |  }
      38 |  
      39 | +std::string GetSignetDataDir()
      40 | +{
      41 | +    std::string base_data_dir = "signet";
      42 | +    const std::string challenge_hex = gArgs.GetArg("-signetchallenge", "");
    


    ajtowns commented at 9:16 PM on August 13, 2026:

    I think this should be auto challenge_hex = gArgs.GetArg("-signetchallenge"); (no default, returns an optional), so that -signetchallenge= is interpreted as an empty challenge and can be treated as distinct from the default challenge. I believe this matches the behaviour in chainparams.cpp.


    ekzyis commented at 2:24 PM on August 14, 2026:

    I see that on master, we support -signetchallenge= as its own distinct network. My code here would interpret it as the default challenge, so there's def a mismatch here, thank you! Fixed in 67a057eb91 and added a test in 1fbfaa11a4.

    However, is it intentional to allow empty -signetchallenge=? Does a signet without any challenge make sense? I thought there must be a challenge for the miner, or at least a trivial challenge like OP_TRUE.

  134. ajtowns commented at 9:33 PM on August 13, 2026: contributor

    Approach ACK 40b712e80ba1930766892ac5da9dc50e1837f5fd

  135. refactor: Import signet constant and helper from kernel/signet.h
    We want to reuse SIGNET_DEFAULT_CHALLENGE and GetSignetMessageStart in
    the following commit, so we import them from a new signet header in
    src/kernel.
    
    We created a new header file instead of reusing src/signet.h to avoid
    unnecessary changes to any CMakeLists.txt.
    75501ddc54
  136. ekzyis force-pushed on Aug 14, 2026
  137. Use different datadirs for different signets
    This adds support for syncing multiple signets.
    
    Each custom signet is stored in a different datadir, using the network
    magic (message start) as the suffix.
    
    The default signet is always stored without a suffix for backward
    compatibility, even if the default challenge is provided explicitly via
    -signetchallenge.
    
    Co-authored-by: Brandon Odiwuor <brandon.odiwuor@gmail.com>
    67a057eb91
  138. test: Add signet datadir tests
    Co-authored-by: Brandon Odiwuor <brandon.odiwuor@gmail.com>
    1fbfaa11a4
  139. doc: Update documentation for signet datadir fac9f02af7
  140. cli: Add -signetchallenge info on RPC auth error f3be3f67a1
  141. ekzyis force-pushed on Aug 14, 2026
  142. DrahtBot added the label CI failed on Aug 14, 2026
  143. DrahtBot removed the label CI failed on Aug 14, 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-08-14 18:51 UTC

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