Repository navigation
feat(platform)!: charge a processing fee for a multiple choice proposal (PV15) - #5897
Conversation
…al (PV15) A multiple choice proposal opens no poll of its own: its first choice opens a contest, and each choice pays the contest's fund. Creating the proposal cost its storage and gas alone, against the 0.5 Dash fund a yes/no proposal pays. Creating one now pays a flat processing fee of 0.2 Dash on top of its gas. - `VoteResolutionFundFees` gains `multiple_choice_proposal_processing_fee`: 0.2 Dash in `VOTE_RESOLUTION_FUND_FEES_VERSION3` (`FEE_VERSION4`, protocol version 15), 0 in every earlier schedule. - The execution event of a document batch adds it to the batch's processing fee once for each `multipleChoiceProposal` of the decision proposals contract the batch creates, as a contract's registration cost is added. It is read off the transitions as state validation left them, so a create that is refused pays its gas alone. - It is not a fund: it prefunds no votes and goes to the processing fee pools. The create does not state it, and a payer whose balance does not cover it creates no proposal (`IdentityInsufficientBalanceError`). The shared execution event code is edited in place. It cannot change what protocol version 14 and earlier do: their fee schedules carry 0, and the decision proposals contract does not exist there. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
|
Waiting for bot review — thepastaclaw not yet. Wait for the missing reviews, or a writer can post |
|
📖 Book Preview built successfully. Download the preview from the workflow artifacts. Updated at 2026-11-12T19:47:41.441Z |
|
✅ Final review complete — no blockers (commit 247d0fa) · triage: normal |
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Phase 1 + Phase 2
The fee is charged in execution and affordability checks, but the identity-paid fee result returned by validation omits the new fixed surcharge. CheckTx therefore under-reports gas and block selection can admit more proposal creates than the execution gas budget supports. Two non-blocking test/documentation improvements remain; Rust and wasm checks passed, while JS and Kotlin checks were still pending in the supplied CI snapshot.
🔴 1 blocking | 🟡 1 suggestion(s) | 💬 1 nitpick(s)
1 finding(s) not shown inline (the lines are not part of this PR's diff)
💬 Nitpick: Document the new fee in the FEE_VERSION4 field comment
packages/rs-platform-version/src/version/v15.rs:886
The protocol-version note immediately above records note 40, but the inline comment on fee_version presents the listed FEE_VERSION4 changes as a complete summary and omits the new multiple-choice proposal processing fee. Add the note 40 change so this maintenance comment does not become stale.
fee_version: FEE_VERSION4, // changed: decision proposals prefund their poll with 0.5 Dash (note 12); a private document entry pays its storage and 10,000,000 credits (note 25); a multiple choice proposal pays a 0.2 Dash processing fee on create (note 40)
source: muse-spark-1.3-contributor (phase1-reviewer: general, rust-quality)
Review provenance
Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
criticalbygpt-6.1-sol(effort low) — The diff changes consensus-critical protocol-versioned fee calculation and funds movement in document-batch execution, including rs-drive-abci execution events and rs-drive state-transition accounting. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity quota lookup failed),glm-5.3-flash(not used above high effort; tier asks max) - Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
- Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review (PastaClaw). How to work through them:
- Verify each finding against the code; fix only what is real, as narrowly as the finding.
- If a finding is wrong, decline it on its thread in one or two sentences; the bot answers once and stops.
- To defer one, link a tracking issue on its thread (or reply asking the bot to file one).
- Suggestions don't block the merge, but most are worth fixing in this PR; judge each on its merits.
- Keep the PR's scope; don't grow it to satisfy hardening suggestions.
- Carried items were raised in an earlier round: read the thread first, and don't re-fix what you already fixed, declined or deferred there. Disputed and deferred items are already handled and are not listed.
In `packages/rs-drive-abci/src/execution/types/execution_event/mod.rs`:
- [BLOCKING] packages/rs-drive-abci/src/execution/types/execution_event/mod.rs:333-334: Advertised fee estimates omit the multiple-choice proposal surcharge
This change places the proposal surcharge in `additional_fixed_fee_cost`, and execution adds that amount to `processing_fee`. However, the identity-paid validation paths return the metered `FeeResult` without adding this fixed cost: v0 adds it only to `required_balance`, while v1 adds it only to `required_gas`. `check_tx` converts the returned `FeeResult` directly into `gas_wanted`, and processing exposes the same estimate to callers. At protocol version 15, a valid multiple-choice proposal therefore advertises only gas and storage even though execution deducts an additional 20,000,000,000 credits. This can let multiple transactions fit the advertised Tenderdash gas budget while their actual execution fees exceed it. Fold the fixed cost into the returned processing fee exactly once in the identity-paid v0/v1 estimate paths, after applying the user fee increase, and adjust affordability calculations so the amount is not double-counted; add a regression covering CheckTx or the returned estimate.
In `packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/multiple_choice_proposal_tests.rs`:
- [SUGGESTION] packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/multiple_choice_proposal_tests.rs:2461-2488: Cover fee exemption after a late state-validation failure
This refused proposal omits both the description document reference and URL, so it is rejected during structural validation before the batch action reaches state validation. The test therefore does not exercise the new invariant that a create replaced by `BumpIdentityDataContractNonce` is excluded from `multiple_choice_proposals_created()`. Add a structurally valid proposal referencing a nonexistent description document, then assert the state error, absence of the stored proposal, and gas-only fees. That directly protects the late-failure path used by the counting logic.
In `packages/rs-platform-version/src/version/v15.rs`:
- [NITPICK] packages/rs-platform-version/src/version/v15.rs:886: Document the new fee in the FEE_VERSION4 field comment
The protocol-version note immediately above records note 40, but the inline comment on `fee_version` presents the listed FEE_VERSION4 changes as a complete summary and omits the new multiple-choice proposal processing fee. Add the note 40 change so this maintenance comment does not become stale.
|
Your move: thepastaclaw requested changes on this head; dismiss the review or push a fix; thepastaclaw left review threads unresolved; resolve them. |
…alidation The fee test refused a proposal described nowhere, which a property constraint rejects before the create becomes an action of the batch. The fee is counted off the batch's transitions as state validation left them, so the case that matters is a create replaced there by a nonce bump: a proposal naming a description document that does not exist. It is refused with 40120, not stored, and pays its gas alone. Also names the fee in the `fee_version` comment of protocol version 15 and in `FEE_VERSION4`'s own, which listed what the schedule changes. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
Your move: thepastaclaw left review threads unresolved; resolve them. |
|
Waiting for bot review — thepastaclaw not yet. Wait for the missing reviews, or a writer can post |
|
Waiting for bot review — thepastaclaw not yet. Wait for the missing reviews, or a writer can post |
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 1 + Phase 2
At the exact review head, the PV15 fee is correctly scoped, counted from post-validation batch actions, and routed through the existing fixed-fee affordability and execution paths; historical schedules and legacy fee serialization remain unchanged. No blocking correctness, security, or consensus issues remain. One non-blocking coverage gap remains: the new counter is only exercised with one matching create, while the reported Rust and wasm CI checks passed and broader suites were skipped.
🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)
Approved: nothing here blocks the merge. The non-blocking findings are optional: fix them forward, in this PR or a follow-up, without waiting for another review round.
Follow-up candidates (optional) (1)
New non-blocking observations on code unchanged since the last full review. No inline threads are opened for them and later reviews do not repeat them: fix forward or track separately if they are worth it.
🟡 Suggestion: Exercise proposal counting with multiple actions in one batch
packages/rs-drive/src/state_transition_action/batch/mod.rs:443-460
The new multiple_choice_proposals_created() method is responsible for turning the number of matching creates into the flat-fee multiplier, but the regression test exercises only one accepted matching proposal. A regression that changed .count() into a presence check would therefore still pass. Although deployed system limits currently cap document batches at one transition, this method explicitly models multiplicity and future protocol versions or direct callers can exercise more than one; add a focused rs-drive unit test constructing two matching CreateActions alongside a nonce bump, an update, and a same-named document under another contract, then assert that only the two matching creates are counted.
source: gpt-6.1-sol (phase2-reviewer: rust-quality)
Review provenance
Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: tenderdash-consensus-security); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 13: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 14: gpt-6.1-sol (agent: phase2-reviewer, role: tenderdash-consensus-security); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
normalbygpt-6.1-sol(effort low) — The diff changes consensus fee charging in ExecutionEvent, but its production logic is small and straightforward rather than large or intricate, so it does not meet both requirements for critical. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity quota lookup failed),glm-5.3-flash(not used above high effort; tier asks max) - Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
- Code unchanged since the last final review (
fb99541f): 1 new suggestion(s) listed as follow-up candidates, not posted inline - Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— platform-versioning (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— tenderdash-consensus-security (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— general (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— platform-versioning (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— tenderdash-consensus-security (completed, effort high); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.
No unresolved findings remain from the prior review on this head.
|
Bots are done — your move: post |
#5897's note took 40; the pattern bound's note is now 41. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y-v51 Conflict in packages/rs-platform-version/src/version/v15.rs: both sides added a note 40 to the PLATFORM_V15 doc comment. The base's note (a multiple choice proposal pays a processing fee, #5897) keeps 40; the low-S note becomes 41, with its reference on the state_transition_method_versions line. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Basic explanation
What this does: Creating a multiple choice proposal pays a flat processing fee of 0.2 Dash, on top of its gas.
Value: A yes/no proposal pays a 0.5 Dash fund when it is created. A multiple choice proposal paid its storage and gas alone, because it opens no poll: the first choice entered for it does, and each choice pays the contest's fund (0.1 Dash). Asking a multiple choice question was therefore nearly free. With the fee, a question with two answers costs 0.4 Dash and its gas.
Risks: It is a consensus fee change at protocol version 15 (unreleased). The create does not state the fee, as a contract create does not state its registration cost, so a client that shows a cost before signing has to add it. No SDK is changed here. Like the registration cost, the fee is required of the payer's balance and charged at execution, but it is not part of the fee estimate CheckTx advertises as gas, so it is not held against a block's
max_gas.Issue being fixed or feature implemented
multipleChoiceProposal(decision proposals contract, protocol version 15) is undeletable and cost only its storage and gas to create. Nothing made a proposer pay for putting a question to the masternodes until someone entered a choice.What was done?
VoteResolutionFundFeesgainsmultiple_choice_proposal_processing_fee: 0.2 Dash inVOTE_RESOLUTION_FUND_FEES_VERSION3(FEE_VERSION4, protocol version 15) and 0 in every earlier schedule.dpp::decision_proposalgainsis_multiple_choice_proposalandmultiple_choice_proposal_processing_fee.additional_fixed_fee_cost), once for eachmultipleChoiceProposalof the decision proposals contract the batch creates, the way a contract's registration cost is added.BatchTransitionAction::multiple_choice_proposals_createdcounts them off the transitions as state validation left them, so a create that was refused and replaced by a nonce bump owes nothing.Also:
docs/protocol/decision-proposals.mdgets a "What it costs" section, the contract README andbook/src/fees/overview.mdname the fee, and protocol version 15 note 40 records it.Choices a reviewer may want to question
proposalkeeps its 0.5 Dash fund, which already ends as processing fees.VoteResolutionFundFees, beside the yes/no proposal's fund, although it is not a fund. It is the schedule group that already holds what decision proposals cost.How Has This Been Tested?
cargo test -p drive-abci --lib -- multiple_choice_proposal: 14 pass. New:should_charge_a_flat_processing_fee_for_a_multiple_choice_proposal, which creates a proposal and checks the processing fee is the gas plus 0.2 Dash and that the balance fell by exactly the fees; enters a choice and checks it pays the contest's fund and no proposal fee; has a create refused and checks it pays gas alone; and has an identity holding 0.1 Dash try to create one, which is refused unpaid with its balance untouched. It also checks the protocol version 14 schedule carries 0.cargo test -p drive-abci --lib -- nullifier_batch_guard::should_preserve_single_entry should_preserve_activation_state multiple_choice_proposal decision_proposal dynamic_document_reference stored_non_document_reference create_genesis_state perform_events_on_first_block should_pin commit_reveal contest execution_event fee: 642 pass, no root or fee pin changes.cargo test -p dpp --lib -- decision_proposal data_contract consensus fee(3544 pass),cargo test -p drive --lib -- state_transition_action::batch(669 pass),cargo test -p platform-version.cargo clippyon dpp, drive, drive-abci and platform-version with--tests: clean.In-place changes to shipped generations
ExecutionEvent::create_from_state_transition_action, the document batch arm (shared by every protocol version)multiple_choice_proposals_created() * multiple_choice_proposal_processing_fee. The fee is 0 in every fee schedule beforeFEE_VERSION4, and the decision proposals contract is registered from protocol version 15, so no batch at protocol version 14 or earlier creates such a document. The added cost isNonethere, as before.VoteResolutionFundFeesgains a fieldVOTE_RESOLUTION_FUND_FEES_VERSION1, which version 2 inherits, and in the conversion from the pre-1.4 stored form. Only version 3 (FEE_VERSION4, protocol version 15, unreleased) sets an amount. The frozen pre-1.4 struct is untouched.Breaking Changes
From protocol version 15 (unreleased), creating a
multipleChoiceProposalon the decision proposals contract costs 0.2 Dash more, charged as processing fee. Protocol version 14 and earlier are unchanged.Checklist:
structure.rs, regeneratedgrovedb-structure.json, and checked the structure viewer link posted on this pull requestFor repository code-owners and collaborators only
🤖 Generated with Claude Code
PR Hygiene ·
247d0fa/self-reviewedWhen every merge requirement is met, the
PR Hygienecheck passes. Reviewer limits do not block merging; other required GitHub checks and protections still apply.Summary by CodeRabbit