Skip to content

WIP implement handle_push_solution on jd_server_sv2 + bitcoin_core_sv2#593

Draft
plebhash wants to merge 13 commits into
stratum-mining:mainfrom
plebhash:2026-07-01-handle-push-solution
Draft

WIP implement handle_push_solution on jd_server_sv2 + bitcoin_core_sv2#593
plebhash wants to merge 13 commits into
stratum-mining:mainfrom
plebhash:2026-07-01-handle-push-solution

Conversation

@plebhash

@plebhash plebhash commented Jul 2, 2026

Copy link
Copy Markdown
Member

NOT READY FOR REVIEW

close #441

blocked by:

  • Bitcoin Core v32 release
  • bitcoin-capnp-types release compatible with v32

builds on top of:


a few conceptual points worthy of note for reviewers:

## JdRequest::PushSolution must carry Block

before this PR, JdRequest::PushSolution carried a PushSolution message

however, Bitcoin Core's new submitBlock capnproto method needs a full block

BitcoinCoreSv2JDP does not have enough context to reconstruct a full block from a PushSolution, so it's JDS's responsability to do that

therefore, JdRequest::PushSolution now carries a bitcoin::Block instead of a PushSolution message

## JdResponse::Success must carry txdata

before this PR, DeclaredCustomJob carried a txid_list, which was used for merkle_path validation of SetCustomMiningJob

given that now it's JDS responsability to reconstruct the full block from a PushSolution, txid_list is no longer sufficient

so JdResponse::Success now carries txdata instead of txid_list

txid_list is computed from txdata for merkle_path validation of SetCustomMiningJob

PushSolution must be handled within isolated Downstream Client context

stratum-mining/sv2-spec#189 established that upon receiving PushSolution, JDS MUST try to reconstruct the block based on the last acknowledged DeclareMiningJob, and MAY try to reconstruct based on previous ones

we're doing a KISS approach here, where we only try to propagate a solution for the last acknowledged DeclareMiningJob

this motivated #590 / #592 as a pre-requisite, because otherwise there would be no way to unambiguously associate a PushSolution with a DeclareMiningJob

Bitcoin Core's new submitSolution capnp method

Bitcoin Core's new submitSolution capnp method is being used at bitcoin_core_sv2::unix_capnp::v32x::job_declaration_protocol::BitcoinCoreSv2JDP::handle_push_solution

when compared to the previous implementations, BitcoinCoreSv2JDP got a new dedicated submit_block_thread_ipc_client so that we don't block solution submission with regular mempool monitoring activity

@plebhash
plebhash force-pushed the 2026-07-01-handle-push-solution branch 2 times, most recently from 1cc987b to e394a70 Compare July 2, 2026 23:18
@plebhash

plebhash commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

currently, there's no official Bitcoin Core v32 release

so this PR is temporarily stubbing ITF to use a binary that's only available locally on my machine

once v32 is released I'll fix this PR and CI should be green

all integration tests pass locally though, so nothing to worry about

@Sjors

Sjors commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

we're doing a KISS approach here, where we only try to propagate a solution for the last acknowledged DeclareMiningJob

That seems fine as a first version, but given that new templates may be proposed quite frequently based on fee updates, and that ASIC's may chew on older jobs for a bit, it's a good TODO to try previous templates. Otherwise you completely rely on the JDC/TP to handle the block broadcast for those.

(update: I guess it won't fail if only fees changed, so the only concern is stale blocks)

so this PR is temporarily stubbing ITF to use a binary that's only available locally on my machine

In the bitcoin-capnp-types we build Bitcoin Core from source in CI, which is pretty quick if you enable ccache and use a release tag. For features that are merged, but not yet released, we use the master branch.

https://github.com/2140-dev/bitcoin-capnp-types/blob/master/.github/workflows/ci.yml#L17-L33

And for open pull requests the actual PR branch, e.g. 2140-dev/bitcoin-capnp-types#28

before this PR, DeclaredCustomJob carried a txid_list

I assume wtxid_list? (or at least, it's using witness ids)

Comment thread bitcoin-core-sv2/src/unix_capnp/v32x/job_declaration_protocol/handlers.rs Outdated
Comment thread bitcoin-core-sv2/src/unix_capnp/v32x/job_declaration_protocol/handlers.rs Outdated
Comment thread bitcoin-core-sv2/src/unix_capnp/v32x/job_declaration_protocol/handlers.rs Outdated
Comment thread bitcoin-core-sv2/src/unix_capnp/v32x/job_declaration_protocol/handlers.rs Outdated
Comment thread bitcoin-core-sv2/src/unix_capnp/v32x/job_declaration_protocol/handlers.rs Outdated
Comment thread bitcoin-core-sv2/src/unix_capnp/v32x/job_declaration_protocol/handlers.rs Outdated
@plebhash

plebhash commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

before this PR, DeclaredCustomJob carried a txid_list

I assume wtxid_list? (or at least, it's using witness ids)

that's used for merkle_path validation, so it's txid_list

plebhash added 7 commits July 16, 2026 14:16
…idation context

Restrict DeclareMiningJob stale-chain-tip classification to actual
prev_hash drift and simplify related context state.

- classify stale-tip strictly by prev_hash drift (v30x + v31x)
- remove redundant min_ntime from ValidationContext
- make jd-server drift checks prev_hash-only
- prune now-unused BIP34 mempool plumbing/logging
- remove unused timestamp from JdResponse::Success
- keep timestamp handling local to handler checkBlock assembly
- rename remaining timestamp references to ntime for clarity
…mempool_mirror_prev_hash

Replace the expensive createNewBlock call (which assembles a full block
template with mempool transactions) with a lightweight getTip query.
Only the prev_hash is needed for stale-tip detection, so updating just
the mirror's prev_hash via getTip is sufficient when the tip changed.

Rename force_update_mempool_mirror to force_update_mempool_mirror_prev_hash
to make the narrowed scope explicit.
…in-tip

When a declared job is already stale at arrival (coinbase built from an
old tip), checkBlock returns bad-cb-height but prev_hash may not have
drifted during validation.  Treat this rejection as stale-chain-tip so
that JDC's non-fatal stale path is exercised instead of triggering a
fallback.
BIP34 mismatch is no longer detectable on `BitcoinCoreSv2JDP`, which now solely relies on `prev_hash` to detect chain tip drifts

that is fine, because if BIP34 drifted, `prev_hash` will be inevitably different

that's why we're changing the shape of `assert_jdp_stale_chain_tip_scenario`
Cover the path where a declared job uses a coinbase built from a
previous tip that is no longer the current tip at validation time.
In this scenario checkBlock rejects with bad-cb-height while no
in-handler prev_hash drift occurs, so the handler must map the
rejection to stale-chain-tip.
Bitcoin Core submitBlock requires a fully assembled block, so JDS must retain non-coinbase transaction bodies from DeclareMiningJob validation.

As a prerequisite for upcoming PushSolution handling with v32 IPC support, JdResponse::Success now carries txdata and no longer returns redundant txid_list.

JDS derives txids directly from txdata when validating merkle-root/merkle-path consistency for SetCustomMiningJob, without storing an extra txid cache in DeclaredCustomJob.

This addresses an earlier design blindspot and does not change v30/v31 validation behavior.
@plebhash
plebhash force-pushed the 2026-07-01-handle-push-solution branch from 39c5689 to 55b1f9d Compare July 16, 2026 17:23
Reconstruct a full block at JDS when receiving PushSolution and submit it to Bitcoin Core via submitBlock.

Follow the clarified Job Declaration semantics from sv2-spec.

Reference: stratum-mining/sv2-spec#188

Reference: stratum-mining/sv2-spec#189

Design choice (KISS): JDS keeps only the latest declared custom job per downstream connection and only attempts PushSolution propagation against that entry. It does not attempt propagation for previously declared jobs.
Comment on lines +33 to +37
/// Submit a fully assembled block to Bitcoin Core (fire-and-forget).
///
/// This request is currently stubbed as a warning-only no-op by the v30.x and v31.x
/// backends.
PushSolution { block: Block },

Copy link
Copy Markdown
Member Author

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::PushSolution take a Block

#609 (comment)

@plebhash plebhash Jul 19, 2026

Copy link
Copy Markdown
Member Author

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:

2026-07-19T18:07:28.604132Z  WARN bitcoin_core_sv2::unix_capnp::v32x::job_declaration_protocol::handlers: Bitcoin Core rejected block via submitBlock reason=O
k("high-hash") debug=Ok("proof of work failed")

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:

2026-07-19T18:12:07.353926Z  WARN bitcoin_core_sv2::unix_capnp::v32x::job_declaration_protocol::handlers: Bitcoin Core rejected block via submitBlock reason=O
k("duplicate") debug=Ok("")

I haven't been able to trace the root cause of the submitBlock failures with 100% certainty, but my suspicion is that the solution is being applied to the wrong custom job, due to DeclareMiningJob vs PushSolution races, which is a direct consequence of the KISS approach of only processing PushSolution against the "latest ACKd DeclareMiningJob"

@plebhash plebhash changed the title implement handle_push_solution on jd_server_sv2 + bitcoin_core_sv2 WIP implement handle_push_solution on jd_server_sv2 + bitcoin_core_sv2 Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

need to implement handle_push_solution on jd_server_sv2 + bitcoin_core_sv2

2 participants