Add an explicit pool-fee-first coinbase output policy (1.x.x) - #106
Merged
Conversation
PRISM_COINBASE_OUTPUT_POLICY selects the coinbase output ordering rule: canonical (compatibility default) keeps the historical lexicographic (order_key, recipient_id, p2mr_program_hex) ordering, while pool-fee-first reserves one direct coinbase settlement slot for a positive pool fee, keeps the fee entitlement out of CTV fanout chunks, and emits the fee output at vout 0 with canonical ordering preserved after it and the witness commitment still last. Job construction fails instead of demoting the fee when the settlement output budget cannot hold the reserved slot. The selected policy is committed in the payout policy manifest (and thereby the on-chain audit commitment and signed audit bundle) and enforced plus reported by verification tooling, so independent verifiers and indexers can recover the intended ordering without operator-local environment configuration. Canonical artifacts keep their historical bytes; the policy applies prospectively at job construction only. Closes #86 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
apply_payout_policy now fails with PoolFeeFirstRequiresPoolFee when the coinbase output policy is pool-fee-first but no pool fee policy is configured, instead of silently building a canonical-ordered artifact whose committed manifest claims pool-fee-first. This closes the Rust CLI/library gap behind the coordinator-level check; verification inherits the same rejection, so stamped bundles no longer verify. A configured zero-bps fee remains valid: its output only appears when dust sweeps make it positive. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
test_build_audit_bundle_passes_coinbase_output_policy_to_cli_payload mocked subprocess.run, but the 1.x.x coordinator drives qbit-prism-build-audit-bundle through the Popen-based job-build worker, so the mock never engaged and the test spawned the real CLI. Reuse the fake_audit_bundle_popen harness that the sibling pool-fee payload test already uses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Closes #86. Replaces #105, retargeted from
mainto1.x.x(the active release line that mainnet deploys track).Retarget notes (vs #105)
Both commits are cherry-picked from #105 with authorship preserved, plus one adaptation commit:
crates/qbit-prism/src/lib.rs:1.x.xwraps the audit-bundle build steps ofbuild_audit_bundle_with_ctv_settlement_optionsinprofile_audit_build_phaseclosures. The resolution keeps the profiling structure and swapsselect_settlement_modeforselect_settlement_mode_with_pinned_direct(plus the pin derivation) inside the payout-derivation phase closure.test_build_audit_bundle_passes_coinbase_output_policy_to_cli_payloadmockedsubprocess.run, but the1.x.xcoordinator drivesqbit-prism-build-audit-bundlethrough thePopen-based job-build worker. The test now uses thefake_audit_bundle_popenharness that the sibling pool-fee payload test already uses.select_settlement_modecall sites on1.x.xare canonical-wrapper unit tests, and all eightpinned_first_outputwiring sites map 1:1.cargo test --locked --workspace --all-targets221 passed / 0 failed;python -m unittest discover -s tests1357 tests OK (3 docker-dependent skips).What
Adds
PRISM_COINBASE_OUTPUT_POLICY=canonical|pool-fee-first(defaultcanonical, unknown values rejected at startup and at the Rust CLI boundary). Underpool-fee-first, when the configured pool fee is positive, settlement:max_direct_coinbase_outputsslot and is counted in the output-budget walk),vout 0, even when the fee is below the direct payout floor or when amount-priority selection would otherwise exclude it,(order_key, recipient_id, p2mr_program_hex)ordering for the direct miner and CTV covenant outputs after it,SettlementModeSelection) instead of silently demoting the fee when the budget cannot hold the reserved slot.How
qbit-pool-builder:CoinbaseBuildRequest.pinned_first_output(serde-defaultNone) rotates the matching entitlement to vout 0 after the canonical sort. Allocation amounts are unchanged by construction — pro-rata split and largest-remainder tie-breaks are account-key-based, not position-based. Missing/ambiguous pins are typedBuilderErrors.qbit-prismsettlement:select_settlement_mode_with_pinned_directreserves the pinned slot ahead of amount-priority selection; the existingselect_settlement_modedelegates with no pin, so canonical behavior is bit-identical.CoinbaseOutputPolicylives onPayoutPolicyand is copied intoPayoutPolicyManifest, which is hashed into the on-chain audit commitment leaf (witness nonce) and carried in the signed audit bundle; the field is omitted forcanonicalso all existing artifacts, hashes, and golden vectors keep their bytes. Existing blocks and persisted CTV artifacts continue to verify under their original ordering; the policy applies prospectively at job construction only.verify_audit_bundlere-derives the pin from the recomputed manifest, plus an explicitcoinbase_output_policycheck (fee at vout 0, direct, absent from every fanout chunk). A canonically-built bundle stampedpool-fee-firstfails withAuditMismatch("coinbase_output_policy"); downgrading a pool-fee-first bundle to canonical breaks the audit commitment leaves.AuditVerificationReportnow reports the policy.parent_coinbase_voutvalues reflect the reordered coinbase and the manifest set still verifies (covered by tests asserting the shifted vouts).prism_payout_policy()validates the env var (SystemExiton unknown values, and onpool-fee-firstwithout an enabled pool fee) and forwardscoinbase_output_policyin theqbit-prism-build-audit-bundlepayload only when non-canonical.scripts/prism-self-check.pygains acoinbase.output_policycheck.Tests
Docs
.env.example,compose.yaml(coordinator env passthrough), and a new "Coinbase Output Ordering" subsection inPRISM.mddocumenting the canonical rule, the new policy, its audit commitment, and the prospective txid/manifest-hash/parent-vout impact.🤖 Generated with Claude Code
Note
Medium Risk
Changes coinbase txids, manifest hashes, and CTV parent vouts when switching to pool-fee-first, and alters settlement slot accounting on the critical payout path; canonical default and byte-stable serialization limit blast radius.
Overview
Introduces
PRISM_COINBASE_OUTPUT_POLICY(canonicaldefault, orpool-fee-first) so operators can fix where the pool fee sits in the coinbase instead of relying on lexicographic(order_key, recipient_id, p2mr_program_hex)ordering.With
pool-fee-firstand a positive fee, settlement reserves a direct coinbase slot for the fee (never CTV fanout), pins it at vout 0 even below the direct floor or when amount-priority would skip it, and fails job construction if the output budget cannot hold that slot. Other outputs stay canonically ordered after the fee; witness commitment remains last. The policy is recorded on payout policy manifests and audit bundles (omitted forcanonicalto preserve existing artifact bytes); verifiers enforce vout 0 and direct-only fee routing.Implementation adds
pinned_first_outputon coinbase build requests (rotate matched entitlement to vout 0 after sort),select_settlement_mode_with_pinned_directfor CTV hybrid paths, and coordinator/self-check validation (pool-fee-firstrequires an enabled pool fee). Docs, compose, and.env.exampledocument the knob.Reviewed by Cursor Bugbot for commit f699dc0. Bugbot is set up for automated code reviews on this repo. Configure here.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.