Skip to content

ipc: add bitcoin-mine test program#30437

Open
ryanofsky wants to merge 5 commits into
bitcoin:masterfrom
ryanofsky:pr/mine
Open

ipc: add bitcoin-mine test program#30437
ryanofsky wants to merge 5 commits into
bitcoin:masterfrom
ryanofsky:pr/mine

Conversation

@ryanofsky

@ryanofsky ryanofsky commented Jul 12, 2024

Copy link
Copy Markdown
Contributor

Add bitcoin-mine executable to test connecting to the bitcoin-node process over a unix socket and calling interface::Mining methods.

This is useful as discussed in #29432 (comment) to work on getting mining code working in an external process.

This PR initially supported spawning a new bitcoin-node process if a connection could not be established with an existing process, but it was dropped to simplify the code. A branch building on this PR and adding that support is
pr/mine-detach.


This PR is part of the process separation project.

@DrahtBot

DrahtBot commented Jul 12, 2024

Copy link
Copy Markdown
Contributor

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

Code Coverage & Benchmarks

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

Reviews

See the guideline for information on the review process.

Type Reviewers
Stale ACK ismaelsadeeq, BrandonOdiwuor, Sjors

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #35551 (test: add interface_gui.py to test bitcoin-qt startup by ryanofsky)
  • #35167 (Convert check-deps.sh to python by ajtowns)
  • #35037 (ipc: support per-address max-connections options on -ipcbind by enirox001)

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 changed the title multiprocess: add bitcoin-mine test program  multiprocess: add bitcoin-mine test program Jul 12, 2024
@Sjors

Sjors commented Jul 12, 2024

Copy link
Copy Markdown
Member

Thanks. I'll try to get the Mining interface in good shape as well, which this can then be rebased on:

@DrahtBot

Copy link
Copy Markdown
Contributor

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

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.

@ryanofsky

Copy link
Copy Markdown
Contributor Author

Updated 8e93673 -> 3c0a13c (pr/mine.1 -> pr/mine.2, compare) filling in missing serialization for CBlockTemplate and BlockValidationState types, also fixing link errors for test programs that were causing CI tasks to fail.

@ryanofsky

ryanofsky commented Jul 12, 2024

Copy link
Copy Markdown
Contributor Author

I'll work on dropping the spawn functionality from this PR next, and just having the bitcoin-mine program print an error when it can't connect to the node socket, instead of starting the node itself. Sjors suggested this in #29432 (comment):

If it helps getting IPC stuff merged quicker, it's fine by me if the miner can't spin up a node process (see e.g. Sjors@b6dfa5f)

This would simplify the PR by allowing 3 commits to be dropped:

Other things I'd like to do:

  • Write unit tests to cover the new code
  • Split up the last commit
  • Fix remaining CI errors
  • Add -ipconnect option to bitcoin-mine to allow specifying custom path for unix socket.

@Sjors

Sjors commented Jul 12, 2024

Copy link
Copy Markdown
Member

filling in missing serialization for CBlockTemplate

You won't need that if you include #30440, though maybe it's better to wait until that's merged to avoid too much code churn.

@ryanofsky

Copy link
Copy Markdown
Contributor Author

filling in missing serialization for CBlockTemplate

You won't need that if you include #30440, though maybe it's better to wait until that's merged to avoid too much code churn.

Yeah definitely some of that should not be needed. Some parts will probably be needed though. For example, the initial version of this PR did not support deserializing CTransaction or CBlock objects (because CTransaction does not have an Unserialize method, and also requires a version parameter to be set).

@Sjors

Sjors commented Jul 12, 2024

Copy link
Copy Markdown
Member

I'll certainly need CBlock. It's probably good to support CTransaction as well, as it might allow for more efficiently fetching only missing transactions in a template update, but that would be a future optimisation.

@Sjors

Sjors commented Jul 12, 2024

Copy link
Copy Markdown
Member

I opened draft PRs for all interface changes that I think I will need, see updated comment above: #30437 (comment)

If you can cherry-pick them all, I'll expand the demo app to simulate all the steps a Template Provider would do. That should reveal any remaining gaps in the interface.

@Sjors

Sjors commented Jul 13, 2024

Copy link
Copy Markdown
Member

I wrote:

I'll expand the demo app to simulate all the steps a Template Provider would do

Once that works the next step for me would be to open an alternative to #29432 which runs the Template Provider in bitcoin-tp. I might wait with that until we ship a release with this interface enabled on the node side, ideally in bitcoind and bitcoin-qt rather than in seperate bitcoin-node and bitcoin-gui binaries.

I could then modify the guix build to only produce bitcoin-tp. This effectively turns it into a separate project that just happens to have access to the entire Bitcoin Core codebase.

That way the user can simply install and run a recent Bitcoin Core release in the manner they're familiar with. They would install bitcoin-tp separately and it should just work(tm), at most having to add a line to bitcoin.conf to turn IPC on.

If later on we can also turn libbitcoin_net into a library (see #30350), I could look into completely separating the codebase (I'd probably copy-paste the Bitcoin Core code and strip everything out I don't need, rather starting a new c++ project and build system from scratch). E.g. it could pull in libbitcoin_net, libbitcoin_util and whatever else it needs, add its own Transport subclass. This lets me reuse #29346, #30315 and #30332, but without having to merge them into Bitcoin Core itself.

This would be in parallel to the alternative approach of writing bitcoin-tp from scratch in rust reusing SRI code.

With both approaches it will take a while to reach feature parity with #29432 so I'll have to keep maintaining that too (using the same interface, but not as a separate process).

@ryanofsky

Copy link
Copy Markdown
Contributor Author

I simplified PR as suggested so bitcoin-mine no longer spawns bitcoin-node if it can't connect to it. Instead it just prints an error message and suggests a command line to run bitcoin-node manually.

I also added a new commit to make the IPC code compatible with all the interfaces added in #30409, #30356, #30440, and #30443, so this PR is easier to test with those interfaces.

I still need to break up the main commit ("Add bitcoin-mine test program") and add unit tests for the new connecting, listening, and serialization code that's introduced.

Rebased 3c0a13c -> 4e1a434 (pr/mine.2 -> pr/mine.3, compare) with those changes.

For reference, the code that was dropped from this PR allowing bitcoin-mine to spawn a bitcoin-node process is in commits:

  • 09621da multiprocess: Add IPC spawnProcess detach option
  • 3d23332 multiprocess: Add IPC attach and detach methods
  • 3c11262 multiprocess: Allow bitcoin-mine to spawn bitcoin-node

These changes would probably be useful in another context, but aren't needed for this test program.

re: #30437 (comment)

I think your ideas for packaging bitcoin-tp make sense. And maybe it would also be good to try to integrate IPC in bitcoind and bitcoin-qt executables instead of separate bitcoin-node and bitcoin-gui executables. Would have to think about how to implement this and what the tradeoffs would be.

@ROLANDSIHOMBING1

This comment was marked as off-topic.

Comment thread src/bitcoin-mine.cpp
}
block_template->submitSolution(block.nVersion, block.nTime, block.nNonce, block_template->getCoinbaseTx());

if (tip->hash != mining->getTip()->hash) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In 1a844b8 bitcoin-mine: Extend example to mine a block: just thought I got lucky on mainnet, but this does not distinguish between actually mining a block and a peer giving ups a new tip :-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

re: #30437 (comment)

In 1a844b8 bitcoin-mine: Extend example to mine a block: just thought I got lucky on mainnet, but this does not distinguish between actually mining a block and a peer giving ups a new tip :-)

I guess technically the message didn't say who mined the block. But reworded it to avoid unnecessary excitement

@Sjors

Sjors commented Nov 4, 2025

Copy link
Copy Markdown
Member

re-ACK a2365f1

Since my last review of e1f139b, commits 8bf2269 and b094d38 were absorbed into #33201 which added direct coverage of IPC via a functional test. This also allowed some simplification of test/functional/interface_ipc_mining.py which this PR adds.

I initially wanted to suggest folding this test into interface_ipc.py, but the latter has a dependency on PyCap.

@Sjors

Sjors commented Nov 18, 2025

Copy link
Copy Markdown
Member

re-ACK 2cb5e64

Trivial rebase after #30595 added libbitcoinkernel (experimental) to the CMake summary.

@Sjors Sjors mentioned this pull request Dec 9, 2025
13 tasks

@ryanofsky ryanofsky left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated 2cb5e64 -> 057ea0a (pr/mine.34 -> pr/mine.35, compare) just tweaking bitcoin-mine status message

Rebased 057ea0a -> 69d23a0 (pr/mine.35 -> pr/mine.36, compare) due to conflicts with #34452

Rebased 69d23a0 -> bffde2f (pr/mine.36 -> pr/mine.37, compare) due to conflict with #34860

Comment thread src/bitcoin-mine.cpp

while (tries_remaining> 0 && block.nNonce < std::numeric_limits<uint32_t>::max() && !CheckProofOfWork(block.GetHash(), block.nBits, consensus_params)) {
++block.nNonce;
--tries_remaining;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

re: #30437 (comment)

+1. I think just having simple code to demonstrate the API with comments pointing out how usage could be more robust is a good middle ground.

Comment thread src/bitcoin-mine.cpp Outdated

// Note: This loop ignores tip changes so the submitSolution call below
// could fail even if the CheckProofOfWork succeeds! A more realistic miner
// could avoid this problem by calling call block_template->waitNext()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

re: #30437 (comment)

Thanks, fixed

Comment thread src/bitcoin-mine.cpp
}
block_template->submitSolution(block.nVersion, block.nTime, block.nNonce, block_template->getCoinbaseTx());

if (tip->hash != mining->getTip()->hash) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

re: #30437 (comment)

In 1a844b8 bitcoin-mine: Extend example to mine a block: just thought I got lucky on mainnet, but this does not distinguish between actually mining a block and a peer giving ups a new tip :-)

I guess technically the message didn't say who mined the block. But reworded it to avoid unnecessary excitement

@Sjors

Sjors commented Jan 5, 2026

Copy link
Copy Markdown
Member

ACK 057ea0a

@sedited

sedited commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

Would be good to figure out how to proceed, given that this has been an open PR with ACKs for a while now.

This is useful as discussed in #29432 (comment) to work on getting mining code working in an external process.

I think this motivation is a bit stale at this point. We both have a rust and c++ client for the existing mining interface under the umbrella of the sv2 project and it seems less pressing to have our own example client. Is there a better motivation that could be provided now?

@Sjors

Sjors commented Feb 2, 2026

Copy link
Copy Markdown
Member

I do think having our own example client is useful, ideally one that covers a variety of methods and data types. It might help catch regressions and add test coverage for fixes. It's more time consuming to wait for downstream projects to report things and test our fixes.

However so far we've put most effort into the functional test interface_ipc.py (e.g. #34452). That gets us most of the above benefits. But there is an important difference: it doesn't use libmultiprocess on the client side. So it does still seem useful to have coverage for both libmultiprocess and a "bare" client.

c++ client for the existing mining interface under the umbrella of the sv2 project

If the Rust client takes off, it might replace most of the c++ client usage due to its easier integration with the rest of sv2. So it could see less maintainance in the future.

@sedited

sedited commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

So it does still seem useful to have coverage for both libmultiprocess and a "bare" client.

Would a more dedicated test binary implementing a libmultiprocess client for these ipc interfaces that we don't consume ourselves be more suited then? Maybe what is introduced here could be morphed into its own standalone binary using boost test, capable of spawning bitcoin-node, waiting for it to warm up and then executing a few units tests?

Comment thread CMakeLists.txt
message(" bitcoin-util ........................ ${BUILD_UTIL}")
message(" bitcoin-wallet ...................... ${BUILD_WALLET_TOOL}")
message(" bitcoin-chainstate (experimental) ... ${BUILD_UTIL_CHAINSTATE}")
message(" bitcoin-mine (experimental) ......... ${bitcoin_daemon_status}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this should get its own variable. If I read this correctly, we would ship this as part of a release with the current configuration? Having a CI job that exercises it seems enough to me for its current purposes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think this is something that needs to be shipped in releases. Looks like some of the code here is for supporting this bin on Windows, but that doesn't seem needed, given IPC isn't even supported on Windows? Maybe it can just be disabled for Windows entirely?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems similar to bitcoin-chainstate: a demo and test app that we should at least build in dev preset, but probably don't need to ship in a release.

Perhaps it's better to move both of them to an "examples" folder?

Regarding Windows: support is still on the agenda for libmultiprocess: bitcoin-core/libmultiprocess#231

And specifically for mining, my understanding is that people actually use that operating system. Although at the same time so far I haven't had anyone ask for it for sv2-tp (stratum-mining/sv2-tp#7).

@ryanofsky ryanofsky Feb 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

re: #30437 (comment)

Yes good catch. This should get it's own variable and not be part of binary releases. At the time this was written ENABLE_IPC was off by default so it didn't seem like another variable was needed, but definitely makes sense now.

re: #30437 (comment)

I do like the idea of treating this similarly to bitcoin-chainstate since they are both examples and tests. Having an examples folder coud be an alternative to renaming the binary to test_bitcoin-mining as suggested #30437 (comment).

@ryanofsky

Copy link
Copy Markdown
Contributor Author

Maybe what is introduced here could be morphed into its own standalone binary using boost test, capable of spawning bitcoin-node, waiting for it to warm up and then executing a few units tests?

Yes, I think something like this makes sense. Right now there is no IPC client executable in bitcoin core to test coverage of client IPC code or to provide an example of what C++ client would look like. So this executable fills that gap, but maybe it would be better to write it more like a test and less like an example.

It would be pretty simple to rename the executable to test_bitcoin-mining to match test_bitcoin-qt as one step in that direction.

The idea of using the boost test framework could also make sense, but it would probably require the test program to spawn the node binary itself instead of relying on python to do it. And earlier version of this PR supported that in 3 commits that were dropped to reduce the size of the PR (#30437 (comment)). These changes could be brought back or be a followup. (They could be useful in other cases like a GUI or other wrapper connecting to the node over IPC and starting it if not started.)

Also want to note that some changes here may be more generally useful. The first commit 1987f89 was also used in #32297 and #32898 and the second commit f28ac51 was used in #32898. Also 3 commits that were originally in this PR became part of #33201.

I guess I generally see the changes here as being useful and I wonder if just renaming the new executable & command to be clear it is for testing would address concerns about it. Exposing this as bitcoin-mine even if installed in libexec and not in binary releases could make it seem like it has more utility than it has, or is ever likely to have.

@sedited

sedited commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

I guess I generally see the changes here as being useful and I wonder if just renaming the new executable & command to be clear it is for testing would address concerns about it. Exposing this as bitcoin-mine even if installed in libexec and not in binary releases could make it seem like it has more utility than it has, or is ever likely to have.

Yes, I think that would be enough (next to making its compilation conditional). I also like the idea of an examples folder, but I don't think the changes here should be conditional on that.

ryanofsky and others added 5 commits May 26, 2026 08:19
Allows InitError and InitWarning to be used by other executables beside
bitcoind and bitcoin-node.
See src/bitcoin-mine.cpp for usage information.

Co-authored-by: Sjors Provoost <sjors@sprovoost.nl>
Co-authored-by: Sjors Provoost <sjors@sprovoost.nl>
This includes createNewBlock fix from bitcoin#34860 to allow mining blocks at
heights <16.

Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
Suggested by Sjors Provoost <sjors@sprovoost.nl> in
bitcoin#30437 (comment)

@w0xlt w0xlt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A few review comment:

Comment thread src/bitcoin-mine.cpp

uint64_t max_tries{std::max<uint64_t>(DEFAULT_MAX_TRIES, args.GetIntArg("-maxtries", DEFAULT_MAX_TRIES))};
auto tries_remaining{max_tries};
auto block_template{mining->createNewBlock({}, /*cooldown=*/false)};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Mining::createNewBlock() can legally return nullptr when bitcoin-node is shutting down or mining is interrupted.

diff --git a/src/bitcoin-mine.cpp b/src/bitcoin-mine.cpp
index f02ed3dfba..d33c9e2326 100644
--- a/src/bitcoin-mine.cpp
+++ b/src/bitcoin-mine.cpp
@@ -133,6 +133,10 @@ MAIN_FUNCTION
     uint64_t max_tries{std::max<uint64_t>(DEFAULT_MAX_TRIES, args.GetIntArg("-maxtries", DEFAULT_MAX_TRIES))};
     auto tries_remaining{max_tries};
     auto block_template{mining->createNewBlock({}, /*cooldown=*/false)};
+    if (!block_template) {
+        tfm::format(std::cerr, "Error: Failed to create block template. bitcoin-node may be shutting down.\n");
+        return EXIT_FAILURE;
+    }
     auto block{block_template->getBlock()};
     block.hashMerkleRoot = BlockMerkleRoot(block);

Comment thread src/bitcoin-mine.cpp

auto consensus_params{Params().GetConsensus()};

uint64_t max_tries{std::max<uint64_t>(DEFAULT_MAX_TRIES, args.GetIntArg("-maxtries", DEFAULT_MAX_TRIES))};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With std::max(DEFAULT_MAX_TRIES, ...), max_tries can never be lower than DEFAULT_MAX_TRIES, so values like -maxtries=0 or -maxtries=100 are ignored and become 10000. Is this intentional ?

Also, GetIntArg() returns a signed integer, but std::max<uint64_t>() converts it to unsigned. A negative value like -maxtries=-1 can become a huge uint64_t.

diff --git a/src/bitcoin-mine.cpp b/src/bitcoin-mine.cpp
index f02ed3dfba..d92a45501e 100644
--- a/src/bitcoin-mine.cpp
+++ b/src/bitcoin-mine.cpp
@@ -130,9 +130,18 @@ MAIN_FUNCTION
 
     auto consensus_params{Params().GetConsensus()};
 
-    uint64_t max_tries{std::max<uint64_t>(DEFAULT_MAX_TRIES, args.GetIntArg("-maxtries", DEFAULT_MAX_TRIES))};
+    int64_t max_tries_arg{args.GetIntArg("-maxtries", DEFAULT_MAX_TRIES)};
+    if (max_tries_arg < 0) {
+        tfm::format(std::cerr, "Error: -maxtries must be non-negative.\n");
+        return EXIT_FAILURE;
+    }
+    uint64_t max_tries{static_cast<uint64_t>(max_tries_arg)};
     auto tries_remaining{max_tries};
     auto block_template{mining->createNewBlock({}, /*cooldown=*/false)};

@Sjors

Sjors commented Jun 30, 2026

Copy link
Copy Markdown
Member

It might be good to emphasize in the PR description that this adds coverage for how a libmultiprocess based client interacts with the node. Our functional tests do not use libmutltiprocess on the client side. We know from experience with sv2-tp vs. the Rust based SRI implementation that these differences can be important.

Exposing this as bitcoin-mine even if installed in libexec and not in binary releases could make it seem like it has more utility than it has, or is ever likely to have.

Yes, I think this should be seen as a combination of a usage example and test coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.