-
Notifications
You must be signed in to change notification settings - Fork 35
WIP implement handle_push_solution on jd_server_sv2 + bitcoin_core_sv2
#593
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
plebhash
wants to merge
13
commits into
stratum-mining:main
Choose a base branch
from
plebhash:2026-07-01-handle-push-solution
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
015efce
bitcoin_core_sv2 + jd-server: stale-tip by prev_hash and simplify val…
plebhash 5458158
bitcoin_core_sv2: replace createNewBlock with getTip in force_update_…
plebhash 277b9e2
bitcoin_core_sv2: map bad-cb-height checkBlock rejection to stale-cha…
plebhash ba5418d
integration-tests: rewrite JDP stale scenario to use missing-txs retry
plebhash 1253474
integration-tests: add JDP IO coverage for stale-at-arrival declare jobs
plebhash 0df1951
prepare JDP responses for full-block submitBlock support
plebhash b067686
refactor(bitcoin-core-sv2): add v32x_v31x_v30x and v32x_v31x shared m…
plebhash df00676
add v32 IPC runtime and align JDP wiring across versions
plebhash dd2f151
reconstruct and submit full blocks on PushSolution
plebhash 915e7e2
WIP: prepare v32 integration test harness
plebhash b1bb9ea
enable v32 integration coverage
plebhash 9b82065
switch ipc config defaults to version 32
plebhash 600ee71
document and warn on v30x/v31x PushSolution no-op
plebhash File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| //! A simple example of how to use `BitcoinCoreSv2TDP`. | ||
| //! | ||
| //! This example demonstrates the pattern used in applications where `BitcoinCoreSv2TDP` is | ||
| //! spawned in a dedicated thread with its own Tokio runtime and `LocalSet`. This allows the | ||
| //! main application to run in a separate async context while `BitcoinCoreSv2TDP` runs in its | ||
| //! own isolated thread. | ||
| //! | ||
| //! We connect to the Bitcoin Core UNIX socket, and log the received Sv2 Template Distribution | ||
| //! Protocol messages. | ||
| //! | ||
| //! We send a `CoinbaseOutputConstraints` message to the `BitcoinCoreSv2TDP` instance once at | ||
| //! startup. | ||
| //! | ||
| //! `BitcoinCoreSv2TDP` will not start distributing new templates until it receives the first | ||
| //! `CoinbaseOutputConstraints` message. | ||
|
|
||
| use bitcoin_core_sv2::unix_capnp::v32x::template_distribution_protocol::BitcoinCoreSv2TDP; | ||
| use std::path::Path; | ||
|
|
||
| use async_channel::unbounded; | ||
| use stratum_core::{ | ||
| parsers_sv2::TemplateDistribution, | ||
| template_distribution_sv2::{CoinbaseOutputConstraints, RequestTransactionData}, | ||
| }; | ||
| use tokio_util::sync::CancellationToken; | ||
| use tracing::{error, info}; | ||
|
|
||
| #[tokio::main] | ||
| async fn main() { | ||
| tracing_subscriber::fmt::init(); | ||
|
|
||
| // the user must provide the path to the Bitcoin Core UNIX socket | ||
| let args: Vec<String> = std::env::args().collect(); | ||
| if args.len() != 2 { | ||
| eprintln!("Usage: {} <bitcoin_core_unix_socket_path>", args[0]); | ||
| eprintln!("Example: {} /path/to/bitcoin/regtest/node.sock", args[0]); | ||
| std::process::exit(1); | ||
| } | ||
|
|
||
| let bitcoin_core_unix_socket_path = Path::new(&args[1]); | ||
|
|
||
| // `BitcoinCoreSv2TDP` uses this to cancel internally spawned tasks | ||
| let cancellation_token = CancellationToken::new(); | ||
|
|
||
| // get new templates whenever the mempool has changed by more than 100 sats | ||
| let fee_threshold = 100; | ||
|
|
||
| // the minimum interval between template updates in seconds | ||
| let min_interval = 5; | ||
|
|
||
| // these messages are sent into the `BitcoinCoreSv2TDP` instance | ||
| let (msg_sender_into_bitcoin_core_sv2, msg_receiver_into_bitcoin_core_sv2) = unbounded(); | ||
| // these messages are received from the `BitcoinCoreSv2TDP` instance | ||
| let (msg_sender_from_bitcoin_core_sv2, msg_receiver_from_bitcoin_core_sv2) = unbounded(); | ||
|
|
||
| // clone so we can move it into the thread | ||
| let cancellation_token_clone = cancellation_token.clone(); | ||
| let bitcoin_core_unix_socket_path_clone = bitcoin_core_unix_socket_path.to_path_buf(); | ||
|
|
||
| // spawn a dedicated thread to run the BitcoinCoreSv2TDP instance | ||
| // because we're limited to tokio::task::LocalSet | ||
| // | ||
| // please note that it's important to keep a reference to the join handle so we can wait for it | ||
| // to finish shutdown this will no longer be a pre-requisite once https://github.com/bitcoin/bitcoin/pull/33676 lands in a release | ||
| // see https://github.com/stratum-mining/sv2-apps/issues/81 for more details | ||
| let join_handle = std::thread::spawn(move || { | ||
| // we need a dedicated runtime so we can spawn an async task inside the LocalSet | ||
| let rt = match tokio::runtime::Runtime::new() { | ||
| Ok(rt) => rt, | ||
| Err(e) => { | ||
| error!("Failed to create Tokio runtime: {:?}", e); | ||
| cancellation_token_clone.cancel(); | ||
| return; | ||
| } | ||
| }; | ||
| let tokio_local_set = tokio::task::LocalSet::new(); | ||
|
|
||
| tokio_local_set.block_on(&rt, async move { | ||
| // create a new `BitcoinCoreSv2TDP` instance | ||
| let mut sv2_bitcoin_core = match BitcoinCoreSv2TDP::new( | ||
| &bitcoin_core_unix_socket_path_clone, | ||
| fee_threshold, | ||
| min_interval, | ||
| msg_receiver_into_bitcoin_core_sv2, | ||
| msg_sender_from_bitcoin_core_sv2, | ||
| cancellation_token_clone.clone(), | ||
| ) | ||
| .await | ||
| { | ||
| Ok(sv2_bitcoin_core) => sv2_bitcoin_core, | ||
| Err(e) => { | ||
| error!("Failed to create BitcoinCoreToSv2: {:?}", e); | ||
| cancellation_token_clone.cancel(); | ||
| return; | ||
| } | ||
| }; | ||
|
|
||
| // run the `BitcoinCoreSv2TDP` instance, which will block until the cancellation token | ||
| // is activated | ||
| sv2_bitcoin_core.run().await; | ||
| }); | ||
| }); | ||
|
|
||
| // clone so we can move it | ||
| let cancellation_token_clone = cancellation_token.clone(); | ||
|
|
||
| // clone so we can move it | ||
| let msg_sender_into_bitcoin_core_sv2_clone = msg_sender_into_bitcoin_core_sv2.clone(); | ||
|
|
||
| // a task to consume and log the received Sv2 Template Distribution Protocol messages | ||
| tokio::spawn(async move { | ||
| loop { | ||
| tokio::select! { | ||
| // monitor for Ctrl+C, activating the cancellation token and exiting the loop | ||
| _ = tokio::signal::ctrl_c() => { | ||
| info!("Ctrl+C received"); | ||
| cancellation_token_clone.cancel(); | ||
| return; | ||
| } | ||
| // monitor potential internal activations of the cancellation token for exiting the loop | ||
| _ = cancellation_token_clone.cancelled() => { | ||
| info!("Cancellation token activated"); | ||
| return; | ||
| } | ||
| // monitor for Sv2 Template Distribution Protocol messages | ||
| // coming from `BitcoinCoreSv2TDP` | ||
| Ok(template_distribution_message) = msg_receiver_from_bitcoin_core_sv2.recv() => { | ||
| // log the message | ||
| info!("Message received: {}", template_distribution_message); | ||
|
|
||
| // send a RequestTransactionData every time a NewTemplate message is received | ||
| if let TemplateDistribution::NewTemplate(new_template) = template_distribution_message { | ||
| let template_id = new_template.template_id; | ||
| let request_transaction_data = TemplateDistribution::RequestTransactionData(RequestTransactionData { | ||
| template_id, | ||
| }); | ||
|
|
||
| match msg_sender_into_bitcoin_core_sv2_clone.send(request_transaction_data).await { | ||
| Ok(_) => (), | ||
| Err(e) => { | ||
| error!("Failed to send request transaction data: {}", e); | ||
| cancellation_token_clone.cancel(); | ||
| return; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| // send CoinbaseOutputConstraints once at startup | ||
| // | ||
| // `BitcoinCoreSv2TDP` will not start distributing new templates until it receives the first | ||
| // `CoinbaseOutputConstraints` message. | ||
| let new_coinbase_output_constraints = | ||
| TemplateDistribution::CoinbaseOutputConstraints(CoinbaseOutputConstraints { | ||
| coinbase_output_max_additional_size: 2, | ||
| coinbase_output_max_additional_sigops: 2, | ||
| }); | ||
|
|
||
| if let Err(e) = msg_sender_into_bitcoin_core_sv2 | ||
| .send(new_coinbase_output_constraints) | ||
| .await | ||
| { | ||
| error!("Failed to send coinbase output constraints: {}", e); | ||
| cancellation_token.cancel(); | ||
| } | ||
| info!("Sent CoinbaseOutputConstraints"); | ||
|
|
||
| // wait for the cancellation token to be activated | ||
| cancellation_token.cancelled().await; | ||
| info!("Shutting down..."); | ||
|
|
||
| // wait for the dedicated thread to finish shutdown | ||
| join_handle.join().unwrap(); | ||
| info!("BitcoinCoreSv2TDP dedicated thread shutdown complete."); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note to self:
we cannot do
JdRequest::PushSolutiontake aBlock#609 (comment)
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another reason to pivot away from this approach:
I'm noticing a lot of JDS log lines like this on my signet e2e tests:
it doesn't happen 100% of the time, because propagation on JDC side is always successful, and I also see JDS log lines like this:
I haven't been able to trace the root cause of the
submitBlockfailures with 100% certainty, but my suspicion is that the solution is being applied to the wrong custom job, due toDeclareMiningJobvsPushSolutionraces, which is a direct consequence of the KISS approach of only processingPushSolutionagainst the "latest ACKdDeclareMiningJob"